mirror of
https://ghfast.top/https://github.com/aeroxw/easy-tdx.git
synced 2026-09-12 14:34:15 +08:00
面向零基础老年用户,easy-tdx 可打包成单一 Windows EXE,双击即用。 新增: - 后端同源托管前端 dist(app.py 三级探测:env → _MEIPASS → web-ui/dist) - easy-tdx serve 默认 --open-browser,启动后自动开浏览器 - PyInstaller 打包入口(__main__.py)+ spec 配置(easy_tdx.spec) - 系统托盘(tray.py):右下角图标,右键"打开浏览器/退出" 解决老人不会用任务管理器关闭的问题 - GitHub Actions release.yml:打 v* tag 自动构建并发布 EXE 到 Releases - docs/packaging.md 打包使用文档 修复: - K 线残缺尾记录导致 500(security_bars.py):通达信服务器偶发 ret_count 与 body 长度不匹配,改为 try/except 优雅降级丢弃残缺尾, 返回已解析的完整记录。GetIndexBarsCmd 同改。加 4 个回归测试。 - PyInstaller frozen 模式三个坑: 1. console=False 下 stdout/stderr 为 None → 重定向到日志文件 2. multiprocessing spawn 子进程重新 import __main__ → freeze_support + 子进程检测 3. 系统托盘需主线程消息泵 → uvicorn 挪到后台线程 文档: - README/手册改为三档分流:EXE(零基础)/ Python(一条命令)/ 源码(打包) - 删除 npm run dev / 5173 / 两个终端的过时说明 - 手册补虚拟环境配置 + EXE 打包附录 + EXE 排错 FAQ
96 lines
3.2 KiB
RPMSpec
96 lines
3.2 KiB
RPMSpec
# -*- mode: python ; coding: utf-8 -*-
|
||
"""PyInstaller spec — 把 easy-tdx + Vue 前端打包成单一 Windows EXE。
|
||
|
||
构建前提(CI 会自动完成,本地手动构建需自行执行)::
|
||
|
||
1. pip install -e ".[web]" pyinstaller
|
||
2. cd web-ui && npm ci && npm run build # 产出 web-ui/dist/
|
||
3. pyinstaller easy_tdx.spec # 产出 dist/easy-tdx.exe
|
||
|
||
设计要点:
|
||
- ``--onefile``:单 EXE,老人双击即用。首次启动解压到临时目录需 2-5 秒。
|
||
- ``console=False``:无黑窗(Windows GUI 子系统)。
|
||
- ``collect_submodules('uvicorn')``:uvicorn 用 importlib 动态加载协议
|
||
实现,PyInstaller 静态分析漏掉会导致启动报 ModuleNotFoundError。
|
||
- ``collect_submodules('easy_tdx')``:routers/backtest.strategies 等大量
|
||
延迟 import,全量收集避免遗漏。
|
||
- ``web-ui/dist → web_dist``:app.py 的 ``_resolve_web_dist_dir`` 会从
|
||
``sys._MEIPASS/web_dist`` 读取前端资源。
|
||
- ``~/.easy_tdx`` 的 SQLite 不在打包范围(用户运行时写入家目录),无需处理。
|
||
"""
|
||
|
||
from PyInstaller.utils.hooks import collect_data_files, collect_submodules
|
||
|
||
hiddenimports: list[str] = []
|
||
hiddenimports += collect_submodules("uvicorn")
|
||
hiddenimports += collect_submodules("easy_tdx")
|
||
# pandas / numpy / scipy 由 PyInstaller 自带 hook 处理(见
|
||
# PyInstaller/hooks/hook-pandas.* 等),无需手动 collect_submodules——
|
||
# 手动全量收集会把 numpy.typing.tests / pandas._numba.kernels 等可选/测试
|
||
# 模块也拖进来,既增加体积又制造噪音 WARNING。
|
||
# 系统托盘(打包态专用):pystray 在 Windows 用 win32 API,Pillow 的
|
||
# 图像格式插件用 importlib 动态加载,两者都需要显式声明。
|
||
hiddenimports += collect_submodules("pystray")
|
||
|
||
datas: list[tuple[str, str]] = []
|
||
datas += collect_data_files("tzdata")
|
||
# Pillow 的图像格式插件(PngImagePlugin 等)随数据文件一起收集
|
||
datas += collect_data_files("PIL")
|
||
# 前端 dist 打包到运行时 sys._MEIPASS/web_dist
|
||
datas += [("web-ui/dist", "web_dist")]
|
||
|
||
block_cipher = None
|
||
|
||
a = Analysis(
|
||
["src/easy_tdx/__main__.py"],
|
||
pathex=["src"],
|
||
binaries=[],
|
||
datas=datas,
|
||
hiddenimports=hiddenimports,
|
||
hookspath=[],
|
||
hooksconfig={},
|
||
runtime_hooks=[],
|
||
excludes=[
|
||
# matplotlib 仅 CLI 表格输出用,EXE 形态不需要
|
||
"matplotlib",
|
||
"tkinter",
|
||
"PyQt5",
|
||
"PyQt6",
|
||
"PySide2",
|
||
"PySide6",
|
||
# 测试框架不打进生产 EXE
|
||
"pytest",
|
||
"IPython",
|
||
"jupyter",
|
||
"notebook",
|
||
],
|
||
win_no_prefer_redirects=False,
|
||
win_private_assemblies=False,
|
||
cipher=block_cipher,
|
||
noarchive=False,
|
||
)
|
||
|
||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||
|
||
exe = EXE(
|
||
pyz,
|
||
a.scripts,
|
||
a.binaries,
|
||
a.zipfiles,
|
||
a.datas,
|
||
[],
|
||
name="easy-tdx",
|
||
debug=False,
|
||
bootloader_ignore_signals=False,
|
||
strip=False,
|
||
upx=True,
|
||
# GUI 子系统:双击不弹控制台黑窗。排查问题时改 console=True 重新打包。
|
||
console=False,
|
||
disable_windowed_traceback=False,
|
||
argv_emulation=False,
|
||
target_arch=None,
|
||
codesign_identity=None,
|
||
entitlements_file=None,
|
||
# icon="assets/easy-tdx.ico", # 暂无图标资源;后续补
|
||
)
|