admin_program.py 111 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865
  1. import abc
  2. import tkinter as tk
  3. import tkinter.ttk as ttk
  4. from tkinter.filedialog import askdirectory, askopenfilename, asksaveasfilename
  5. from math import ceil
  6. import matplotlib.pyplot
  7. from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
  8. from matplotlib.axes import Axes
  9. import numpy as np
  10. from matplotlib.colorbar import Colorbar
  11. from matplotlib.figure import Figure
  12. from tool.color import random_color
  13. from tool.type_ import *
  14. from tool.tk import make_font, set_tk_disable_from_list
  15. from tool.login import create_uid
  16. from conf import Config
  17. from . import admin
  18. from . import admin_event as tk_event
  19. from sql import DBBit
  20. from sql.user import find_user_by_name
  21. from core.garbage import GarbageType
  22. class AdminProgram(metaclass=abc.ABCMeta):
  23. def __init__(self, station: "admin.AdminStation", win: Union[tk.Frame, tk.Toplevel, tk.Tk], color: str, title: str):
  24. self.station = station
  25. self.win = win
  26. self.color = color
  27. self.frame = tk.Frame(self.win)
  28. self.frame['bg'] = color
  29. self.program_title = title
  30. @abc.abstractmethod
  31. def set_disable(self):
  32. ...
  33. @abc.abstractmethod
  34. def reset_disable(self):
  35. ...
  36. @abc.abstractmethod
  37. def conf_gui(self, n: int = 1):
  38. ...
  39. def to_program(self):
  40. pass
  41. def get_title(self) -> str:
  42. return self.program_title
  43. def get_program_frame(self) -> Tuple[str, tk.Frame]:
  44. return self.program_title, self.frame
  45. class WelcomeProgram(AdminProgram):
  46. def __init__(self, station, win, color):
  47. super().__init__(station, win, color, "欢迎页")
  48. self.title = tk.Label(self.frame)
  49. self.info = tk.Label(self.frame)
  50. self.__conf_font()
  51. def __conf_font(self, n: int = 1):
  52. self.title_font_size = int(25 * n)
  53. self.info_font_size = int(14 * n)
  54. def conf_gui(self, n: int = 1):
  55. self.__conf_font(n)
  56. title_font = make_font(size=self.title_font_size, weight="bold")
  57. info_font = make_font(size=self.info_font_size)
  58. self.title['font'] = title_font
  59. self.title['bg'] = self.color
  60. self.title['text'] = '欢迎使用 HGSSystem 管理员系统\n[帮助]'
  61. self.info['bg'] = self.color
  62. self.info['font'] = info_font
  63. self.info['anchor'] = 'nw'
  64. self.info['justify'] = 'left'
  65. self.info['text'] = (f'''
  66. HGSSystem 管理者界面:
  67. 1) 点击菜单按钮进入子菜单或程序
  68. 2) 创建 菜单包含创建类的程序
  69. 3) 删除 菜单包含删除类的程序
  70. 4) 搜索 菜单包含数据分析类的程序
  71. 5) 更新 菜单包含数据更新类的程序
  72. 6) 当离开操作系统时请退出登录以确保安全
  73. 7) 只能使用具有管理员权限的账号登陆系统
  74. 8) 只有admin用户可以完成危险操作(例如删除所有垃圾袋数据)
  75. 程序的运行:
  76. 1) 在菜单中选中程序后,根据程序界面提示完成操作
  77. 2) 操作过程通常会显示进度条,除非任务执行迅速
  78. 3) 结果通常会被反馈, 且不会自动消失
  79. 系统登录:
  80. 1) 仅Manager用户可以登录
  81. 关于彩蛋: 存在
  82. '''.strip())
  83. self.title.place(relx=0.1, rely=0.0, relwidth=0.8, relheight=0.2)
  84. self.info.place(relx=0.05, rely=0.21, relwidth=0.90, relheight=0.75)
  85. def set_disable(self):
  86. pass
  87. def reset_disable(self):
  88. pass
  89. class AboutProgram(AdminProgram):
  90. def __init__(self, station, win, color):
  91. super().__init__(station, win, color, "关于")
  92. self.title = tk.Label(self.frame)
  93. self.info = tk.Label(self.frame)
  94. self.__conf_font()
  95. def __conf_font(self, n: int = 1):
  96. self.title_font_size = int(25 * n)
  97. self.info_font_size = int(14 * n)
  98. def conf_gui(self, n: int = 1):
  99. self.__conf_font(n)
  100. title_font = make_font(size=self.title_font_size, weight="bold")
  101. info_font = make_font(size=self.info_font_size)
  102. self.title['font'] = title_font
  103. self.title['bg'] = self.color
  104. self.title['text'] = '关于 HGSSystem 管理员系统'
  105. self.info['bg'] = self.color
  106. self.info['font'] = info_font
  107. self.info['anchor'] = 'nw'
  108. self.info['justify'] = 'left'
  109. self.info['text'] = Config.about_info
  110. self.title.place(relx=0.1, rely=0.0, relwidth=0.8, relheight=0.2)
  111. self.info.place(relx=0.05, rely=0.21, relwidth=0.90, relheight=0.75)
  112. def set_disable(self):
  113. pass
  114. def reset_disable(self):
  115. pass
  116. class CreateUserProgramBase(AdminProgram):
  117. def __init__(self, station, win, color, title: str):
  118. super().__init__(station, win, color, title)
  119. self.enter_frame = tk.Frame(self.frame)
  120. self.title: List[tk.Label] = [tk.Label(self.enter_frame) for _ in range(3)]
  121. self.enter: List[tk.Entry] = [tk.Entry(self.enter_frame) for _ in range(3)]
  122. self.var: List[tk.Variable] = [tk.StringVar() for _ in range(3)]
  123. self.btn: List[tk.Button] = [tk.Button(self.frame) for _ in range(2)]
  124. self._conf("#FA8072", False) # 默认颜色
  125. self.__conf_font()
  126. def _conf(self, bg_color, is_manager: bool):
  127. self.bg_color = bg_color
  128. self.is_manager = is_manager
  129. return self
  130. def __conf_font(self, n: int = 1):
  131. self.title_font_size = int(16 * n)
  132. self.btn_font_size = int(14 * n)
  133. def conf_gui(self, n: int = 1):
  134. self.__conf_font(n)
  135. title_font = make_font(size=self.title_font_size)
  136. btn_font = make_font(size=self.btn_font_size)
  137. self.enter_frame['bg'] = self.bg_color
  138. self.enter_frame['bd'] = 5
  139. self.enter_frame['relief'] = "ridge"
  140. self.enter_frame.place(relx=0.2, rely=0.3, relwidth=0.6, relheight=0.30)
  141. height = 0.1
  142. for lb, text, enter, var in zip(self.title, ["用户名:", "用户密码:", "手机号:"], self.enter, self.var):
  143. lb['font'] = title_font
  144. lb['text'] = text
  145. lb['bg'] = self.bg_color
  146. lb['anchor'] = 'e'
  147. enter['font'] = title_font
  148. enter['textvariable'] = var
  149. lb.place(relx=0.01, rely=height, relwidth=0.30, relheight=0.17)
  150. enter.place(relx=0.35, rely=height, relwidth=0.60, relheight=0.17)
  151. height += 0.30
  152. for btn, text, x, func in zip(self.btn,
  153. ["创建用户", "获取用户ID"],
  154. [0.2, 0.6],
  155. [lambda: self.create_by_name(), lambda: self.get_uid()]):
  156. btn['font'] = btn_font
  157. btn['text'] = text
  158. btn['bg'] = Config.tk_btn_bg
  159. btn['command'] = func
  160. btn.place(relx=x, rely=0.7, relwidth=0.2, relheight=0.08)
  161. def __get_info(self) -> Optional[Tuple[uname_t, passwd_t, str]]:
  162. name: uname_t = self.var[0].get()
  163. passwd: passwd_t = self.var[1].get()
  164. phone: str = self.var[2].get()
  165. if len(name) == 0 or len(passwd) == 0 or len(phone) != 11:
  166. self.station.show_msg("用户创建失败", "请再次尝试, 输入用户名, 用户密码和11位手机号")
  167. return None
  168. return name, passwd, phone
  169. def create_by_name(self):
  170. res = self.__get_info()
  171. if res is None:
  172. return
  173. name, passwd, phone = res
  174. event = tk_event.CreateUserEvent(self.station).start(name, passwd, phone, self.is_manager)
  175. self.station.push_event(event)
  176. def get_uid(self):
  177. res = self.__get_info()
  178. if res is None:
  179. return
  180. name, passwd, phone = res
  181. uid = create_uid(name, passwd, phone)
  182. self.station.show_msg("获取用户ID", f"用户名: {name}\n用户ID: {uid}")
  183. def set_disable(self):
  184. set_tk_disable_from_list(self.btn)
  185. set_tk_disable_from_list(self.enter)
  186. def reset_disable(self):
  187. set_tk_disable_from_list(self.btn, flat='normal')
  188. set_tk_disable_from_list(self.enter, flat='normal')
  189. class CreateNormalUserProgram(CreateUserProgramBase):
  190. def __init__(self, station, win, color):
  191. super(CreateNormalUserProgram, self).__init__(station, win, color, "创建普通用户")
  192. class CreateManagerUserProgram(CreateUserProgramBase):
  193. def __init__(self, station, win, color):
  194. super(CreateManagerUserProgram, self).__init__(station, win, color, "创建管理员")
  195. self._conf("#4b5cc4", True)
  196. class CreateAutoNormalUserProgram(AdminProgram):
  197. def __init__(self, station, win, color):
  198. super().__init__(station, win, color, "创建自动用户")
  199. self.enter_frame = tk.Frame(self.frame)
  200. self.title: tk.Label = tk.Label(self.enter_frame)
  201. self.enter: tk.Entry = tk.Entry(self.enter_frame)
  202. self.var: tk.Variable = tk.StringVar()
  203. self.btn: tk.Button = tk.Button(self.frame) # create(生成用户) try(计算uid)
  204. self.__conf_font()
  205. def __conf_font(self, n: int = 1):
  206. self.title_font_size = int(16 * n)
  207. self.btn_font_size = int(14 * n)
  208. def conf_gui(self, n: int = 1):
  209. self.__conf_font(n)
  210. title_font = make_font(size=self.title_font_size)
  211. btn_font = make_font(size=self.btn_font_size)
  212. self.enter_frame['bg'] = "#bce672"
  213. self.enter_frame['bd'] = 5
  214. self.enter_frame['relief'] = "ridge"
  215. self.enter_frame.place(relx=0.2, rely=0.3, relwidth=0.6, relheight=0.12)
  216. self.title['font'] = title_font
  217. self.title['text'] = "手机号:"
  218. self.title['bg'] = "#bce672"
  219. self.title['anchor'] = 'e'
  220. self.enter['font'] = title_font
  221. self.enter['textvariable'] = self.var
  222. self.title.place(relx=0.02, rely=0.25, relwidth=0.25, relheight=0.50)
  223. self.enter.place(relx=0.30, rely=0.25, relwidth=0.60, relheight=0.50)
  224. self.btn['font'] = btn_font
  225. self.btn['text'] = "创建用户"
  226. self.btn['bg'] = Config.tk_btn_bg
  227. self.btn['command'] = lambda: self.create_user()
  228. self.btn.place(relx=0.4, rely=0.7, relwidth=0.2, relheight=0.08)
  229. def create_user(self):
  230. phone = self.var.get()
  231. if len(phone) != 11:
  232. self.station.show_msg("UserInfoError", "Please, enter Phone(11)")
  233. event = tk_event.CreateUserEvent(self.station).start(None, None, phone, False)
  234. self.station.push_event(event)
  235. def set_disable(self):
  236. self.btn['state'] = 'disable'
  237. self.enter['state'] = 'disable'
  238. def reset_disable(self):
  239. self.btn['state'] = 'normal'
  240. self.enter['state'] = 'normal'
  241. class CreateGarbageProgram(AdminProgram):
  242. def __init__(self, station, win, color):
  243. super().__init__(station, win, color, "创建垃圾袋")
  244. self.enter_frame = tk.Frame(self.frame)
  245. self.title: List[tk.Label] = [tk.Label(self.enter_frame), tk.Label(self.enter_frame)]
  246. self.enter: List[tk.Entry] = [tk.Entry(self.enter_frame), tk.Entry(self.enter_frame)]
  247. self.var: List[tk.Variable] = [tk.StringVar(), tk.StringVar()]
  248. self.create_btn: tk.Button = tk.Button(self.frame)
  249. self.file_btn: tk.Button = tk.Button(self.frame)
  250. self.__conf_font()
  251. def __conf_font(self, n: int = 1):
  252. self.title_font_size = int(16 * n)
  253. self.btn_font_size = int(14 * n)
  254. def conf_gui(self, n: int = 1):
  255. self.__conf_font(n)
  256. title_font = make_font(size=self.title_font_size)
  257. btn_font = make_font(size=self.btn_font_size)
  258. self.enter_frame['bg'] = "#b69968"
  259. self.enter_frame['bd'] = 5
  260. self.enter_frame['relief'] = "ridge"
  261. self.enter_frame.place(relx=0.2, rely=0.3, relwidth=0.6, relheight=0.17)
  262. height = 0.1
  263. for lb, text, enter, var in zip(self.title, ["数量:", "导出位置:"], self.enter, self.var):
  264. lb['font'] = title_font
  265. lb['text'] = text
  266. lb['bg'] = "#b69968"
  267. lb['anchor'] = 'e'
  268. enter['font'] = title_font
  269. enter['textvariable'] = var
  270. lb.place(relx=0.01, rely=height, relwidth=0.30, relheight=0.35)
  271. enter.place(relx=0.35, rely=height, relwidth=0.60, relheight=0.35)
  272. height += 0.43
  273. for btn, text, x, func in zip([self.create_btn, self.file_btn],
  274. ["创建垃圾袋", "选择目录"],
  275. [0.2, 0.6],
  276. [lambda: self.create_garbage(), lambda: self.choose_file()]):
  277. btn['font'] = btn_font
  278. btn['text'] = text
  279. btn['bg'] = Config.tk_btn_bg
  280. btn['command'] = func
  281. btn.place(relx=x, rely=0.7, relwidth=0.2, relheight=0.08)
  282. def choose_file(self):
  283. path = askdirectory(title='选择二维码导出位置')
  284. self.var[1].set(path)
  285. def create_garbage(self):
  286. try:
  287. count = int(self.var[0].get())
  288. if count <= 0:
  289. raise ValueError
  290. except (ValueError, TypeError):
  291. self.station.show_msg("类型错误", "数量必须为大于0的数字")
  292. else:
  293. path = self.var[1].get()
  294. if len(path) == 0:
  295. path = None
  296. event = tk_event.CreateGarbageEvent(self.station).start(path, count)
  297. self.station.push_event(event)
  298. def set_disable(self):
  299. self.create_btn['state'] = 'disable'
  300. self.file_btn['state'] = 'disable'
  301. set_tk_disable_from_list(self.enter)
  302. def reset_disable(self):
  303. self.create_btn['state'] = 'normal'
  304. self.file_btn['state'] = 'normal'
  305. set_tk_disable_from_list(self.enter, flat='normal')
  306. class ExportProgramBase(AdminProgram):
  307. def __init__(self, station, win, color, title: str):
  308. super().__init__(station, win, color, title)
  309. self.gid_frame = tk.Frame(self.frame)
  310. self.gid_title: List[tk.Label] = [tk.Label(self.gid_frame), tk.Label(self.gid_frame)]
  311. self.gid_enter: List[tk.Entry] = [tk.Entry(self.gid_frame), tk.Entry(self.gid_frame)]
  312. self.gid_var: List[tk.Variable] = [tk.StringVar(), tk.StringVar()]
  313. self.where_frame = tk.Frame(self.frame)
  314. self.where_title: List[tk.Label] = [tk.Label(self.where_frame), tk.Label(self.where_frame)]
  315. self.where_enter: List[tk.Entry] = [tk.Entry(self.where_frame), tk.Entry(self.where_frame)]
  316. self.where_var: List[tk.Variable] = [tk.StringVar(), tk.StringVar()]
  317. self.create_btn: List[tk.Button] = [tk.Button(self.frame), tk.Button(self.frame)]
  318. self.file_btn: List[tk.Button] = [tk.Button(self.frame), tk.Button(self.frame)]
  319. self._conf("", [], [], [])
  320. self.__conf_font()
  321. def _conf(self, bg_color: str, title_id, title_where, title_command):
  322. self.bg_color = bg_color
  323. self.title_id = title_id
  324. self.title_where = title_where
  325. self.title_command = title_command
  326. def __conf_font(self, n: int = 1):
  327. self.title_font_size = int(16 * n)
  328. self.btn_font_size = int(14 * n)
  329. def conf_gui(self, n: int = 1):
  330. self.__conf_font(n)
  331. title_font = make_font(size=self.title_font_size)
  332. btn_font = make_font(size=self.btn_font_size)
  333. self.where_frame['bg'] = self.bg_color
  334. self.where_frame['bd'] = 5
  335. self.where_frame['relief'] = "ridge"
  336. self.where_frame.place(relx=0.2, rely=0.2, relwidth=0.6, relheight=0.17)
  337. self.gid_frame['bg'] = self.bg_color
  338. self.gid_frame['bd'] = 5
  339. self.gid_frame['relief'] = "ridge"
  340. self.gid_frame.place(relx=0.2, rely=0.6, relwidth=0.6, relheight=0.17)
  341. height = 0.1
  342. for lb, text, enter, var, lb_w, text_w, enter_w, var_w in zip(
  343. self.gid_title, self.title_id, self.gid_enter, self.gid_var,
  344. self.where_title, self.title_where, self.where_enter, self.where_var):
  345. lb['font'] = title_font
  346. lb['text'] = text
  347. lb['bg'] = self.bg_color
  348. lb['anchor'] = 'e'
  349. lb_w['font'] = title_font
  350. lb_w['text'] = text_w
  351. lb_w['bg'] = self.bg_color
  352. lb_w['anchor'] = 'e'
  353. enter['textvariable'] = var
  354. enter['font'] = title_font
  355. enter_w['textvariable'] = var_w
  356. enter_w['font'] = title_font
  357. lb.place(relx=0.01, rely=height, relwidth=0.30, relheight=0.35)
  358. enter.place(relx=0.35, rely=height, relwidth=0.60, relheight=0.35)
  359. lb_w.place(relx=0.01, rely=height, relwidth=0.30, relheight=0.35)
  360. enter_w.place(relx=0.35, rely=height, relwidth=0.60, relheight=0.35)
  361. height += 0.43
  362. for btn, text in zip(self.create_btn + self.file_btn, self.title_command):
  363. btn['font'] = btn_font
  364. btn['text'] = text
  365. btn['bg'] = Config.tk_btn_bg
  366. self.create_btn[1]['command'] = self.export_where
  367. self.create_btn[0]['command'] = self.export_id
  368. self.create_btn[1].place(relx=0.2, rely=0.39, relwidth=0.25, relheight=0.08)
  369. self.create_btn[0].place(relx=0.2, rely=0.79, relwidth=0.25, relheight=0.08)
  370. self.file_btn[1]['command'] = self.choose_file_where
  371. self.file_btn[0]['command'] = self.choose_file_id
  372. self.file_btn[1].place(relx=0.6, rely=0.39, relwidth=0.2, relheight=0.08)
  373. self.file_btn[0].place(relx=0.6, rely=0.79, relwidth=0.2, relheight=0.08)
  374. def choose_file_id(self):
  375. path = askdirectory(title='选择二维码导出位置')
  376. self.gid_var[1].set(path)
  377. def choose_file_where(self):
  378. path = askdirectory(title='选择二维码导出位置')
  379. self.where_var[1].set(path)
  380. def export_id(self):
  381. ...
  382. def export_where(self):
  383. ...
  384. def set_disable(self):
  385. set_tk_disable_from_list(self.gid_enter)
  386. set_tk_disable_from_list(self.create_btn)
  387. set_tk_disable_from_list(self.file_btn)
  388. def reset_disable(self):
  389. set_tk_disable_from_list(self.gid_enter, flat='normal')
  390. set_tk_disable_from_list(self.create_btn, flat='normal')
  391. set_tk_disable_from_list(self.file_btn, flat='normal')
  392. class ExportGarbageProgram(ExportProgramBase):
  393. def __init__(self, station, win, color):
  394. super().__init__(station, win, color, "导出垃圾袋二维码")
  395. self._conf("#afdfe4", ["垃圾袋ID:", "导出位置:"], ["条件:", "导出位置:"],
  396. ["根据垃圾袋ID导出", "根据条件导出", "选择目录", "选择目录"])
  397. def export_id(self):
  398. gid = self.gid_var[0].get()
  399. path = self.gid_var[1].get()
  400. event = tk_event.ExportGarbageByIDEvent(self.station).start(path, gid)
  401. self.station.push_event(event)
  402. def export_where(self):
  403. where = self.where_var[0].get()
  404. path = self.where_var[1].get()
  405. event = tk_event.ExportGarbageAdvancedEvent(self.station).start(path, where)
  406. self.station.push_event(event)
  407. class ExportUserProgram(ExportProgramBase):
  408. def __init__(self, station, win, color):
  409. super().__init__(station, win, color, "导出用户二维码")
  410. self._conf("#f69c9f", ["用户ID:", "导出位置:"], ["条件:", "导出位置:"],
  411. ["根据用户ID导出", "根据条件导出", "选择目录", "选择目录"])
  412. def export_id(self):
  413. uid = self.gid_var[0].get()
  414. path = self.gid_var[1].get()
  415. event = tk_event.ExportUserByIDEvent(self.station).start(path, uid)
  416. self.station.push_event(event)
  417. def export_where(self):
  418. where = self.where_var[0].get()
  419. path = self.where_var[1].get()
  420. event = tk_event.ExportUserAdvancedEvent(self.station).start(path, where)
  421. self.station.push_event(event)
  422. class CreateUserFromCSVProgram(AdminProgram):
  423. def __init__(self, station, win, color):
  424. super().__init__(station, win, color, "从CSV导入用户")
  425. self.auto_frame = tk.Frame(self.frame)
  426. self.auto_title: tk.Label = tk.Label(self.auto_frame)
  427. self.auto_enter: tk.Entry = tk.Entry(self.auto_frame)
  428. self.auto_var: tk.Variable = tk.StringVar()
  429. self.enter_frame = tk.Frame(self.frame)
  430. self.path_title: tk.Label = tk.Label(self.enter_frame)
  431. self.path_enter: tk.Entry = tk.Entry(self.enter_frame)
  432. self.path_var: tk.Variable = tk.StringVar()
  433. self.create_btn: List[tk.Button] = [tk.Button(self.frame), tk.Button(self.frame)]
  434. self.file_btn: List[tk.Button] = [tk.Button(self.frame), tk.Button(self.frame)]
  435. self.__conf_font()
  436. def __conf_font(self, n: int = 1):
  437. self.title_font_size = int(16 * n)
  438. self.btn_font_size = int(14 * n)
  439. def conf_gui(self, n: int = 1):
  440. self.__conf_font(n)
  441. title_font = make_font(size=self.title_font_size)
  442. btn_font = make_font(size=self.btn_font_size)
  443. self.enter_frame['bg'] = "#EEE8AA"
  444. self.enter_frame['bd'] = 5
  445. self.enter_frame['relief'] = "ridge"
  446. self.enter_frame.place(relx=0.2, rely=0.2, relwidth=0.6, relheight=0.12)
  447. self.auto_frame['bg'] = "#EEE8AA"
  448. self.auto_frame['bd'] = 5
  449. self.auto_frame['relief'] = "ridge"
  450. self.auto_frame.place(relx=0.2, rely=0.6, relwidth=0.6, relheight=0.12)
  451. self.auto_title['font'] = title_font
  452. self.auto_title['text'] = "CSV文件:"
  453. self.auto_title['bg'] = "#EEE8AA"
  454. self.auto_title['anchor'] = 'e'
  455. self.path_title['font'] = title_font
  456. self.path_title['text'] = "CSV文件:"
  457. self.path_title['bg'] = "#EEE8AA"
  458. self.path_title['anchor'] = 'e'
  459. self.auto_enter['textvariable'] = self.auto_var
  460. self.auto_enter['font'] = title_font
  461. self.path_enter['textvariable'] = self.path_var
  462. self.path_enter['font'] = title_font
  463. self.auto_title.place(relx=0.01, rely=0.25, relwidth=0.30, relheight=0.50)
  464. self.auto_enter.place(relx=0.35, rely=0.25, relwidth=0.60, relheight=0.50)
  465. self.path_title.place(relx=0.01, rely=0.25, relwidth=0.30, relheight=0.50)
  466. self.path_enter.place(relx=0.35, rely=0.25, relwidth=0.60, relheight=0.50)
  467. for btn, text in zip(self.create_btn + self.file_btn,
  468. ["创建用户", "创建自动用户", "选择CSV", "选择CSV"]):
  469. btn['font'] = btn_font
  470. btn['text'] = text
  471. btn['bg'] = Config.tk_btn_bg
  472. self.create_btn[0]['command'] = self.create
  473. self.create_btn[1]['command'] = self.create_auto
  474. self.create_btn[0].place(relx=0.2, rely=0.34, relwidth=0.25, relheight=0.08)
  475. self.create_btn[1].place(relx=0.2, rely=0.74, relwidth=0.25, relheight=0.08)
  476. self.file_btn[0]['command'] = self.choose_file
  477. self.file_btn[1]['command'] = self.choose_file_auto
  478. self.file_btn[0].place(relx=0.6, rely=0.34, relwidth=0.2, relheight=0.08)
  479. self.file_btn[1].place(relx=0.6, rely=0.74, relwidth=0.2, relheight=0.08)
  480. def choose_file_auto(self):
  481. path = askopenfilename(title='选择CSV文件', filetypes=[("CSV", ".csv")])
  482. self.auto_var.set(path)
  483. def choose_file(self):
  484. path = askopenfilename(title='选择CSV文件', filetypes=[("CSV", ".csv")])
  485. self.path_var.set(path)
  486. def create_auto(self):
  487. path = self.auto_var.get()
  488. event = tk_event.CreateAutoUserFromCSVEvent(self.station).start(path)
  489. self.station.push_event(event)
  490. def create(self):
  491. path = self.path_var.get()
  492. event = tk_event.CreateUserFromCSVEvent(self.station).start(path)
  493. self.station.push_event(event)
  494. def set_disable(self):
  495. self.auto_enter['state'] = 'disable'
  496. self.path_enter['state'] = 'disable'
  497. set_tk_disable_from_list(self.create_btn)
  498. set_tk_disable_from_list(self.file_btn)
  499. def reset_disable(self):
  500. self.auto_enter['state'] = 'normal'
  501. self.path_enter['state'] = 'normal'
  502. set_tk_disable_from_list(self.create_btn, flat='normal')
  503. set_tk_disable_from_list(self.file_btn, flat='normal')
  504. class DeleteUserProgram(AdminProgram):
  505. def __init__(self, station, win, color):
  506. super().__init__(station, win, color, "删除用户")
  507. self.uid_frame = tk.Frame(self.frame)
  508. self.uid_title: tk.Label = tk.Label(self.uid_frame)
  509. self.uid_enter: tk.Entry = tk.Entry(self.uid_frame)
  510. self.uid_var: tk.Variable = tk.StringVar()
  511. self.name_frame = tk.Frame(self.frame)
  512. self.name_title: List[tk.Label] = [tk.Label(self.name_frame) for _ in range(2)]
  513. self.name_enter: List[tk.Entry] = [tk.Entry(self.name_frame) for _ in range(2)]
  514. self.name_var: List[tk.Variable] = [tk.StringVar() for _ in range(2)]
  515. self.btn: List[tk.Button] = [tk.Button(self.frame) for _ in range(2)] # uid-del, name-passwd-del
  516. self.__conf_font()
  517. def __conf_font(self, n: int = 1):
  518. self.title_font_size = int(16 * n)
  519. self.btn_font_size = int(14 * n)
  520. def conf_gui(self, n: int = 1):
  521. self.__conf_font(n)
  522. title_font = make_font(size=self.title_font_size)
  523. btn_font = make_font(size=self.btn_font_size)
  524. self.uid_frame['bg'] = "#FA8072"
  525. self.uid_frame['bd'] = 5
  526. self.uid_frame['relief'] = "ridge"
  527. self.uid_frame.place(relx=0.2, rely=0.20, relwidth=0.6, relheight=0.10)
  528. self.name_frame['bg'] = "#FA8072"
  529. self.name_frame['bd'] = 5
  530. self.name_frame['relief'] = "ridge"
  531. self.name_frame.place(relx=0.2, rely=0.48, relwidth=0.6, relheight=0.25)
  532. height = 0.17
  533. for lb, text, enter, var in zip(self.name_title, ["用户名:", "密码:"], self.name_enter, self.name_var):
  534. lb['font'] = title_font
  535. lb['text'] = text
  536. lb['bg'] = "#FA8072"
  537. lb['anchor'] = 'e'
  538. enter['font'] = title_font
  539. enter['textvariable'] = var
  540. lb.place(relx=0.01, rely=height, relwidth=0.30, relheight=0.20)
  541. enter.place(relx=0.35, rely=height, relwidth=0.60, relheight=0.20)
  542. height += 0.45
  543. self.uid_title['font'] = title_font
  544. self.uid_title['text'] = "用户ID:"
  545. self.uid_title['bg'] = "#FA8072"
  546. self.uid_title['anchor'] = 'e'
  547. self.uid_enter['font'] = title_font
  548. self.uid_enter['textvariable'] = self.uid_var
  549. self.uid_title.place(relx=0.01, rely=0.25, relwidth=0.30, relheight=0.50)
  550. self.uid_enter.place(relx=0.35, rely=0.25, relwidth=0.60, relheight=0.50)
  551. for btn, text, func in zip(self.btn,
  552. ["通过用户ID删除", "通过用户名删除"],
  553. [lambda: self.del_by_uid(), lambda: self.del_by_name()]):
  554. btn['font'] = btn_font
  555. btn['text'] = text
  556. btn['bg'] = Config.tk_btn_bg
  557. btn['command'] = func
  558. self.btn[0].place(relx=0.6, rely=0.32, relwidth=0.2, relheight=0.08)
  559. self.btn[1].place(relx=0.6, rely=0.75, relwidth=0.2, relheight=0.08)
  560. def del_by_uid(self):
  561. uid = self.uid_var.get()
  562. if len(uid) != 32:
  563. self.station.show_warning("用户ID错误", "用户ID必须为32位")
  564. return
  565. event = tk_event.DelUserEvent(self.station).start(uid)
  566. self.station.push_event(event)
  567. def del_by_name(self):
  568. name = self.name_var[0].get()
  569. passwd = self.name_var[1].get()
  570. if len(name) == 0 or len(passwd) == 0:
  571. self.station.show_warning("用户名或密码错误", "请输入用户名和密码")
  572. return
  573. uid = create_uid(name, passwd)
  574. event = tk_event.DelUserEvent(self.station).start(uid)
  575. self.station.push_event(event)
  576. def set_disable(self):
  577. set_tk_disable_from_list(self.btn)
  578. set_tk_disable_from_list(self.name_enter)
  579. self.uid_enter['state'] = 'disable'
  580. def reset_disable(self):
  581. set_tk_disable_from_list(self.btn, flat='normal')
  582. set_tk_disable_from_list(self.name_enter, flat='normal')
  583. self.uid_enter['state'] = 'normal'
  584. class DeleteUsersProgram(AdminProgram):
  585. def __init__(self, station, win, color):
  586. super().__init__(station, win, color, "删除多个用户")
  587. self.enter_frame = tk.Frame(self.frame)
  588. self.title: tk.Label = tk.Label(self.enter_frame)
  589. self.enter: tk.Entry = tk.Entry(self.enter_frame)
  590. self.var: tk.Variable = tk.StringVar()
  591. self.btn: List[tk.Button] = [tk.Button(self.frame) for _ in range(2)] # del, scan
  592. self.__conf_font()
  593. def __conf_font(self, n: int = 1):
  594. self.title_font_size = int(16 * n)
  595. self.btn_font_size = int(14 * n)
  596. def conf_gui(self, n: int = 1):
  597. self.__conf_font(n)
  598. title_font = make_font(size=self.title_font_size)
  599. btn_font = make_font(size=self.btn_font_size)
  600. self.enter_frame['bg'] = "#48c0a3"
  601. self.enter_frame['bd'] = 5
  602. self.enter_frame['relief'] = "ridge"
  603. self.enter_frame.place(relx=0.2, rely=0.30, relwidth=0.6, relheight=0.10)
  604. self.title['font'] = title_font
  605. self.title['text'] = "条件:"
  606. self.title['anchor'] = 'e'
  607. self.title['bg'] = "#48c0a3"
  608. self.enter['font'] = title_font
  609. self.enter['textvariable'] = self.var
  610. self.title.place(relx=0.01, rely=0.25, relwidth=0.30, relheight=0.50)
  611. self.enter.place(relx=0.35, rely=0.25, relwidth=0.60, relheight=0.50)
  612. for btn, text, x, func in zip(self.btn,
  613. ["删除", "扫描"],
  614. [0.2, 0.6],
  615. [lambda: self.delete_user(), lambda: self.scan_user()]):
  616. btn['font'] = btn_font
  617. btn['text'] = text
  618. btn['bg'] = Config.tk_btn_bg
  619. btn['command'] = func
  620. btn.place(relx=x, rely=0.6, relwidth=0.2, relheight=0.08)
  621. def delete_user(self):
  622. where = self.var.get()
  623. if len(where) == 0:
  624. self.station.show_warning("条件错误", "条件必须为正确的SQL语句")
  625. return
  626. event = tk_event.DelUserFromWhereEvent(self.station).start(where)
  627. self.station.push_event(event)
  628. def scan_user(self):
  629. where = self.var.get()
  630. if len(where) == 0:
  631. self.station.show_warning("条件错误", "条件必须为正确的SQL语句")
  632. return
  633. event = tk_event.DelUserFromWhereScanEvent(self.station).start(where)
  634. self.station.push_event(event)
  635. def set_disable(self):
  636. set_tk_disable_from_list(self.btn)
  637. self.enter['state'] = 'disable'
  638. def reset_disable(self):
  639. set_tk_disable_from_list(self.btn, flat='normal')
  640. self.enter['state'] = 'normal'
  641. class DeleteGarbageProgramBase(AdminProgram):
  642. def __init__(self, station, win, color, title: str):
  643. super().__init__(station, win, color, title)
  644. self.enter_frame = tk.Frame(self.frame)
  645. self.title: tk.Label = tk.Label(self.enter_frame)
  646. self.enter: tk.Entry = tk.Entry(self.enter_frame)
  647. self.var: tk.Variable = tk.StringVar()
  648. self.int_var: tk.Variable = tk.IntVar()
  649. self.int_var.set(0)
  650. self.radio: List[tk.Radiobutton] = [tk.Radiobutton(self.frame) for _ in range(4)]
  651. self.btn: tk.Button = tk.Button(self.frame)
  652. self.__conf_font()
  653. self._conf()
  654. def _conf(self, title: str = "垃圾袋ID:", color: str = "#b69968", support_del_all: bool = True):
  655. self.frame_title = title
  656. self.frame_color = color
  657. self.support_del_all = support_del_all
  658. def __conf_font(self, n: int = 1):
  659. self.title_font_size = int(16 * n)
  660. self.btn_font_size = int(14 * n)
  661. def conf_gui(self, n: int = 1):
  662. self.__conf_font(n)
  663. title_font = make_font(size=self.title_font_size)
  664. btn_font = make_font(size=self.btn_font_size)
  665. self.enter_frame['bg'] = self.frame_color
  666. self.enter_frame['bd'] = 5
  667. self.enter_frame['relief'] = "ridge"
  668. self.enter_frame.place(relx=0.2, rely=0.30, relwidth=0.6, relheight=0.10)
  669. self.title['font'] = title_font
  670. self.title['text'] = self.frame_title
  671. self.title['bg'] = self.frame_color
  672. self.title['anchor'] = 'e'
  673. self.enter['font'] = title_font
  674. self.enter['textvariable'] = self.var
  675. self.title.place(relx=0.01, rely=0.25, relwidth=0.30, relheight=0.50)
  676. self.enter.place(relx=0.35, rely=0.25, relwidth=0.60, relheight=0.50)
  677. for i in range(4):
  678. radio = self.radio[i]
  679. radio['font'] = btn_font
  680. radio['text'] = ['均可', '仅未使用', '仅待检测', '仅已检测'][i]
  681. radio['bg'] = self.color
  682. radio['value'] = i
  683. radio['variable'] = self.int_var
  684. radio['anchor'] = 'w'
  685. if not self.support_del_all:
  686. self.int_var.set(1)
  687. self.radio[0]['state'] = 'disable'
  688. self.radio[0].place(relx=0.20, rely=0.43, relwidth=0.20, relheight=0.1)
  689. self.radio[1].place(relx=0.60, rely=0.43, relwidth=0.20, relheight=0.1)
  690. self.radio[2].place(relx=0.20, rely=0.55, relwidth=0.20, relheight=0.1)
  691. self.radio[3].place(relx=0.60, rely=0.55, relwidth=0.20, relheight=0.1)
  692. self.btn['font'] = btn_font
  693. self.btn['text'] = '删除'
  694. self.btn['bg'] = Config.tk_btn_bg
  695. self.btn['command'] = lambda: self.delete_garbage()
  696. self.btn.place(relx=0.4, rely=0.68, relwidth=0.2, relheight=0.08)
  697. def delete_garbage(self):
  698. ...
  699. def set_disable(self):
  700. self.enter['state'] = 'disable'
  701. self.btn['state'] = 'disable'
  702. def reset_disable(self):
  703. self.enter['state'] = 'normal'
  704. self.btn['state'] = 'normal'
  705. class DeleteGarbageProgram(DeleteGarbageProgramBase):
  706. def __init__(self, station, win, color):
  707. super(DeleteGarbageProgram, self).__init__(station, win, color, "删除垃圾袋")
  708. def delete_garbage(self):
  709. where = self.int_var.get()
  710. assert where in [0, 1, 2, 3]
  711. gid = self.var.get()
  712. if len(gid) == 0:
  713. self.station.show_warning("垃圾袋ID错误", "请输入正确的垃圾袋ID")
  714. return
  715. event = tk_event.DelGarbageEvent(self.station).start(gid, where)
  716. self.station.push_event(event)
  717. class DeleteGarbageMoreProgram(DeleteGarbageProgramBase):
  718. def __init__(self, station, win, color):
  719. super(DeleteGarbageMoreProgram, self).__init__(station, win, color, "删除多个垃圾袋")
  720. self.scan_btn = tk.Button(self.frame)
  721. self._conf("条件:", "#f58f98", False)
  722. def conf_gui(self, n: int = 1):
  723. super(DeleteGarbageMoreProgram, self).conf_gui(n)
  724. self.btn.place_forget()
  725. self.btn.place(relx=0.2, rely=0.68, relwidth=0.2, relheight=0.08)
  726. self.scan_btn['font'] = make_font(size=self.btn_font_size)
  727. self.scan_btn['text'] = '扫描'
  728. self.scan_btn['bg'] = Config.tk_btn_bg
  729. self.scan_btn['command'] = self.scan_garbage
  730. self.scan_btn.place(relx=0.6, rely=0.68, relwidth=0.2, relheight=0.08)
  731. def set_disable(self):
  732. super(DeleteGarbageMoreProgram, self).set_disable()
  733. self.scan_btn['state'] = 'disable'
  734. def reset_disable(self):
  735. super(DeleteGarbageMoreProgram, self).reset_disable()
  736. self.scan_btn['state'] = 'normal'
  737. def delete_garbage(self):
  738. where = self.int_var.get()
  739. assert where in [1, 2, 3]
  740. where_sql = self.var.get()
  741. if len(where_sql) == 0:
  742. self.station.show_warning("条件错误", "条件必须为正确的SQL语句")
  743. return
  744. event = tk_event.DelGarbageWhereEvent(self.station).start(where, where_sql)
  745. self.station.push_event(event)
  746. def scan_garbage(self):
  747. where = self.int_var.get()
  748. assert where in [1, 2, 3]
  749. where_sql = self.var.get()
  750. if len(where_sql) == 0:
  751. self.station.show_warning("条件错误", "条件必须为正确的SQL语句")
  752. return
  753. event = tk_event.DelGarbageWhereScanEvent(self.station).start(where, where_sql)
  754. self.station.push_event(event)
  755. class DeleteAllGarbageProgram(AdminProgram):
  756. def __init__(self, station, win, color):
  757. super().__init__(station, win, color, "删除所有垃圾袋")
  758. self.dangerous: tk.Label = tk.Label(self.frame)
  759. self.enter_frame = tk.Frame(self.frame)
  760. self.title: tk.Label = tk.Label(self.enter_frame)
  761. self.enter: tk.Entry = tk.Entry(self.enter_frame)
  762. self.var: tk.Variable = tk.StringVar()
  763. self.btn: List[tk.Button] = [tk.Button(self.frame) for _ in range(2)] # del, scan
  764. self.__conf_font()
  765. def __conf_font(self, n: int = 1):
  766. self.danger_font_size = int(20 * n)
  767. self.title_font_size = int(16 * n)
  768. self.btn_font_size = int(14 * n)
  769. def conf_gui(self, n: int = 1):
  770. self.__conf_font(n)
  771. danger_font = make_font(size=self.danger_font_size, weight="bold", underline=1)
  772. title_font = make_font(size=self.title_font_size)
  773. btn_font = make_font(size=self.btn_font_size)
  774. danger_btn_font = make_font(size=self.btn_font_size, weight="bold", overstrike=1)
  775. self.dangerous['bg'] = self.color
  776. self.dangerous['font'] = danger_font
  777. self.dangerous['fg'] = "#f20c00"
  778. self.dangerous['text'] = ("确定要从数据库删除所有垃圾袋吗?\n"
  779. "请输入[admin]用户的密码再继续操作.\n"
  780. "只有[admin]用户具有该操作的权限.\n"
  781. "这是相当危险的操作.\n"
  782. "操作后数据库可能无法恢复原数据.\n"
  783. "SuperHuan和程序的缔造者不会对\n"
  784. "此操作负责.\n"
  785. "删库跑路可不是一件好事.\n"
  786. "请遵守当地法律法规.")
  787. self.dangerous.place(relx=0.05, rely=0.03, relwidth=0.9, relheight=0.53)
  788. self.enter_frame['bg'] = "#f20c00"
  789. self.enter_frame['bd'] = 5
  790. self.enter_frame['relief'] = "ridge"
  791. self.enter_frame.place(relx=0.2, rely=0.60, relwidth=0.6, relheight=0.10)
  792. self.title['font'] = title_font
  793. self.title['text'] = "密码:"
  794. self.title['bg'] = "#f20c00"
  795. self.title['anchor'] = 'e'
  796. self.enter['font'] = title_font
  797. self.enter['textvariable'] = self.var
  798. self.title.place(relx=0.01, rely=0.25, relwidth=0.30, relheight=0.50)
  799. self.enter.place(relx=0.35, rely=0.25, relwidth=0.60, relheight=0.50)
  800. for btn, text, x in zip(self.btn, ["删除", "扫描"], [0.2, 0.6]):
  801. btn['text'] = text
  802. btn.place(relx=x, rely=0.78, relwidth=0.2, relheight=0.08)
  803. self.btn[0]['font'] = danger_btn_font
  804. self.btn[0]['bg'] = "#f20c00"
  805. self.btn[0]['command'] = lambda: self.delete_garbage()
  806. self.btn[1]['font'] = btn_font
  807. self.btn[1]['bg'] = Config.tk_btn_bg
  808. self.btn[1]['command'] = lambda: self.scan_garbage()
  809. def scan_garbage(self):
  810. event = tk_event.DelAllGarbageScanEvent(self.station) # 不需要start
  811. self.station.push_event(event)
  812. def delete_garbage(self):
  813. passwd = self.var.get()
  814. if len(passwd) == 0:
  815. self.station.show_warning("密码错误", "请输入正确的[admin]用户密码")
  816. user = find_user_by_name('admin', passwd, self.station.get_db())
  817. if user is None or not user.is_manager():
  818. self.station.show_warning("密码错误", "请输入正确的[admin]用户密码")
  819. return
  820. event = tk_event.DelAllGarbageEvent(self.station) # 不需要start
  821. self.station.push_event(event)
  822. def set_disable(self):
  823. set_tk_disable_from_list(self.btn)
  824. self.enter['state'] = 'disable'
  825. def reset_disable(self):
  826. set_tk_disable_from_list(self.btn, flat='normal')
  827. self.enter['state'] = 'normal'
  828. class SearchProgramBase(AdminProgram, metaclass=abc.ABCMeta):
  829. def __init__(self, station, win, color, title: str):
  830. super().__init__(station, win, color, title)
  831. self.view_frame = tk.Frame(self.frame)
  832. self.view = ttk.Treeview(self.view_frame)
  833. self.y_scroll = tk.Scrollbar(self.view_frame)
  834. self.x_scroll = tk.Scrollbar(self.view_frame)
  835. def conf_view_gui(self, columns: list, relx, rely, relwidth, relheight,
  836. x_scroll=0.05, y_scroll=0.02, color: str = "#FA8072"):
  837. self.view_frame['bg'] = color
  838. self.view_frame['bd'] = 2
  839. self.view_frame['relief'] = "ridge"
  840. self.view_frame.place(relx=relx, rely=rely, relwidth=relwidth, relheight=relheight)
  841. self.view['columns'] = columns
  842. self.view['show'] = 'headings'
  843. self.view['selectmode'] = 'none'
  844. for i in columns:
  845. self.view.column(i, anchor="c")
  846. self.view.heading(i, text=i)
  847. self.y_scroll['orient'] = 'vertical'
  848. self.y_scroll['command'] = self.view.yview
  849. self.view['yscrollcommand'] = self.y_scroll.set
  850. self.x_scroll['orient'] = 'horizontal'
  851. self.x_scroll['command'] = self.view.xview
  852. self.view['xscrollcommand'] = self.x_scroll.set
  853. self.view.place(relx=0.0, rely=0.0, relwidth=1 - y_scroll, relheight=1 - x_scroll)
  854. self.y_scroll.place(relx=0.98, rely=0.0, relwidth=y_scroll, relheight=1.0)
  855. self.x_scroll.place(relx=0.0, rely=1 - x_scroll, relwidth=1 - y_scroll, relheight=x_scroll)
  856. class SearchUserProgram(SearchProgramBase):
  857. def __init__(self, station, win, color):
  858. super().__init__(station, win, color, "搜索用户")
  859. self.enter_frame = tk.Frame(self.frame)
  860. self.title: List[tk.Label] = [tk.Label(self.enter_frame) for _ in range(3)]
  861. self.enter: List[tk.Entry] = [tk.Entry(self.enter_frame) for _ in range(3)]
  862. self.var: List[tk.Variable] = [tk.StringVar() for _ in range(3)]
  863. self.check: List[Tuple[tk.Checkbutton, tk.Variable]] = [(tk.Checkbutton(self.enter_frame), tk.IntVar())
  864. for _ in range(3)]
  865. self.btn: tk.Button = tk.Button(self.frame)
  866. self._columns = ["UserID", "Name", "Phone", "Score", "Reputation", "IsManager"]
  867. self._columns_ch = ["用户ID[UserID]", "用户名[Name]", "手机号[Phone]",
  868. "积分[Score]", "垃圾分类信用[Reputation]", "是否管理员[IsManager]"]
  869. self.__conf_font()
  870. def __conf_font(self, n: int = 1):
  871. self.title_font_size = int(16 * n)
  872. self.btn_font_size = int(14 * n)
  873. def conf_gui(self, n: int = 1):
  874. self.__conf_font(n)
  875. title_font = make_font(size=self.title_font_size)
  876. btn_font = make_font(size=self.btn_font_size)
  877. self.enter_frame['bg'] = "#FA8072"
  878. self.enter_frame['bd'] = 5
  879. self.enter_frame['relief'] = "ridge"
  880. self.enter_frame.place(relx=0.2, rely=0.0, relwidth=0.6, relheight=0.30)
  881. height = 0.1
  882. for lb, text, enter, var, check in zip(self.title,
  883. ["用户ID:", "用户名:", "手机号:"],
  884. self.enter, self.var, self.check):
  885. lb['font'] = title_font
  886. lb['text'] = text
  887. lb['bg'] = "#FA8072"
  888. lb['anchor'] = 'e'
  889. enter['font'] = title_font
  890. enter['textvariable'] = var
  891. check[0]['font'] = title_font
  892. check[0]['text'] = ''
  893. check[0]['bg'] = "#FA8072"
  894. check[0]['variable'] = check[1]
  895. check[1].set(1)
  896. lb.place(relx=0.01, rely=height, relwidth=0.30, relheight=0.17)
  897. enter.place(relx=0.35, rely=height, relwidth=0.55, relheight=0.17)
  898. check[0].place(relx=0.92, rely=height, relwidth=0.04, relheight=0.17)
  899. height += 0.30
  900. self.btn['font'] = btn_font
  901. self.btn['text'] = "搜索"
  902. self.btn['bg'] = Config.tk_btn_bg
  903. self.btn['command'] = self.search_user
  904. self.btn.place(relx=0.4, rely=0.9, relwidth=0.2, relheight=0.08)
  905. self.conf_view_gui(self._columns_ch, relx=0.05, rely=0.32, relwidth=0.9, relheight=0.55)
  906. def search_user(self):
  907. use_uid = self.check[0][1].get()
  908. use_name = self.check[1][1].get()
  909. use_phone = self.check[2][1].get()
  910. uid = None
  911. name = None
  912. phone = None
  913. if use_uid:
  914. uid = self.var[0].get()
  915. if len(uid) == 0:
  916. uid = None
  917. if use_name:
  918. name = self.var[1].get()
  919. if len(name) == 0:
  920. name = None
  921. if use_phone:
  922. phone = self.var[2].get()
  923. if len(phone) == 0:
  924. phone = None
  925. event = tk_event.SearchUserEvent(self.station).start(self._columns, uid, name, phone, self)
  926. self.station.push_event(event)
  927. def set_disable(self):
  928. self.btn['state'] = 'disable'
  929. set_tk_disable_from_list(self.enter)
  930. def reset_disable(self):
  931. self.btn['state'] = 'normal'
  932. set_tk_disable_from_list(self.enter, flat='normal')
  933. class SearchAdvancedProgramBase(SearchProgramBase, metaclass=abc.ABCMeta):
  934. def __init__(self, station, win, color, title: str):
  935. super().__init__(station, win, color, title)
  936. self.enter_frame = tk.Frame(self.frame)
  937. self.title: tk.Label = tk.Label(self.enter_frame)
  938. self.enter: tk.Entry = tk.Entry(self.enter_frame)
  939. self.var: tk.Variable = tk.StringVar()
  940. self.btn: tk.Button = tk.Button(self.frame)
  941. self._conf([], [], "#FA8072") # 默认颜色
  942. self.__conf_font()
  943. def _conf(self, columns: list, columns_ch: list, bg_color):
  944. self.bg_color = bg_color
  945. self._columns = columns
  946. self._columns_ch = columns_ch
  947. return self
  948. def __conf_font(self, n: int = 1):
  949. self.title_font_size = int(16 * n)
  950. self.btn_font_size = int(14 * n)
  951. def conf_gui(self, n: int = 1):
  952. self.__conf_font(n)
  953. title_font = make_font(size=self.title_font_size)
  954. btn_font = make_font(size=self.btn_font_size)
  955. self.enter_frame['bg'] = self.bg_color
  956. self.enter_frame['bd'] = 5
  957. self.enter_frame['relief'] = "ridge"
  958. self.enter_frame.place(relx=0.2, rely=0.00, relwidth=0.6, relheight=0.10)
  959. self.title['font'] = title_font
  960. self.title['bg'] = self.bg_color
  961. self.title['text'] = "条件:"
  962. self.title['anchor'] = 'e'
  963. self.enter['font'] = title_font
  964. self.enter['textvariable'] = self.var
  965. self.title.place(relx=0.01, rely=0.25, relwidth=0.30, relheight=0.50)
  966. self.enter.place(relx=0.35, rely=0.25, relwidth=0.60, relheight=0.50)
  967. self.btn['text'] = "搜索"
  968. self.btn['font'] = btn_font
  969. self.btn['bg'] = Config.tk_btn_bg
  970. self.btn['command'] = self.search
  971. self.btn.place(relx=0.4, rely=0.9, relwidth=0.2, relheight=0.08)
  972. self.conf_view_gui(self._columns_ch, relx=0.05, rely=0.12, relwidth=0.9, relheight=0.76)
  973. def search(self):
  974. ...
  975. def set_disable(self):
  976. self.btn['state'] = 'disable'
  977. self.enter['state'] = 'disable'
  978. def reset_disable(self):
  979. self.btn['state'] = 'normal'
  980. self.enter['state'] = 'normal'
  981. class SearchUserAdvancedProgram(SearchAdvancedProgramBase):
  982. def __init__(self, station, win, color):
  983. super(SearchUserAdvancedProgram, self).__init__(station, win, color, "高级搜索-用户")
  984. columns = ["UserID", "Name", "Phone", "Score", "Reputation", "IsManager"]
  985. columns_ch = ["用户ID[UserID]", "用户名[Name]", "手机号[Phone]",
  986. "积分[Score]", "垃圾分类信用[Reputation]", "是否管理员[IsManager]"]
  987. self._conf(columns, columns_ch, '#48c0a3')
  988. def search(self):
  989. where = self.var.get()
  990. event = tk_event.SearchUserAdvancedEvent(self.station).start(self._columns, where, self)
  991. self.station.push_event(event)
  992. class SearchGarbageProgram(SearchProgramBase):
  993. def __init__(self, station, win, color):
  994. super().__init__(station, win, color, "搜索垃圾袋")
  995. self.enter_frame = tk.Frame(self.frame)
  996. self.title: List[tk.Label] = [tk.Label(self.enter_frame) for _ in range(8)]
  997. self.enter: List[tk.Entry] = [tk.Entry(self.enter_frame) for _ in range(8)]
  998. self.var: List[tk.Variable] = [tk.StringVar() for _ in range(8)]
  999. self.check: List[Tuple[tk.Checkbutton, tk.Variable]] = [(tk.Checkbutton(self.enter_frame), tk.IntVar())
  1000. for _ in range(8)]
  1001. self._columns = ["GarbageID", "UserID", "CheckerID", "CreateTime", "UseTime", "Location", "GarbageType",
  1002. "CheckResult"]
  1003. self._columns_zh = ["垃圾袋ID[GarbageID]", "使用者ID[UserID]", "检测者ID[CheckerID]", "创建时间[CreateTime]",
  1004. "使用时间[UseTime]", "使用地点[Location]", "垃圾类型[GarbageType]", "检测结果[CheckResult]"]
  1005. self.btn: tk.Button = tk.Button(self.frame)
  1006. self.__conf_font()
  1007. def __conf_font(self, n: int = 1):
  1008. self.title_font_size = int(16 * n)
  1009. self.btn_font_size = int(14 * n)
  1010. def conf_gui(self, n: int = 1):
  1011. self.__conf_font(n)
  1012. title_font = make_font(size=self.title_font_size)
  1013. btn_font = make_font(size=self.btn_font_size)
  1014. self.enter_frame['bg'] = "#7bbfea"
  1015. self.enter_frame['bd'] = 5
  1016. self.enter_frame['relief'] = "ridge"
  1017. self.enter_frame.place(relx=0.2, rely=0.0, relwidth=0.6, relheight=0.47)
  1018. height = 0.02
  1019. for lb, text, enter, var, check in zip(self.title,
  1020. ["垃圾袋ID:", "使用者ID:", "检查者ID:", "创建时间:", "使用时间:",
  1021. "使用地点:", "垃圾类型:", "检测结果:"],
  1022. self.enter, self.var, self.check):
  1023. lb['font'] = title_font
  1024. lb['text'] = text
  1025. lb['bg'] = "#7bbfea"
  1026. lb['anchor'] = 'e'
  1027. enter['font'] = title_font
  1028. enter['textvariable'] = var
  1029. check[0]['font'] = title_font
  1030. check[0]['bg'] = "#7bbfea"
  1031. check[0]['text'] = ''
  1032. check[0]['variable'] = check[1]
  1033. check[1].set(1)
  1034. lb.place(relx=0.01, rely=height, relwidth=0.30, relheight=0.10)
  1035. enter.place(relx=0.35, rely=height, relwidth=0.55, relheight=0.10)
  1036. check[0].place(relx=0.92, rely=height, relwidth=0.04, relheight=0.10)
  1037. height += 0.121
  1038. self.btn['font'] = btn_font
  1039. self.btn['bg'] = Config.tk_btn_bg
  1040. self.btn['text'] = "Search"
  1041. self.btn['command'] = self.search_user
  1042. self.btn.place(relx=0.4, rely=0.9, relwidth=0.2, relheight=0.08)
  1043. self.conf_view_gui(self._columns_zh, relx=0.05, rely=0.49, relwidth=0.9, relheight=0.38, x_scroll=0.07)
  1044. def search_user(self):
  1045. keys = ["gid", "uid", "cuid", "create_time", "use_time", "loc", "type_", "check"]
  1046. key_values = {}
  1047. for i, key in enumerate(keys):
  1048. ck = self.check[i][1].get()
  1049. if ck:
  1050. res = self.enter[i].get()
  1051. if len(res) > 0:
  1052. key_values[key] = res
  1053. continue
  1054. key_values[key] = None
  1055. event = tk_event.SearchGarbageEvent(self.station).start(self._columns, key_values, self)
  1056. self.station.push_event(event)
  1057. def set_disable(self):
  1058. self.btn['state'] = 'disable'
  1059. set_tk_disable_from_list(self.enter)
  1060. def reset_disable(self):
  1061. self.btn['state'] = 'normal'
  1062. set_tk_disable_from_list(self.enter, flat='normal')
  1063. class SearchGarbageAdvancedProgram(SearchAdvancedProgramBase):
  1064. def __init__(self, station, win, color):
  1065. super(SearchGarbageAdvancedProgram, self).__init__(station, win, color, "高级搜索-垃圾袋")
  1066. columns = ["GarbageID", "UserID", "CheckerID", "CreateTime", "UseTime", "Location", "GarbageType",
  1067. "CheckResult"]
  1068. columns_zh = ["垃圾袋ID[GarbageID]", "使用者ID[UserID]", "检测者ID[CheckerID]", "创建时间[CreateTime]",
  1069. "使用时间[UseTime]", "使用地点[Location]", "垃圾类型[GarbageType]", "检测结果[CheckResult]"]
  1070. self._conf(columns, columns_zh, '#d1923f')
  1071. def search(self):
  1072. where = self.var.get()
  1073. event = tk_event.SearchGarbageAdvancedEvent(self.station).start(self._columns, where, self)
  1074. self.station.push_event(event)
  1075. class SearchAdvancedProgram(SearchAdvancedProgramBase):
  1076. def __init__(self, station, win, color):
  1077. super(SearchAdvancedProgram, self).__init__(station, win, color, "高级搜索")
  1078. columns = ["GarbageID", "UserID", "UserName", "UserPhone", "UserScore",
  1079. "UserReputation", "CheckerID", "CheckerName", "CheckerPhone",
  1080. "CreateTime", "UseTime", "Location", "GarbageType", "CheckResult"]
  1081. columns_zh = ["垃圾袋ID[GarbageID]", "使用者ID[UserID]", "使用者名[UserName]", "使用者手机号[UserPhone]",
  1082. "使用者积分[UserScore]", "使用者垃圾分类信用[UserReputation]", "检测者ID[CheckerID]",
  1083. "检测这名[CheckerName]", "检测者手机号[CheckerPhone]", "创建时间[CreateTime]", "使用时间[UseTime]",
  1084. "使用地点[Location]", "垃圾类型[GarbageType]", "检测结果[CheckResult]"]
  1085. self._conf(columns, columns_zh, '#426ab3')
  1086. def search(self):
  1087. where = self.var.get()
  1088. event = tk_event.SearchAdvancedEvent(self.station).start(self._columns, where, self)
  1089. self.station.push_event(event)
  1090. class UpdateUserProgramBase(AdminProgram):
  1091. def __init__(self, station, win, color, title: str):
  1092. super().__init__(station, win, color, title)
  1093. self.enter_frame = tk.Frame(self.frame)
  1094. self.title: List[tk.Label] = [tk.Label(self.enter_frame) for _ in range(2)]
  1095. self.enter: List[tk.Entry] = [tk.Entry(self.enter_frame) for _ in range(2)]
  1096. self.var: List[tk.Variable] = [tk.StringVar() for _ in range(2)]
  1097. self.where_frame = tk.Frame(self.frame)
  1098. self.where_title: List[tk.Label] = [tk.Label(self.where_frame) for _ in range(2)]
  1099. self.where_enter: List[tk.Entry] = [tk.Entry(self.where_frame) for _ in range(2)]
  1100. self.where_var: List[tk.Variable] = [tk.StringVar() for _ in range(2)]
  1101. self.btn: List[tk.Button] = [tk.Button(self.frame), tk.Button(self.frame)]
  1102. self._conf(["", ""], "#FA8072")
  1103. self.__conf_font()
  1104. def _conf(self, title: List[str], bg_color: str):
  1105. self.bg_color = bg_color
  1106. self.bg_color_where = bg_color
  1107. self.enter_title = title
  1108. def __conf_font(self, n: int = 1):
  1109. self.title_font_size = int(16 * n)
  1110. self.btn_font_size = int(14 * n)
  1111. def conf_gui(self, n: int = 1):
  1112. self.__conf_font(n)
  1113. title_font = make_font(size=self.title_font_size)
  1114. btn_font = make_font(size=self.btn_font_size)
  1115. self.where_frame['bg'] = self.bg_color_where
  1116. self.where_frame['bd'] = 5
  1117. self.where_frame['relief'] = "ridge"
  1118. self.where_frame.place(relx=0.2, rely=0.20, relwidth=0.6, relheight=0.17)
  1119. self.enter_frame['bg'] = self.bg_color
  1120. self.enter_frame['bd'] = 5
  1121. self.enter_frame['relief'] = "ridge"
  1122. self.enter_frame.place(relx=0.2, rely=0.58, relwidth=0.6, relheight=0.17)
  1123. height = 0.1
  1124. for lb, text, enter, var, lb_w, text_w, enter_w, var_w in (
  1125. zip(self.title, self.enter_title, self.enter, self.var,
  1126. self.where_title, ["条件:", self.enter_title[1]], self.where_enter, self.where_var)):
  1127. lb['font'] = title_font
  1128. lb['text'] = text
  1129. lb['bg'] = self.bg_color
  1130. lb['anchor'] = 'e'
  1131. lb_w['font'] = title_font
  1132. lb_w['text'] = text_w
  1133. lb_w['bg'] = self.bg_color_where
  1134. lb_w['anchor'] = 'e'
  1135. enter['font'] = title_font
  1136. enter['textvariable'] = var
  1137. enter_w['font'] = title_font
  1138. enter_w['textvariable'] = var_w
  1139. lb.place(relx=0.01, rely=height, relwidth=0.30, relheight=0.35)
  1140. enter.place(relx=0.35, rely=height, relwidth=0.60, relheight=0.35)
  1141. lb_w.place(relx=0.01, rely=height, relwidth=0.30, relheight=0.35)
  1142. enter_w.place(relx=0.35, rely=height, relwidth=0.60, relheight=0.35)
  1143. height += 0.43
  1144. for btn, text, func in zip(self.btn,
  1145. ["通过条件更新", "通过用户ID更新"],
  1146. [self.update_by_where, self.update_by_uid]):
  1147. btn['font'] = btn_font
  1148. btn['text'] = text
  1149. btn['bg'] = Config.tk_btn_bg
  1150. btn['command'] = func
  1151. self.btn[0].place(relx=0.55, rely=0.40, relwidth=0.25, relheight=0.08)
  1152. self.btn[1].place(relx=0.55, rely=0.78, relwidth=0.25, relheight=0.08)
  1153. def update_by_uid(self):
  1154. ...
  1155. def update_by_where(self):
  1156. ...
  1157. def set_disable(self):
  1158. set_tk_disable_from_list(self.btn)
  1159. set_tk_disable_from_list(self.enter)
  1160. def reset_disable(self):
  1161. set_tk_disable_from_list(self.btn, flat='normal')
  1162. set_tk_disable_from_list(self.enter, flat='normal')
  1163. class UpdateUserScore(UpdateUserProgramBase):
  1164. def __init__(self, station, win, color):
  1165. super(UpdateUserScore, self).__init__(station, win, color, "更新用户-积分")
  1166. self._conf(["用户ID:", "积分:"], "#afdfe4")
  1167. def update_by_uid(self):
  1168. uid = self.enter[0].get()
  1169. score = int(self.enter[1].get())
  1170. event = tk_event.UpdateUserScoreEvent(self.station).start(score, f"UserID='{uid}'")
  1171. self.station.push_event(event)
  1172. def update_by_where(self):
  1173. where = self.where_enter[0].get()
  1174. score = int(self.where_enter[1].get())
  1175. event = tk_event.UpdateUserScoreEvent(self.station).start(score, where)
  1176. self.station.push_event(event)
  1177. class UpdateUserReputation(UpdateUserProgramBase):
  1178. def __init__(self, station, win, color):
  1179. super(UpdateUserReputation, self).__init__(station, win, color, "更新用户-垃圾分类信用")
  1180. self._conf(["用户ID:", "垃圾分类信用:"], "#f8aba6")
  1181. def update_by_uid(self):
  1182. uid = self.enter[0].get()
  1183. reputation = int(self.enter[1].get())
  1184. event = tk_event.UpdateUserReputationEvent(self.station).start(reputation, f"UserID='{uid}'")
  1185. self.station.push_event(event)
  1186. def update_by_where(self):
  1187. where = self.where_enter[0].get()
  1188. reputation = int(self.where_enter[1].get())
  1189. event = tk_event.UpdateUserReputationEvent(self.station).start(reputation, where)
  1190. self.station.push_event(event)
  1191. class UpdateGarbageTypeProgram(AdminProgram):
  1192. def __init__(self, station, win, color):
  1193. super().__init__(station, win, color, "更新垃圾袋-垃圾类型")
  1194. self.enter_frame = tk.Frame(self.frame)
  1195. self.title: tk.Label = tk.Label(self.enter_frame)
  1196. self.enter: tk.Entry = tk.Entry(self.enter_frame)
  1197. self.type: List[tk.Radiobutton] = [tk.Radiobutton(self.frame) for _ in range(4)]
  1198. self.var: List[tk.Variable] = [tk.StringVar, tk.IntVar()]
  1199. self.where_frame = tk.Frame(self.frame)
  1200. self.where_title: tk.Label = tk.Label(self.where_frame)
  1201. self.where_enter: tk.Entry = tk.Entry(self.where_frame)
  1202. self.where_type: List[tk.Radiobutton] = [tk.Radiobutton(self.frame) for _ in range(4)]
  1203. self.where_var: List[tk.Variable] = [tk.StringVar, tk.IntVar()]
  1204. self.btn: List[tk.Button] = [tk.Button(self.frame), tk.Button(self.frame)]
  1205. self.__conf_font()
  1206. def __conf_font(self, n: int = 1):
  1207. self.title_font_size = int(16 * n)
  1208. self.btn_font_size = int(14 * n)
  1209. def conf_gui(self, n: int = 1):
  1210. self.__conf_font(n)
  1211. title_font = make_font(size=self.title_font_size)
  1212. btn_font = make_font(size=self.btn_font_size)
  1213. self.where_frame['bg'] = "#fdb933"
  1214. self.where_frame['bd'] = 5
  1215. self.where_frame['relief'] = "ridge"
  1216. self.where_frame.place(relx=0.2, rely=0.20, relwidth=0.6, relheight=0.10)
  1217. self.enter_frame['bg'] = "#fdb933"
  1218. self.enter_frame['bd'] = 5
  1219. self.enter_frame['relief'] = "ridge"
  1220. self.enter_frame.place(relx=0.2, rely=0.60, relwidth=0.6, relheight=0.10)
  1221. for lb, enter, radios, var, y, text in zip([self.title, self.where_title],
  1222. [self.enter, self.where_enter],
  1223. [self.type, self.where_type],
  1224. [self.var, self.where_var],
  1225. [0.32, 0.72],
  1226. ["垃圾袋ID:", "条件:"]):
  1227. lb['font'] = title_font
  1228. lb['text'] = text
  1229. lb['bg'] = "#fdb933"
  1230. lb['anchor'] = 'e'
  1231. enter['font'] = title_font
  1232. enter['textvariable'] = var[0]
  1233. for i, radio in enumerate(radios):
  1234. radio['font'] = btn_font
  1235. radio['bg'] = self.color
  1236. radio['text'] = GarbageType.GarbageTypeStrList_ch[i + 1]
  1237. radio['value'] = i + 1
  1238. radio['variable'] = var[1]
  1239. radio['anchor'] = 'w'
  1240. var[1].set(1)
  1241. radios[0].place(relx=0.20, rely=y + 0.00, relwidth=0.20, relheight=0.04)
  1242. radios[1].place(relx=0.60, rely=y + 0.00, relwidth=0.20, relheight=0.04)
  1243. radios[2].place(relx=0.20, rely=y + 0.05, relwidth=0.20, relheight=0.04)
  1244. radios[3].place(relx=0.60, rely=y + 0.05, relwidth=0.20, relheight=0.04)
  1245. lb.place(relx=0.02, rely=0.2, relwidth=0.25, relheight=0.48)
  1246. enter.place(relx=0.30, rely=0.2, relwidth=0.60, relheight=0.48)
  1247. for btn, text, func in zip(self.btn,
  1248. ["通过条件更新", "通过垃圾袋ID更新"],
  1249. [self.update_by_where, self.update_by_gid]):
  1250. btn['font'] = btn_font
  1251. btn['text'] = text
  1252. btn['bg'] = Config.tk_btn_bg
  1253. btn['command'] = func
  1254. self.btn[0].place(relx=0.55, rely=0.43, relwidth=0.25, relheight=0.08)
  1255. self.btn[1].place(relx=0.55, rely=0.83, relwidth=0.25, relheight=0.08)
  1256. def update_by_gid(self):
  1257. gid = self.enter.get()
  1258. type_ = self.var[1].get()
  1259. event = tk_event.UpdateGarbageTypeEvent(self.station).start(type_, f"GarbageID={gid}")
  1260. self.station.push_event(event)
  1261. def update_by_where(self):
  1262. where = self.where_enter.get()
  1263. type_ = self.where_var[1].get()
  1264. event = tk_event.UpdateGarbageTypeEvent(self.station).start(type_, where)
  1265. self.station.push_event(event)
  1266. def set_disable(self):
  1267. set_tk_disable_from_list(self.btn)
  1268. self.enter['state'] = 'disable'
  1269. self.where_enter['state'] = 'normal'
  1270. def reset_disable(self):
  1271. set_tk_disable_from_list(self.btn, flat='normal')
  1272. self.enter['state'] = 'normal'
  1273. self.where_enter['state'] = 'normal'
  1274. class UpdateGarbageCheckResultProgram(AdminProgram):
  1275. def __init__(self, station, win, color):
  1276. super().__init__(station, win, color, "更新垃圾袋-检测结果")
  1277. self.enter_frame = tk.Frame(self.frame)
  1278. self.title: tk.Label = tk.Label(self.enter_frame)
  1279. self.enter: tk.Entry = tk.Entry(self.enter_frame)
  1280. self.type: List[tk.Radiobutton] = [tk.Radiobutton(self.frame) for _ in range(2)]
  1281. self.var: List[tk.Variable] = [tk.StringVar, tk.IntVar()]
  1282. self.where_frame = tk.Frame(self.frame)
  1283. self.where_title: tk.Label = tk.Label(self.where_frame)
  1284. self.where_enter: tk.Entry = tk.Entry(self.where_frame)
  1285. self.where_type: List[tk.Radiobutton] = [tk.Radiobutton(self.frame) for _ in range(2)]
  1286. self.where_var: List[tk.Variable] = [tk.StringVar, tk.IntVar()]
  1287. self.btn: List[tk.Button] = [tk.Button(self.frame), tk.Button(self.frame)]
  1288. self.__conf_font()
  1289. def __conf_font(self, n: int = 1):
  1290. self.title_font_size = int(16 * n)
  1291. self.btn_font_size = int(14 * n)
  1292. def conf_gui(self, n: int = 1):
  1293. self.__conf_font(n)
  1294. title_font = make_font(size=self.title_font_size)
  1295. btn_font = make_font(size=self.btn_font_size)
  1296. self.where_frame['bg'] = "#abc88b"
  1297. self.where_frame['bd'] = 5
  1298. self.where_frame['relief'] = "ridge"
  1299. self.where_frame.place(relx=0.2, rely=0.20, relwidth=0.6, relheight=0.10)
  1300. self.enter_frame['bg'] = "#abc88b"
  1301. self.enter_frame['bd'] = 5
  1302. self.enter_frame['relief'] = "ridge"
  1303. self.enter_frame.place(relx=0.2, rely=0.60, relwidth=0.6, relheight=0.10)
  1304. for lb, enter, radios, var, y, text in zip([self.title, self.where_title],
  1305. [self.enter, self.where_enter],
  1306. [self.type, self.where_type],
  1307. [self.var, self.where_var],
  1308. [0.32, 0.72],
  1309. ["垃圾袋ID:", "条件:"]):
  1310. lb['font'] = title_font
  1311. lb['text'] = text
  1312. lb['bg'] = "#abc88b"
  1313. lb['anchor'] = 'e'
  1314. enter['font'] = title_font
  1315. enter['textvariable'] = var[0]
  1316. for i, radio in enumerate(radios):
  1317. radio['font'] = btn_font
  1318. radio['bg'] = self.color
  1319. radio['text'] = ["投放错误", "投放正确"][i]
  1320. radio['value'] = i
  1321. radio['variable'] = var[1]
  1322. radio['anchor'] = 'w'
  1323. var[1].set(1)
  1324. radios[0].place(relx=0.20, rely=y + 0.00, relwidth=0.20, relheight=0.04)
  1325. radios[1].place(relx=0.60, rely=y + 0.00, relwidth=0.20, relheight=0.04)
  1326. lb.place(relx=0.02, rely=0.2, relwidth=0.25, relheight=0.48)
  1327. enter.place(relx=0.30, rely=0.2, relwidth=0.60, relheight=0.48)
  1328. for btn, text, func in zip(self.btn,
  1329. ["通过条件更新", "通过垃圾袋ID更新"],
  1330. [self.update_by_where, self.update_by_gid]):
  1331. btn['font'] = btn_font
  1332. btn['bg'] = Config.tk_btn_bg
  1333. btn['text'] = text
  1334. btn['command'] = func
  1335. self.btn[0].place(relx=0.55, rely=0.38, relwidth=0.25, relheight=0.08)
  1336. self.btn[1].place(relx=0.55, rely=0.78, relwidth=0.25, relheight=0.08)
  1337. def update_by_gid(self):
  1338. gid = self.enter.get()
  1339. check = (self.var[1].get() == 1)
  1340. event = tk_event.UpdateGarbageCheckEvent(self.station).start(check, f"GarbageID={gid}")
  1341. self.station.push_event(event)
  1342. def update_by_where(self):
  1343. where = self.where_enter.get()
  1344. check = (self.where_var[1].get() == 1)
  1345. event = tk_event.UpdateGarbageCheckEvent(self.station).start(check, where)
  1346. self.station.push_event(event)
  1347. def set_disable(self):
  1348. set_tk_disable_from_list(self.btn)
  1349. self.enter['state'] = 'disable'
  1350. self.where_enter['state'] = 'normal'
  1351. def reset_disable(self):
  1352. set_tk_disable_from_list(self.btn, flat='normal')
  1353. self.enter['state'] = 'normal'
  1354. self.where_enter['state'] = 'normal'
  1355. class StatisticsTimeProgramBase(AdminProgram):
  1356. def __init__(self, station, win, color, title: str):
  1357. super().__init__(station, win, color, title)
  1358. self.figure_frame = tk.Frame(self.frame)
  1359. self.figure = Figure(dpi=100)
  1360. self.plt_1: Axes = self.figure.add_subplot(211) # 添加子图:2行1列第1个
  1361. self.plt_2: Axes = self.figure.add_subplot(212) # 添加子图:2行1列第2个
  1362. self.figure.subplots_adjust(hspace=0.5)
  1363. self.canvas = FigureCanvasTkAgg(self.figure, master=self.figure_frame)
  1364. self.canvas_tk = self.canvas.get_tk_widget()
  1365. self.toolbar = NavigationToolbar2Tk(self.canvas, self.figure_frame)
  1366. self.color_frame = tk.Frame(self.frame)
  1367. self.show_list_tk = tk.Listbox(self.color_frame)
  1368. self.show_list_scroll = tk.Scrollbar(self.color_frame)
  1369. self.hide_list_tk = tk.Listbox(self.color_frame)
  1370. self.hide_list_scroll = tk.Scrollbar(self.color_frame)
  1371. self.btn_show = tk.Button(self.color_frame)
  1372. self.btn_hide = tk.Button(self.color_frame)
  1373. self.color_show_dict = {}
  1374. self.color_hide_dict = {}
  1375. self.export_lst = []
  1376. self.export_btn = tk.Button(self.frame)
  1377. self.refresh_btn = tk.Button(self.frame)
  1378. self.reset_btn = tk.Button(self.frame)
  1379. self.reverse_btn = tk.Button(self.frame)
  1380. self.legend_show = tk.Checkbutton(self.frame), tk.IntVar()
  1381. self._conf("#abc88b")
  1382. self.__conf_font()
  1383. def _conf(self, bg_color):
  1384. self.bg_color = bg_color
  1385. def __conf_font(self, n: int = 1):
  1386. self.btn_font_size = int(14 * n)
  1387. self.little_btn_font_size = int(12 * n)
  1388. def to_program(self):
  1389. self.refresh()
  1390. def update_listbox(self):
  1391. self.show_list_tk.delete(0, tk.END) # 清空列表
  1392. self.hide_list_tk.delete(0, tk.END) # 清空列表
  1393. for i in self.color_show_dict:
  1394. self.show_list_tk.insert(tk.END, i)
  1395. self.show_list_tk.itemconfig(tk.END,
  1396. selectbackground=self.color_show_dict[i],
  1397. bg=self.color_show_dict[i],
  1398. selectforeground='#FFFFFF',
  1399. fg='#000000')
  1400. for i in self.color_hide_dict:
  1401. self.hide_list_tk.insert(tk.END, i)
  1402. self.hide_list_tk.itemconfig(tk.END,
  1403. selectbackground=self.color_hide_dict[i],
  1404. bg=self.color_hide_dict[i],
  1405. selectforeground='#FFFFFF',
  1406. fg='#000000')
  1407. def check_show(self, res: str):
  1408. color = self.color_show_dict.get(res)
  1409. if color is not None:
  1410. return color
  1411. color = self.color_hide_dict.get(res)
  1412. if color is not None:
  1413. return None
  1414. color = random_color()
  1415. self.color_show_dict[res] = color
  1416. return color
  1417. def hide(self):
  1418. i = self.show_list_tk.curselection()
  1419. if len(i) == 0:
  1420. return
  1421. res = self.show_list_tk.get(i[0])
  1422. self.hide_(res)
  1423. self.update_listbox()
  1424. def show(self):
  1425. i = self.hide_list_tk.curselection()
  1426. if len(i) == 0:
  1427. return
  1428. res = self.hide_list_tk.get(i[0])
  1429. self.show_(res)
  1430. self.update_listbox()
  1431. def hide_(self, res):
  1432. color = self.color_show_dict.get(res)
  1433. if color is not None:
  1434. del self.color_show_dict[res]
  1435. self.color_hide_dict[res] = color
  1436. def show_(self, res):
  1437. color = self.color_hide_dict.get(res)
  1438. if color is not None:
  1439. del self.color_hide_dict[res]
  1440. self.color_show_dict[res] = color
  1441. def conf_gui(self, n: int = 1):
  1442. self.__conf_font(n)
  1443. btn_font = make_font(size=self.btn_font_size)
  1444. little_btn_font = make_font(size=self.little_btn_font_size)
  1445. self.color_frame['bg'] = self.bg_color
  1446. self.color_frame['bd'] = 5
  1447. self.color_frame['relief'] = "ridge"
  1448. self.show_list_tk.place(relx=0, rely=0, relwidth=0.90, relheight=0.475)
  1449. self.show_list_scroll.place(relx=0.90, rely=0, relwidth=0.10, relheight=0.475)
  1450. self.show_list_scroll['orient'] = 'vertical'
  1451. self.show_list_scroll['command'] = self.show_list_tk.yview
  1452. self.show_list_tk['yscrollcommand'] = self.show_list_scroll.set
  1453. self.show_list_tk['activestyle'] = tk.NONE
  1454. self.hide_list_tk.place(relx=0, rely=0.525, relwidth=0.90, relheight=0.475)
  1455. self.hide_list_scroll.place(relx=0.90, rely=0.525, relwidth=0.10, relheight=0.475)
  1456. self.hide_list_scroll['orient'] = 'vertical'
  1457. self.hide_list_scroll['command'] = self.hide_list_tk.yview
  1458. self.hide_list_tk['yscrollcommand'] = self.hide_list_scroll.set
  1459. self.hide_list_tk['activestyle'] = tk.NONE
  1460. for btn, text, func, x in zip([self.btn_show, self.btn_hide],
  1461. ["Show", "Hide"],
  1462. [self.show, self.hide],
  1463. [0.00, 0.50]):
  1464. btn['font'] = little_btn_font
  1465. btn['bg'] = Config.tk_btn_bg
  1466. btn['text'] = text
  1467. btn['command'] = func
  1468. btn.place(relx=x, rely=0.475, relwidth=0.50, relheight=0.05)
  1469. self.color_frame.place(relx=0.01, rely=0.02, relwidth=0.18, relheight=0.88)
  1470. self.figure_frame['bg'] = self.bg_color
  1471. self.figure_frame['bd'] = 5
  1472. self.figure_frame['relief'] = "ridge"
  1473. self.figure_frame.place(relx=0.21, rely=0.02, relwidth=0.79, relheight=0.88)
  1474. self.canvas_tk.place(relx=0, rely=0, relwidth=1.0, relheight=0.9)
  1475. self.toolbar.place(relx=0, rely=0.9, relwidth=1.0, relheight=0.1)
  1476. for btn, text, func, x in zip([self.reset_btn, self.reverse_btn, self.refresh_btn, self.export_btn],
  1477. ["复位选择", "反转选择", "刷新数据", "导出数据"],
  1478. [self.reset, self.reverse, self.refresh, self.export],
  1479. [0.37, 0.53, 0.69, 0.85]):
  1480. btn['font'] = btn_font
  1481. btn['bg'] = Config.tk_btn_bg
  1482. btn['text'] = text
  1483. btn['command'] = func
  1484. btn.place(relx=x, rely=0.91, relwidth=0.15, relheight=0.08)
  1485. self.legend_show[0]['font'] = btn_font
  1486. self.legend_show[0]['bg'] = self.color
  1487. self.legend_show[0]['text'] = "显示图例"
  1488. self.legend_show[0]['variable'] = self.legend_show[1]
  1489. self.legend_show[0].place(relx=0.21, rely=0.91, relwidth=0.15, relheight=0.08)
  1490. def export(self, title, func: Callable):
  1491. path = asksaveasfilename(title='选择CSV文件保存位置', filetypes=[("CSV", ".csv")])
  1492. if not path.endswith(".csv"):
  1493. path += ".csv"
  1494. with open(path, "w") as f:
  1495. f.write(f"Hour, Count, {title}\n")
  1496. for i in self.export_lst:
  1497. f.write(f"{i[0]}, {i[1]}, {func(i)}\n")
  1498. self.station.show_msg("保存数据", f"数据导出成功\n保存位置:\n {path}")
  1499. def refresh(self):
  1500. self.plt_1.cla()
  1501. self.plt_2.cla()
  1502. def reset(self):
  1503. self.color_show_dict.update(self.color_hide_dict)
  1504. self.color_hide_dict = {}
  1505. self.update_listbox()
  1506. def reverse(self):
  1507. tmp = self.color_show_dict
  1508. self.color_show_dict = self.color_hide_dict
  1509. self.color_hide_dict = tmp
  1510. self.update_listbox()
  1511. def show_result(self, res: Dict[str, any], lst: List):
  1512. bottom = np.zeros(24)
  1513. label_num = [i for i in range(24)]
  1514. label_str = [f"{i}" for i in range(24)]
  1515. res_type_lst: List = res['res_type']
  1516. self.export_lst = lst
  1517. for res_type in res_type_lst:
  1518. res_count: Tuple[str] = res[res_type]
  1519. if len(res_count) != 0:
  1520. color = self.check_show(res_type)
  1521. if color is None:
  1522. continue
  1523. y = [0 for _ in range(24)]
  1524. for i in res_count:
  1525. y[int(i[0])] += int(i[1])
  1526. self.color_show_dict[res_type] = color
  1527. self.plt_1.bar(label_num, y,
  1528. color=color,
  1529. align="center",
  1530. bottom=bottom,
  1531. tick_label=label_str,
  1532. label=res_type)
  1533. self.plt_2.plot(label_num, y,
  1534. color=color,
  1535. label=res_type)
  1536. bottom += np.array(y)
  1537. if self.legend_show[1].get() == 1: # 显示图例
  1538. self.plt_1.legend(loc="upper left")
  1539. self.plt_2.legend(loc="upper left")
  1540. self.plt_1.set_xlim(-1, 24)
  1541. self.plt_1.set_xticks([i for i in range(0, 24, 2)])
  1542. self.plt_1.set_xticklabels([f"{i}h" for i in range(0, 24, 2)])
  1543. self.plt_1.set_title(f"{self.program_title}柱状图")
  1544. self.plt_2.set_xlim(-1, 24)
  1545. self.plt_2.set_xticks([i for i in range(0, 24, 2)])
  1546. self.plt_2.set_xticklabels([f"{i}h" for i in range(0, 24, 2)])
  1547. self.plt_2.set_title(f"{self.program_title}折现图")
  1548. self.canvas.draw()
  1549. self.toolbar.update()
  1550. self.update_listbox()
  1551. def set_disable(self):
  1552. self.export_btn['state'] = 'disable'
  1553. self.reset_btn['state'] = 'disable'
  1554. self.refresh_btn['state'] = 'disable'
  1555. self.reverse_btn['state'] = 'disable'
  1556. self.btn_show['state'] = 'disable'
  1557. self.btn_hide['state'] = 'disable'
  1558. def reset_disable(self):
  1559. self.export_btn['state'] = 'normal'
  1560. self.reset_btn['state'] = 'normal'
  1561. self.refresh_btn['state'] = 'normal'
  1562. self.reverse_btn['state'] = 'normal'
  1563. self.btn_show['state'] = 'normal'
  1564. self.btn_hide['state'] = 'normal'
  1565. class StatisticsTimeLocProgram(StatisticsTimeProgramBase):
  1566. def __init__(self, station, win, color):
  1567. super().__init__(station, win, color, "时段分析-按投放区域")
  1568. self._conf("#abc88b")
  1569. def refresh(self):
  1570. super().refresh()
  1571. event = tk_event.CountTimeEvent(self.station).start(["Location"], lambda i: i[2], self)
  1572. self.station.push_event(event)
  1573. def export(self, *_, **__):
  1574. super().export("Location", lambda i: i[2])
  1575. class StatisticsTimeTypeProgram(StatisticsTimeProgramBase):
  1576. def __init__(self, station, win, color):
  1577. super().__init__(station, win, color, "时段分析-按投放类型")
  1578. self._conf("#abc88b")
  1579. self.color_show_dict[GarbageType.GarbageTypeStrList_ch[1]] = "#00BFFF"
  1580. self.color_show_dict[GarbageType.GarbageTypeStrList_ch[2]] = "#32CD32"
  1581. self.color_show_dict[GarbageType.GarbageTypeStrList_ch[3]] = "#DC143C"
  1582. self.color_show_dict[GarbageType.GarbageTypeStrList_ch[4]] = "#A9A9A9"
  1583. def refresh(self):
  1584. super().refresh()
  1585. event = tk_event.CountTimeEvent(self.station).start(["GarbageType"], self.get_name, self)
  1586. self.station.push_event(event)
  1587. def export(self, *_, **__):
  1588. super().export("Type", self.get_name)
  1589. @staticmethod
  1590. def get_name(i: Tuple):
  1591. data: bytes = i[2]
  1592. return GarbageType.GarbageTypeStrList_ch[int(data.decode('utf-8'))]
  1593. class StatisticsTimeTypeLocProgram(StatisticsTimeProgramBase):
  1594. def __init__(self, station, win, color):
  1595. super().__init__(station, win, color, "时段分析-按投放类型和区域")
  1596. self._conf("#abc88b")
  1597. def refresh(self):
  1598. super().refresh()
  1599. event = tk_event.CountTimeEvent(self.station).start(["GarbageType", "Location"], self.get_name, self)
  1600. self.station.push_event(event)
  1601. def export(self, *_, **__):
  1602. super().export("Type-Location", self.get_name)
  1603. @staticmethod
  1604. def get_name(i: Tuple):
  1605. data: bytes = i[2]
  1606. return f"{GarbageType.GarbageTypeStrList_ch[int(data.decode('utf-8'))]}-{i[3]}"
  1607. class StatisticsTimeCheckResultProgram(StatisticsTimeProgramBase):
  1608. def __init__(self, station, win, color):
  1609. super().__init__(station, win, color, "时段分析-按检查结果")
  1610. self._conf("#abc88b")
  1611. self.color_show_dict['Pass'] = "#00BFFF"
  1612. self.color_show_dict['Fail'] = "#DC143C"
  1613. def refresh(self):
  1614. super().refresh()
  1615. event = tk_event.CountTimeEvent(self.station).start(["CheckResult"], self.get_name, self)
  1616. self.station.push_event(event)
  1617. def export(self, *_, **__):
  1618. super().export("Result", self.get_name)
  1619. @staticmethod
  1620. def get_name(i: Tuple):
  1621. data: bytes = i[2]
  1622. return 'Pass' if data == DBBit.BIT_1 else 'Fail'
  1623. class StatisticsTimeCheckResultAndTypeProgram(StatisticsTimeProgramBase):
  1624. def __init__(self, station, win, color):
  1625. super().__init__(station, win, color, "时段分析-按检查结果和类型")
  1626. self._conf("#abc88b")
  1627. def refresh(self):
  1628. super().refresh()
  1629. event = tk_event.CountTimeEvent(self.station).start(["CheckResult", "GarbageType"], self.get_name, self)
  1630. self.station.push_event(event)
  1631. def export(self, *_, **__):
  1632. super().export("Result-Location", self.get_name)
  1633. @staticmethod
  1634. def get_name(i: Tuple):
  1635. data_1: bytes = i[2]
  1636. data_2: bytes = i[3]
  1637. return ((f'Pass' if data_1 == DBBit.BIT_1 else 'Fail') +
  1638. f'-{GarbageType.GarbageTypeStrList_ch[int(data_2.decode("utf-8"))]}')
  1639. class StatisticsTimeCheckResultAndLocProgram(StatisticsTimeProgramBase):
  1640. def __init__(self, station, win, color):
  1641. super().__init__(station, win, color, "时段分析-按检查结果和区域")
  1642. self._conf("#abc88b")
  1643. def refresh(self):
  1644. super().refresh()
  1645. event = tk_event.CountTimeEvent(self.station).start(["CheckResult", "Location"], self.get_name, self)
  1646. self.station.push_event(event)
  1647. def export(self, *_, **__):
  1648. super().export("Result-Type", self.get_name)
  1649. @staticmethod
  1650. def get_name(i: Tuple):
  1651. data_1: bytes = i[2]
  1652. return (f'Pass' if data_1 == DBBit.BIT_1 else 'Fail') + f"-{i[3]}"
  1653. class StatisticsTimeDetailProgram(StatisticsTimeProgramBase):
  1654. def __init__(self, station, win, color):
  1655. super().__init__(station, win, color, "时段分析-详细分类")
  1656. self._conf("#abc88b")
  1657. def refresh(self):
  1658. super().refresh()
  1659. event = tk_event.CountTimeEvent(self.station)
  1660. event.start(["CheckResult", "GarbageType", "Location"], self.get_name, self)
  1661. self.station.push_event(event)
  1662. def export(self, *_, **__):
  1663. super().export("Detail", self.get_name)
  1664. @staticmethod
  1665. def get_name(i: Tuple):
  1666. data_1: bytes = i[2]
  1667. data_2: bytes = i[3]
  1668. return ((f'Pass' if data_1 == DBBit.BIT_1 else 'Fail') +
  1669. f'-{GarbageType.GarbageTypeStrList_ch[int(data_2.decode("utf-8"))]}' + f'-{i[4]}')
  1670. class StatisticsUserBaseProgram(AdminProgram):
  1671. def __init__(self, station, win, color, title: str):
  1672. super().__init__(station, win, color, title)
  1673. self.figure_frame = tk.Frame(self.frame)
  1674. self.figure = Figure(dpi=100)
  1675. self.plt: Axes = self.figure.add_subplot(111) # 添加子图:1行1列第1个
  1676. self.figure.subplots_adjust(bottom=0.2, top=0.93)
  1677. self.canvas = FigureCanvasTkAgg(self.figure, master=self.figure_frame)
  1678. self.canvas_tk = self.canvas.get_tk_widget()
  1679. self.toolbar = NavigationToolbar2Tk(self.canvas, self.figure_frame)
  1680. self.color_bar: Optional[Colorbar] = None
  1681. self.export_lst: Optional[np.array] = None
  1682. self.export_btn = tk.Button(self.frame)
  1683. self.refresh_btn = tk.Button(self.frame)
  1684. self._conf("#abc88b")
  1685. self.__conf_font()
  1686. def _conf(self, bg_color):
  1687. self.bg_color = bg_color
  1688. def __conf_font(self, n: int = 1):
  1689. self.btn_font_size = int(14 * n)
  1690. def conf_gui(self, n: int = 1):
  1691. self.__conf_font(n)
  1692. btn_font = make_font(size=self.btn_font_size)
  1693. self.figure_frame['bg'] = self.bg_color
  1694. self.figure_frame['bd'] = 5
  1695. self.figure_frame['relief'] = "ridge"
  1696. self.figure_frame.place(relx=0.00, rely=0.02, relwidth=1, relheight=0.88)
  1697. self.canvas_tk.place(relx=0, rely=0, relwidth=1.0, relheight=0.9)
  1698. self.toolbar.place(relx=0, rely=0.9, relwidth=1.0, relheight=0.1)
  1699. for btn, text, func, x in zip([self.refresh_btn, self.export_btn],
  1700. ["刷新数据", "导出数据"],
  1701. [self.refresh, self.export],
  1702. [0.34, 0.51]):
  1703. btn['font'] = btn_font
  1704. btn['bg'] = Config.tk_btn_bg
  1705. btn['text'] = text
  1706. btn['command'] = func
  1707. btn.place(relx=x, rely=0.91, relwidth=0.15, relheight=0.08)
  1708. def export(self):
  1709. ...
  1710. def refresh(self, event_class):
  1711. self.plt.cla()
  1712. if self.color_bar is not None:
  1713. self.color_bar.remove()
  1714. event = event_class(self.station).start(self)
  1715. self.station.push_event(event)
  1716. def set_disable(self):
  1717. self.export_btn['state'] = 'disable'
  1718. self.refresh_btn['state'] = 'disable'
  1719. def reset_disable(self):
  1720. self.export_btn['state'] = 'normal'
  1721. self.refresh_btn['state'] = 'normal'
  1722. class StatisticsUserTinyProgram(StatisticsUserBaseProgram):
  1723. def __init__(self, station, win, color):
  1724. super(StatisticsUserTinyProgram, self).__init__(station, win, color, "积分信用分析-细致")
  1725. def show_result(self, lst: np.array):
  1726. self.export_lst = lst
  1727. x_label = [f'{i * 10}' for i in range(0, 51, 10)]
  1728. y_label = [f'{i * 10}' for i in range(0, 101, 20)]
  1729. im = self.plt.pcolormesh(lst, cmap='Blues') # 用cmap设置配色方案
  1730. self.plt.set_xticks(range(0, 101, 20)) # 设置x轴刻度
  1731. self.plt.set_yticks(range(0, 101, 20)) # 设置y轴刻度
  1732. self.plt.set_xticklabels(x_label) # 设置x轴刻度标签
  1733. self.plt.set_yticklabels(y_label) # 设置y轴刻度标签
  1734. self.plt.set_xlabel("用户积分") # 设置x轴刻度标签
  1735. self.plt.set_ylabel("垃圾分类信用") # 设置y轴刻度标签
  1736. self.color_bar = self.figure.colorbar(im, pad=0.03, ax=self.plt) # 设置颜色条
  1737. self.plt.set_title("积分信用分析-细致热图") # 设置标题以及其位置和字体大小
  1738. self.canvas.draw()
  1739. self.toolbar.update()
  1740. def export(self):
  1741. if self.export_lst is None:
  1742. self.station.show_msg("保存数据", f"没有数据需要保存")
  1743. return
  1744. path = asksaveasfilename(title='选择CSV文件保存位置', filetypes=[("CSV", ".csv")])
  1745. if not path.endswith(".csv"):
  1746. path += ".csv"
  1747. with open(path, "w") as f:
  1748. f.write("#, " + ", ".join([f'[{i * 10} {i * 10 + 10}]' for i in range(0, 100, 1)]) + "\n")
  1749. for i, lst in zip(range(0, 50, 1), self.export_lst):
  1750. f.write(f"[{i * 10} {i * 10 + 10}], " + ", ".join([f"{a}" for a in lst]) + "\n")
  1751. self.station.show_msg("保存数据", f"数据导出成功\n保存位置:\n {path}")
  1752. def to_program(self):
  1753. self.refresh()
  1754. def refresh(self, _=None):
  1755. super().refresh(tk_event.CountScoreReputationTinyEvent)
  1756. class StatisticsUserLargeProgram(StatisticsUserBaseProgram):
  1757. def __init__(self, station, win, color):
  1758. super(StatisticsUserLargeProgram, self).__init__(station, win, color, "积分信用分析-大致")
  1759. def show_result(self, lst: np.array):
  1760. self.export_lst = lst
  1761. x_label = [f'{i * 10}' for i in range(0, 51, 10)]
  1762. y_label = [f'{i * 10}' for i in range(0, 101, 20)]
  1763. im = self.plt.pcolormesh(lst, cmap='Blues') # 用cmap设置配色方案
  1764. self.plt.set_xticks(range(0, 11, 2)) # 设置x轴刻度
  1765. self.plt.set_yticks(range(0, 11, 2)) # 设置y轴刻度
  1766. self.plt.set_xticklabels(x_label) # 设置x轴刻度标签
  1767. self.plt.set_yticklabels(y_label) # 设置y轴刻度标签
  1768. self.plt.set_xlabel("用户积分") # 设置x轴刻度标签
  1769. self.plt.set_ylabel("垃圾分类信用") # 设置y轴刻度标签
  1770. self.color_bar = self.figure.colorbar(im, pad=0.03, ax=self.plt) # 设置颜色条
  1771. self.plt.set_title("积分信用分析-大致热图") # 设置标题以及其位置和字体大小
  1772. self.canvas.draw()
  1773. self.toolbar.update()
  1774. def export(self):
  1775. if self.export_lst is None:
  1776. self.station.show_msg("保存数据", f"没有数据需要保存")
  1777. return
  1778. path = asksaveasfilename(title='选择CSV文件保存位置', filetypes=[("CSV", ".csv")])
  1779. if not path.endswith(".csv"):
  1780. path += ".csv"
  1781. with open(path, "w") as f:
  1782. f.write("#, " + ", ".join([f'[{i * 10} {i * 10 + 100}]' for i in range(0, 100, 10)]) + "\n")
  1783. for i, lst in zip(range(0, 50, 5), self.export_lst):
  1784. f.write(f"[{i * 10} {i * 10 + 50}], " + ", ".join([f"{a}" for a in lst]) + "\n")
  1785. self.station.show_msg("保存数据", f"数据导出成功\n保存位置:\n {path}")
  1786. def to_program(self):
  1787. self.refresh()
  1788. def refresh(self, _=None):
  1789. super().refresh(tk_event.CountScoreReputationLargeEvent)
  1790. class StatisticsScoreDistributedProgram(StatisticsUserBaseProgram):
  1791. def __init__(self, station, win, color):
  1792. super(StatisticsScoreDistributedProgram, self).__init__(station, win, color, "积分分布")
  1793. def show_result(self, lst: np.array):
  1794. bins = [i for i in range(0, 501, 10)]
  1795. res = self.plt.hist(lst, bins)
  1796. self.export_lst = res[0]
  1797. self.plt.set_xlabel("用户积分") # 设置x轴刻度标签
  1798. self.plt.set_ylabel("分布") # 设置x轴刻度标签
  1799. self.plt.set_title("积分分布直方图") # 设置标题以及其位置和字体大小
  1800. self.canvas.draw()
  1801. self.toolbar.update()
  1802. def export(self):
  1803. if self.export_lst is None:
  1804. self.station.show_msg("保存数据", f"没有数据需要保存")
  1805. return
  1806. path = asksaveasfilename(title='选择CSV文件保存位置', filetypes=[("CSV", ".csv")])
  1807. if not path.endswith(".csv"):
  1808. path += ".csv"
  1809. with open(path, "w") as f:
  1810. f.write("积分区间," + ", ".join([f'[{i * 10} {i * 10 + 100}]' for i in range(0, 501, 10)]) + "\n")
  1811. f.write("积分分布," + ", ".join([f'{i}' for i in self.export_lst]) + "\n")
  1812. self.station.show_msg("保存数据", f"数据导出成功\n保存位置:\n {path}")
  1813. def to_program(self):
  1814. self.refresh()
  1815. def refresh(self, _=None):
  1816. self.plt.cla()
  1817. if self.color_bar is not None:
  1818. self.color_bar.remove()
  1819. event = tk_event.ScoreReputationDistributedEvent(self.station).start("Score", self)
  1820. self.station.push_event(event)
  1821. class StatisticsReputationDistributedProgram(StatisticsUserBaseProgram):
  1822. def __init__(self, station, win, color):
  1823. super(StatisticsReputationDistributedProgram, self).__init__(station, win, color, "垃圾分类信用分布")
  1824. def show_result(self, lst: np.array):
  1825. bins = [i for i in range(0, 1001, 20)]
  1826. res = self.plt.hist(lst, bins)
  1827. self.export_lst = res[0]
  1828. self.plt.set_xlabel("垃圾分类信用") # 设置x轴刻度标签
  1829. self.plt.set_ylabel("分布") # 设置x轴刻度标签
  1830. self.plt.set_title("垃圾分类信用分布直方图") # 设置标题以及其位置和字体大小
  1831. self.canvas.draw()
  1832. self.toolbar.update()
  1833. def export(self):
  1834. if self.export_lst is None:
  1835. self.station.show_msg("保存数据", f"没有数据需要保存")
  1836. return
  1837. path = asksaveasfilename(title='选择CSV文件保存位置', filetypes=[("CSV", ".csv")])
  1838. if not path.endswith(".csv"):
  1839. path += ".csv"
  1840. with open(path, "w") as f:
  1841. f.write("信用区间," + ", ".join([f'[{i * 10} {i * 10 + 100}]' for i in range(0, 501, 10)]) + "\n")
  1842. f.write("信用分布," + ", ".join([f'{i}' for i in self.export_lst]) + "\n")
  1843. self.station.show_msg("保存数据", f"数据导出成功\n保存位置:\n {path}")
  1844. def to_program(self):
  1845. self.refresh()
  1846. def refresh(self, _=None):
  1847. self.plt.cla()
  1848. if self.color_bar is not None:
  1849. self.color_bar.remove()
  1850. event = tk_event.ScoreReputationDistributedEvent(self.station).start("Reputation", self)
  1851. self.station.push_event(event)
  1852. class StatisticsPassRateGlobalProgram(StatisticsUserBaseProgram):
  1853. def __init__(self, station, win, color):
  1854. super(StatisticsPassRateGlobalProgram, self).__init__(station, win, color, "通过率-全局")
  1855. def show_result(self, lst: np.array):
  1856. passing = float(lst[0][0])
  1857. not_passing = 1 - passing
  1858. data = [passing, not_passing]
  1859. label = ["通过", "未通过"]
  1860. res = self.plt.pie(data, radius=1, pctdistance=0.7, textprops=dict(color='w'), # 不显示文字
  1861. startangle=45, autopct="%6.3f%%", wedgeprops=dict(width=0.6, edgecolor="w"))
  1862. self.plt.legend(res[0], label, loc="lower left")
  1863. self.plt.set_title("全局垃圾分类通过率") # 设置标题以及其位置和字体大小
  1864. self.plt.table(cellText=[data], cellLoc="center", colLabels=label,
  1865. rowLabels=['全局'], rowLoc='center', loc='bottom', colWidths=[0.4] * 2)
  1866. self.canvas.draw()
  1867. self.toolbar.update()
  1868. def export(self):
  1869. self.station.show_msg("保存数据", f"数据不支持导出")
  1870. return
  1871. def to_program(self):
  1872. self.refresh()
  1873. def refresh(self, _=None):
  1874. self.plt.cla()
  1875. event = tk_event.PassingRateEvent(self.station).start([], [], [], [], self)
  1876. self.station.push_event(event)
  1877. class StatisticsPassRateTypeProgram(StatisticsUserBaseProgram):
  1878. def __init__(self, station, win, color):
  1879. super(StatisticsPassRateTypeProgram, self).__init__(station, win, color, "通过率-按类型")
  1880. def show_result(self, lst: List[Tuple[bytes, any]]):
  1881. data_1, data_2, data_3, data_4 = [1.0, 0.0], [1.0, 0.0], [1.0, 0.0], [1.0, 0.0]
  1882. for i in lst:
  1883. tmp: bytes = i[0]
  1884. type_ = tmp.decode('utf-8')
  1885. if type_ == '1':
  1886. data_1 = [float(i[1]), 1 - float(i[1])]
  1887. elif type_ == '2':
  1888. data_2 = [float(i[1]), 1 - float(i[1])]
  1889. elif type_ == '3':
  1890. data_3 = [float(i[1]), 1 - float(i[1])]
  1891. elif type_ == '4':
  1892. data_4 = [float(i[1]), 1 - float(i[1])]
  1893. legend_text = []
  1894. for data, r, s in zip([data_1, data_2, data_3, data_4], [0.3, 0.6, 0.9, 1.2], [0, 15, 30, 45]):
  1895. res = self.plt.pie(data, radius=r, pctdistance=0.7, # 不显示文字
  1896. startangle=s, autopct="%6.3f%%", wedgeprops=dict(width=0.3, edgecolor="w"))
  1897. legend_text += res[0]
  1898. label = []
  1899. for i in GarbageType.GarbageTypeStrList_ch[1:]:
  1900. label.append(f"{i}-通过")
  1901. label.append(f"{i}-不通过")
  1902. self.plt.table(cellText=[data_1, data_2, data_3, data_4], cellLoc="center", colLabels=['通过', '未通过'],
  1903. rowLabels=GarbageType.GarbageTypeStrList_ch[1:], rowLoc='center', loc='bottom')
  1904. self.plt.legend(legend_text, label)
  1905. self.plt.set_title("全局垃圾分类通过率") # 设置标题以及其位置和字体大小
  1906. self.canvas.draw()
  1907. self.toolbar.update()
  1908. def export(self):
  1909. self.station.show_msg("保存数据", f"数据不支持导出")
  1910. return
  1911. def to_program(self):
  1912. self.refresh()
  1913. def refresh(self, _=None):
  1914. self.plt.cla()
  1915. event = tk_event.PassingRateEvent(self.station).start(["GarbageType"],
  1916. [],
  1917. ["g.GarbageType=garbage.GarbageType"],
  1918. ["GarbageType"], self)
  1919. self.station.push_event(event)
  1920. class StatisticsPassRateLocProgram(StatisticsUserBaseProgram):
  1921. def __init__(self, station, win, color):
  1922. super(StatisticsPassRateLocProgram, self).__init__(station, win, color, "通过率-按区域")
  1923. self.loc_frame = tk.Frame(self.frame)
  1924. self.loc_title = tk.Label(self.loc_frame)
  1925. self.loc_enter = tk.Entry(self.loc_frame), tk.StringVar()
  1926. def conf_gui(self, n: int = 1):
  1927. super(StatisticsPassRateLocProgram, self).conf_gui(n)
  1928. title_font = make_font(size=16)
  1929. self.loc_frame['bg'] = self.bg_color
  1930. self.loc_frame['bd'] = 5
  1931. self.loc_frame['relief'] = "ridge"
  1932. self.loc_frame.place(relx=0.0, rely=0.92, relwidth=0.33, relheight=0.07)
  1933. self.loc_title['font'] = title_font
  1934. self.loc_title['text'] = "区域:"
  1935. self.loc_title['bg'] = self.bg_color
  1936. self.loc_title['anchor'] = 'e'
  1937. self.loc_enter[0]['font'] = title_font
  1938. self.loc_enter[0]['textvariable'] = self.loc_enter[1]
  1939. self.loc_title.place(relx=0.0, rely=0.02, relwidth=0.3, relheight=0.96)
  1940. self.loc_enter[0].place(relx=0.3, rely=0.02, relwidth=0.7, relheight=0.96)
  1941. def show_result(self, lst: np.array):
  1942. passing = float(lst[0][0])
  1943. label = ["通过", "未通过"]
  1944. not_passing = 1 - passing
  1945. data = [passing, not_passing]
  1946. res = self.plt.pie(data, radius=1, pctdistance=0.7, textprops=dict(color='w'), # 不显示文字
  1947. startangle=45, autopct="%6.3f%%", wedgeprops=dict(width=0.6, edgecolor="w"))
  1948. self.plt.legend(res[0], label, loc="lower left")
  1949. self.plt.table(cellText=[data], cellLoc="center", colLabels=label,
  1950. rowLabels=[f"区域"], rowLoc='center', loc='bottom')
  1951. self.canvas.draw()
  1952. self.toolbar.update()
  1953. def to_program(self):
  1954. self.refresh()
  1955. def refresh(self, _=None):
  1956. where = self.loc_enter[1].get()
  1957. if len(where) == 0:
  1958. where = "全局"
  1959. where_ = []
  1960. else:
  1961. where_ = [f"Location='{where}'"]
  1962. self.plt.cla()
  1963. self.plt.set_title(f"{where}垃圾分类通过率") # 设置标题以及其位置和字体大小
  1964. event = tk_event.PassingRateEvent(self.station).start([], where_, where_, [], self)
  1965. self.station.push_event(event)
  1966. class StatisticsPassRateTypeAndLocProgram(StatisticsUserBaseProgram):
  1967. def __init__(self, station, win, color):
  1968. super(StatisticsPassRateTypeAndLocProgram, self).__init__(station, win, color, "通过率-按类型和区域")
  1969. self.loc_frame = tk.Frame(self.frame)
  1970. self.loc_title = tk.Label(self.loc_frame)
  1971. self.loc_enter = tk.Entry(self.loc_frame), tk.StringVar()
  1972. def conf_gui(self, n: int = 1):
  1973. super(StatisticsPassRateTypeAndLocProgram, self).conf_gui(n)
  1974. title_font = make_font(size=16)
  1975. self.loc_frame['bg'] = self.bg_color
  1976. self.loc_frame['relief'] = "ridge"
  1977. self.loc_frame['bd'] = 5
  1978. self.loc_frame.place(relx=0.0, rely=0.92, relwidth=0.33, relheight=0.07)
  1979. self.loc_title['font'] = title_font
  1980. self.loc_title['bg'] = self.bg_color
  1981. self.loc_title['text'] = "区域:"
  1982. self.loc_title['anchor'] = 'e'
  1983. self.loc_enter[0]['font'] = title_font
  1984. self.loc_enter[0]['textvariable'] = self.loc_enter[1]
  1985. self.loc_title.place(relx=0.0, rely=0.02, relwidth=0.3, relheight=0.96)
  1986. self.loc_enter[0].place(relx=0.3, rely=0.02, relwidth=0.7, relheight=0.96)
  1987. def show_result(self, lst: List[Tuple[bytes, any]]):
  1988. data_1, data_2, data_3, data_4 = [1.0, 0.0], [1.0, 0.0], [1.0, 0.0], [1.0, 0.0]
  1989. for i in lst:
  1990. tmp: bytes = i[0]
  1991. type_ = tmp.decode('utf-8')
  1992. if type_ == '4':
  1993. data_4 = [float(i[1]), 1 - float(i[1])]
  1994. elif type_ == '3':
  1995. data_3 = [float(i[1]), 1 - float(i[1])]
  1996. elif type_ == '2':
  1997. data_2 = [float(i[1]), 1 - float(i[1])]
  1998. elif type_ == '1':
  1999. data_1 = [float(i[1]), 1 - float(i[1])]
  2000. legend_text = []
  2001. for data, r, s in zip([data_1, data_2, data_3, data_4], [0.3, 0.6, 0.9, 1.2], [5, 20, 35, 50]):
  2002. res = self.plt.pie(data, radius=r, pctdistance=0.7, # 不显示文字
  2003. startangle=s, autopct="%6.3f%%", wedgeprops=dict(width=0.3, edgecolor="w"))
  2004. legend_text += res[0]
  2005. label = []
  2006. for i in GarbageType.GarbageTypeStrList_ch[1:]:
  2007. label.append(f"{i}-通过")
  2008. label.append(f"{i}-不通过")
  2009. self.plt.table(cellText=[data_1, data_2, data_3, data_4], cellLoc="center", colLabels=['通过', '未通过'],
  2010. rowLabels=GarbageType.GarbageTypeStrList_ch[1:], rowLoc='center', loc='bottom')
  2011. self.plt.legend(legend_text, label)
  2012. self.canvas.draw()
  2013. self.toolbar.update()
  2014. def export(self):
  2015. self.station.show_msg("保存数据", f"数据不支持导出")
  2016. return
  2017. def to_program(self):
  2018. self.refresh()
  2019. def refresh(self, _=None):
  2020. where = self.loc_enter[1].get()
  2021. if len(where) == 0:
  2022. where = "全局"
  2023. where_ = []
  2024. else:
  2025. where_ = [f"Location='{where}'"]
  2026. self.plt.cla()
  2027. self.plt.set_title(f"{where}垃圾分类通过率") # 设置标题以及其位置和字体大小
  2028. event = tk_event.PassingRateEvent(self.station).start(["GarbageType"],
  2029. where_,
  2030. where_ + ["g.GarbageType=garbage.GarbageType"],
  2031. ["GarbageType"], self)
  2032. self.station.push_event(event)
  2033. class StatisticsDateProgramBase(StatisticsTimeProgramBase):
  2034. def _conf(self, bg_color, days: int = 7, days_sep: int = 1):
  2035. super(StatisticsDateProgramBase, self)._conf(bg_color)
  2036. self._days = days
  2037. self._days_sep = days_sep
  2038. def export(self, title, func: Callable):
  2039. path = asksaveasfilename(title='选择CSV文件保存位置', filetypes=[("CSV", ".csv")])
  2040. if not path.endswith(".csv"):
  2041. path += ".csv"
  2042. with open(path, "w") as f:
  2043. f.write(f"Days, Count, {title}\n")
  2044. for i in self.export_lst:
  2045. f.write(f"{i[0]}, {i[1]}, {func(i)}\n")
  2046. self.station.show_msg("保存数据", f"数据导出成功\n保存位置:\n {path}")
  2047. def show_result(self, res: Dict[str, any], lst: List):
  2048. bottom = np.zeros(self._days)
  2049. label_num = [i for i in range(self._days)]
  2050. label_str = [f"{i}" for i in range(self._days)]
  2051. res_type_lst: List = res['res_type']
  2052. self.export_lst = lst
  2053. max_y_plot = 1 # max_y的最大值
  2054. max_y_bar = 1 # max_y的最大值
  2055. for res_type in res_type_lst:
  2056. res_count: Tuple[str] = res[res_type]
  2057. if len(res_count) != 0:
  2058. color = self.check_show(res_type)
  2059. if color is None:
  2060. continue
  2061. y = [0 for _ in range(self._days)]
  2062. for i in range(0, len(res_count)): # 反向迭代列表
  2063. index = self._days - res_count[i - 1][0] - 1 # 反向排序
  2064. y[index] += int(res_count[i - 1][1])
  2065. max_y_plot = max(max(y), max_y_plot)
  2066. self.color_show_dict[res_type] = color
  2067. self.plt_1.plot(label_num, y,
  2068. color=color,
  2069. label=res_type)
  2070. self.plt_2.bar(label_num, y,
  2071. color=color,
  2072. align="center",
  2073. bottom=bottom,
  2074. tick_label=label_str,
  2075. label=res_type)
  2076. bottom += np.array(y)
  2077. if self.legend_show[1].get() == 1: # 显示图例
  2078. self.plt_1.legend(loc="upper left")
  2079. self.plt_2.legend(loc="upper left")
  2080. self.plt_1.set_xlim(-1, self._days)
  2081. self.plt_1.set_xticks([i for i in range(0, self._days, self._days_sep)])
  2082. self.plt_1.set_xticklabels([f"{i}d" for i in range(self._days - 1, -1, -self._days_sep)]) # 倒序
  2083. self.plt_1.set_ylim(0, max_y_plot + max_y_plot * 0.1)
  2084. step = ceil(max_y_plot / 4) # 向上取整
  2085. if step > 0:
  2086. y_ticks = [i for i in range(0, max_y_plot, step)]
  2087. y_ticklabels = [f'{i}' for i in range(0, max_y_plot, step)]
  2088. else:
  2089. y_ticks = []
  2090. y_ticklabels = []
  2091. y_ticks.append(max_y_plot)
  2092. y_ticklabels.append(f"{max_y_plot}")
  2093. self.plt_1.set_yticks(y_ticks)
  2094. self.plt_1.set_yticklabels(y_ticklabels) # 倒序
  2095. self.plt_1.set_title(f"{self.program_title}折现图")
  2096. self.plt_2.set_xlim(-1, self._days)
  2097. self.plt_2.set_xticks([i for i in range(0, self._days, self._days_sep)])
  2098. self.plt_2.set_xticklabels([f"{i}d" for i in range(self._days - 1, -1, -self._days_sep)])
  2099. max_y_bar = int(max(bottom.max(), max_y_bar))
  2100. self.plt_2.set_ylim(0, max_y_bar + max_y_bar * 0.1)
  2101. step = ceil(max_y_bar / 4) # 向上取整
  2102. if step > 0:
  2103. y_ticks = [i for i in range(0, max_y_bar, step)]
  2104. y_ticklabels = [f'{i}' for i in range(0, max_y_bar, step)]
  2105. else:
  2106. y_ticks = []
  2107. y_ticklabels = []
  2108. y_ticks.append(max_y_bar)
  2109. y_ticklabels.append(f"{max_y_bar}")
  2110. self.plt_2.set_yticks(y_ticks)
  2111. self.plt_2.set_yticklabels(y_ticklabels) # 倒序
  2112. self.plt_2.set_title(f"{self.program_title}柱状图")
  2113. self.canvas.draw()
  2114. self.toolbar.update()
  2115. self.update_listbox()
  2116. class StatisticsDateTypeProgram(StatisticsDateProgramBase):
  2117. def __init__(self, station, win, color):
  2118. super().__init__(station, win, color, "最近7日-按投放类型")
  2119. self._conf("#abc88b", 7, 1)
  2120. self.color_show_dict[GarbageType.GarbageTypeStrList_ch[1]] = "#00BFFF"
  2121. self.color_show_dict[GarbageType.GarbageTypeStrList_ch[2]] = "#32CD32"
  2122. self.color_show_dict[GarbageType.GarbageTypeStrList_ch[3]] = "#DC143C"
  2123. self.color_show_dict[GarbageType.GarbageTypeStrList_ch[4]] = "#A9A9A9"
  2124. def refresh(self):
  2125. super().refresh()
  2126. event = tk_event.CountDateEvent(self.station).start(7, ["GarbageType"], self.get_name, self)
  2127. self.station.push_event(event)
  2128. def export(self, *_, **__):
  2129. super().export("Type", self.get_name)
  2130. @staticmethod
  2131. def get_name(i: Tuple):
  2132. data: bytes = i[2]
  2133. return GarbageType.GarbageTypeStrList_ch[int(data.decode('utf-8'))]
  2134. class StatisticsDateLocProgram(StatisticsDateProgramBase):
  2135. def __init__(self, station, win, color):
  2136. super().__init__(station, win, color, "最近7日-按投放区域")
  2137. self._conf("#abc88b", 7, 1)
  2138. def refresh(self):
  2139. super().refresh()
  2140. event = tk_event.CountDateEvent(self.station).start(7, ["Location"], lambda i: i[2], self)
  2141. self.station.push_event(event)
  2142. def export(self, *_, **__):
  2143. super().export("Location", lambda i: i[2])
  2144. class StatisticsDateTypeLocProgram(StatisticsDateProgramBase):
  2145. def __init__(self, station, win, color):
  2146. super().__init__(station, win, color, "最近7日-按投放类型和区域")
  2147. self._conf("#abc88b", 7, 1)
  2148. def refresh(self):
  2149. super().refresh()
  2150. event = tk_event.CountDateEvent(self.station).start(7, ["GarbageType", "Location"], self.get_name, self)
  2151. self.station.push_event(event)
  2152. def export(self, *_, **__):
  2153. super().export("Type-Location", self.get_name)
  2154. @staticmethod
  2155. def get_name(i: Tuple):
  2156. data: bytes = i[2]
  2157. return f"{GarbageType.GarbageTypeStrList_ch[int(data.decode('utf-8'))]}-{i[3]}"
  2158. class StatisticsDateCheckResultProgram(StatisticsDateProgramBase):
  2159. def __init__(self, station, win, color):
  2160. super().__init__(station, win, color, "最近7日-按检查结果")
  2161. self._conf("#abc88b", 7, 1)
  2162. self.color_show_dict['Pass'] = "#00BFFF"
  2163. self.color_show_dict['Fail'] = "#DC143C"
  2164. def refresh(self):
  2165. super().refresh()
  2166. event = tk_event.CountDateEvent(self.station).start(7, ["CheckResult"], self.get_name, self)
  2167. self.station.push_event(event)
  2168. def export(self, *_, **__):
  2169. super().export("Result", self.get_name)
  2170. @staticmethod
  2171. def get_name(i: Tuple):
  2172. data: int = i[2] # 返回garbage表时, BIT类型都是按bytes回传的, 但garbage_7和garbage_30会以int的方式回传
  2173. return 'Pass' if data == 1 else 'Fail'
  2174. class StatisticsDateCheckResultAndTypeProgram(StatisticsDateProgramBase):
  2175. def __init__(self, station, win, color):
  2176. super().__init__(station, win, color, "最近7日-按检查结果和类型")
  2177. self._conf("#abc88b", 7, 1)
  2178. def refresh(self):
  2179. super().refresh()
  2180. event = tk_event.CountDateEvent(self.station).start(7, ["CheckResult", "GarbageType"], self.get_name, self)
  2181. self.station.push_event(event)
  2182. def export(self, *_, **__):
  2183. super().export("Result-Location", self.get_name)
  2184. @staticmethod
  2185. def get_name(i: Tuple):
  2186. data_1: int = i[2]
  2187. data_2: bytes = i[3]
  2188. return ((f'Pass' if data_1 == 1 else 'Fail') +
  2189. f'-{GarbageType.GarbageTypeStrList_ch[int(data_2.decode("utf-8"))]}')
  2190. class StatisticsDateCheckResultAndLocProgram(StatisticsDateProgramBase):
  2191. def __init__(self, station, win, color):
  2192. super().__init__(station, win, color, "最近7日-按检查结果和区域")
  2193. self._conf("#abc88b", 7, 1)
  2194. def refresh(self):
  2195. super().refresh()
  2196. event = tk_event.CountDateEvent(self.station).start(7, ["CheckResult", "Location"], self.get_name, self)
  2197. self.station.push_event(event)
  2198. def export(self, *_, **__):
  2199. super().export("Result-Type", self.get_name)
  2200. @staticmethod
  2201. def get_name(i: Tuple):
  2202. data_1: int = i[2]
  2203. return (f'Pass' if data_1 == 1 else 'Fail') + f"-{i[3]}"
  2204. class StatisticsDateDetailProgram(StatisticsDateProgramBase):
  2205. def __init__(self, station, win, color):
  2206. super().__init__(station, win, color, "最近7日-详细分类")
  2207. self._conf("#abc88b", 7, 1)
  2208. def refresh(self):
  2209. super().refresh()
  2210. event = tk_event.CountDateEvent(self.station)
  2211. event.start(7, ["CheckResult", "GarbageType", "Location"], self.get_name, self)
  2212. self.station.push_event(event)
  2213. def export(self, *_, **__):
  2214. super().export("Detail", self.get_name)
  2215. @staticmethod
  2216. def get_name(i: Tuple):
  2217. data_1: int = i[2]
  2218. data_2: bytes = i[3]
  2219. return ((f'Pass' if data_1 == 1 else 'Fail') +
  2220. f'-{GarbageType.GarbageTypeStrList_ch[int(data_2.decode("utf-8"))]}' + f'-{i[4]}')
  2221. all_program = [WelcomeProgram, CreateNormalUserProgram, CreateManagerUserProgram, CreateAutoNormalUserProgram,
  2222. CreateGarbageProgram, DeleteUserProgram, DeleteUsersProgram, DeleteGarbageProgram,
  2223. DeleteGarbageMoreProgram, DeleteAllGarbageProgram, SearchUserProgram, SearchUserAdvancedProgram,
  2224. SearchGarbageProgram, SearchGarbageAdvancedProgram, SearchAdvancedProgram, UpdateUserScore,
  2225. UpdateUserReputation, UpdateGarbageTypeProgram, UpdateGarbageCheckResultProgram,
  2226. ExportGarbageProgram, ExportUserProgram, CreateUserFromCSVProgram, AboutProgram,
  2227. StatisticsTimeLocProgram, StatisticsTimeTypeProgram, StatisticsTimeTypeLocProgram,
  2228. StatisticsTimeCheckResultProgram, StatisticsTimeCheckResultAndTypeProgram,
  2229. StatisticsTimeCheckResultAndLocProgram, StatisticsTimeDetailProgram, StatisticsUserTinyProgram,
  2230. StatisticsUserLargeProgram, StatisticsScoreDistributedProgram, StatisticsReputationDistributedProgram,
  2231. StatisticsPassRateGlobalProgram, StatisticsPassRateTypeProgram, StatisticsPassRateLocProgram,
  2232. StatisticsPassRateTypeAndLocProgram, StatisticsDateTypeProgram, StatisticsDateLocProgram,
  2233. StatisticsDateTypeLocProgram, StatisticsDateCheckResultProgram, StatisticsDateCheckResultAndTypeProgram,
  2234. StatisticsDateCheckResultAndLocProgram, StatisticsDateDetailProgram]