# coding:utf-8 # MainEntry 负责应用主窗口与菜单的统一构建: # - 通过 build_menu_model 定义跨平台统一的菜单数据结构 # - 在 macOS 上使用 Tk 菜单栏;在非 macOS 上使用 pystray 系统托盘 # - 所有菜单项均绑定到同名处理函数,切换平台无需改动业务逻辑 import tkinter as tk from core.logger import LogLevel, PrintLog import threading import sys class MainEntry: def __init__(self, master): # 初始化 Tk 窗口属性与基础状态 self.master = master self.master.title("Main Board") self.master.geometry("800x600") self.master.configure(bg="#f0f0f0") self.master.resizable(False, False) self.master.protocol("WM_DELETE_WINDOW", self.hide_window) # QMT 开关状态用于动态更新菜单文案 self.qmt_enabled = False self.icon = None # 非 macOS 使用系统托盘(pystray);macOS 使用原生菜单栏 self.systray_supported = sys.platform != "darwin" # 首次进入根据平台构建菜单 self.create_menu() def build_menu_model(self): # 菜单模型统一描述所有菜单: # - 每个分组包含 label 与 items # - item 支持:label 文案、action 处理函数名、enabled 启用状态、default 默认项、separator 分隔符 # - 文案可根据状态动态生成(如 QMT 开关) qmt_label = "QMT (已开启)" if self.qmt_enabled else "QMT (已关闭)" return [ { "label": "-- 交易大师 --", "items": [ {"label": "交易复盘", "action": "handler", "enabled": True}, {"label": "市场数据", "action": "handler", "enabled": True}, {"label": "快速下单", "action": "handler", "enabled": True}, ], }, { "label": "-- 策略交易 --", "items": [ {"label": "交易看板", "action": "handler", "enabled": True}, {"label": "策略中心", "action": None, "enabled": False}, {"label": "策略定制", "action": None, "enabled": False}, ], }, { "label": "-- 实时数据 --", "items": [ {"label": qmt_label, "action": "marketDataSwitch", "enabled": True}, ], }, { "label": "-- 系统 --", "items": [ {"label": "控制台", "action": "show_window", "enabled": True, "default": True}, {"label": "设置", "action": "marketDataSwitch", "enabled": True}, {"separator": True}, {"label": "退出", "action": "quit_window", "enabled": True}, ], }, ] def create_menu(self): # 根据统一菜单模型与平台类型,渲染到系统托盘或 Tk 菜单栏 model = self.build_menu_model() if self.systray_supported: # 非 macOS:延迟导入 pystray 与 PIL,避免在 macOS 上引入不兼容依赖 from PIL import Image import pystray image = Image.open("logo.png") items = [] for group in model: # 分组标题作为禁用的头部项 items.append(pystray.MenuItem(group["label"], None, enabled=False)) for it in group["items"]: if it.get("separator"): items.append(pystray.Menu.SEPARATOR) else: fn = getattr(self, it["action"]) if it.get("action") else None items.append(pystray.MenuItem(it["label"], fn, default=it.get("default", False), enabled=it.get("enabled", True))) menu = tuple(items) if self.icon: # 已存在托盘图标:更新菜单 self.icon.menu = menu self.icon.update_menu() else: # 首次创建托盘图标并在后台线程运行 self.icon = pystray.Icon("name", image, "标题", menu) self.trayThread = threading.Thread(target=self.icon.run, daemon=True) self.trayThread.start() else: # macOS:使用 Tk 菜单栏 menu_bar = tk.Menu(self.master) for group in model: m = tk.Menu(menu_bar, tearoff=0) for it in group["items"]: if it.get("separator"): m.add_separator() else: fn = getattr(self, it["action"]) if it.get("action") else None if it.get("enabled", True) and fn: m.add_command(label=it["label"], command=fn) else: m.add_command(label=it["label"], state="disabled") menu_bar.add_cascade(label=group["label"], menu=m) self.master.config(menu=menu_bar) def marketDataSwitch(self): # 切换 QMT 开关,并触发菜单重建以更新文案 if self.qmt_enabled: self.qmt_enabled = False PrintLog(LogLevel.INFO, "QMT 市场数据已关闭") else: self.qmt_enabled = True PrintLog(LogLevel.INFO, "QMT 市场数据已开启") self.create_menu() def handler(self): # 通用占位处理:当前仅记录点击行为,后续可替换为具体业务逻辑 PrintLog(LogLevel.INFO, f"点击了") def hide_window(self): # 关闭窗口事件:隐藏但不退出应用 PrintLog(LogLevel.INFO, "隐藏主窗口") self.master.withdraw() # 隐藏主窗口 def show_window(self): # 显示主窗口;在非 macOS 平台同步让托盘图标可见 if self.icon: self.icon.visible = True PrintLog(LogLevel.INFO, "显示主窗口") self.master.deiconify() # 显示主窗口 def quit_window(self, icon=None): # 退出应用;在非 macOS 平台时关闭托盘图标 if icon: icon.stop() PrintLog(LogLevel.INFO, "退出应用") self.master.quit() self.master.destroy() def run(self): # 主事件循环入口 self.master.mainloop()