phone_list.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. import logging
  2. import tkinter as tk
  3. from tkinter import ttk
  4. from control.base.base_control import BaseControl, UIControl
  5. class LeftPanel(tk.LabelFrame):
  6. def __init__(self, master=None, control: UIControl = None):
  7. super().__init__(master, text="手机列表")
  8. self.scrollbar = None
  9. self.canvas = None
  10. self.list_frame = None
  11. self.control = control
  12. self.master = master
  13. self.phone_items_data = [] # 用于存储每个手机项的数据 (包含 CheckVar)
  14. self.create_widgets()
  15. self._check_scrollable() # 初始检查是否需要滚动条
  16. def create_widgets(self):
  17. title_label = ttk.Frame(self, relief="flat", borderwidth=1)
  18. title_label.grid(row=0, column=0, padx=10, pady=10, sticky="ew")
  19. # 添加分割线
  20. separator = ttk.Separator(title_label, orient="horizontal")
  21. separator.grid(row=1, column=0, columnspan=4, sticky="ew", padx=5, pady=5)
  22. name_label = ttk.Label(title_label, text='全选')
  23. name_label.grid(row=0, column=1, padx=5, pady=10, sticky="e")
  24. # 选择框
  25. check_all = tk.BooleanVar()
  26. check_button = ttk.Checkbutton(title_label, variable=check_all,
  27. command=lambda v=check_all: self.toggle_check(v))
  28. check_button.grid(row=0, column=2, padx=5, pady=10, sticky="e")
  29. # 添加数据按钮
  30. add_data_button = ttk.Button(title_label, text="刷新", command=self.add_data)
  31. add_data_button.grid(row=0, column=3, padx=5, pady=10, sticky="e")
  32. # 创建 Canvas 和 Scrollbar
  33. self.canvas = tk.Canvas(self)
  34. self.scrollbar = ttk.Scrollbar(self, orient="vertical", command=self.canvas.yview)
  35. self.canvas.configure(yscrollcommand=self.scrollbar.set)
  36. self.canvas.grid(row=1, column=0, columnspan=4, sticky="nsew")
  37. self.scrollbar.grid(row=1, column=4, sticky="ns")
  38. # 创建 Frame 放入 Canvas 中
  39. self.list_frame = ttk.Frame(self.canvas)
  40. self.canvas.create_window((0, 0), window=self.list_frame, anchor="nw")
  41. self.list_frame.bind("<Configure>", self._on_frame_configure)
  42. self.canvas.bind("<Enter>", self._bind_mousewheel_enter)
  43. self.canvas.bind("<Leave>", self._bind_mousewheel_leave)
  44. # 配置 Grid 的行和列的权重,使 Canvas 可以扩展
  45. self.grid_rowconfigure(1, weight=1)
  46. self.grid_columnconfigure(0, weight=1)
  47. self.grid_columnconfigure(1, weight=0) # 全选按钮不扩展
  48. self.grid_columnconfigure(2, weight=0) # 添加数据按钮不扩展
  49. self.grid_columnconfigure(3, weight=0) # 取消全选按钮不扩展
  50. self.grid_columnconfigure(4, weight=0) # 滚动条列不扩展
  51. self._populate_list()
  52. def _populate_list(self):
  53. """根据 self.phone_data 填充列表"""
  54. for widget in self.list_frame.winfo_children():
  55. widget.destroy()
  56. self.phone_items_data = []
  57. # for phone in self.phone_data:
  58. # self.create_list_item(phone["name"], phone["status"])
  59. for pkg, device in BaseControl.connect_dict.items():
  60. logging.info(f'==添加手机信息=>{pkg} {device}')
  61. self.create_list_item(device)
  62. self._on_frame_configure(None) # 更新滚动区域
  63. def create_list_item(self, device):
  64. item_frame = ttk.Frame(self.list_frame, padding=(5, 5))
  65. item_frame.pack(fill="x", expand=True)
  66. status = device['status']
  67. # 选择框
  68. check_var = tk.BooleanVar()
  69. check_var.set(True)
  70. check_button = ttk.Checkbutton(item_frame, variable=check_var)
  71. check_button.pack(side="left")
  72. # 手机名称
  73. name_label = ttk.Label(item_frame, text=f'{device["name"]}:【{device["pkg"]}】', anchor="w", width=20)
  74. name_label.pack(side="left", padx=5)
  75. # 状态指示器
  76. status_canvas = tk.Canvas(item_frame, width=10, height=10, highlightthickness=0)
  77. status_canvas.pack(side="left", padx=5)
  78. if status == "online":
  79. status_canvas.create_oval(0, 0, 10, 10, fill="green")
  80. else:
  81. status_canvas.create_oval(0, 0, 10, 10, fill="red")
  82. # 状态按钮 (这里我们用一个简单的 Label 模拟)
  83. status_button = ttk.Label(item_frame, text="@", width=3, anchor="center", relief="groove")
  84. status_button.pack(side="right", padx=5)
  85. self.phone_items_data.append((check_var, device["serial"], status)) # 存储 CheckVar 和数据
  86. def toggle_check(self, check_var):
  87. if check_var.get():
  88. print("复选框被选中")
  89. else:
  90. print("复选框被取消选中")
  91. for var, _, _ in self.phone_items_data:
  92. var.set(check_var.get())
  93. pass
  94. def add_data(self):
  95. """
  96. 动态添加数据
  97. """
  98. self._populate_list() # 重新填充列表以显示新数据
  99. self._check_scrollable() # 添加数据后检查是否需要滚动条
  100. def _on_frame_configure(self, event):
  101. """当列表 Frame 大小改变时,更新 Canvas 的滚动区域并检查是否需要绑定滚轮事件"""
  102. self.canvas.configure(scrollregion=self.canvas.bbox("all"))
  103. self._check_scrollable()
  104. def _check_scrollable(self):
  105. """检查内容是否需要滚动条,并决定是否需要显示"""
  106. if self.canvas.winfo_height() < self.canvas.bbox("all")[3]:
  107. self.scrollbar.grid(row=1, column=4, sticky="ns") # 显示滚动条
  108. else:
  109. self.scrollbar.grid_remove() # 隐藏滚动条
  110. def _bind_mousewheel_enter(self, event):
  111. """鼠标进入 Canvas 时绑定滚轮事件"""
  112. if self.canvas.winfo_height() < self.canvas.bbox("all")[3]: # 只有在需要滚动时才绑定
  113. self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
  114. self.canvas.bind_all("<Button-4>", self._on_mousewheel) # Linux
  115. self.canvas.bind_all("<Button-5>", self._on_mousewheel) # Linux
  116. def _bind_mousewheel_leave(self, event):
  117. """鼠标离开 Canvas 时解绑滚轮事件"""
  118. self.canvas.unbind_all("<MouseWheel>")
  119. self.canvas.unbind_all("<Button-4>")
  120. self.canvas.unbind_all("<Button-5>")
  121. def _on_mousewheel(self, event):
  122. """鼠标滚轮滚动事件处理"""
  123. if event.delta:
  124. self.canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
  125. else:
  126. if event.num == 4:
  127. self.canvas.yview_scroll(-1, "units")
  128. elif event.num == 5:
  129. self.canvas.yview_scroll(1, "units")
  130. class RightPanel(tk.Frame):
  131. def __init__(self, master=None, control: UIControl = None, *args, **kwargs):
  132. super().__init__(master, *args, **kwargs)
  133. self.control = control
  134. self.master = master
  135. self.create_args()
  136. self.create_info_ares()
  137. self.grid_columnconfigure(0, weight=1) # 让列可以水平扩展
  138. self.grid_rowconfigure(0, weight=0) # 参数设定区域不需要扩展
  139. self.grid_rowconfigure(1, weight=1) # 让操作区域可以垂直扩展
  140. def create_args(self):
  141. # 右侧区域的组件可以在这里创建
  142. # label = ttk.Label(self, text="右侧区域", font=("Arial", 12, "bold"))
  143. # label.pack(padx=10, pady=10)
  144. #
  145. # # 添加更多组件...
  146. # button = ttk.Button(self, text="按钮", command=lambda: print("按钮被点击"))
  147. # button.pack(padx=10, pady=10)
  148. fieldset = ttk.LabelFrame(self, text="参数设定")
  149. fieldset.grid(row=0, column=0, padx=10, pady=10, sticky="nsew")
  150. # fieldset.grid_columnconfigure(0, weight=1)
  151. # fieldset.grid_rowconfigure(1, weight=1)
  152. # name_label = ttk.Label(fieldset, text="币种:", width=5)
  153. # name_label.grid(row=0, column=0, padx=5, pady=5, sticky="nw")
  154. #
  155. # name_val = tk.Text(fieldset, width=20, height=1.2, wrap='word')
  156. # name_val.grid(row=0, column=1, padx=5, pady=5, sticky="nw")
  157. # name_val.insert('1.0', 'HMSTR') # 插入文本
  158. # name_val.config(state='disabled') # 再禁用
  159. cmd_label = ttk.Label(fieldset, text="数量:", width=5)
  160. cmd_label.grid(row=0, column=1, padx=5, pady=5, sticky="nw")
  161. cmd_text = tk.Text(fieldset, height=1.2, width=10, wrap='word')
  162. cmd_text.grid(row=0, column=2, padx=5, pady=5, sticky="nw")
  163. button1 = ttk.Button(fieldset, text="设置", command=lambda: print('----'))
  164. button1.grid(row=0, column=3, padx=2, pady=(5, 2), sticky="ew")
  165. def create_info_ares(self):
  166. # 创建一个 Frame 来模拟 <fieldset>
  167. fieldset = ttk.Frame(self)
  168. fieldset.grid(row=1, column=0, padx=10, pady=10, sticky="nsew")
  169. # 设置 fieldset 的网格权重配置
  170. fieldset.grid_columnconfigure(1, weight=3) # 让信息区域占据更多水平空间
  171. fieldset.grid_columnconfigure(0, weight=1) # 操作区域占据较少水平空间
  172. fieldset.grid_rowconfigure(0, weight=1) # 让内容区域可以垂直扩展
  173. fieldset_action = ttk.LabelFrame(fieldset, text="操作")
  174. fieldset_action.grid(row=0, column=0, padx=10, pady=10, sticky="new")
  175. # 设置列权重使按钮平分宽度
  176. fieldset_action.grid_columnconfigure(0, weight=1)
  177. fieldset_action.grid_columnconfigure(1, weight=1)
  178. fieldset_log = ttk.LabelFrame(fieldset, text="操作日志")
  179. fieldset_log.grid(row=0, column=1, padx=10, pady=10, sticky="nsew")
  180. fieldset_log.grid_columnconfigure(0, weight=1) # 文本框列可扩展
  181. fieldset_log.grid_rowconfigure(1, weight=1) # 文本框行可扩展
  182. # 给fieldset_log 添加一个多行文本区域
  183. log_text = tk.Text(fieldset_log, wrap='word')
  184. log_text.grid(row=1, column=0, padx=5, pady=5, sticky="nsew")
  185. log_text_scroll = ttk.Scrollbar(fieldset_log, command=log_text.yview)
  186. log_text_scroll.grid(row=1, column=1, sticky="ns")
  187. log_text.config(yscrollcommand=log_text_scroll.set)
  188. def handle_keypress(event):
  189. if event.keysym in ('Return', 'KP_Enter'):
  190. log_text.insert('end', '\n')
  191. return 'break'
  192. return None
  193. log_text.bind('<KeyPress>', handle_keypress)
  194. log_text.focus_set() # 让文本框获得焦点
  195. log_text.focus_set() # 让文本框获得焦点
  196. # 给fieldset_action 添加7个按钮,每行2个 共3行
  197. green_btn_style = ttk.Style()
  198. green_btn_style.configure('Green.TButton', background='#00cc00', foreground='#009900')
  199. red_btn_style = ttk.Style()
  200. red_btn_style.configure('Red.TButton', background='#ff4444', foreground='#cc0000')
  201. gray_btn_style = ttk.Style()
  202. gray_btn_style.configure('Gray.TButton', background='#888888', foreground='#666666')
  203. button1 = ttk.Button(fieldset_action, text="买入/开多", command=lambda: self.control.event_2(),
  204. style='Green.TButton')
  205. button1.grid(row=0, column=0, padx=2, pady=(5, 2), sticky="ew")
  206. button2 = ttk.Button(fieldset_action, text="买入/平多", command=lambda: print("买入/平多"),
  207. style='Green.TButton')
  208. button2.grid(row=0, column=1, padx=2, pady=(5, 2), sticky="ew")
  209. button3 = ttk.Button(fieldset_action, text="卖出/开空", command=lambda: print("卖出/开空"), style='Red.TButton')
  210. button3.grid(row=1, column=0, padx=2, pady=2, sticky="ew")
  211. button4 = ttk.Button(fieldset_action, text="卖出/平空", command=lambda: print("卖出/平空"), style='Red.TButton')
  212. button4.grid(row=1, column=1, padx=2, pady=2, sticky="ew")
  213. button5 = ttk.Button(fieldset_action, text="撤销委托", command=lambda: print("撤销委托"))
  214. button5.grid(row=2, column=0, padx=2, pady=(2, 5), sticky="ew")
  215. button6 = ttk.Button(fieldset_action, text="一键撤单", command=lambda: print("一键撤单"))
  216. button6.grid(row=2, column=1, padx=2, pady=(2, 5), sticky="ew")
  217. class PhoneListUI(tk.Frame):
  218. """
  219. 手机列表控制页面
  220. """
  221. def __init__(self, control: UIControl = None, master=None, cnf=None, **kw):
  222. super().__init__(master, cnf if cnf is not None else {}, **kw)
  223. self.control = control
  224. self.init_ui()
  225. def init_ui(self):
  226. left_panel = LeftPanel(self, control=self.control)
  227. left_panel.pack(side="left", fill="y")
  228. # 为了演示,我们创建一个简单的右侧区域
  229. right_panel = RightPanel(self, width=400, height=400, control=self.control)
  230. right_panel.pack(side="left", fill="both", expand=True)