import logging import tkinter as tk from tkinter import ttk, messagebox from control.base.base_control import BaseControl, UIControl class LeftPanel(tk.LabelFrame): def __init__(self, master=None, control: UIControl = None): super().__init__(master, text="手机列表") self.scrollbar = None self.canvas = None self.list_frame = None self.control = control 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.flush_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"]) logging.info(f"发现连接手机:{BaseControl.connect_dict}") 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) 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, device["serial"], 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 flush_data(self): """ 刷新数据 """ 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, control: UIControl = None, *args, **kwargs): super().__init__(master, *args, **kwargs) self._num_text = None self.control = control 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): 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') # 再禁用 num_label = ttk.Label(fieldset, text="数量:", width=5) num_label.grid(row=0, column=1, padx=5, pady=5, sticky="nw") num_text = tk.Text(fieldset, height=1.2, width=10, wrap='word') num_text.grid(row=0, column=2, padx=5, pady=5, sticky="nw") # button1 = ttk.Button(fieldset, text="设置", command=lambda: print('----')) # button1.grid(row=0, column=3, padx=2, pady=(5, 2), sticky="ew") self._num_text = num_text 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(1, weight=3) # 让信息区域占据更多水平空间 fieldset.grid_columnconfigure(0, 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="new") # 设置列权重使按钮平分宽度 fieldset_action.grid_columnconfigure(0, weight=1) fieldset_action.grid_columnconfigure(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') def check_number(text): try: float(text) # 尝试转换为浮点数,可以接受整数和小数 return True except ValueError: return False def fun1(): num = self._num_text.get("1.0", "end").rstrip() if not check_number(num): messagebox.showinfo("提示", "请输入数量!") return self.control.event_2(**{ 'shuliang': num, 'log': log_text, }) button1 = ttk.Button(fieldset_action, text="买入/开多", command=lambda: fun1(), style='Green.TButton') button1.grid(row=0, column=0, padx=2, pady=(5, 2), sticky="ew") def fun2(): num = self._num_text.get("1.0", "end").rstrip() if not check_number(num): messagebox.showinfo("提示", "请输入数量!") return self.control.event_4(**{ 'shuliang': num, 'log': log_text, }) button2 = ttk.Button(fieldset_action, text="买入/平多", command=lambda: fun2(), style='Green.TButton') button2.grid(row=0, column=1, padx=2, pady=(5, 2), sticky="ew") def fun3(): num = self._num_text.get("1.0", "end").rstrip() if not check_number(num): messagebox.showinfo("提示", "请输入数量!") return self.control.event_6(**{ 'shuliang': num, 'log': log_text, }) button3 = ttk.Button(fieldset_action, text="卖出/开空", command=lambda: fun3(), style='Red.TButton') button3.grid(row=1, column=0, padx=2, pady=2, sticky="ew") def fun4(): num = self._num_text.get("1.0", "end").rstrip() if not check_number(num): messagebox.showinfo("提示", "请输入数量!") return self.control.event_8(**{ 'shuliang': num, 'log': log_text, }) button4 = ttk.Button(fieldset_action, text="卖出/平空", command=lambda: fun4(), style='Red.TButton') button4.grid(row=1, column=1, padx=2, pady=2, sticky="ew") button5 = ttk.Button(fieldset_action, text="一键全平", command=lambda: self.control.event_11(**{ 'shuliang': self._num_text.get("1.0", "end").rstrip(), 'log': log_text, })) button5.grid(row=2, column=0, padx=2, pady=(2, 5), sticky="ew") button6 = ttk.Button(fieldset_action, text="一键撤单", command=lambda: self.control.event_9(**{ 'shuliang': self._num_text.get("1.0", "end").rstrip(), 'log': log_text, })) button6.grid(row=2, column=1, padx=2, pady=(2, 5), sticky="ew") class PhoneListUI(tk.Frame): """ 手机列表控制页面 """ def __init__(self, control: UIControl = None, master=None, cnf=None, **kw): super().__init__(master, cnf if cnf is not None else {}, **kw) self.right_panel = None self.left_panel = None self.control = control self.init_ui() def init_ui(self): self.left_panel = LeftPanel(self, control=self.control) self.left_panel.pack(side="left", fill="y") # 为了演示,我们创建一个简单的右侧区域 self.right_panel = RightPanel(self, width=400, height=400, control=self.control) self.right_panel.pack(side="left", fill="both", expand=True)