new ui
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from core.logger import LogLevel, LogData, PrintLog
|
||||
from core.ui.tkinter.sfgrid_view import TradeTargetUI
|
||||
|
||||
# 检测运行环境,决定使用真实或模拟 QMT
|
||||
def get_qmt_module():
|
||||
try:
|
||||
# 尝试导入真实 QMT,如果失败则使用模拟
|
||||
from core.qmt import qmtv
|
||||
return qmtv
|
||||
except ImportError:
|
||||
from core.qmt_dummy import qmtv
|
||||
return qmtv
|
||||
|
||||
qmtv = get_qmt_module()
|
||||
|
||||
from core.eventbus import EventPrintLog
|
||||
from core.eventbus import event_bus as eBus
|
||||
|
||||
|
||||
class MainWindow:
|
||||
def __init__(self, configLogLevel:str, progress=None):
|
||||
self.root = tk.Tk()
|
||||
self.root.title("神之一手 - 交易系统")
|
||||
self.root.geometry("1400x700")
|
||||
|
||||
self.logLevel = LogLevel[configLogLevel]
|
||||
PrintLog(LogLevel.DEBUG, f"系统启动成功 {self.logLevel.name}")
|
||||
# 存储各个Frame的引用
|
||||
self.strategy_frames = {}
|
||||
# 日志面板可见性标志
|
||||
self.log_visible = False
|
||||
self.create_ui(progress)
|
||||
|
||||
eBus.subscribe(EventPrintLog, self.on_log_event)
|
||||
|
||||
|
||||
def create_ui(self, progress=None):
|
||||
"""创建UI界面"""
|
||||
# 主容器
|
||||
main_container = ttk.Frame(self.root)
|
||||
main_container.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
||||
|
||||
# 中间主体区域
|
||||
content_area = ttk.Frame(main_container)
|
||||
content_area.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
# 右侧内容区域容器
|
||||
self.content_container = ttk.Frame(content_area)
|
||||
self.content_container.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
|
||||
# 创建策略Frame
|
||||
strategy_names = ["网格"]
|
||||
self.create_strategy_frames(strategy_names, progress)
|
||||
|
||||
# 创建全局日志面板(默认隐藏)
|
||||
self.create_global_log_panel(main_container)
|
||||
|
||||
# 默认显示第一个策略
|
||||
self.show_strategy_frame(0)
|
||||
|
||||
def create_global_log_panel(self, parent):
|
||||
"""创建全局日志面板"""
|
||||
# 日志区域(默认隐藏)
|
||||
self.log_frame = ttk.LabelFrame(parent, text="操作日志", padding=10)
|
||||
# 默认不显示,通过工具栏按钮控制
|
||||
|
||||
# 创建日志表格
|
||||
columns = ("timestamp", "level", "message")
|
||||
|
||||
self.log_table = ttk.Treeview(self.log_frame, columns=columns, show='headings', height=8)
|
||||
|
||||
log_column_configs = {
|
||||
"timestamp": ("时间", 100),
|
||||
"level": ("级别", 50),
|
||||
"message": ("消息", 1150) # 调整宽度适应全局布局
|
||||
}
|
||||
|
||||
for col in columns:
|
||||
title, width = log_column_configs[col]
|
||||
self.log_table.heading(col, text=title)
|
||||
self.log_table.column(col, width=width, anchor=tk.W)
|
||||
|
||||
# 添加初始日志
|
||||
from datetime import datetime
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
self.log_table.insert('', tk.END, values=(timestamp, "INFO", "系统启动成功"))
|
||||
|
||||
# 滚动条
|
||||
scrollbar = ttk.Scrollbar(self.log_frame, orient=tk.VERTICAL, command=self.log_table.yview)
|
||||
self.log_table.configure(yscrollcommand=scrollbar.set)
|
||||
|
||||
self.log_table.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
|
||||
def on_log_event(self, event:LogData):
|
||||
if self.logLevel.value <= event.level.value:
|
||||
self.add_log(event.level, event.message)
|
||||
|
||||
|
||||
def add_log(self, level:LogLevel, message):
|
||||
"""添加日志记录 - 全局方法"""
|
||||
from datetime import datetime
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
self.log_table.insert('', 0, values=(timestamp, level.name, message))
|
||||
|
||||
def clear_logs(self):
|
||||
"""清空日志记录"""
|
||||
# 删除所有日志项
|
||||
for item in self.log_table.get_children():
|
||||
self.log_table.delete(item)
|
||||
|
||||
def create_strategy_frames(self, strategy_names, progress=None):
|
||||
"""创建各个策略的Frame"""
|
||||
frame = TradeTargetUI(self.content_container, progress=progress)
|
||||
self.strategy_frames[0] = frame
|
||||
|
||||
def show_strategy_frame(self, index):
|
||||
"""显示策略Frame"""
|
||||
if index in self.strategy_frames:
|
||||
self.strategy_frames[index].pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
def toggle_log_panel(self):
|
||||
"""切换日志面板的显示/隐藏"""
|
||||
if self.log_visible:
|
||||
self.log_frame.pack_forget()
|
||||
self.log_visible = False
|
||||
else:
|
||||
self.log_frame.pack(side=tk.BOTTOM, fill=tk.X, pady=(5, 0))
|
||||
self.log_visible = True
|
||||
|
||||
def on_exit(self):
|
||||
"""退出程序"""
|
||||
from tkinter import messagebox
|
||||
result = messagebox.askyesno("确认退出", "确定要退出系统吗?")
|
||||
if result:
|
||||
self.root.destroy()
|
||||
|
||||
def run(self):
|
||||
"""运行程序"""
|
||||
self.root.mainloop()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,112 @@
|
||||
"""
|
||||
启动进度窗口 — 无边框小窗口,负责整个初始化流程。
|
||||
"""
|
||||
import time
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox
|
||||
|
||||
|
||||
class SplashWindow:
|
||||
"""初始化进度窗口,所有者启动逻辑"""
|
||||
|
||||
def __init__(self):
|
||||
self.root = tk.Tk()
|
||||
self.root.title("神之一手")
|
||||
self.root.geometry("380x120")
|
||||
self.root.resizable(False, False)
|
||||
self.root.overrideredirect(True)
|
||||
|
||||
self.root.update_idletasks()
|
||||
sw = self.root.winfo_screenwidth()
|
||||
sh = self.root.winfo_screenheight()
|
||||
w, h = 380, 120
|
||||
self.root.geometry(f"{w}x{h}+{(sw - w) // 2}+{(sh - h) // 2}")
|
||||
|
||||
frame = ttk.Frame(self.root, padding=20)
|
||||
frame.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
ttk.Label(frame, text="神之一手", font=('Microsoft YaHei', 14, 'bold')).pack(pady=(0, 5))
|
||||
self._status = ttk.Label(frame, text="正在初始化...", font=('Microsoft YaHei', 9))
|
||||
self._status.pack(pady=(0, 10))
|
||||
|
||||
self._bar = ttk.Progressbar(frame, mode='determinate', length=340)
|
||||
self._bar.pack()
|
||||
self.root.update()
|
||||
|
||||
def progress(self, text: str, pct: float):
|
||||
self._status.configure(text=text)
|
||||
self._bar.configure(value=pct)
|
||||
self.root.update()
|
||||
|
||||
def _destroy(self):
|
||||
self.root.destroy()
|
||||
|
||||
def run(self):
|
||||
"""执行完整启动流程,成功返回主窗口,失败返回 None"""
|
||||
from core.qmt_real import RealQmtV, qmtv as selected_qmtv
|
||||
|
||||
while True:
|
||||
_t_total = time.time()
|
||||
|
||||
# 步骤1: 探测 QMT 环境
|
||||
self.progress("正在检查 QMT 环境...", 10)
|
||||
_t = time.time()
|
||||
try:
|
||||
discovered = RealQmtV._discover_qmt_port()
|
||||
except Exception:
|
||||
discovered = 0
|
||||
print(f'[计时] 步骤1-探测QMT环境: {time.time() - _t:.2f}s')
|
||||
|
||||
if not discovered:
|
||||
self._destroy()
|
||||
messagebox.showerror(
|
||||
"启动失败",
|
||||
"未能自动探测到 QMT 环境。\n\n"
|
||||
"请确认:\n"
|
||||
"1. 极简QMT(GJQMT)已启动并登录\n"
|
||||
"2. XtMiniQmt.exe 和 miniquote.exe 进程在运行"
|
||||
)
|
||||
return None
|
||||
|
||||
# 步骤2: 初始化交易器
|
||||
self.progress("正在初始化交易器...", 35)
|
||||
_t = time.time()
|
||||
selected_qmtv.init_qmtv()
|
||||
print(f'[计时] 步骤2-初始化交易器: {time.time() - _t:.2f}s')
|
||||
|
||||
# 步骤3: 连接 QMT
|
||||
self.progress("正在连接 QMT...", 55)
|
||||
_t = time.time()
|
||||
connected = selected_qmtv.connect()
|
||||
print(f'[计时] 步骤3-连接QMT: {time.time() - _t:.2f}s')
|
||||
|
||||
if not connected:
|
||||
self._destroy()
|
||||
option = messagebox.askokcancel(
|
||||
"连接失败",
|
||||
"QMT 连接失败。\n\n"
|
||||
"请确认极简QMT 已启动并登录交易账号。\n"
|
||||
"点击「确定」重试,或「取消」退出。"
|
||||
)
|
||||
if not option:
|
||||
return None
|
||||
# 重试:重新创建进度窗口
|
||||
self.__init__()
|
||||
continue
|
||||
|
||||
# 步骤4: 加载主界面
|
||||
self.progress("正在加载持仓与策略...", 75)
|
||||
_t = time.time()
|
||||
from core.ui.tkinter.main_window import MainWindow
|
||||
window = MainWindow('INFO', progress=lambda t, p: self.progress(t, 75 + p * 0.2))
|
||||
print(f'[计时] 步骤4-主界面加载: {time.time() - _t:.2f}s')
|
||||
|
||||
window.root.update()
|
||||
|
||||
# 步骤5: 完成
|
||||
self.progress("启动完成", 100)
|
||||
self.root.update()
|
||||
self.root.after(300, self._destroy)
|
||||
print(f'[计时] 总启动耗时: {time.time() - _t_total:.2f}s')
|
||||
|
||||
return window
|
||||
Reference in New Issue
Block a user