admin_program.py 118 KB

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