|
|
@@ -0,0 +1,275 @@
|
|
|
+import logging
|
|
|
+import tkinter as tk
|
|
|
+from tkinter import ttk
|
|
|
+
|
|
|
+from plat.base.base_control import BaseControl
|
|
|
+
|
|
|
+
|
|
|
+class LeftPanel(tk.LabelFrame):
|
|
|
+ def __init__(self, master=None):
|
|
|
+ super().__init__(master, text="手机列表")
|
|
|
+ self.master = master
|
|
|
+ self.phone_items_data = [] # 用于存储每个手机项的数据 (包含 CheckVar)
|
|
|
+ self.create_widgets()
|
|
|
+ self._check_scrollable() # 初始检查是否需要滚动条
|
|
|
+
|
|
|
+ def create_widgets(self):
|
|
|
+
|
|
|
+ title_label = ttk.Frame(self, relief="flat", borderwidth=1)
|
|
|
+ title_label.grid(row=0, column=0, padx=10, pady=10, sticky="ew")
|
|
|
+
|
|
|
+ # 添加分割线
|
|
|
+ separator = ttk.Separator(title_label, orient="horizontal")
|
|
|
+ separator.grid(row=1, column=0, columnspan=4, sticky="ew", padx=5, pady=5)
|
|
|
+
|
|
|
+ name_label = ttk.Label(title_label, text='全选')
|
|
|
+ name_label.grid(row=0, column=1, padx=5, pady=10, sticky="e")
|
|
|
+ # 选择框
|
|
|
+ check_all = tk.BooleanVar()
|
|
|
+ check_button = ttk.Checkbutton(title_label, variable=check_all, command=lambda v=check_all: self.toggle_check(v))
|
|
|
+ check_button.grid(row=0, column=2, padx=5, pady=10, sticky="e")
|
|
|
+
|
|
|
+ # 添加数据按钮
|
|
|
+ add_data_button = ttk.Button(title_label, text="刷新", command=self.add_data)
|
|
|
+ add_data_button.grid(row=0, column=3, padx=5, pady=10, sticky="e")
|
|
|
+
|
|
|
+ # 创建 Canvas 和 Scrollbar
|
|
|
+ self.canvas = tk.Canvas(self)
|
|
|
+ self.scrollbar = ttk.Scrollbar(self, orient="vertical", command=self.canvas.yview)
|
|
|
+ self.canvas.configure(yscrollcommand=self.scrollbar.set)
|
|
|
+
|
|
|
+ self.canvas.grid(row=1, column=0, columnspan=4, sticky="nsew")
|
|
|
+ self.scrollbar.grid(row=1, column=4, sticky="ns")
|
|
|
+
|
|
|
+ # 创建 Frame 放入 Canvas 中
|
|
|
+ self.list_frame = ttk.Frame(self.canvas)
|
|
|
+ self.canvas.create_window((0, 0), window=self.list_frame, anchor="nw")
|
|
|
+ self.list_frame.bind("<Configure>", self._on_frame_configure)
|
|
|
+ self.canvas.bind("<Enter>", self._bind_mousewheel_enter)
|
|
|
+ self.canvas.bind("<Leave>", self._bind_mousewheel_leave)
|
|
|
+
|
|
|
+ # 配置 Grid 的行和列的权重,使 Canvas 可以扩展
|
|
|
+ self.grid_rowconfigure(1, weight=1)
|
|
|
+ self.grid_columnconfigure(0, weight=1)
|
|
|
+ self.grid_columnconfigure(1, weight=0) # 全选按钮不扩展
|
|
|
+ self.grid_columnconfigure(2, weight=0) # 添加数据按钮不扩展
|
|
|
+ self.grid_columnconfigure(3, weight=0) # 取消全选按钮不扩展
|
|
|
+ self.grid_columnconfigure(4, weight=0) # 滚动条列不扩展
|
|
|
+
|
|
|
+ self._populate_list()
|
|
|
+
|
|
|
+ def _populate_list(self):
|
|
|
+ """根据 self.phone_data 填充列表"""
|
|
|
+ for widget in self.list_frame.winfo_children():
|
|
|
+ widget.destroy()
|
|
|
+ self.phone_items_data = []
|
|
|
+ # for phone in self.phone_data:
|
|
|
+ # self.create_list_item(phone["name"], phone["status"])
|
|
|
+
|
|
|
+ for pkg, device in BaseControl.connect_dict.items():
|
|
|
+ logging.info(f'==添加手机信息=>{pkg} {device}')
|
|
|
+ self.create_list_item(device)
|
|
|
+
|
|
|
+ self._on_frame_configure(None) # 更新滚动区域
|
|
|
+
|
|
|
+ def create_list_item(self, device):
|
|
|
+ item_frame = ttk.Frame(self.list_frame, padding=(5, 5))
|
|
|
+ item_frame.pack(fill="x", expand=True)
|
|
|
+
|
|
|
+ name = device['device']
|
|
|
+ status = device['status']
|
|
|
+
|
|
|
+ # 选择框
|
|
|
+ check_var = tk.BooleanVar()
|
|
|
+ check_var.set(True)
|
|
|
+ check_button = ttk.Checkbutton(item_frame, variable=check_var)
|
|
|
+ check_button.pack(side="left")
|
|
|
+
|
|
|
+ # 手机名称
|
|
|
+ name_label = ttk.Label(item_frame, text=f'{device["name"]}:【{device["pkg"]}】', anchor="w", width=20)
|
|
|
+ name_label.pack(side="left", padx=5)
|
|
|
+
|
|
|
+ # 状态指示器
|
|
|
+ status_canvas = tk.Canvas(item_frame, width=10, height=10, highlightthickness=0)
|
|
|
+ status_canvas.pack(side="left", padx=5)
|
|
|
+ if status == "online":
|
|
|
+ status_canvas.create_oval(0, 0, 10, 10, fill="green")
|
|
|
+ else:
|
|
|
+ status_canvas.create_oval(0, 0, 10, 10, fill="red")
|
|
|
+
|
|
|
+ # 状态按钮 (这里我们用一个简单的 Label 模拟)
|
|
|
+ status_button = ttk.Label(item_frame, text="@", width=3, anchor="center", relief="groove")
|
|
|
+ status_button.pack(side="right", padx=5)
|
|
|
+
|
|
|
+ self.phone_items_data.append((check_var, name, status)) # 存储 CheckVar 和数据
|
|
|
+
|
|
|
+ def toggle_check(self, check_var):
|
|
|
+ if check_var.get():
|
|
|
+ print("复选框被选中")
|
|
|
+ else:
|
|
|
+ print("复选框被取消选中")
|
|
|
+ for var, _, _ in self.phone_items_data:
|
|
|
+ var.set(check_var.get())
|
|
|
+ pass
|
|
|
+
|
|
|
+ def add_data(self):
|
|
|
+ """
|
|
|
+ 动态添加数据
|
|
|
+ """
|
|
|
+ # logging.info(f"==手机信息=>{BaseControl.connect_dict}")
|
|
|
+ # new_index = len(self.phone_data) + 1
|
|
|
+ # new_phone = {"name": f"Tel - {new_index:02d} [New Device]", "status": "pending"}
|
|
|
+ # self.phone_data.append(new_phone)
|
|
|
+ self._populate_list() # 重新填充列表以显示新数据
|
|
|
+ self._check_scrollable() # 添加数据后检查是否需要滚动条
|
|
|
+
|
|
|
+ def _on_frame_configure(self, event):
|
|
|
+ """当列表 Frame 大小改变时,更新 Canvas 的滚动区域并检查是否需要绑定滚轮事件"""
|
|
|
+ self.canvas.configure(scrollregion=self.canvas.bbox("all"))
|
|
|
+ self._check_scrollable()
|
|
|
+
|
|
|
+ def _check_scrollable(self):
|
|
|
+ """检查内容是否需要滚动条,并决定是否需要显示"""
|
|
|
+ if self.canvas.winfo_height() < self.canvas.bbox("all")[3]:
|
|
|
+ self.scrollbar.grid(row=1, column=4, sticky="ns") # 显示滚动条
|
|
|
+ else:
|
|
|
+ self.scrollbar.grid_remove() # 隐藏滚动条
|
|
|
+
|
|
|
+ def _bind_mousewheel_enter(self, event):
|
|
|
+ """鼠标进入 Canvas 时绑定滚轮事件"""
|
|
|
+ if self.canvas.winfo_height() < self.canvas.bbox("all")[3]: # 只有在需要滚动时才绑定
|
|
|
+ self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
|
|
|
+ self.canvas.bind_all("<Button-4>", self._on_mousewheel) # Linux
|
|
|
+ self.canvas.bind_all("<Button-5>", self._on_mousewheel) # Linux
|
|
|
+
|
|
|
+ def _bind_mousewheel_leave(self, event):
|
|
|
+ """鼠标离开 Canvas 时解绑滚轮事件"""
|
|
|
+ self.canvas.unbind_all("<MouseWheel>")
|
|
|
+ self.canvas.unbind_all("<Button-4>")
|
|
|
+ self.canvas.unbind_all("<Button-5>")
|
|
|
+
|
|
|
+ def _on_mousewheel(self, event):
|
|
|
+ """鼠标滚轮滚动事件处理"""
|
|
|
+ if event.delta:
|
|
|
+ self.canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
|
|
|
+ else:
|
|
|
+ if event.num == 4:
|
|
|
+ self.canvas.yview_scroll(-1, "units")
|
|
|
+ elif event.num == 5:
|
|
|
+ self.canvas.yview_scroll(1, "units")
|
|
|
+
|
|
|
+
|
|
|
+class RightPanel(tk.Frame):
|
|
|
+ def __init__(self, master=None, *args, **kwargs):
|
|
|
+ super().__init__(master)
|
|
|
+ self.master = master
|
|
|
+ self.create_args()
|
|
|
+ self.create_info_ares()
|
|
|
+ self.grid_columnconfigure(0, weight=1) # 让列可以水平扩展
|
|
|
+ self.grid_rowconfigure(0, weight=0) # 参数设定区域不需要扩展
|
|
|
+ self.grid_rowconfigure(1, weight=1) # 让操作区域可以垂直扩展
|
|
|
+
|
|
|
+ def create_args(self):
|
|
|
+ # 右侧区域的组件可以在这里创建
|
|
|
+ # label = ttk.Label(self, text="右侧区域", font=("Arial", 12, "bold"))
|
|
|
+ # label.pack(padx=10, pady=10)
|
|
|
+ #
|
|
|
+ # # 添加更多组件...
|
|
|
+ # button = ttk.Button(self, text="按钮", command=lambda: print("按钮被点击"))
|
|
|
+ # button.pack(padx=10, pady=10)
|
|
|
+
|
|
|
+ fieldset = ttk.LabelFrame(self, text="参数设定")
|
|
|
+ fieldset.grid(row=0, column=0, padx=10, pady=10, sticky="nsew")
|
|
|
+ # fieldset.grid_columnconfigure(0, weight=1)
|
|
|
+ # fieldset.grid_rowconfigure(1, weight=1)
|
|
|
+ name_label = ttk.Label(fieldset, text="币种:", width=5)
|
|
|
+ name_label.grid(row=0, column=0, padx=5, pady=5, sticky="nw")
|
|
|
+
|
|
|
+ name_val = tk.Text(fieldset, width=20, height=1.2, wrap='word')
|
|
|
+ name_val.grid(row=0, column=1, padx=5, pady=5, sticky="nw")
|
|
|
+ name_val.insert('1.0', 'HMSTR') # 插入文本
|
|
|
+ name_val.config(state='disabled') # 再禁用
|
|
|
+
|
|
|
+ cmd_label = ttk.Label(fieldset, text="数量:", width=5)
|
|
|
+ cmd_label.grid(row=0, column=2, padx=5, pady=5, sticky="nw")
|
|
|
+
|
|
|
+ cmd_text = tk.Text(fieldset, height=1.2, width=10, wrap='word')
|
|
|
+ cmd_text.grid(row=0, column=3, padx=5, pady=5, sticky="nw")
|
|
|
+
|
|
|
+ def create_info_ares(self):
|
|
|
+ # 创建一个 Frame 来模拟 <fieldset>
|
|
|
+ fieldset = ttk.Frame(self)
|
|
|
+ fieldset.grid(row=1, column=0, padx=10, pady=10, sticky="nsew")
|
|
|
+
|
|
|
+ # 设置 fieldset 的网格权重配置
|
|
|
+ fieldset.grid_columnconfigure(0, weight=3) # 让信息区域占据更多水平空间
|
|
|
+ fieldset.grid_columnconfigure(1, weight=1) # 操作区域占据较少水平空间
|
|
|
+ fieldset.grid_rowconfigure(0, weight=1) # 让内容区域可以垂直扩展
|
|
|
+
|
|
|
+ fieldset_action = ttk.LabelFrame(fieldset, text="操作")
|
|
|
+ fieldset_action.grid(row=0, column=0, padx=10, pady=10, sticky="nsew")
|
|
|
+ fieldset_action.grid_columnconfigure(0, weight=1)
|
|
|
+ fieldset_action.grid_rowconfigure(1, weight=1)
|
|
|
+
|
|
|
+ fieldset_log = ttk.LabelFrame(fieldset, text="操作日志")
|
|
|
+ fieldset_log.grid(row=0, column=1, padx=10, pady=10, sticky="nsew")
|
|
|
+ fieldset_log.grid_columnconfigure(0, weight=1) # 文本框列可扩展
|
|
|
+ fieldset_log.grid_rowconfigure(1, weight=1) # 文本框行可扩展
|
|
|
+
|
|
|
+ # 给fieldset_log 添加一个多行文本区域
|
|
|
+ log_text = tk.Text(fieldset_log, wrap='word')
|
|
|
+ log_text.grid(row=1, column=0, padx=5, pady=5, sticky="nsew")
|
|
|
+ log_text_scroll = ttk.Scrollbar(fieldset_log, command=log_text.yview)
|
|
|
+ log_text_scroll.grid(row=1, column=1, sticky="ns")
|
|
|
+ log_text.config(yscrollcommand=log_text_scroll.set)
|
|
|
+
|
|
|
+ def handle_keypress(event):
|
|
|
+ if event.keysym in ('Return', 'KP_Enter'):
|
|
|
+ log_text.insert('end', '\n')
|
|
|
+ return 'break'
|
|
|
+ return None
|
|
|
+
|
|
|
+ log_text.bind('<KeyPress>', handle_keypress)
|
|
|
+ log_text.focus_set() # 让文本框获得焦点
|
|
|
+ log_text.focus_set() # 让文本框获得焦点
|
|
|
+
|
|
|
+ # 给fieldset_action 添加7个按钮,每行2个 共3行
|
|
|
+ green_btn_style = ttk.Style()
|
|
|
+ green_btn_style.configure('Green.TButton', background='#00cc00', foreground='#009900')
|
|
|
+ red_btn_style = ttk.Style()
|
|
|
+ red_btn_style.configure('Red.TButton', background='#ff4444', foreground='#cc0000')
|
|
|
+
|
|
|
+ gray_btn_style = ttk.Style()
|
|
|
+ gray_btn_style.configure('Gray.TButton', background='#888888', foreground='#666666')
|
|
|
+ button1 = ttk.Button(fieldset_action, text="买入/开多", command=lambda: print("买入/开多"), style='Green.TButton')
|
|
|
+ button1.grid(row=0, column=0, padx=5, pady=5, sticky="nw")
|
|
|
+
|
|
|
+ button2 = ttk.Button(fieldset_action, text="买入/平多", command=lambda: print("买入/平多"), style='Green.TButton')
|
|
|
+ button2.grid(row=0, column=1, padx=5, pady=5, sticky="nw")
|
|
|
+ button3 = ttk.Button(fieldset_action, text="卖出/开空", command=lambda: print("卖出/开空"), style='Red.TButton')
|
|
|
+ button3.grid(row=1, column=0, padx=5, pady=5, sticky="nw")
|
|
|
+ button4 = ttk.Button(fieldset_action, text="卖出/平空", command=lambda: print("卖出/平空"), style='Red.TButton')
|
|
|
+ button4.grid(row=1, column=1, padx=5, pady=5, sticky="nw")
|
|
|
+ button5 = ttk.Button(fieldset_action, text="撤销委托", command=lambda: print("撤销委托"))
|
|
|
+ button5.grid(row=2, column=0, padx=5, pady=5, sticky="nw")
|
|
|
+ button6 = ttk.Button(fieldset_action, text="一键撤单", command=lambda: print("一键撤单"))
|
|
|
+ button6.grid(row=2, column=1, padx=5, pady=5, sticky="nw")
|
|
|
+
|
|
|
+
|
|
|
+class PhoneListUI(tk.Frame):
|
|
|
+ """
|
|
|
+ 手机列表控制页面
|
|
|
+ """
|
|
|
+
|
|
|
+ def __init__(self, control: BaseControl = None, master=None, cnf=None, **kw):
|
|
|
+ super().__init__(master, cnf if cnf is not None else {}, **kw)
|
|
|
+ self.control = control
|
|
|
+ self.init_ui()
|
|
|
+
|
|
|
+ def init_ui(self):
|
|
|
+ left_panel = LeftPanel(self)
|
|
|
+ left_panel.pack(side="left", fill="y")
|
|
|
+
|
|
|
+ # 为了演示,我们创建一个简单的右侧区域
|
|
|
+ right_panel = RightPanel(self, width=400, height=400)
|
|
|
+ right_panel.pack(side="left", fill="both", expand=True)
|