Files
sfgrid/core/main_entry.py
T

180 lines
7.5 KiB
Python

# 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.main_frame = tk.Frame(self.master, bg="#f0f0f0")
self.main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
# 首次进入根据平台构建菜单
self.create_menu()
self.create_dashboard()
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_dashboard(self):
# 根据菜单模型构建主窗口按钮面板
for widget in self.main_frame.winfo_children():
widget.destroy()
model = self.build_menu_model()
for group in model:
# 为每个分组创建 LabelFrame
group_frame = tk.LabelFrame(self.main_frame, text=group["label"], bg="#f0f0f0", padx=10, pady=10)
group_frame.pack(fill=tk.X, pady=10, padx=10)
for it in group["items"]:
if it.get("separator"):
continue
fn = getattr(self, it["action"]) if it.get("action") else None
state = tk.NORMAL if it.get("enabled", True) else tk.DISABLED
# 创建按钮
btn = tk.Button(group_frame, text=it["label"], command=fn, state=state)
btn.pack(side=tk.LEFT, padx=5)
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()
self.create_dashboard()
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()