Files
easy-tdx/easy_tdx.spec
T
Justin Gu 0b4ed9af62 feat(packaging): v1.19.1 支持 Windows 单 EXE 打包 + 系统托盘 + 自动发版
面向零基础老年用户,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
2026-07-07 01:48:07 +08:00

96 lines
3.2 KiB
RPMSpec
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- 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 APIPillow 的
# 图像格式插件用 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", # 暂无图标资源;后续补
)