admin.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  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
  10. from sql.garbage import creat_new_garbage, del_garbage_not_use, del_garbage_not_use_many
  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: 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. res = write_gid_qr(gar.get_gid(), path, self._db)
  36. re.append(res)
  37. return re
  38. def creat_user(self, name: uname_t, passwd: passwd_t, phone: str, manager: bool) -> Optional[User]:
  39. user = creat_new_user(name, passwd, phone, manager, self._db)
  40. if user is None:
  41. raise CreatUserError
  42. return user
  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. @abc.abstractmethod
  66. def login_call(self):
  67. ...
  68. def login(self, user: User) -> bool:
  69. if user is not None and user.is_manager():
  70. self._admin = user
  71. return True
  72. else:
  73. return False
  74. @abc.abstractmethod
  75. def show_loading(self, title: str):
  76. ...
  77. @abc.abstractmethod
  78. def stop_loading(self):
  79. ...
  80. @abc.abstractmethod
  81. def set_after_run(self, ms, func, *args):
  82. ...
  83. import admin_program as tk_program
  84. import admin_menu as tk_menu
  85. import admin_event as tk_event
  86. class AdminStation(AdminStationBase):
  87. def set_after_run(self, ms, func, *args): # super.__init__可能会调用
  88. self.init_after_run_list.append((ms, func, args))
  89. def __conf_set_after_run(self):
  90. for ms, func, args in self.init_after_run_list:
  91. self._window.after(ms, func, *args)
  92. def set_after_run_now(self, ms, func, *args):
  93. self._window.after(ms, func, *args)
  94. def __init__(self, db: DB, refresh_delay: int = conf.tk_refresh_delay):
  95. self.init_after_run_list: List[Tuple[int, Callable, Tuple]] = []
  96. super().__init__(db)
  97. self.refresh_delay = refresh_delay
  98. self._window = tk.Tk()
  99. self.login_window = None
  100. self._sys_height = self._window.winfo_screenheight()
  101. self._sys_width = self._window.winfo_screenwidth()
  102. self._win_height = int(self._sys_height * (2 / 3))
  103. self._win_width = int(self._sys_width * (2 / 3))
  104. self.__conf_windows()
  105. self._full_screen = False
  106. self._is_loading = False
  107. self._disable_all_btn = False
  108. self._now_program: Optional[Tuple[str, tk.Frame, tk_program.AdminProgram]] = None
  109. self.__conf_font_size()
  110. self.__conf_creak_tk()
  111. self.__conf_creak_menu()
  112. self.__conf_creak_program()
  113. self.__conf_tk()
  114. # self.__show_login_window()
  115. self.__conf_set_after_run()
  116. def __conf_windows(self):
  117. self._window.title('HGSSystem: Manage Station')
  118. self._window.geometry(f'{self._win_width}x{self._win_height}')
  119. self._window['bg'] = "#F0FFF0"
  120. self._window.resizable(False, False)
  121. self._window.protocol("WM_DELETE_WINDOW", lambda: self.main_exit())
  122. def __conf_creak_tk(self):
  123. self._win_ctrl_button: List[tk.Button, tk.Button, tk.Button] = [tk.Button(self._window),
  124. tk.Button(self._window),
  125. tk.Button(self._window),
  126. tk.Button(self._window),
  127. tk.Button(self._window)]
  128. self._menu_back = tk.Frame(self._window)
  129. self._menu_line = tk.Label(self._menu_back) # 下划线
  130. self._menu_title: Tuple[tk.Label, tk.Variable] = tk.Label(self._menu_back), tk.StringVar()
  131. self._menu_dict: Dict[str, tk_menu.AdminMenu] = {}
  132. self._menu_list: List[str] = [] # 菜单回溯
  133. self._program_back = tk.Frame(self._window)
  134. self._program_title: Tuple[tk.Label, tk.Variable] = tk.Label(self._program_back), tk.StringVar()
  135. self._program_dict: Dict[str, tk_program.AdminProgram] = {}
  136. self._msg_frame = tk.Frame(self._window)
  137. self._msg_label = tk.Label(self._msg_frame), tk.Label(self._msg_frame), tk.StringVar(), tk.StringVar()
  138. self._msg_hide = tk.Button(self._msg_frame)
  139. self._loading_pro = ttk.Progressbar(self._window)
  140. def __conf_font_size(self, n: int = 1):
  141. self._login_title_font_size = int(12 * n)
  142. self._login_btn_font_size = int(11 * n)
  143. self._win_ctrl_font_size = int(15 * n)
  144. self._menu_title_font_size = int(17 * n)
  145. self._program_title_font_size = int(14 * n)
  146. self._msg_font_size = int(20 * n)
  147. def __conf_tk(self, n: int = 1):
  148. self.__conf_win_ctrl_button()
  149. self.__conf_menu_title()
  150. self.__conf_menu(n)
  151. self.__conf_program_title()
  152. self.__conf_program(n)
  153. self.__conf_loading()
  154. self.__conf_msg()
  155. self.to_menu() # 显示主页面
  156. self.to_program()
  157. def __conf_win_ctrl_button(self):
  158. title_font = make_font(size=self._win_ctrl_font_size)
  159. for bt in self._win_ctrl_button:
  160. bt: tk.Button
  161. bt['bg'] = "#B0C4DE" # 浅钢青
  162. bt['font'] = title_font
  163. rely = 0.02
  164. bt_help: tk.Button = self._win_ctrl_button[0]
  165. bt_help['text'] = 'Back'
  166. bt_help['bg'] = '#DCDCDC'
  167. bt_help.place(relx=0.69, rely=rely, relwidth=0.05, relheight=0.05)
  168. bt_about: tk.Button = self._win_ctrl_button[1]
  169. bt_about['text'] = 'Main'
  170. bt_about['bg'] = '#DCDCDC'
  171. bt_about.place(relx=0.75, rely=rely, relwidth=0.05, relheight=0.05)
  172. bt_help: tk.Button = self._win_ctrl_button[2]
  173. bt_help['text'] = 'Help'
  174. bt_help['bg'] = '#DCDCDC'
  175. bt_help.place(relx=0.81, rely=rely, relwidth=0.05, relheight=0.05)
  176. bt_about: tk.Button = self._win_ctrl_button[3]
  177. bt_about['text'] = 'About'
  178. bt_about['bg'] = '#DCDCDC'
  179. bt_about.place(relx=0.87, rely=rely, relwidth=0.05, relheight=0.05)
  180. bt_exit: tk.Button = self._win_ctrl_button[4]
  181. bt_exit['text'] = 'Exit'
  182. bt_exit['bg'] = '#DCDCDC'
  183. bt_exit['command'] = lambda: self.main_exit()
  184. bt_exit.place(relx=0.93, rely=rely, relwidth=0.05, relheight=0.05)
  185. def __conf_creak_menu(self):
  186. frame_list = [
  187. tk_menu.MainMenu(self, self._menu_back, '#fffffb')
  188. ]
  189. for i in frame_list:
  190. name, _ = i.get_menu_frame()
  191. self._menu_dict[name] = i
  192. def __conf_menu(self, n: int = 1):
  193. for i in self._menu_dict:
  194. menu = self._menu_dict[i]
  195. menu.conf_gui("#DCDCDC", n)
  196. def __conf_menu_title(self):
  197. self._menu_back['bg'] = "#fffffb"
  198. self._menu_back['bd'] = 5
  199. self._menu_back['relief'] = "ridge"
  200. title_font = make_font(size=self._menu_title_font_size, weight="bold")
  201. self._menu_title[0]['bg'] = '#fffffb'
  202. self._menu_title[0]['font'] = title_font
  203. self._menu_title[0]['textvariable'] = self._menu_title[1]
  204. self._menu_line['bg'] = '#000000'
  205. # 不立即显示
  206. def to_menu(self, name: str = "Main"):
  207. menu = self._menu_dict.get(name)
  208. if menu is None:
  209. ...
  210. name, frame = menu.get_menu_frame()
  211. self._menu_title[1].set(name)
  212. self._menu_back.place(relx=0.02, rely=0.02, relwidth=0.20, relheight=0.96)
  213. self._menu_line.place(relx=0.06, rely=0.065, relwidth=0.88, height=1) # 一个像素的高度即可
  214. self._menu_title[0].place(relx=0.02, rely=0.02, relwidth=0.96, relheight=0.03)
  215. frame.place(relx=0.02, rely=0.07, relwidth=0.96, relheight=0.84)
  216. self._menu_list.append(name)
  217. def __conf_program_title(self):
  218. self._program_back['bg'] = "#fffffb"
  219. self._program_back['relief'] = "ridge"
  220. self._program_back['bd'] = 5
  221. title_font = make_font(size=self._program_title_font_size, weight="bold")
  222. self._program_title[0]['bg'] = '#2468a2'
  223. self._program_title[0]['fg'] = "#F0F8FF"
  224. self._program_title[0]['font'] = title_font
  225. self._program_title[0]['anchor'] = 'w'
  226. self._program_title[0]['textvariable'] = self._program_title[1]
  227. # 不立即显示
  228. def __conf_creak_program(self):
  229. program_list = [
  230. tk_program.WelcomeProgram(self, self._program_back, '#fffffb')
  231. ]
  232. for i in program_list:
  233. name, _ = i.get_program_frame()
  234. self._program_dict[name] = i
  235. def __conf_program(self, n: int = 1):
  236. for i in self._program_dict:
  237. program = self._program_dict[i]
  238. program.conf_gui(n)
  239. def to_program(self, name: str = "Welcome"):
  240. program = self._program_dict.get(name)
  241. if program is None:
  242. ...
  243. name, frame = program.get_program_frame()
  244. self.__show_program()
  245. self._program_title[1].set(f' {name}')
  246. self._program_title[0].place(relx=0.00, rely=0.00, relwidth=1, relheight=0.05)
  247. frame.place(relx=0.02, rely=0.12, relwidth=0.96, relheight=0.86)
  248. self._now_program = name, frame, program
  249. def __show_program(self):
  250. self._program_back.place(relx=0.26, rely=0.1, relwidth=0.68, relheight=0.84)
  251. def __hide_program(self):
  252. self._program_back.place_forget()
  253. def __conf_loading(self):
  254. self._loading_pro['mode'] = 'indeterminate'
  255. self._loading_pro['orient'] = tk.HORIZONTAL
  256. self._loading_pro['maximum'] = 50
  257. def show_loading(self, _):
  258. self._is_loading = True
  259. self.set_all_btn_disable()
  260. self._loading_pro['value'] = 0
  261. self._loading_pro.place(relx=0.30, rely=0.035, relwidth=0.35, relheight=0.03)
  262. self._loading_pro.start(50)
  263. def stop_loading(self):
  264. self._is_loading = False
  265. self._loading_pro.place_forget()
  266. self._loading_pro.stop()
  267. self.set_reset_all_btn()
  268. def __conf_msg(self):
  269. title_font = make_font(size=self._msg_font_size + 1, weight="bold")
  270. info_font = make_font(size=self._msg_font_size - 1)
  271. self._msg_frame['bg'] = "#fffffb"
  272. self._msg_frame['bd'] = 5
  273. self._msg_frame['relief'] = "ridge"
  274. # frame 不会立即显示
  275. self._msg_label[0]['font'] = title_font
  276. self._msg_label[0]['bg'] = "#fffffb"
  277. self._msg_label[0]['anchor'] = 'w'
  278. self._msg_label[0]['textvariable'] = self._msg_label[2]
  279. self._msg_label[1]['font'] = info_font
  280. self._msg_label[1]['bg'] = "#fffffb"
  281. self._msg_label[1]['anchor'] = 'nw'
  282. self._msg_label[1]['textvariable'] = self._msg_label[3]
  283. self._msg_label[1]['justify'] = 'left'
  284. self._msg_label[0].place(relx=0.05, rely=0.05, relwidth=0.9, relheight=0.1)
  285. self._msg_label[1].place(relx=0.075, rely=0.2, relwidth=0.85, relheight=0.58)
  286. self._msg_hide['font'] = info_font
  287. self._msg_hide['text'] = 'close'
  288. self._msg_hide['bg'] = "#DCDCDC"
  289. self._msg_hide['command'] = lambda: self.hide_msg()
  290. self._msg_hide.place(relx=0.375, rely=0.80, relwidth=0.25, relheight=0.13)
  291. def show_msg(self, title, info, msg_type='info'):
  292. assert not self._is_loading # loading 时不显示msg
  293. self.set_all_btn_disable()
  294. self._msg_label[2].set(f'{msg_type}: {title}')
  295. self._msg_label[3].set(f'{info}')
  296. frame_width = self._win_width * 0.50
  297. self._msg_label[1]['wraplength'] = frame_width * 0.85 - 5 # 设定自动换行的像素
  298. self._msg_frame.place(relx=0.30, rely=0.25, relwidth=0.55, relheight=0.50)
  299. self.__hide_program()
  300. def hide_msg(self):
  301. self.set_reset_all_btn()
  302. self._msg_frame.place_forget()
  303. self.__show_program()
  304. def set_all_btn_disable(self):
  305. for btn in self._win_ctrl_button[:-1]: # Exit 不设置disable
  306. btn['state'] = 'disable'
  307. if self._menu_list != 0:
  308. menu = self._menu_dict.get(self._menu_list[-1], None)
  309. assert menu is not None
  310. menu.set_disable()
  311. if self._now_program is not None:
  312. self._now_program[2].set_disable()
  313. def set_reset_all_btn(self):
  314. for btn in self._win_ctrl_button[:-1]:
  315. btn['state'] = 'normal'
  316. if self._menu_list != 0:
  317. menu = self._menu_dict.get(self._menu_list[-1], None)
  318. assert menu is not None
  319. menu.reset_disable()
  320. if self._now_program is not None:
  321. self._now_program[2].reset_disable()
  322. def __show_login_window(self):
  323. self.login_window: Optional[tk.Toplevel] = tk.Toplevel()
  324. self.login_window.title("HGSSystem Login")
  325. height = int(self._sys_height * (1 / 6))
  326. width = int(height * 2)
  327. if width > self._sys_width:
  328. width = int(self._sys_width * (2 / 3))
  329. height = int(width / 2)
  330. self.login_window.geometry(f'{width}x{height}')
  331. self.login_window['bg'] = "#d1d9e0"
  332. self.login_window.resizable(False, False)
  333. self.login_window.protocol("WM_DELETE_WINDOW", lambda: self.login_exit())
  334. self._login_name = [tk.Label(self.login_window), tk.Entry(self.login_window), tk.StringVar()]
  335. self._login_passwd = [tk.Label(self.login_window), tk.Entry(self.login_window), tk.StringVar()]
  336. self._login_btn = [tk.Button(self.login_window), tk.Button(self.login_window)]
  337. self.__conf_login_window()
  338. self.hide_main()
  339. def __conf_login_window(self):
  340. title_font = make_font(size=self._login_title_font_size, weight="bold")
  341. btn_font = make_font(size=self._login_btn_font_size, weight="bold")
  342. for lb, text in zip([self._login_name[0], self._login_passwd[0]], ["User:", "Passwd:"]):
  343. lb['bg'] = "#d1d9e0" # 蜜瓜绿
  344. lb['font'] = title_font
  345. lb['text'] = text
  346. lb['anchor'] = 'e'
  347. for lb, var in zip([self._login_name[1], self._login_passwd[1]], [self._login_name[2], self._login_passwd[2]]):
  348. lb['font'] = title_font
  349. lb['textvariable'] = var
  350. self._login_name[0].place(relx=0.00, rely=0.20, relwidth=0.35, relheight=0.15)
  351. self._login_passwd[0].place(relx=0.00, rely=0.40, relwidth=0.35, relheight=0.15)
  352. self._login_name[1].place(relx=0.40, rely=0.20, relwidth=0.45, relheight=0.15)
  353. self._login_passwd[1]['show'] = "*"
  354. self._login_passwd[1].place(relx=0.40, rely=0.40, relwidth=0.45, relheight=0.15)
  355. self._login_btn[0]['bg'] = "#a1afc9"
  356. self._login_btn[0]['font'] = btn_font
  357. self._login_btn[0]['text'] = 'Login'
  358. self._login_btn[0]['command'] = lambda: self.login_call()
  359. self._login_btn[0].place(relx=0.50, rely=0.70, relwidth=0.16, relheight=0.15)
  360. self._login_btn[1]['bg'] = "#a1afc9"
  361. self._login_btn[1]['font'] = btn_font
  362. self._login_btn[1]['text'] = 'Exit'
  363. self._login_btn[1]['command'] = lambda: self.login_exit()
  364. self._login_btn[1].place(relx=0.70, rely=0.70, relwidth=0.16, relheight=0.15)
  365. def login_call(self):
  366. event = tk_event.LoginEvent(self).start(self._login_name[2].get(), self._login_passwd[2].get())
  367. self.push_event(event)
  368. def login(self, user: User):
  369. if super(AdminStation, self).login(user):
  370. self.login_window.destroy()
  371. self.login_window = None
  372. self.show_main()
  373. else:
  374. msg.showerror("Login error", "Please, try again")
  375. self._login_name[2].set('')
  376. self._login_passwd[2].set('')
  377. def login_exit(self):
  378. if not msg.askokcancel('Sure?', 'Exit manager system.'):
  379. return
  380. if self.login_window is not None:
  381. self.login_window.destroy()
  382. self.exit_win()
  383. def main_exit(self):
  384. if not msg.askokcancel('Sure?', 'Exit manager system.'):
  385. return
  386. self.exit_win()
  387. def hide_main(self):
  388. self._window.withdraw()
  389. def show_main(self):
  390. self._window.update()
  391. self._window.deiconify()
  392. def mainloop(self):
  393. self._window.mainloop()
  394. def exit_win(self):
  395. self._window.destroy()
  396. if __name__ == '__main__':
  397. mysql_db = DB()
  398. station_ = AdminStation(mysql_db)
  399. station_.mainloop()