ui.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. import tktool
  2. import tkinter
  3. import tkinter.ttk as ttk
  4. import tkinter.filedialog as fd
  5. import tkinter.messagebox as msg
  6. from typing import List, Tuple, Callable, Optional
  7. import abc
  8. import db
  9. import word
  10. class HEnglishTkinter(tktool.TkEventMain, metaclass=abc.ABCMeta):
  11. tk_zoom = 1
  12. def set_after_run(self, ms, func, *args): # 正常运行前设置定时任务 super.__init__可能会调用
  13. self.init_after_run_list.append((ms, func, args))
  14. def __conf_set_after_run(self): # 配合 set_after_run 使用
  15. for ms, func, args in self.init_after_run_list:
  16. self.set_after_run_now(ms, func, *args)
  17. def set_after_run_now(self, ms, func, *args): # 正常运行时设置定时任务
  18. self._window.after(ms, func, *args)
  19. def __init__(self, title: str,
  20. top: Optional["HEnglishTkinter"] = None,
  21. size: Tuple[float, float] = ((1 / 3), (2 / 3))):
  22. self.init_after_run_list: List[Tuple[int, Callable, Tuple]] = []
  23. super(HEnglishTkinter, self).__init__()
  24. if top:
  25. self._window = tkinter.Toplevel(top._window)
  26. top._window.lift()
  27. else:
  28. self._window = tkinter.Tk()
  29. self._sys_height = self._window.winfo_screenheight()
  30. self._sys_width = self._window.winfo_screenwidth()
  31. self._win_height = int(self._sys_height * size[1] * self.tk_zoom) # 窗口高度
  32. self._win_width = int(self._sys_width * size[0] * self.tk_zoom) # 窗口宽度
  33. self.__conf_windows(title)
  34. self.__conf_set_after_run()
  35. def __conf_windows(self, title: str):
  36. self._window.title(title)
  37. self._window.geometry(f'{self._win_width}x{self._win_height}')
  38. self._window['bg'] = '#F0FFFF'
  39. self._window.resizable(True, True) # 禁止缩放
  40. self._window.overrideredirect(False) # 显示标题栏
  41. self._window.bind('<Configure>', self.__window_resize) # 调整界面大小
  42. self._window.minsize(int(self._sys_width * (1 / 3) * self.tk_zoom),
  43. int(self._sys_height * (1 / 3) * self.tk_zoom))
  44. self._create_windows()
  45. self._set_windows()
  46. def __window_resize(self, event=None):
  47. if self._win_width != self._window.winfo_width() or self._win_height != self._window.winfo_height():
  48. self._win_height = self._window.winfo_height()
  49. self._win_width = self._window.winfo_width()
  50. self._set_windows()
  51. @abc.abstractmethod
  52. def _create_windows(self):
  53. pass
  54. @abc.abstractmethod
  55. def _set_windows(self):
  56. pass
  57. def mainloop(self):
  58. self._window.mainloop()
  59. class HEnglish(HEnglishTkinter):
  60. def __init__(self):
  61. super(HEnglish, self).__init__("H-English")
  62. self.db = db.WordDatabase()
  63. self.wd = self.db.wd
  64. def _create_windows(self):
  65. self._title_label = tkinter.Label(self._window)
  66. self._control_frame = tkinter.Frame(self._window)
  67. self._control_btn = [tkinter.Button(self._control_frame) for _ in range(6)]
  68. def _set_windows(self):
  69. self.__conf_title()
  70. self.__conf_control_btn()
  71. def __conf_title(self):
  72. if self._win_width >= self._win_height:
  73. font = tktool.make_font(size=int(self._win_height * 0.06), weight="bold")
  74. else:
  75. font = tktool.make_font(size=int(self._win_width * 0.05), weight="bold")
  76. self._title_label['font'] = font
  77. self._title_label['bg'] = '#F0FFFF'
  78. self._title_label['text'] = "Huan-English-Dictionary" # 使用英语标题在GUI更美观
  79. self._title_label['anchor'] = 'c'
  80. self._title_label.place(relx=0.0, rely=0.03, relwidth=1.0, relheight=0.13)
  81. self._title = tkinter.Label()
  82. def __conf_control_btn(self):
  83. if self._win_width >= self._win_height:
  84. font = tktool.make_font(size=int(self._win_height * 0.03))
  85. self._control_btn[0].place(relx=0.07, rely=0.10, relwidth=0.4, relheight=0.2)
  86. self._control_btn[1].place(relx=0.53, rely=0.10, relwidth=0.4, relheight=0.2)
  87. self._control_btn[2].place(relx=0.07, rely=0.40, relwidth=0.4, relheight=0.2)
  88. self._control_btn[3].place(relx=0.53, rely=0.40, relwidth=0.4, relheight=0.2)
  89. self._control_btn[4].place(relx=0.07, rely=0.70, relwidth=0.4, relheight=0.2)
  90. self._control_btn[5].place(relx=0.53, rely=0.70, relwidth=0.4, relheight=0.2)
  91. else:
  92. font = tktool.make_font(size=int(self._win_width * 0.03))
  93. self._control_btn[0].place(relx=0.1, rely=0.08, relwidth=0.8, relheight=0.1)
  94. self._control_btn[1].place(relx=0.1, rely=0.23, relwidth=0.8, relheight=0.1)
  95. self._control_btn[2].place(relx=0.1, rely=0.38, relwidth=0.8, relheight=0.1)
  96. self._control_btn[3].place(relx=0.1, rely=0.53, relwidth=0.8, relheight=0.1)
  97. self._control_btn[4].place(relx=0.1, rely=0.68, relwidth=0.8, relheight=0.1)
  98. self._control_btn[5].place(relx=0.1, rely=0.83, relwidth=0.8, relheight=0.1)
  99. self._control_frame['bg'] = "#FFFFFF"
  100. self._control_frame['relief'] = "ridge"
  101. self._control_frame['bd'] = 5
  102. self._control_frame.place(relx=0.05, rely=0.20, relwidth=0.90, relheight=0.75)
  103. for i in zip(self._control_btn,
  104. ["Word Test", "Dictionary", "Export", "Import", "Delete", "About"],
  105. ["#DCDCDC", "#DCDCDC", "#DCDCDC", "#DCDCDC", "#DCDCDC", "#DCDCDC"],
  106. [None, None, None, self.import_word, None, None]):
  107. i[0]['font'] = font
  108. i[0]['fg'] = "#000000"
  109. i[0]['bg'] = i[2]
  110. i[0]['activeforeground'] = "#FFFFFF"
  111. i[0]['activebackground'] = i[2]
  112. i[0]['anchor'] = 'c'
  113. i[0]['relief'] = "ridge"
  114. i[0]['bd'] = 5
  115. i[0]['text'] = i[1]
  116. i[0]['command'] = i[3]
  117. def import_word(self):
  118. Import(self, self._window)
  119. def show_loading(self, title: str): # 有子线程时显示加载
  120. ...
  121. def stop_loading(self): # 子线程运行完成后关闭加载
  122. ...
  123. def disable(self):
  124. self._window.state('icon')
  125. for i in self._control_btn:
  126. i['state'] = 'disable'
  127. def enable(self):
  128. self._window.state('normal')
  129. for i in self._control_btn:
  130. i['state'] = 'normal'
  131. class Import(HEnglishTkinter):
  132. class ImportEvent(tktool.TkEventBase, metaclass=abc.ABCMeta):
  133. def __init__(self, imp: "Import"):
  134. super().__init__()
  135. self.imp = imp
  136. self.thread = None
  137. self.file = ""
  138. @abc.abstractmethod
  139. def func(self, *args):
  140. ...
  141. def get_title(self) -> str:
  142. return "Import"
  143. def start(self, *args):
  144. self.thread = tktool.TkThreading(self.func, *args)
  145. return self
  146. def done_after_event(self):
  147. res = self.thread.wait_event()
  148. if res:
  149. msg.showinfo("操作成功", f"成功从{self.file}中读取单词")
  150. class ImportFromText(ImportEvent):
  151. def __init__(self, imp: "Import"):
  152. super().__init__(imp)
  153. def func(self, file: str):
  154. self.file = file
  155. return self.imp._father.db.update_from_txt(file)
  156. class ImportFromTable(ImportEvent):
  157. def __init__(self, imp: "Import"):
  158. super().__init__(imp)
  159. def func(self, file: str, t: str):
  160. self.file = file
  161. return self.imp._father.db.update_from_table(file, t)
  162. def __init__(self, father: HEnglish, father_windows: tkinter.Tk):
  163. super(Import, self).__init__("Import", father, size=(1 / 3, 1 / 3))
  164. self._father = father
  165. self._father_windows = father_windows
  166. self._father.disable()
  167. self._window.protocol("WM_DELETE_WINDOW", self.close)
  168. def close(self):
  169. self._window.destroy()
  170. self._father.enable()
  171. def _create_windows(self):
  172. self._title_label = tkinter.Label(self._window)
  173. self._loading_pro = ttk.Progressbar(self._window)
  174. self._control_btn = [tkinter.Button(self._window) for _ in range(6)]
  175. def _set_windows(self):
  176. self.__conf_title()
  177. self.__conf_loader()
  178. self.__conf_control_btn()
  179. def __conf_title(self):
  180. if self._win_width >= self._win_height:
  181. font = tktool.make_font(size=int(self._win_height * 0.06), weight="bold")
  182. else:
  183. font = tktool.make_font(size=int(self._win_width * 0.05), weight="bold")
  184. self._title_label['font'] = font
  185. self._title_label['bg'] = '#F0FFFF'
  186. self._title_label['text'] = "Import Word" # 使用英语标题在GUI更美观
  187. self._title_label['anchor'] = 'c'
  188. self._title_label.place(relx=0.0, rely=0.03, relwidth=1.0, relheight=0.13)
  189. self._title = tkinter.Label()
  190. def __conf_loader(self):
  191. self._loading_pro['mode'] = 'indeterminate' # 来回显示
  192. self._loading_pro['orient'] = 'horizontal' # 横向进度条
  193. self._loading_pro['maximum'] = 100
  194. def __conf_control_btn(self):
  195. if self._win_width >= self._win_height:
  196. font = tktool.make_font(size=int(self._win_height * 0.04))
  197. self._control_btn[0].place(relx=0.07, rely=0.28, relwidth=0.4, relheight=0.2)
  198. self._control_btn[1].place(relx=0.53, rely=0.28, relwidth=0.4, relheight=0.2)
  199. self._control_btn[2].place(relx=0.07, rely=0.66, relwidth=0.4, relheight=0.2)
  200. self._control_btn[3].place(relx=0.53, rely=0.66, relwidth=0.4, relheight=0.2)
  201. else:
  202. font = tktool.make_font(size=int(self._win_width * 0.04))
  203. self._control_btn[0].place(relx=0.1, rely=0.20, relwidth=0.8, relheight=0.1)
  204. self._control_btn[1].place(relx=0.1, rely=0.33, relwidth=0.8, relheight=0.1)
  205. self._control_btn[2].place(relx=0.1, rely=0.46, relwidth=0.8, relheight=0.1)
  206. self._control_btn[3].place(relx=0.1, rely=0.59, relwidth=0.8, relheight=0.1)
  207. for i in zip(self._control_btn,
  208. ["From Text", "From CSV", "From Excel", "From Json"],
  209. ["#DCDCDC", "#DCDCDC", "#DCDCDC", "#DCDCDC"],
  210. [self.import_from_text, self.import_from_csv, self.import_from_excel, self.import_from_json]):
  211. i[0]['font'] = font
  212. i[0]['fg'] = "#000000"
  213. i[0]['bg'] = i[2]
  214. i[0]['activeforeground'] = "#FFFFFF"
  215. i[0]['activebackground'] = i[2]
  216. i[0]['anchor'] = 'c'
  217. i[0]['relief'] = "ridge"
  218. i[0]['bd'] = 5
  219. i[0]['text'] = i[1]
  220. i[0]['command'] = i[3]
  221. def import_from_text(self):
  222. file = fd.askopenfilename(filetypes=[("Text", ".txt"), ("All", "*")])
  223. if file != "":
  224. self.push_event(self.ImportFromText(self).start(file))
  225. def import_from_csv(self):
  226. file = fd.askopenfilename(filetypes=[("CSV", ".csv"), ("All", "*")])
  227. if file != "":
  228. self.push_event(self.ImportFromTable(self).start(file, "csv"))
  229. def import_from_excel(self):
  230. file = fd.askopenfilename(filetypes=[("Excel", ".xlsx"), ("All", "*")])
  231. if file != "":
  232. self.push_event(self.ImportFromTable(self).start(file, "excel"))
  233. def import_from_json(self):
  234. file = fd.askopenfilename(filetypes=[("Json", ".json"), ("All", "*")])
  235. if file != "":
  236. self.push_event(self.ImportFromTable(self).start(file, "json"))
  237. def show_loading(self, title: str):
  238. self._loading_pro['value'] = 0
  239. self._loading_pro.place(relx=0.10, rely=0.17, relwidth=0.80, relheight=0.05)
  240. self._loading_pro.start(50)
  241. def stop_loading(self):
  242. self._loading_pro.place_forget()
  243. self._loading_pro.stop()
  244. if __name__ == '__main__':
  245. hgui = HEnglish()
  246. hgui.mainloop()