station.py 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395
  1. import threading
  2. import time
  3. import conf
  4. import cv2
  5. import sys
  6. import random
  7. import traceback
  8. import abc
  9. import tkinter as tk
  10. from tkinter import ttk
  11. import tkinter.font as font
  12. import datetime
  13. from PIL import Image, ImageTk
  14. from tool.type_ import *
  15. from core.user import User, UserNotSupportError
  16. from core.garbage import GarbageBag, GarbageType, GarbageBagNotUse
  17. from sql.db import DB
  18. from sql.user import update_user, find_user_by_id
  19. from sql.garbage import update_garbage
  20. from equipment.scan import HGSCapture, HGSQRCoder, QRCode
  21. from equipment.scan_user import scan_user
  22. from equipment.scan_garbage import scan_garbage
  23. from tk_event import TkEventBase
  24. class GarbageStationException(Exception):
  25. ...
  26. class ControlNotLogin(GarbageStationException):
  27. ...
  28. class ThrowGarbageError(GarbageStationException):
  29. ...
  30. class CheckGarbageError(GarbageStationException):
  31. ...
  32. class RankingUserError(GarbageStationException):
  33. ...
  34. class StationEventBase(TkEventBase):
  35. def __init__(self, gb_station, db: DB, title: str = 'unknown'):
  36. self.station: GarbageStationBase = gb_station
  37. self._db: DB = db
  38. self._title = title
  39. def get_title(self) -> str:
  40. return self._title
  41. def is_end(self) -> bool:
  42. raise GarbageStationException
  43. def done_after_event(self):
  44. raise GarbageStationException
  45. class GarbageStationBase(metaclass=abc.ABCMeta):
  46. status_normal = 1
  47. status_get_garbage_type = 2
  48. status_get_garbage_check = 3
  49. scan_switch_user = 1
  50. scan_throw_garbage = 2
  51. scan_check_garbage = 3
  52. scan_no_to_done = 4
  53. def __init__(self,
  54. db: DB,
  55. cap: HGSCapture,
  56. qr: HGSQRCoder,
  57. loc: location_t = conf.base_location):
  58. self._db: DB = db
  59. self._cap: HGSCapture = cap
  60. self._qr: HGSQRCoder = qr
  61. self._loc: location_t = loc
  62. self._user: Optional[User] = None # 操作者
  63. self._user_last_time: time_t = 0
  64. self._garbage: Optional[GarbageBag] = None
  65. self._flat = GarbageStationBase.status_normal
  66. self._have_easter_eggs = False
  67. self.rank = None
  68. self.rank_index = 0
  69. self._event_list: List[StationEventBase] = []
  70. self.set_after_run(conf.tk_refresh_delay, lambda: self.run_event())
  71. def update_user_time(self):
  72. if self.check_user():
  73. self._user_last_time = time.time()
  74. def get_user(self):
  75. return self._user
  76. def is_manager(self):
  77. if not self.check_user():
  78. return False
  79. return self._user.is_manager()
  80. def check_user(self):
  81. if self._user is None:
  82. return False
  83. if not self._user.is_manager() and time.time() - self._user_last_time > 20:
  84. self._user = None
  85. return False
  86. return True
  87. def __check_user(self):
  88. if not self.check_user():
  89. raise ControlNotLogin
  90. self._user_last_time = time.time()
  91. def __check_normal_user(self):
  92. self.__check_user()
  93. if self._user.is_manager():
  94. raise UserNotSupportError
  95. def __check_manager_user(self):
  96. self.__check_user()
  97. if not self._user.is_manager():
  98. raise UserNotSupportError
  99. def get_user_info(self):
  100. self.__check_user()
  101. return self._user.get_info()
  102. def get_uid_no_update(self):
  103. if not self.check_user():
  104. return ""
  105. return self._user.get_uid()
  106. def get_user_info_no_update(self) -> Dict[str, str]:
  107. if not self.check_user():
  108. return {}
  109. return self._user.get_info()
  110. def get_cap_img(self):
  111. return self._cap.get_frame()
  112. def switch_user(self, user: User) -> bool:
  113. """
  114. 切换用户: 退出/登录
  115. :param user: 新用户
  116. :return: 登录-True, 退出-False
  117. """
  118. if self._user is not None and self._user.get_uid() == user.get_uid() and self.check_user(): # 正在登陆期
  119. self._user = None # 退出登录
  120. self._user_last_time = 0
  121. return False
  122. self._user = user
  123. self._user_last_time = time.time()
  124. return True # 登录
  125. def throw_garbage_core(self, garbage: GarbageBag, garbage_type: enum):
  126. self.__check_normal_user()
  127. if not self._user.throw_rubbish(garbage, garbage_type, self._loc):
  128. raise ThrowGarbageError
  129. update_garbage(garbage, self._db)
  130. update_user(self._user, self._db)
  131. def check_garbage_core(self, garbage: GarbageBag, check_result: bool):
  132. self.__check_manager_user()
  133. user = find_user_by_id(garbage.get_user(), self._db)
  134. if user is None:
  135. raise GarbageBagNotUse
  136. if not self._user.check_rubbish(garbage, check_result, user):
  137. raise CheckGarbageError
  138. update_garbage(garbage, self._db)
  139. update_user(self._user, self._db)
  140. update_user(user, self._db)
  141. def ranking(self, limit: int = 0, order_by: str = 'DESC') -> list[Tuple[uid_t, uname_t, score_t, score_t]]:
  142. """
  143. 获取排行榜的功能
  144. :return:
  145. """
  146. if limit > 0:
  147. limit = f"LIMIT {int(limit)}"
  148. else:
  149. limit = ""
  150. if order_by != 'ASC' and order_by != 'DESC':
  151. order_by = 'DESC'
  152. cur = self._db.search((f"SELECT uid, name, score, reputation "
  153. f"FROM user "
  154. f"WHERE manager = 0 "
  155. f"ORDER BY reputation {order_by}, score {order_by} "
  156. f"{limit}"))
  157. if cur is None:
  158. raise RankingUserError
  159. return list(cur.fetchall())
  160. def to_get_garbage_type(self, garbage: GarbageBag):
  161. self._flat = GarbageStationBase.status_get_garbage_type
  162. self.set_garbage(garbage)
  163. def to_get_garbage_check(self, garbage: GarbageBag):
  164. self._flat = GarbageStationBase.status_get_garbage_check
  165. self.set_garbage(garbage)
  166. def set_garbage(self, garbage: GarbageBag):
  167. self._garbage = garbage
  168. def get_garbage(self) -> Optional[GarbageBag]:
  169. if not self.check_user():
  170. self._garbage = None
  171. return self._garbage
  172. def throw_garbage(self, garbage_type: enum):
  173. self.update_user_time()
  174. if self._flat != GarbageStationBase.status_get_garbage_type or self._garbage is None:
  175. self.show_warning("Operation Fail", "You should login first and scan the QR code of the trash bag")
  176. return
  177. event = ThrowGarbageEvent(self, self._db).start(self._garbage, garbage_type)
  178. self.push_event(event)
  179. self._flat = GarbageStationBase.status_normal
  180. self._garbage = None
  181. def check_garbage(self, check: bool):
  182. self.update_user_time()
  183. if self._flat != GarbageStationBase.status_get_garbage_check or self._garbage is None:
  184. self.show_warning("Operation Fail", "You should login first and scan the QR code of the trash bag")
  185. return
  186. event = CheckGarbageEvent(self, self._db).start(self._garbage, check)
  187. self.push_event(event)
  188. self._flat = GarbageStationBase.status_normal
  189. self._garbage = None
  190. def show_garbage_info(self):
  191. self.update_user_time()
  192. if self._flat != GarbageStationBase.status_get_garbage_check or self._garbage is None:
  193. self.show_warning("Operation Fail", "You should login first and scan the QR code of the trash bag")
  194. return
  195. if not self._garbage.is_use():
  196. self.show_warning("Operation Fail", "The garbage bag has not been used")
  197. return
  198. info = self._garbage.get_info()
  199. garbage_type = GarbageType.GarbageTypeStrList[int(info['type'])]
  200. if self._garbage.is_check():
  201. time_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(float(info['use_time'])))
  202. check = f'Checker is f{info["checker"][0:conf.tk_show_uid_len]}\n'
  203. if info["check"] == '1':
  204. check += f'CheckResult is Pass\n'
  205. else:
  206. check += f'CheckResult is Fail\n'
  207. self.show_msg("Garbage Info", (f"Type is {garbage_type}\n"
  208. f"User is {info['user'][0:conf.tk_show_uid_len]}\n"
  209. f"Location:\n {info['loc']}\n"
  210. f"{check}"
  211. f"Date:\n {time_str}")) # 遮蔽Pass和Fail按键
  212. elif self._garbage.is_use():
  213. time_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(float(info['use_time'])))
  214. self.show_msg("Garbage Info", (f"Type is {garbage_type}\n"
  215. f"User is {info['user'][0:conf.tk_show_uid_len]}\n"
  216. f"Location:\n {info['loc']}\n"
  217. f"Date:\n {time_str}"), big=False) # 不遮蔽Pass和Fail按键
  218. else:
  219. self.show_msg("Garbage Info", f"Garbage has not use") # 遮蔽Pass和Fail按键
  220. def show_user_info(self):
  221. self.update_user_time()
  222. if not self.check_user():
  223. self.show_warning("Operation Fail", "You should login first")
  224. return
  225. info = self.get_user_info()
  226. if info.get('manager', '0') == '1':
  227. self.show_msg("About User", (f"Manager User\n"
  228. f"UserName: {info['name']}\n"
  229. f"UserID:\n {info['uid']}"))
  230. else:
  231. self.show_msg("About User", (f"Normal User\n"
  232. f"UserName: {info['name']}\n"
  233. f"UserID:\n {info['uid']}\n"
  234. f"Score: {info['score']}\n"
  235. f"Reputation: {info['reputation']}\n"
  236. f"Rubbish: {info['rubbish']}"))
  237. def show_help_info(self):
  238. self.update_user_time()
  239. self.show_msg("Help", f'''
  240. HGSSystem:
  241. 1) Scan User QR code
  242. 2) Scan Garbage QR code
  243. 3) Select the type of garbage
  244. 4) Garbage submitted successfully
  245. (You can leave now)
  246. 5) Waiting for feedback
  247. '''.strip())
  248. def show_about_info(self):
  249. self.update_user_time()
  250. self.show_msg("About", f'''
  251. HGSSystem (c) SuperHuan
  252. From github
  253. HGSSystem is Garbage Sorting System
  254. Author: SongZihuan[SuperHuan]
  255. Run on python {sys.version}
  256. '''.strip())
  257. def show_exit(self):
  258. self.update_user_time()
  259. if self.is_manager():
  260. self.exit_win()
  261. return
  262. self.show_warning("Exit", f'Permission not permitted'.strip())
  263. def easter_eggs(self):
  264. self.update_user_time()
  265. if (not self._have_easter_eggs) and random.randint(0, 10) != 1: # 10% 概率触发
  266. return
  267. self._have_easter_eggs = True
  268. self.show_msg("Easter Agg", f'''
  269. 恭喜触发彩蛋[中文]
  270. 尝试一下新的编程语言: aFunlang.
  271. 来自: github
  272. [斯人若彩虹, 遇上方知有]
  273. [期待再次与你相遇]
  274. '''.strip())
  275. def show_search_info(self):
  276. self.update_user_time()
  277. self.show_msg("Search", f'''
  278. He will get the camera content and feedback the garbage type.
  279. The function has not yet been implemented.
  280. '''.strip())
  281. def thread_show_rank(self, rank_list):
  282. self.rank = [[]]
  283. for i, r in enumerate(rank_list):
  284. if len(self.rank[-1]) == 5:
  285. self.rank.append([])
  286. color = None
  287. if i == 0:
  288. color = "#eaff56"
  289. elif i == 1:
  290. color = "#ffa631"
  291. elif i == 2:
  292. color = "#ff7500"
  293. elif r[0] == self.get_uid_no_update():
  294. color = "#b0a4e3"
  295. self.rank[-1].append((i + 1, r[1], r[0], r[2], r[3], color))
  296. if len(self.rank[0]) == 0:
  297. self.rank = None
  298. self.show_warning("RankError", f'Unable to get leaderboard data')
  299. return
  300. self.rank_index = 0
  301. self.get_rank(0)
  302. def get_rank(self, n: int):
  303. self.update_user_time()
  304. self.rank_index += n
  305. if self.rank_index < 0 or self.rank_index >= len(self.rank):
  306. self.show_msg("RankError", f'Unable to get leaderboard data')
  307. return
  308. self.show_rank(self.rank_index + 1, len(self.rank), self.rank[self.rank_index])
  309. def scan(self):
  310. """
  311. 处理扫码事务
  312. 二维码扫描的任务包括: 登录, 扔垃圾, 标记垃圾
  313. :return:
  314. """
  315. self._cap.get_image()
  316. qr_code = self._qr.get_qr_code()
  317. if qr_code is None:
  318. return GarbageStationBase.scan_no_to_done, None
  319. user_scan_event = ScanUserEvent(self, self._db).start(qr_code)
  320. self.push_event(user_scan_event)
  321. def get_show_rank(self):
  322. event = RankingEvent(self, self._db)
  323. self.push_event(event)
  324. def push_event(self, event: StationEventBase):
  325. self._event_list.append(event)
  326. self.show_loading(event.get_title())
  327. self.run_event()
  328. def run_event(self):
  329. if len(self._event_list) == 0:
  330. return
  331. new_event: List[StationEventBase] = []
  332. done_event: List[StationEventBase] = []
  333. for event in self._event_list:
  334. if event.is_end():
  335. done_event.append(event)
  336. else:
  337. new_event.append(event)
  338. self._event_list = new_event
  339. if len(self._event_list) == 0:
  340. self.stop_loading()
  341. for event in done_event: # 隐藏进度条后执行Event-GUI任务
  342. try:
  343. event.done_after_event()
  344. except:
  345. traceback.print_exc()
  346. @abc.abstractmethod
  347. def show_msg(self, title, info, msg_type='info', big: bool = True):
  348. ...
  349. @abc.abstractmethod
  350. def show_warning(self, title, info):
  351. ...
  352. @abc.abstractmethod
  353. def show_rank(self, page: int, page_c: int,
  354. rank_info: List[Tuple[int, uname_t, uid_t, score_t, score_t, Optional[str]]],
  355. title: str = 'Ranking'):
  356. ...
  357. @abc.abstractmethod
  358. def hide_msg_rank(self, update: bool = False):
  359. ...
  360. @abc.abstractmethod
  361. def show_loading(self, title: str):
  362. ...
  363. @abc.abstractmethod
  364. def stop_loading(self):
  365. ...
  366. @abc.abstractmethod
  367. def set_after_run(self, ms, func, *args):
  368. ...
  369. @abc.abstractmethod
  370. def update_control(self):
  371. ...
  372. @abc.abstractmethod
  373. def update_scan(self):
  374. ...
  375. @abc.abstractmethod
  376. def update_msg(self):
  377. ...
  378. @abc.abstractmethod
  379. def mainloop(self):
  380. ...
  381. @abc.abstractmethod
  382. def exit_win(self):
  383. ...
  384. class GarbageStation(GarbageStationBase):
  385. def set_after_run(self, ms, func, *args): # super.__init__可能会调用
  386. self.init_after_run_list.append((ms, func, args))
  387. def __conf_set_after_run(self):
  388. for ms, func, args in self.init_after_run_list:
  389. self.__define_after(ms, func, *args)
  390. def __init__(self,
  391. db: DB,
  392. cap: HGSCapture,
  393. qr: HGSQRCoder,
  394. loc: location_t = conf.base_location,
  395. refresh_delay: int = conf.tk_refresh_delay):
  396. self.init_after_run_list: List[Tuple[int, Callable, Tuple]] = []
  397. super(GarbageStation, self).__init__(db, cap, qr, loc)
  398. self.refresh_delay = refresh_delay
  399. self._window = tk.Tk()
  400. self._sys_height = self._window.winfo_screenheight()
  401. self._sys_width = self._window.winfo_screenwidth()
  402. self._win_height = int(self._sys_height * (2 / 3))
  403. self._win_width = int(self._sys_width * (2 / 3))
  404. self._full_screen = False
  405. self.__conf_windows()
  406. self._cap_img = None # 存储 PIL.image 的变量 防止gc释放
  407. self._user_im = None
  408. self._msg_time: Optional[float] = None # msg 显示时间累计
  409. self._disable_all_btn: bool = False # 禁用所有按钮和操作
  410. self.__creat_tk()
  411. self.__conf_tk()
  412. self.__conf_after()
  413. self.__switch_to_no_user()
  414. def __creat_tk(self):
  415. self.__conf_font_size()
  416. self._title_label = tk.Label(self._window)
  417. self._win_ctrl_button: Tuple[tk.Button, tk.Button, tk.Button] = (tk.Button(self._window),
  418. tk.Button(self._window),
  419. tk.Button(self._window))
  420. win_info_type = Tuple[tk.Label, tk.Label, tk.Variable, str]
  421. self._user_frame = tk.Frame(self._window)
  422. self._user_name: win_info_type = (tk.Label(self._user_frame), tk.Label(self._user_frame),
  423. tk.StringVar(), "UserName")
  424. self._user_uid: win_info_type = (tk.Label(self._user_frame), tk.Label(self._user_frame),
  425. tk.StringVar(), "UserID")
  426. self._user_score: win_info_type = (tk.Label(self._user_frame), tk.Label(self._user_frame),
  427. tk.StringVar(), "Score")
  428. self._user_rubbish: win_info_type = (tk.Label(self._user_frame), tk.Label(self._user_frame),
  429. tk.StringVar(), "Garbage")
  430. self._user_eval: win_info_type = (tk.Label(self._user_frame), tk.Label(self._user_frame),
  431. tk.StringVar(), "Reputation")
  432. self._user_img = tk.Label(self._user_frame)
  433. self._throw_ctrl_frame = tk.Frame(self._window)
  434. self._throw_ctrl_btn: List[tk.Button] = [tk.Button(self._throw_ctrl_frame), tk.Button(self._throw_ctrl_frame),
  435. tk.Button(self._throw_ctrl_frame), tk.Button(self._throw_ctrl_frame)]
  436. self._check_ctrl_frame = tk.Frame(self._window)
  437. self._check_ctrl_btn: List[tk.Button] = [tk.Button(self._check_ctrl_frame), tk.Button(self._check_ctrl_frame)]
  438. self._sys_info_frame = tk.Frame(self._window)
  439. self._garbage_id: win_info_type = (tk.Label(self._sys_info_frame), tk.Label(self._sys_info_frame),
  440. tk.StringVar(), "GID")
  441. self._sys_date: win_info_type = (tk.Label(self._sys_info_frame), tk.Label(self._sys_info_frame),
  442. tk.StringVar(), "Date")
  443. self._cap_label = tk.Label(self._window)
  444. self._user_btn_frame = tk.Frame(self._window)
  445. self._user_btn: List[tk.Button] = [tk.Button(self._user_btn_frame), tk.Button(self._user_btn_frame),
  446. tk.Button(self._user_btn_frame)]
  447. self._msg_frame = tk.Frame(self._window)
  448. self._msg_label = tk.Label(self._msg_frame), tk.Label(self._msg_frame), tk.StringVar(), tk.StringVar()
  449. self._msg_hide = tk.Button(self._msg_frame)
  450. self._rank_frame = tk.Frame(self._window)
  451. self._rank_label = [tk.Label(self._rank_frame),
  452. tk.Label(self._rank_frame),
  453. tk.Label(self._rank_frame),
  454. tk.Label(self._rank_frame),
  455. tk.Label(self._rank_frame),
  456. tk.Label(self._rank_frame)]
  457. self._rank_var = [tk.StringVar(),
  458. tk.StringVar(),
  459. tk.StringVar(),
  460. tk.StringVar(),
  461. tk.StringVar(),
  462. tk.StringVar()]
  463. self._rank_btn = [tk.Button(self._rank_frame), tk.Button(self._rank_frame), tk.Button(self._rank_frame)]
  464. self._loading_frame = tk.Frame(self._window)
  465. self._loading_title: Tuple[tk.Label, tk.Variable] = tk.Label(self._loading_frame), tk.StringVar()
  466. self._loading_pro = ttk.Progressbar(self._loading_frame)
  467. def __conf_font_size(self, n: Union[int, float] = 1):
  468. self._title_font_size = int(27 * n)
  469. self._win_ctrl_font_size = int(15 * n)
  470. self._win_info_font_size = int(18 * n)
  471. self._throw_ctrl_btn_font_size = int(20 * n)
  472. self._check_ctrl_btn_font_size = int(20 * n)
  473. self._sys_info_font_size = int(18 * n)
  474. self._user_btn_font_size = int(20 * n)
  475. self._msg_font_size = int(20 * n)
  476. self._rank_font_title_size = int(24 * n)
  477. self._rank_font_size = int(16 * n)
  478. self._loading_tile_font = int(20 * n)
  479. def __conf_tk(self):
  480. self.__conf_title_label()
  481. self.__conf_win_ctrl_button()
  482. self.__conf_user_info_label()
  483. self.__conf_throw_btn()
  484. self.__conf_check_btn()
  485. self.__conf_sys_info_label()
  486. self.__conf_cap_label()
  487. self.__conf_user_btn()
  488. self.__conf_msg()
  489. self.__conf_rank()
  490. self.__conf_loading()
  491. self.hide_msg_rank() # 隐藏消息
  492. def __conf_windows(self):
  493. self._window.title('HGSSystem: Garbage Station')
  494. self._window.geometry(f'{self._win_width}x{self._win_height}')
  495. self._window['bg'] = "#F0FFF0" # 蜜瓜绿
  496. self._window.attributes("-topmost", True)
  497. self._window.resizable(False, False)
  498. self._window.protocol("WM_DELETE_WINDOW", lambda: self.show_exit())
  499. self._window.overrideredirect(False) # 显示标题栏
  500. self._window.bind("<Alt-Control-KeyPress-s>", lambda _: self.__set_windows_overrideredirect(True)) # 锁定窗口
  501. def lock_windows(_):
  502. if self._disable_all_btn:
  503. return
  504. self.__set_windows_overrideredirect(True)
  505. def unlock_windows(_):
  506. if self._disable_all_btn:
  507. return
  508. if self.is_manager():
  509. self.__set_windows_overrideredirect(False)
  510. return
  511. self.show_warning("Exit", f'Permission not permitted'.strip())
  512. def full_screen_windows(_):
  513. if self._disable_all_btn:
  514. return
  515. if not self._full_screen or self.is_manager():
  516. self.__full_screen(not self._full_screen)
  517. return
  518. self.show_warning("Exit", f'Permission not permitted'.strip())
  519. def easter_eggs(_):
  520. if self._disable_all_btn:
  521. return
  522. self.easter_eggs()
  523. self._window.bind("<Alt-Control-KeyPress-s>", lock_windows) # 锁定窗口
  524. self._window.bind("<Alt-Control-KeyPress-e>", unlock_windows)
  525. self._window.bind("<F11>", full_screen_windows)
  526. self._window.bind("<F5>", easter_eggs)
  527. def __full_screen(self, full: bool = True):
  528. self._window.attributes("-fullscreen", full)
  529. self._full_screen = full
  530. width = self._sys_width * (2 / 3)
  531. height = self._sys_height * (2 / 3)
  532. self._win_width = self._window.winfo_width()
  533. self._win_height = self._window.winfo_height()
  534. n = ((self._win_height / height) + (self._win_width / width)) / 2 # 平均放大倍数
  535. self.__conf_font_size(n)
  536. self.__conf_tk()
  537. def __set_windows_overrideredirect(self, show: bool = False):
  538. self._window.overrideredirect(show)
  539. def __conf_title_label(self):
  540. title_font = self.__make_font(size=self._title_font_size, weight="bold")
  541. self._title_label['font'] = title_font
  542. self._title_label['bg'] = "#F0FFF0" # 蜜瓜绿
  543. self._title_label['text'] = "HGSSystem: GarbageStation Control Center"
  544. self._title_label['anchor'] = 'w'
  545. self._title_label.place(relx=0.02, rely=0.0, relwidth=0.6, relheight=0.07)
  546. def __conf_win_ctrl_button(self):
  547. title_font = self.__make_font(size=self._win_ctrl_font_size)
  548. for bt in self._win_ctrl_button:
  549. bt: tk.Button
  550. bt['font'] = title_font
  551. bt['bg'] = "#B0C4DE" # 浅钢青
  552. bt_help: tk.Button = self._win_ctrl_button[0]
  553. bt_help['text'] = 'Help'
  554. bt_help['bg'] = '#A9A9A9'
  555. bt_help['command'] = lambda: self.show_help_info()
  556. bt_help.place(relx=0.81, rely=0.01, relwidth=0.05, relheight=0.05)
  557. bt_about: tk.Button = self._win_ctrl_button[1]
  558. bt_about['text'] = 'About'
  559. bt_about['bg'] = '#A9A9A9'
  560. bt_about['command'] = lambda: self.show_about_info()
  561. bt_about.place(relx=0.87, rely=0.01, relwidth=0.05, relheight=0.05)
  562. bt_exit: tk.Button = self._win_ctrl_button[2]
  563. bt_exit['text'] = 'Exit'
  564. bt_exit['bg'] = '#A9A9A9'
  565. bt_exit['command'] = lambda: self.show_exit()
  566. bt_exit.place(relx=0.93, rely=0.01, relwidth=0.05, relheight=0.05)
  567. def __conf_user_info_label(self):
  568. title_font = self.__make_font(size=self._win_info_font_size - 1, weight="bold")
  569. info_font = self.__make_font(size=self._win_info_font_size + 1)
  570. frame_width = self._win_width * 0.4
  571. frame_height = self._win_height * 0.4
  572. self._user_frame['bg'] = "#FA8072"
  573. self._user_frame.place(relx=0.02, rely=0.1, relwidth=0.4, relheight=0.40)
  574. self._user_frame['bd'] = 5
  575. self._user_frame['relief'] = "ridge"
  576. h_label = 5
  577. h_label_s = 1
  578. h_top = 2
  579. height_count = h_label * 5 + h_label_s * 4 + h_top * 2
  580. height_label = h_label / height_count
  581. height = h_top / height_count
  582. for lb_list in [self._user_score, self._user_rubbish, self._user_eval, self._user_name, self._user_uid]:
  583. lb_list[0]['font'] = title_font
  584. lb_list[0]['bg'] = "#FA8072"
  585. lb_list[0]['fg'] = "#FFB6C1"
  586. lb_list[0]['text'] = lb_list[3] + " " * (10 - len(lb_list[3])) + " :"
  587. lb_list[0]['anchor'] = 'e'
  588. lb_list[0].place(relx=0.0, rely=height, relwidth=0.35, relheight=height_label)
  589. height += height_label + h_label_s / height_count
  590. for lb_list in [self._user_score, self._user_rubbish, self._user_eval, self._user_name, self._user_uid]:
  591. lb_list[1]['font'] = info_font
  592. lb_list[1]['bg'] = "#FA8073"
  593. lb_list[1]['fg'] = "#000000"
  594. lb_list[1]['textvariable'] = lb_list[2]
  595. lb_list[1]['anchor'] = 'w'
  596. lb_list[2].set('test')
  597. height = h_top / height_count
  598. for lb_list in [self._user_score, self._user_rubbish, self._user_eval]:
  599. lb_list[1].place(relx=0.36, rely=height, relwidth=0.19, relheight=height_label)
  600. height += height_label + h_label_s / height_count
  601. for lb_list in [self._user_name, self._user_uid]:
  602. lb_list[1].place(relx=0.36, rely=height, relwidth=0.63, relheight=height_label)
  603. height += height_label + h_label_s / height_count
  604. img_relwidth = 0.30
  605. img_relheight = height_label * 3 + (h_label_s / height_count) * 2
  606. img = (Image.open(conf.pic_d['head']).
  607. resize((int(img_relwidth * frame_width), int(img_relheight * frame_height))))
  608. self._user_im = ImageTk.PhotoImage(image=img)
  609. self._user_img['image'] = self._user_im
  610. self._user_img.place(relx=1 - img_relwidth - 0.06, rely=0.09,
  611. relwidth=img_relwidth, relheight=img_relheight)
  612. self._user_img['bd'] = 5
  613. self._user_img['relief'] = "ridge"
  614. def __conf_throw_btn(self):
  615. btn_font = self.__make_font(size=self._throw_ctrl_btn_font_size, weight="bold")
  616. btn_info: List[Tuple[str, str]] = [("Recyclable", "#00BFFF"),
  617. ("Other", "#A9A9A9"),
  618. ("Hazardous", "#DC143C"),
  619. ("Kitchen", "#32CD32")]
  620. self.__show_throw_frame()
  621. for btn, info in zip(self._throw_ctrl_btn, btn_info):
  622. btn['font'] = btn_font
  623. btn['bg'] = info[1]
  624. btn['text'] = info[0]
  625. self._throw_ctrl_btn[0]['command'] = lambda: self.throw_garbage(GarbageType.recyclable)
  626. self._throw_ctrl_btn[1]['command'] = lambda: self.throw_garbage(GarbageType.other)
  627. self._throw_ctrl_btn[2]['command'] = lambda: self.throw_garbage(GarbageType.hazardous)
  628. self._throw_ctrl_btn[3]['command'] = lambda: self.throw_garbage(GarbageType.kitchen)
  629. self._throw_ctrl_btn[0].place(relx=0.000, rely=0.000, relwidth=0.495, relheight=0.495)
  630. self._throw_ctrl_btn[1].place(relx=0.505, rely=0.000, relwidth=0.495, relheight=0.495)
  631. self._throw_ctrl_btn[2].place(relx=0.000, rely=0.505, relwidth=0.495, relheight=0.495)
  632. self._throw_ctrl_btn[3].place(relx=0.505, rely=0.505, relwidth=0.495, relheight=0.495)
  633. def __conf_check_btn(self):
  634. btn_font = self.__make_font(size=self._check_ctrl_btn_font_size, weight="bold")
  635. btn_info: List[Tuple[str, str]] = [("Fail", "#ef7a82"),
  636. ("Pass", "#70f3ff")]
  637. self.__show_check_frame()
  638. for btn, info in zip(self._check_ctrl_btn, btn_info):
  639. btn['font'] = btn_font
  640. btn['text'] = info[0]
  641. btn['bg'] = info[1]
  642. self._check_ctrl_btn[0]['command'] = lambda: self.check_garbage(False)
  643. self._check_ctrl_btn[1]['command'] = lambda: self.check_garbage(True)
  644. self._check_ctrl_btn[0].place(relx=0.000, rely=0.000, relwidth=0.495, relheight=1)
  645. self._check_ctrl_btn[1].place(relx=0.505, rely=0.000, relwidth=0.495, relheight=1)
  646. def __conf_sys_info_label(self):
  647. title_font = self.__make_font(size=self._sys_info_font_size - 1, weight="bold")
  648. info_font = self.__make_font(size=self._sys_info_font_size + 1)
  649. self._sys_info_frame['bg'] = "#F0F8FF"
  650. self._sys_info_frame.place(relx=0.02, rely=0.51, relwidth=0.4, relheight=0.14)
  651. self._sys_info_frame['bd'] = 5
  652. self._sys_info_frame['relief'] = "ridge"
  653. h_label = 5
  654. h_label_s = 1
  655. h_top = 2
  656. height_count = h_label * 2 + h_label_s * 1 + h_top * 2
  657. height_label = h_label / height_count
  658. height = h_top / height_count
  659. for info_list in [self._garbage_id, self._sys_date]:
  660. info_list[0]['font'] = title_font
  661. info_list[0]['bg'] = "#F0F8FF"
  662. info_list[0]['anchor'] = 'e'
  663. info_list[0]['text'] = info_list[3] + " " * (10 - len(info_list[3])) + " :"
  664. info_list[0].place(relx=0.0, rely=height, relwidth=0.35, relheight=height_label)
  665. height += height_label + h_label_s / height_count
  666. height = h_top / height_count
  667. for info_list in [self._garbage_id, self._sys_date]:
  668. info_list[1]['font'] = info_font
  669. info_list[1]['bg'] = "#F0F8FF"
  670. info_list[1]['textvariable'] = info_list[2]
  671. info_list[1]['anchor'] = 'w'
  672. info_list[2].set('test')
  673. info_list[1].place(relx=0.36, rely=height, relwidth=0.63, relheight=height_label)
  674. height += height_label + h_label_s / height_count
  675. def __conf_user_btn(self):
  676. btn_font = self.__make_font(size=self._user_btn_font_size, weight="bold")
  677. btn_info: List[Tuple[str, str]] = [("Detail", "#b0a4e3"),
  678. ("Ranking", "#b0a4e3"),
  679. ("Search", "#b0a4e3")]
  680. self._user_btn_frame.place(relx=0.02, rely=0.66, relwidth=0.19, relheight=0.32)
  681. self._user_btn_frame['bg'] = "#F0FFF0"
  682. h_label = 5
  683. h_label_s = 1
  684. height_count = h_label * 3 + h_label_s * 2
  685. height_label = h_label / (h_label * 3 + h_label_s * 2)
  686. height = 0
  687. for btn, info in zip(self._user_btn, btn_info):
  688. btn['font'] = btn_font
  689. btn['text'] = info[0]
  690. btn['bg'] = info[1]
  691. btn.place(relx=0.0, rely=height, relwidth=1.00, relheight=height_label)
  692. height += height_label + h_label_s / height_count
  693. self._user_btn[0]['state'] = 'disable'
  694. self._user_btn[1]['command'] = lambda: self.get_show_rank()
  695. self._user_btn[2]['command'] = lambda: self.show_search_info()
  696. def __conf_cap_label(self):
  697. self._cap_label['bg'] = "#000000"
  698. self._cap_label.place(relx=0.22, rely=0.66, relwidth=0.2, relheight=0.32)
  699. def __conf_msg(self):
  700. title_font = self.__make_font(size=self._msg_font_size + 1, weight="bold")
  701. info_font = self.__make_font(size=self._msg_font_size - 1)
  702. self._msg_frame['bg'] = "#F5FFFA"
  703. self._msg_frame['bd'] = 5
  704. self._msg_frame['relief'] = "ridge"
  705. # frame 不会立即显示
  706. self._msg_label[0]['font'] = title_font
  707. self._msg_label[0]['bg'] = "#F5FFFA"
  708. self._msg_label[0]['anchor'] = 'w'
  709. self._msg_label[0]['textvariable'] = self._msg_label[2]
  710. self._msg_label[1]['font'] = info_font
  711. self._msg_label[1]['bg'] = "#F5FFFA"
  712. self._msg_label[1]['anchor'] = 'nw'
  713. self._msg_label[1]['justify'] = 'left'
  714. self._msg_label[1]['textvariable'] = self._msg_label[3]
  715. self._msg_label[0].place(relx=0.05, rely=0.05, relwidth=0.9, relheight=0.1)
  716. self._msg_label[1].place(relx=0.075, rely=0.2, relwidth=0.85, relheight=0.64)
  717. self._msg_hide['font'] = info_font
  718. self._msg_hide['bg'] = "#00CED1"
  719. self._msg_hide['text'] = 'close'
  720. self._msg_hide['command'] = lambda: self.hide_msg_rank(True)
  721. self._msg_hide.place(relx=0.375, rely=0.85, relwidth=0.25, relheight=0.1)
  722. def show_msg(self, title, info, msg_type='info', big: bool = True):
  723. if self._disable_all_btn:
  724. return
  725. self._msg_label[2].set(f'{msg_type}: {title}')
  726. self._msg_label[3].set(f'{info}')
  727. frame_width = self._win_width * 0.53
  728. self._msg_label[1]['wraplength'] = frame_width * 0.85 - 5 # 设定自动换行的像素
  729. if big:
  730. self._msg_frame.place(relx=0.45, rely=0.15, relwidth=0.53, relheight=0.70)
  731. self._check_ctrl_frame.place_forget()
  732. self._throw_ctrl_frame.place_forget()
  733. self._rank_frame.place_forget()
  734. else:
  735. self._msg_frame.place(relx=0.45, rely=0.20, relwidth=0.53, relheight=0.50)
  736. self.__show_check_frame()
  737. self._throw_ctrl_frame.place_forget()
  738. self._rank_frame.place_forget()
  739. self._msg_time = time.time()
  740. def show_warning(self, title, info):
  741. self.show_msg(title, info, msg_type='Warning')
  742. def __conf_rank(self):
  743. title_font = self.__make_font(size=self._rank_font_title_size, weight="bold")
  744. info_font = self.__make_font(size=self._rank_font_size)
  745. btn_font = self.__make_font(size=self._msg_font_size - 1)
  746. self._rank_frame['bg'] = "#F5F5DC"
  747. self._rank_frame['relief'] = "ridge"
  748. self._rank_frame['bd'] = 5
  749. # frame 不会立即显示
  750. self._rank_label[0]['font'] = title_font
  751. self._rank_label[0]['bg'] = "#F5F5DC"
  752. self._rank_label[0]['textvariable'] = self._rank_var[0]
  753. self._rank_label[0].place(relx=0.02, rely=0.00, relwidth=0.96, relheight=0.1)
  754. for lb, v in zip(self._rank_label[1:], self._rank_var[1:]):
  755. lb['font'] = info_font
  756. lb['bg'] = "#F5FFFA"
  757. lb['textvariable'] = v
  758. lb['relief'] = "ridge"
  759. lb['bd'] = 2
  760. # 标签结束的高度为 0.12 + 0.15 * 5 = 0.87
  761. for btn, text in zip(self._rank_btn, ("prev", "close", "next")):
  762. btn['font'] = btn_font
  763. btn['bg'] = "#00CED1"
  764. btn['text'] = text
  765. self._rank_btn[0].place(relx=0.050, rely=0.88, relwidth=0.25, relheight=0.1)
  766. self._rank_btn[0]['command'] = lambda: self.get_rank(-1)
  767. self._rank_btn[1].place(relx=0.375, rely=0.88, relwidth=0.25, relheight=0.1)
  768. self._rank_btn[1]['command'] = lambda: self.hide_msg_rank(True)
  769. self._rank_btn[2].place(relx=0.700, rely=0.88, relwidth=0.25, relheight=0.1)
  770. self._rank_btn[2]['command'] = lambda: self.get_rank(+1)
  771. def __set_rank_info(self, rank_info: List[Tuple[int, uname_t, uid_t, score_t, score_t, Optional[str]]]):
  772. if len(rank_info) > 5:
  773. rank_info = rank_info[:5]
  774. for lb in self._rank_label[1:]: # 隐藏全部标签
  775. lb.place_forget()
  776. height = 0.12
  777. for i, info in enumerate(rank_info):
  778. no, name, uid, score, eval_, color = info
  779. self._rank_var[i + 1].set(f"NO.{no} {name}\nUID: {uid[0:conf.ranking_tk_show_uid_len]}\n"
  780. f"Score: {score} Reputation: {eval_}")
  781. if color is None:
  782. self._rank_label[i + 1]['bg'] = "#F5FFFA"
  783. else:
  784. self._rank_label[i + 1]['bg'] = color
  785. self._rank_label[i + 1].place(relx=0.04, rely=height, relwidth=0.92, relheight=0.13)
  786. height += 0.15
  787. def show_rank(self, page: int, page_c: int,
  788. rank_info: List[Tuple[int, uname_t, uid_t, score_t, score_t, Optional[str]]],
  789. title: str = 'Ranking'):
  790. if self._disable_all_btn:
  791. return
  792. self._rank_var[0].set(f'{title} ({page}/{page_c})')
  793. self._rank_frame.place(relx=0.47, rely=0.15, relwidth=0.47, relheight=0.80)
  794. frame_width = self._win_width * 0.53
  795. for lb in self._rank_label[1:]:
  796. lb['wraplength'] = frame_width * 0.85 - 5 # 设定自动换行的像素
  797. if page == 1:
  798. self._rank_btn[0]['state'] = 'disable'
  799. else:
  800. self._rank_btn[0]['state'] = 'normal'
  801. if page == page_c:
  802. self._rank_btn[2]['state'] = 'disable'
  803. else:
  804. self._rank_btn[2]['state'] = 'normal'
  805. self.__set_rank_info(rank_info)
  806. self._throw_ctrl_frame.place_forget()
  807. self._check_ctrl_frame.place_forget()
  808. self._msg_frame.place_forget()
  809. self._msg_time = None
  810. def hide_msg_rank(self, update: bool = False):
  811. self.__show_check_frame() # rank会令此消失
  812. self.__show_throw_frame() # rank和msg令此消失
  813. self._msg_frame.place_forget()
  814. self._rank_frame.place_forget()
  815. self._msg_time = None
  816. if update:
  817. self.update_user_time()
  818. def __conf_loading(self):
  819. title_font = self.__make_font(size=self._loading_tile_font, weight="bold")
  820. self._loading_frame['bg'] = "#808080"
  821. self._loading_frame['bd'] = 5
  822. self._loading_frame['relief'] = "ridge"
  823. # frame 不会立即显示
  824. self._loading_title[0]['font'] = title_font
  825. self._loading_title[0]['bg'] = "#808080"
  826. self._loading_title[0]['fg'] = "#F8F8FF"
  827. self._loading_title[0]['anchor'] = 'w'
  828. self._loading_title[0]['textvariable'] = self._loading_title[1]
  829. self._loading_title[0].place(relx=0.02, rely=0.00, relwidth=0.96, relheight=0.7)
  830. self._loading_pro['mode'] = 'indeterminate'
  831. self._loading_pro['orient'] = tk.HORIZONTAL
  832. self._loading_pro['maximum'] = 100
  833. self._loading_pro.place(relx=0.02, rely=0.73, relwidth=0.96, relheight=0.22)
  834. def show_loading(self, title: str):
  835. self.set_all_btn_disable()
  836. self._loading_title[1].set(f"Loading: {title}")
  837. self._loading_pro['value'] = 0
  838. self._loading_frame.place(relx=0.30, rely=0.40, relwidth=0.40, relheight=0.15)
  839. self._loading_pro.start(50)
  840. def stop_loading(self):
  841. self._loading_frame.place_forget()
  842. self._loading_pro.stop()
  843. self.set_reset_all_btn()
  844. def __show_check_frame(self):
  845. self._check_ctrl_frame.place(relx=0.45, rely=0.82, relwidth=0.53, relheight=0.16)
  846. def __show_throw_frame(self):
  847. self._throw_ctrl_frame.place(relx=0.45, rely=0.1, relwidth=0.53, relheight=0.70)
  848. def __define_after(self, ms, func, *args):
  849. self._window.after(ms, self.__after_func_maker(func), *args)
  850. def __conf_after(self):
  851. self.__define_after(self.refresh_delay, self.update_time)
  852. self.__define_after(self.refresh_delay, self.update_control)
  853. self.__define_after(self.refresh_delay, self.update_scan)
  854. self.__define_after(self.refresh_delay, self.update_msg)
  855. self.__conf_set_after_run()
  856. def __after_func_maker(self, func):
  857. def new_func(*args, **kwargs):
  858. try:
  859. func(*args, **kwargs)
  860. except: # 捕获未考虑的错误
  861. self.show_msg("System Exception", "Run Error...", "Error")
  862. traceback.print_exc()
  863. finally:
  864. self._window.after(self.refresh_delay, new_func)
  865. return new_func
  866. def update_time(self):
  867. var: tk.Variable = self._sys_date[2]
  868. t = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  869. var.set(f"{t}")
  870. def update_control(self):
  871. name: tk.Variable = self._user_name[2]
  872. uid: tk.Variable = self._user_uid[2]
  873. score: tk.Variable = self._user_score[2]
  874. rubbish: tk.Variable = self._user_rubbish[2]
  875. eval_: tk.Variable = self._user_eval[2]
  876. user_info: Dict[str, str] = self.get_user_info_no_update()
  877. if user_info.get('uid') is None:
  878. name.set('Not-Login')
  879. uid.set('Not-Login')
  880. eval_.set('---')
  881. rubbish.set('---')
  882. score.set('---')
  883. self.__switch_to_no_user()
  884. elif user_info.get('manager', '0') == '1':
  885. name.set(user_info.get('name'))
  886. uid_get = user_info.get('uid', None)
  887. if uid_get is None or len(uid_get) < 32:
  888. uid.set('error')
  889. else:
  890. uid.set(uid_get[0:21])
  891. eval_.set('Manager')
  892. rubbish.set('Manager')
  893. score.set('Manager')
  894. self.__switch_to_manager_user()
  895. else:
  896. name.set(user_info.get('name'))
  897. uid_get = user_info.get('uid', None)
  898. if uid_get is None or len(uid_get) < 32:
  899. uid.set('error')
  900. else:
  901. uid.set(uid_get[0:conf.tk_show_uid_len])
  902. eval_.set(user_info.get('reputation'))
  903. rubbish.set(user_info.get('rubbish'))
  904. score.set(user_info.get('score'))
  905. self.__switch_to_normal_user()
  906. garbage = self.get_garbage()
  907. if garbage is None:
  908. self._garbage_id[2].set('---')
  909. else:
  910. gid = garbage.get_gid()
  911. if len(gid) > 20:
  912. gid = gid[-20:]
  913. self._garbage_id[2].set(gid)
  914. def update_scan(self):
  915. self.scan()
  916. # 需要存储一些数据 谨防被gc释放
  917. _cap_img_info = (Image.fromarray(cv2.cvtColor(self.get_cap_img(), cv2.COLOR_BGR2RGB)).
  918. transpose(Image.FLIP_LEFT_RIGHT))
  919. self._cap_img = ImageTk.PhotoImage(image=_cap_img_info)
  920. self._cap_label['image'] = self._cap_img
  921. def update_msg(self):
  922. if self._msg_time is None:
  923. return
  924. if time.time() - self._msg_time > 10: # 10s 自动关闭消息
  925. self.hide_msg_rank()
  926. def __switch_to_normal_user(self):
  927. if self._disable_all_btn:
  928. return
  929. self.normal_user_disable()
  930. self.normal_user_able()
  931. def __switch_to_manager_user(self):
  932. if self._disable_all_btn:
  933. return
  934. self.manager_user_disable()
  935. self.manager_user_able()
  936. def __switch_to_no_user(self):
  937. self.manager_user_disable()
  938. self.normal_user_disable()
  939. self._user_btn[0]['state'] = 'disable'
  940. def normal_user_disable(self):
  941. for btn in self._check_ctrl_btn:
  942. btn['state'] = 'disabled'
  943. self._user_btn[0]['state'] = 'normal'
  944. self._user_btn[0]['command'] = lambda: self.show_user_info()
  945. def manager_user_disable(self):
  946. for btn in self._throw_ctrl_btn:
  947. btn['state'] = 'disabled'
  948. self._user_btn[0]['state'] = 'normal'
  949. self._user_btn[0]['command'] = lambda: self.show_garbage_info()
  950. def normal_user_able(self):
  951. for btn in self._throw_ctrl_btn:
  952. btn['state'] = 'normal'
  953. def manager_user_able(self):
  954. for btn in self._check_ctrl_btn:
  955. btn['state'] = 'normal'
  956. def set_all_btn_disable(self):
  957. self.__switch_to_no_user() # 禁用所有操作性按钮
  958. self.hide_msg_rank()
  959. for btn in self._user_btn:
  960. btn['state'] = 'disable'
  961. for btn in self._win_ctrl_button:
  962. btn['state'] = 'disable'
  963. self._disable_all_btn = True
  964. def set_reset_all_btn(self):
  965. for btn in self._user_btn:
  966. btn['state'] = 'normal'
  967. for btn in self._win_ctrl_button:
  968. btn['state'] = 'normal'
  969. self.update_control() # 位于_user_btn之后, 会自动设定detail按钮
  970. self._disable_all_btn = False
  971. @staticmethod
  972. def __make_font(family: str = 'noto', **kwargs):
  973. return font.Font(family=conf.font_d[family], **kwargs)
  974. def mainloop(self):
  975. self._window.mainloop()
  976. def exit_win(self):
  977. self._window.destroy()
  978. class ScanUserEvent(StationEventBase):
  979. class ScanUserThread(threading.Thread): # 继承父类threading.Thread
  980. def __init__(self, qr_: QRCode, db_: DB):
  981. threading.Thread.__init__(self)
  982. self.thread_db = db_
  983. self.thread_qr = qr_
  984. self.result: Optional[User] = None
  985. def run(self):
  986. self.result = scan_user(self.thread_qr, self.thread_db)
  987. def __init__(self, gb_station: GarbageStationBase, db: DB):
  988. super().__init__(gb_station, db, "Scan User")
  989. self._user: User = gb_station.get_user()
  990. self._qr_code: Optional[QRCode] = None
  991. self.thread: Optional[ScanUserEvent.ScanUserThread] = None
  992. def start(self, qr_code: QRCode):
  993. self._qr_code = qr_code
  994. self.thread = ScanUserEvent.ScanUserThread(qr_code, self._db)
  995. self.thread.start()
  996. return self
  997. def is_end(self) -> bool:
  998. return self.thread is not None and not self.thread.is_alive()
  999. def done_after_event(self):
  1000. self.thread.join()
  1001. if self.thread.result is not None:
  1002. self.station.switch_user(self.thread.result)
  1003. self.station.update_control()
  1004. else:
  1005. event = ScanGarbageEvent(self.station, self._db).start(self._qr_code)
  1006. self.station.push_event(event)
  1007. class ScanGarbageEvent(StationEventBase):
  1008. class ScanUserThread(threading.Thread): # 继承父类threading.Thread
  1009. def __init__(self, qr_: QRCode, db_: DB):
  1010. threading.Thread.__init__(self)
  1011. self.thread_db = db_
  1012. self.thread_qr = qr_
  1013. self.result: Optional[GarbageBag] = None
  1014. def run(self):
  1015. self.result = scan_garbage(self.thread_qr, self.thread_db)
  1016. def __init__(self, gb_station: GarbageStationBase, db: DB):
  1017. super().__init__(gb_station, db, "Scan Garbage")
  1018. self._user: User = gb_station.get_user()
  1019. self._qr_code: Optional[QRCode] = None
  1020. self.thread: Optional[ScanGarbageEvent.ScanUserThread] = None
  1021. def start(self, qr_code: QRCode):
  1022. self._qr_code = qr_code
  1023. self.thread = ScanGarbageEvent.ScanUserThread(self._qr_code, self._db)
  1024. self.thread.start()
  1025. return self
  1026. def is_end(self) -> bool:
  1027. return self.thread is not None and not self.thread.is_alive()
  1028. def done_after_event(self):
  1029. self.thread.join()
  1030. if self.thread.result is not None:
  1031. if self._user is None:
  1032. self.station.show_warning("Operation Fail", "The garbage bags have been used.")
  1033. elif self._user.is_manager():
  1034. self.station.to_get_garbage_check(self.thread.result)
  1035. self.station.show_garbage_info() # 显示信息
  1036. self.station.update_control()
  1037. else:
  1038. self.station.to_get_garbage_type(self.thread.result)
  1039. self.station.hide_msg_rank() # 如果有msg也马上隐藏
  1040. self.station.update_control()
  1041. class RankingEvent(StationEventBase):
  1042. class RankingThread(threading.Thread):
  1043. def __init__(self, db_: DB):
  1044. threading.Thread.__init__(self)
  1045. self.thread_db = db_
  1046. self.result: Optional[List[Tuple[uid_t, uname_t, score_t, score_t]]] = None
  1047. def run(self):
  1048. cur = self.thread_db.search((f"SELECT uid, name, score, reputation "
  1049. f"FROM user "
  1050. f"WHERE manager = 0 "
  1051. f"ORDER BY reputation DESC, score DESC "
  1052. f"LIMIT 20;"))
  1053. if cur is None:
  1054. self.result = []
  1055. self.result = list(cur.fetchall())
  1056. def __init__(self, gb_station: GarbageStationBase, db: DB):
  1057. super().__init__(gb_station, db, "Ranking")
  1058. self._user: User = gb_station.get_user()
  1059. self.thread: Optional[RankingEvent.RankingThread] = RankingEvent.RankingThread(self._db)
  1060. self.thread.start()
  1061. def is_end(self) -> bool:
  1062. return not self.thread.is_alive()
  1063. def done_after_event(self):
  1064. self.thread.join()
  1065. if self.thread.result is not None:
  1066. self.station.thread_show_rank(self.thread.result)
  1067. class ThrowGarbageEvent(StationEventBase):
  1068. class ThrowGarbageThread(threading.Thread):
  1069. def __init__(self, gb_station: GarbageStationBase, garbage: GarbageBag, garbage_type: enum, db_: DB):
  1070. threading.Thread.__init__(self)
  1071. self.thread_station = gb_station
  1072. self.thread_db = db_
  1073. self.thread_garbage = garbage
  1074. self.thread_garbage_type = garbage_type
  1075. self.result: bool = False
  1076. def run(self):
  1077. try:
  1078. self.thread_station.throw_garbage_core(self.thread_garbage, self.thread_garbage_type)
  1079. except (ThrowGarbageError, UserNotSupportError, ControlNotLogin):
  1080. self.thread_station.show_warning("Operation Fail", "The garbage bags have been used.")
  1081. self.result = False
  1082. finally:
  1083. self.result = True
  1084. def __init__(self, gb_station: GarbageStationBase, db: DB):
  1085. super().__init__(gb_station, db, "ThrowGarbage")
  1086. self._user: User = gb_station.get_user()
  1087. self.thread: Optional[ThrowGarbageEvent.ThrowGarbageThread] = None
  1088. def start(self, garbage: GarbageBag, garbage_type: enum):
  1089. self.thread = ThrowGarbageEvent.ThrowGarbageThread(self.station, garbage, garbage_type, self._db)
  1090. self.thread.start()
  1091. return self
  1092. def is_end(self) -> bool:
  1093. return not self.thread.is_alive()
  1094. def done_after_event(self):
  1095. self.thread.join()
  1096. if not self.thread.result:
  1097. self.station.show_warning("Operation Fail", "The garbage bag throw error")
  1098. class CheckGarbageEvent(StationEventBase):
  1099. class CheckGarbageThread(threading.Thread):
  1100. def __init__(self, gb_station: GarbageStationBase, garbage: GarbageBag, check: bool, db_: DB):
  1101. threading.Thread.__init__(self)
  1102. self.thread_station = gb_station
  1103. self.thread_db = db_
  1104. self.thread_garbage = garbage
  1105. self.thread_garbage_check = check
  1106. self.result: bool = False
  1107. def run(self):
  1108. try:
  1109. self.thread_station.check_garbage_core(self.thread_garbage, self.thread_garbage_check)
  1110. except (ThrowGarbageError, UserNotSupportError, ControlNotLogin, CheckGarbageError):
  1111. self.thread_station.show_warning("Operation Fail", "The garbage bag has been checked")
  1112. self.result = False
  1113. finally:
  1114. self.result = True
  1115. def __init__(self, gb_station: GarbageStationBase, db: DB):
  1116. super().__init__(gb_station, db, "CheckGarbage")
  1117. self._user: User = gb_station.get_user()
  1118. self.thread: Optional[CheckGarbageEvent.CheckGarbageThread] = None
  1119. def start(self, garbage: GarbageBag, garbage_check: bool):
  1120. self.thread = CheckGarbageEvent.CheckGarbageThread(self.station, garbage, garbage_check, self._db)
  1121. self.thread.start()
  1122. return self
  1123. def is_end(self) -> bool:
  1124. return not self.thread.is_alive()
  1125. def done_after_event(self):
  1126. self.thread.join()
  1127. if not self.thread.result:
  1128. self.station.show_warning("Operation Fail", "The garbage bag check error")
  1129. if __name__ == '__main__':
  1130. mysql_db = DB()
  1131. capture = HGSCapture()
  1132. qr_capture = HGSQRCoder(capture)
  1133. station = GarbageStation(mysql_db, capture, qr_capture)
  1134. station.mainloop()