phone_list.py 12 KB

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