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("", self._on_frame_configure) self.canvas.bind("", self._bind_mousewheel_enter) self.canvas.bind("", 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("", self._on_mousewheel) self.canvas.bind_all("", self._on_mousewheel) # Linux self.canvas.bind_all("", self._on_mousewheel) # Linux def _bind_mousewheel_leave(self, event): """鼠标离开 Canvas 时解绑滚轮事件""" self.canvas.unbind_all("") self.canvas.unbind_all("") self.canvas.unbind_all("") 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 = 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('', 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)