admin.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. import tkinter as tk
  2. from tkinter import ttk
  3. from tkinter import messagebox as msg
  4. import abc
  5. import conf
  6. from tool.type_ import *
  7. from tool.tk import make_font
  8. from sql.db import DB
  9. from sql.user import creat_new_user, del_user, del_user_from_where, del_user_from_where_scan
  10. from sql.garbage import creat_new_garbage, del_garbage_not_use, del_garbage_not_use_many, find_garbage
  11. from equipment.scan_user import write_uid_qr, write_all_uid_qr
  12. from equipment.scan_garbage import write_gid_qr
  13. from core.user import User
  14. from core.garbage import GarbageBag
  15. from event import TkEventMain
  16. class AdminStationException(Exception):
  17. ...
  18. class CreatGarbageError(AdminStationException):
  19. ...
  20. class CreatUserError(AdminStationException):
  21. ...
  22. class AdminStationBase(TkEventMain, metaclass=abc.ABCMeta):
  23. def __init__(self, db: DB):
  24. self._admin: Optional[User] = None
  25. self._db = db
  26. super(AdminStationBase, self).__init__()
  27. def get_db(self):
  28. return self._db
  29. def creat_garbage(self, path: Optional[str], num: int = 1) -> List[tuple[str, Optional[GarbageBag]]]:
  30. re = []
  31. for _ in range(num):
  32. gar = creat_new_garbage(self._db)
  33. if gar is None:
  34. raise CreatGarbageError
  35. if path is not None:
  36. res = write_gid_qr(gar.get_gid(), path, self._db)
  37. re.append(res)
  38. else:
  39. re.append(("", gar))
  40. return re
  41. def creat_user(self, name: uname_t, passwd: passwd_t, phone: str, manager: bool) -> Optional[User]:
  42. return creat_new_user(name, passwd, phone, manager, self._db)
  43. def creat_user_from_list(self, user_list: List[Tuple[uname_t, passwd_t, str]], manager: bool) -> List[User]:
  44. re = []
  45. for i in user_list:
  46. user = creat_new_user(i[0], i[1], i[2], manager, self._db)
  47. if user is None:
  48. raise CreatUserError
  49. re.append(user)
  50. return re
  51. def get_gid_qrcode(self, gid: gid_t, path: str) -> Tuple[str, Optional[GarbageBag]]:
  52. return write_gid_qr(gid, path, self._db)
  53. def get_all_gid_qrcode(self, path: str, where: str = "") -> List[str]:
  54. return write_all_uid_qr(path, self._db, where=where)
  55. def get_uid_qrcode(self, uid: uid_t, path: str) -> Tuple[str, Optional[User]]:
  56. return write_uid_qr(uid, path, self._db)
  57. def get_all_uid_qrcode(self, path: str, where: str = "") -> List[str]:
  58. return write_all_uid_qr(path, self._db, where=where)
  59. def del_garbage(self, gid: gid_t) -> bool:
  60. return del_garbage_not_use(gid, self._db)
  61. def del_garbage_many(self, from_: gid_t, to_: gid_t) -> int:
  62. return del_garbage_not_use_many(from_, to_, self._db)
  63. def del_user(self, uid: uid_t) -> bool:
  64. return del_user(uid, self._db)
  65. def del_user_from_where_scan(self, where: str) -> int:
  66. return del_user_from_where_scan(where, self._db)
  67. def del_user_from_where(self, where: str) -> int:
  68. return del_user_from_where(where, self._db)
  69. @abc.abstractmethod
  70. def login_call(self):
  71. ...
  72. def login(self, user: User) -> bool:
  73. if user is not None and user.is_manager():
  74. self._admin = user
  75. return True
  76. else:
  77. return False
  78. def logout(self):
  79. self._admin = None
  80. @abc.abstractmethod
  81. def show_loading(self, title: str):
  82. ...
  83. @abc.abstractmethod
  84. def stop_loading(self):
  85. ...
  86. @abc.abstractmethod
  87. def set_after_run(self, ms, func, *args):
  88. ...
  89. @abc.abstractmethod
  90. def to_menu(self, name: str = "Main"):
  91. ...
  92. @abc.abstractmethod
  93. def to_program(self, name: str = "Welcome"):
  94. ...
  95. @abc.abstractmethod
  96. def show_msg(self, title, info, msg_type='info'):
  97. ...
  98. @abc.abstractmethod
  99. def hide_msg(self):
  100. ...
  101. import admin_program as tk_program
  102. import admin_menu as tk_menu
  103. import admin_event as tk_event
  104. class AdminStation(AdminStationBase):
  105. def set_after_run(self, ms, func, *args): # super.__init__可能会调用
  106. self.init_after_run_list.append((ms, func, args))
  107. def __conf_set_after_run(self):
  108. for ms, func, args in self.init_after_run_list:
  109. self._window.after(ms, func, *args)
  110. def set_after_run_now(self, ms, func, *args):
  111. self._window.after(ms, func, *args)
  112. def __init__(self, db: DB, refresh_delay: int = conf.tk_refresh_delay):
  113. self.init_after_run_list: List[Tuple[int, Callable, Tuple]] = []
  114. super().__init__(db)
  115. self.refresh_delay = refresh_delay
  116. self._window = tk.Tk()
  117. self.login_window = None
  118. self._sys_height = self._window.winfo_screenheight()
  119. self._sys_width = self._window.winfo_screenwidth()
  120. self._win_height = int(self._sys_height * (2 / 3))
  121. self._win_width = int(self._sys_width * (2 / 3))
  122. self.__conf_windows()
  123. self._full_screen = False
  124. self._is_loading = False
  125. self._disable_all_btn = False
  126. self._menu_now: Optional[Tuple[str, tk.Frame, tk_menu.AdminMenu]] = None
  127. self._program_now: Optional[Tuple[str, tk.Frame, tk_program.AdminProgram]] = None
  128. self.__conf_font_size()
  129. self.__conf_creak_tk()
  130. self.__conf_creak_menu()
  131. self.__conf_creak_program()
  132. self.__conf_tk()
  133. # self.__show_login_window()
  134. self.__conf_set_after_run()
  135. def __conf_windows(self):
  136. self._window.title('HGSSystem: Manage Station')
  137. self._window.geometry(f'{self._win_width}x{self._win_height}')
  138. self._window['bg'] = "#F0FFF0"
  139. self._window.resizable(False, False)
  140. self._window.protocol("WM_DELETE_WINDOW", lambda: self.main_exit())
  141. def __conf_creak_tk(self):
  142. self._menu_back = tk.Frame(self._window)
  143. self._menu_line = tk.Label(self._menu_back) # 下划线
  144. self._menu_title: Tuple[tk.Label, tk.Variable] = tk.Label(self._menu_back), tk.StringVar()
  145. self._menu_dict: Dict[str, tk_menu.AdminMenu] = {}
  146. self._menu_list: List[str] = [] # 菜单回溯
  147. self._program_back = tk.Frame(self._window)
  148. self._program_title: Tuple[tk.Label, tk.Variable] = tk.Label(self._program_back), tk.StringVar()
  149. self._program_dict: Dict[str, tk_program.AdminProgram] = {}
  150. self._win_ctrl_button: List[tk.Button, tk.Button, tk.Button] = [tk.Button(self._menu_back),
  151. tk.Button(self._menu_back),
  152. tk.Button(self._window),
  153. tk.Button(self._window),
  154. tk.Button(self._window)]
  155. self._msg_frame = tk.Frame(self._window)
  156. self._msg_label = tk.Label(self._msg_frame), tk.Label(self._msg_frame), tk.StringVar(), tk.StringVar()
  157. self._msg_hide = tk.Button(self._msg_frame)
  158. self._loading_pro = ttk.Progressbar(self._window)
  159. def __conf_font_size(self, n: int = 1):
  160. self._login_title_font_size = int(12 * n)
  161. self._login_btn_font_size = int(11 * n)
  162. self._win_ctrl_font_size = int(15 * n)
  163. self._menu_title_font_size = int(17 * n)
  164. self._program_title_font_size = int(14 * n)
  165. self._msg_font_size = int(20 * n)
  166. def __conf_tk(self, n: int = 1):
  167. self.__conf_win_ctrl_button()
  168. self.__conf_menu_title()
  169. self.__conf_menu(n)
  170. self.__conf_program_title()
  171. self.__conf_program(n)
  172. self.__conf_loading()
  173. self.__conf_msg()
  174. self.to_menu() # 显示主页面
  175. self.to_program()
  176. def __conf_win_ctrl_button(self):
  177. title_font = make_font(size=self._win_ctrl_font_size)
  178. title_font_bold = make_font(size=self._win_ctrl_font_size, weight="bold")
  179. for bt in self._win_ctrl_button:
  180. bt: tk.Button
  181. bt['bg'] = conf.tk_btn_bg
  182. bt['font'] = title_font
  183. bt_main: tk.Button = self._win_ctrl_button[1]
  184. bt_main['text'] = 'Main'
  185. bt_main['font'] = title_font_bold
  186. bt_main['command'] = lambda: self.__to_main_menu()
  187. bt_main.place(relx=0.02, rely=0.86, relwidth=0.96, relheight=0.06)
  188. bt_back: tk.Button = self._win_ctrl_button[0]
  189. bt_back['text'] = 'back'
  190. bt_back['font'] = title_font_bold
  191. bt_back['state'] = 'disable'
  192. bt_back['command'] = lambda: self.__to_back_menu()
  193. bt_back.place(relx=0.02, rely=0.93, relwidth=0.96, relheight=0.06)
  194. rely = 0.02
  195. bt_help: tk.Button = self._win_ctrl_button[2]
  196. bt_help['text'] = 'Help'
  197. bt_help.place(relx=0.81, rely=rely, relwidth=0.05, relheight=0.05)
  198. bt_about: tk.Button = self._win_ctrl_button[3]
  199. bt_about['text'] = 'About'
  200. bt_about.place(relx=0.87, rely=rely, relwidth=0.05, relheight=0.05)
  201. bt_exit: tk.Button = self._win_ctrl_button[4]
  202. bt_exit['text'] = 'Exit'
  203. bt_exit['command'] = lambda: self.main_exit()
  204. bt_exit.place(relx=0.93, rely=rely, relwidth=0.05, relheight=0.05)
  205. def set_ctrl_back_button(self):
  206. if len(self._menu_list) <= 1:
  207. self._win_ctrl_button[0]['state'] = 'disable'
  208. else:
  209. self._win_ctrl_button[0]['state'] = 'normal'
  210. def __to_main_menu(self):
  211. self._menu_list = []
  212. self.to_menu()
  213. self.to_program()
  214. def __to_back_menu(self):
  215. assert len(self._menu_list) > 1
  216. self._menu_list.pop() # 弹出最后一个元素
  217. self.to_menu(self._menu_list.pop()) # 再弹出一个元素
  218. def __conf_creak_menu(self):
  219. frame_list = []
  220. for i in tk_menu.all_menu:
  221. frame_list.append(i(self, self._menu_back, conf.tk_win_bg))
  222. for i in frame_list:
  223. name, _ = i.get_menu_frame()
  224. self._menu_dict[name] = i
  225. def __conf_menu(self, n: int = 1):
  226. for i in self._menu_dict:
  227. menu = self._menu_dict[i]
  228. menu.conf_gui(conf.tk_btn_bg, n)
  229. def __conf_menu_title(self):
  230. self._menu_back['bg'] = conf.tk_win_bg
  231. self._menu_back['bd'] = 5
  232. self._menu_back['relief'] = "ridge"
  233. title_font = make_font(size=self._menu_title_font_size, weight="bold")
  234. self._menu_title[0]['bg'] = conf.tk_win_bg
  235. self._menu_title[0]['font'] = title_font
  236. self._menu_title[0]['textvariable'] = self._menu_title[1]
  237. self._menu_line['bg'] = '#000000'
  238. # 不立即显示
  239. def to_menu(self, name: str = "Main"):
  240. if self._menu_now is not None:
  241. self._menu_now[1].place_forget()
  242. menu = self._menu_dict.get(name)
  243. if menu is None:
  244. self.show_msg("Menu not found", f"System can not found menu:\n {name}")
  245. return
  246. name, frame = menu.get_menu_frame()
  247. self._menu_title[1].set(name)
  248. self._menu_back.place(relx=0.02, rely=0.02, relwidth=0.20, relheight=0.96)
  249. self._menu_line.place(relx=0.06, rely=0.065, relwidth=0.88, height=1) # 一个像素的高度即可
  250. self._menu_title[0].place(relx=0.02, rely=0.02, relwidth=0.96, relheight=0.03)
  251. frame.place(relx=0.02, rely=0.07, relwidth=0.96, relheight=0.79)
  252. self._menu_list.append(name)
  253. self._menu_now = name, frame, menu
  254. self.set_ctrl_back_button()
  255. def __conf_program_title(self):
  256. self._program_back['bg'] = conf.tk_win_bg
  257. self._program_back['relief'] = "ridge"
  258. self._program_back['bd'] = 5
  259. title_font = make_font(size=self._program_title_font_size, weight="bold")
  260. self._program_title[0]['bg'] = '#2468a2'
  261. self._program_title[0]['fg'] = "#F0F8FF"
  262. self._program_title[0]['font'] = title_font
  263. self._program_title[0]['anchor'] = 'w'
  264. self._program_title[0]['textvariable'] = self._program_title[1]
  265. # 不立即显示
  266. def __conf_creak_program(self):
  267. program_list = []
  268. for i in tk_program.all_program:
  269. program_list.append(i(self, self._program_back, conf.tk_win_bg))
  270. for i in program_list:
  271. name, _ = i.get_program_frame()
  272. self._program_dict[name] = i
  273. def __conf_program(self, n: int = 1):
  274. for i in self._program_dict:
  275. program = self._program_dict[i]
  276. program.conf_gui(n)
  277. def to_program(self, name: str = "Welcome"):
  278. if self._program_now is not None:
  279. self._program_now[1].place_forget()
  280. program = self._program_dict.get(name)
  281. if program is None:
  282. self.show_msg("Program not found", f"System can not found program:\n {name}")
  283. return
  284. name, frame = program.get_program_frame()
  285. self.__show_program()
  286. self._program_title[1].set(f' {name}')
  287. self._program_title[0].place(relx=0.00, rely=0.00, relwidth=1, relheight=0.05)
  288. frame.place(relx=0.02, rely=0.12, relwidth=0.96, relheight=0.86)
  289. self._program_now = name, frame, program
  290. def __show_program(self):
  291. self._program_back.place(relx=0.26, rely=0.1, relwidth=0.68, relheight=0.84)
  292. def __hide_program(self):
  293. self._program_back.place_forget()
  294. def __conf_loading(self):
  295. self._loading_pro['mode'] = 'indeterminate'
  296. self._loading_pro['orient'] = 'horizontal'
  297. self._loading_pro['maximum'] = 50
  298. def show_loading(self, _):
  299. self._is_loading = True
  300. self.set_all_btn_disable()
  301. self._loading_pro['value'] = 0
  302. self._loading_pro.place(relx=0.30, rely=0.035, relwidth=0.48, relheight=0.03)
  303. self._loading_pro.start(50)
  304. def stop_loading(self):
  305. self._is_loading = False
  306. self._loading_pro.place_forget()
  307. self._loading_pro.stop()
  308. self.set_reset_all_btn()
  309. def __conf_msg(self):
  310. title_font = make_font(size=self._msg_font_size + 1, weight="bold")
  311. info_font = make_font(size=self._msg_font_size - 1)
  312. self._msg_frame['bg'] = conf.tk_win_bg
  313. self._msg_frame['bd'] = 5
  314. self._msg_frame['relief'] = "ridge"
  315. # frame 不会立即显示
  316. self._msg_label[0]['font'] = title_font
  317. self._msg_label[0]['bg'] = conf.tk_win_bg
  318. self._msg_label[0]['anchor'] = 'w'
  319. self._msg_label[0]['textvariable'] = self._msg_label[2]
  320. self._msg_label[1]['font'] = info_font
  321. self._msg_label[1]['bg'] = conf.tk_win_bg
  322. self._msg_label[1]['anchor'] = 'nw'
  323. self._msg_label[1]['textvariable'] = self._msg_label[3]
  324. self._msg_label[1]['justify'] = 'left'
  325. self._msg_label[0].place(relx=0.05, rely=0.05, relwidth=0.9, relheight=0.1)
  326. self._msg_label[1].place(relx=0.075, rely=0.2, relwidth=0.85, relheight=0.58)
  327. self._msg_hide['font'] = info_font
  328. self._msg_hide['text'] = 'close'
  329. self._msg_hide['bg'] = conf.tk_btn_bg
  330. self._msg_hide['command'] = lambda: self.hide_msg()
  331. self._msg_hide.place(relx=0.375, rely=0.80, relwidth=0.25, relheight=0.13)
  332. def show_msg(self, title, info, msg_type='info'):
  333. assert not self._is_loading # loading 时不显示msg
  334. self.set_all_btn_disable()
  335. self._msg_label[2].set(f'{msg_type}: {title}')
  336. self._msg_label[3].set(f'{info}')
  337. frame_width = self._win_width * 0.50
  338. self._msg_label[1]['wraplength'] = frame_width * 0.85 - 5 # 设定自动换行的像素
  339. self._msg_frame.place(relx=0.30, rely=0.25, relwidth=0.55, relheight=0.50)
  340. # 不隐藏元素, 隐藏后界面会显得单调
  341. def hide_msg(self):
  342. self.set_reset_all_btn()
  343. self._msg_frame.place_forget()
  344. def set_all_btn_disable(self):
  345. for btn in self._win_ctrl_button[:-1]: # Exit 不设置disable
  346. btn['state'] = 'disable'
  347. if self._menu_list != 0:
  348. menu = self._menu_dict.get(self._menu_list[-1], None)
  349. assert menu is not None
  350. menu.set_disable()
  351. if self._program_now is not None:
  352. self._program_now[2].set_disable()
  353. def set_reset_all_btn(self):
  354. for btn in self._win_ctrl_button[:-1]:
  355. btn['state'] = 'normal'
  356. self.set_ctrl_back_button()
  357. if self._menu_list != 0:
  358. menu = self._menu_dict.get(self._menu_list[-1], None)
  359. assert menu is not None
  360. menu.reset_disable()
  361. if self._program_now is not None:
  362. self._program_now[2].reset_disable()
  363. def __show_login_window(self):
  364. self.login_window: Optional[tk.Toplevel] = tk.Toplevel()
  365. self.login_window.title("HGSSystem Login")
  366. height = int(self._sys_height * (1 / 6))
  367. width = int(height * 2)
  368. if width > self._sys_width:
  369. width = int(self._sys_width * (2 / 3))
  370. height = int(width / 2)
  371. self.login_window.geometry(f'{width}x{height}')
  372. self.login_window['bg'] = "#d1d9e0"
  373. self.login_window.resizable(False, False)
  374. self.login_window.protocol("WM_DELETE_WINDOW", lambda: self.login_exit())
  375. self._login_name = [tk.Label(self.login_window), tk.Entry(self.login_window), tk.StringVar()]
  376. self._login_passwd = [tk.Label(self.login_window), tk.Entry(self.login_window), tk.StringVar()]
  377. self._login_btn = [tk.Button(self.login_window), tk.Button(self.login_window)]
  378. self.__conf_login_window()
  379. self.hide_main()
  380. def __conf_login_window(self):
  381. title_font = make_font(size=self._login_title_font_size, weight="bold")
  382. btn_font = make_font(size=self._login_btn_font_size, weight="bold")
  383. for lb, text in zip([self._login_name[0], self._login_passwd[0]], ["User:", "Passwd:"]):
  384. lb['bg'] = "#d1d9e0" # 蜜瓜绿
  385. lb['font'] = title_font
  386. lb['text'] = text
  387. lb['anchor'] = 'e'
  388. for lb, var in zip([self._login_name[1], self._login_passwd[1]], [self._login_name[2], self._login_passwd[2]]):
  389. lb['font'] = title_font
  390. lb['textvariable'] = var
  391. self._login_name[0].place(relx=0.00, rely=0.20, relwidth=0.35, relheight=0.15)
  392. self._login_passwd[0].place(relx=0.00, rely=0.40, relwidth=0.35, relheight=0.15)
  393. self._login_name[1].place(relx=0.40, rely=0.20, relwidth=0.45, relheight=0.15)
  394. self._login_passwd[1]['show'] = "*"
  395. self._login_passwd[1].place(relx=0.40, rely=0.40, relwidth=0.45, relheight=0.15)
  396. self._login_btn[0]['bg'] = "#a1afc9"
  397. self._login_btn[0]['font'] = btn_font
  398. self._login_btn[0]['text'] = 'Login'
  399. self._login_btn[0]['command'] = lambda: self.login_call()
  400. self._login_btn[0].place(relx=0.50, rely=0.70, relwidth=0.16, relheight=0.15)
  401. self._login_btn[1]['bg'] = "#a1afc9"
  402. self._login_btn[1]['font'] = btn_font
  403. self._login_btn[1]['text'] = 'Exit'
  404. self._login_btn[1]['command'] = lambda: self.login_exit()
  405. self._login_btn[1].place(relx=0.70, rely=0.70, relwidth=0.16, relheight=0.15)
  406. def login_call(self):
  407. event = tk_event.LoginEvent(self).start(self._login_name[2].get(), self._login_passwd[2].get())
  408. self.push_event(event)
  409. def login(self, user: User):
  410. if super(AdminStation, self).login(user):
  411. self.login_window.destroy()
  412. self.login_window = None
  413. self.show_main()
  414. else:
  415. msg.showerror("Login error", "Please, try again")
  416. self._login_name[2].set('')
  417. self._login_passwd[2].set('')
  418. def logout(self):
  419. super(AdminStation, self).logout()
  420. self.__show_login_window()
  421. def login_exit(self):
  422. if not msg.askokcancel('Sure?', 'Exit manager system.'):
  423. return
  424. if self.login_window is not None:
  425. self.login_window.destroy()
  426. self.exit_win()
  427. def main_exit(self):
  428. if not msg.askokcancel('Sure?', 'Exit manager system.'):
  429. return
  430. self.exit_win()
  431. def hide_main(self):
  432. self._window.withdraw()
  433. def show_main(self):
  434. self._window.update()
  435. self._window.deiconify()
  436. def mainloop(self):
  437. self._window.mainloop()
  438. def exit_win(self):
  439. self._window.destroy()
  440. if __name__ == '__main__':
  441. mysql_db = DB()
  442. station_ = AdminStation(mysql_db)
  443. station_.mainloop()