feat(EDID-Viewer): v0.1.0 入库 — Fluent EDID 查看器,目录对齐 DLPilot
- src/ 人写代码:main.py(Fluent 界面、Light/Dark/dark1/dark2/跟随系统主题、 关于按钮;主题三件套 = setTheme + QPalette/应用级 QSS + DWM 标题栏, 修复 Light 下窗口背景/页签/标题栏仍为系统深色的问题)、edid.py (EDID/CTA/HDMI/HDR 解析)、windows_edid.py(WMI 只读)、theme.py、 test_edid.py(9 项解析测试,含随机畸形数据无崩溃检查) - assets/ 图标与生成脚本;release/ 交付:EDID-Viewer.exe(onefile 63.7MB, 启动/图标/主题已验证)+ README,与 DLPilot/release 同约定 exe 直接入库 - 流水线对齐 DLPilot:src/build_exe.py(spec 由脚本生成不入库,剔除 PATH 上 Poppler/Conda 版本化 ICU,避免 QtCore 报 WinError 127)→ package_release.py(组装 EDID-Viewer-v*-win64.zip,zip 只上传 Gitea)→ publish_gitea.py(token 走 GITEA_TOKEN,自签证书 --insecure) - agent.md 更名 AGENTS.md(DLPilot 双文档约定:README 给人 / AGENTS.md 给 Agent);根 .gitignore 增加 EDID-viewer/release/*.zip - validation/ 保留 EDID 本机/合成样本与窗口截图、关于对话框自检脚本
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
把 src/main.py 打包成单文件 exe(工具名: EDID-Viewer)
|
||||
================================================
|
||||
与 DLPilot/src/build_exe.py 同一套路: onefile + --noconsole, Python 解释器、
|
||||
PySide6 和 PySide6-Fluent-Widgets 全部打进一个 exe, 目标机器无需安装依赖。
|
||||
|
||||
用法:
|
||||
python build_exe.py 自动安装 PyInstaller(如缺)并打包
|
||||
python build_exe.py --debug 额外打一个带控制台的变体
|
||||
|
||||
产物: release/EDID-Viewer.exe
|
||||
spec 由本脚本生成到 src/build/(不入库), 调整打包逻辑直接改这里的模板。
|
||||
"""
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# 本脚本位于 src/, 打包产物输出到工具根的 release/
|
||||
SRC = os.path.dirname(os.path.abspath(__file__))
|
||||
BASE = os.path.dirname(SRC)
|
||||
RELEASE = os.path.join(BASE, 'release')
|
||||
WORK = os.path.join(SRC, 'build')
|
||||
ICO = os.path.join(BASE, 'assets', 'edid-viewer.ico')
|
||||
|
||||
SPEC_TEMPLATE = """\
|
||||
# -*- coding: utf-8 -*-
|
||||
# 由 build_exe.py 生成, 不要手改; 调整打包逻辑请改 src/build_exe.py。
|
||||
from PyInstaller.utils.hooks import collect_all
|
||||
|
||||
qfluent_datas, qfluent_binaries, qfluent_hidden = collect_all('qfluentwidgets')
|
||||
a = Analysis(
|
||||
[r'{entry}'], pathex=[r'{src}'], binaries=qfluent_binaries,
|
||||
datas=qfluent_datas + {icon_datas}, hiddenimports=qfluent_hidden,
|
||||
noarchive=False)
|
||||
# Qt 6.11 使用 Windows 非版本化 ICU 导出; PATH 上 Poppler/Conda 的版本化
|
||||
# ICU(icuuc.dll/icudt78.dll)会被 PyInstaller 误收, 令 QtCore.pyd 启动报
|
||||
# WinError 127 —— 打包时剔除, 运行时用系统自带 ICU。
|
||||
a.binaries = [b for b in a.binaries if b[0].lower() not in ('icuuc.dll', 'icudt78.dll')]
|
||||
pyz = PYZ(a.pure)
|
||||
exe = EXE(pyz, a.scripts, a.binaries, a.datas, [], name='{name}',
|
||||
debug=False, strip=False, upx=False, console={console}{icon_clause})
|
||||
"""
|
||||
|
||||
|
||||
def ensure_pyinstaller():
|
||||
try:
|
||||
import PyInstaller # noqa: F401
|
||||
return
|
||||
except ImportError:
|
||||
pass
|
||||
print('PyInstaller 未安装, 正在安装...')
|
||||
subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'pyinstaller'])
|
||||
|
||||
|
||||
def remove_stale(exe_path):
|
||||
"""删除旧 exe; 若被占用(上次验证的进程还在), 先结束同名进程再重试"""
|
||||
if not os.path.exists(exe_path):
|
||||
return
|
||||
try:
|
||||
os.remove(exe_path)
|
||||
return
|
||||
except PermissionError:
|
||||
pass
|
||||
import time
|
||||
print(f'{exe_path} 被占用, 结束残留进程后重试...')
|
||||
subprocess.run(['taskkill', '/F', '/IM', os.path.basename(exe_path)],
|
||||
capture_output=True)
|
||||
time.sleep(1.0)
|
||||
os.remove(exe_path)
|
||||
|
||||
|
||||
def build(name, console):
|
||||
remove_stale(os.path.join(RELEASE, name + '.exe'))
|
||||
if os.path.exists(ICO):
|
||||
icon_datas = f"[(r'{ICO}', 'assets')]"
|
||||
icon_clause = f", icon=r'{ICO}'"
|
||||
else:
|
||||
print('警告: 未找到 assets/edid-viewer.ico(先运行 assets/make_icon.py), 本次打包不带图标')
|
||||
icon_datas, icon_clause = '[]', ''
|
||||
spec = SPEC_TEMPLATE.format(entry=os.path.join(SRC, 'main.py'), src=SRC,
|
||||
icon_datas=icon_datas, name=name,
|
||||
console=console, icon_clause=icon_clause)
|
||||
os.makedirs(WORK, exist_ok=True)
|
||||
spec_path = os.path.join(WORK, name + '.spec')
|
||||
with open(spec_path, 'w', encoding='utf-8') as f:
|
||||
f.write(spec)
|
||||
cmd = [sys.executable, '-m', 'PyInstaller', '--noconfirm', '--clean',
|
||||
'--distpath', RELEASE, '--workpath', WORK, spec_path]
|
||||
print('\n>>', ' '.join(cmd))
|
||||
subprocess.check_call(cmd)
|
||||
out = os.path.join(RELEASE, name + '.exe')
|
||||
print(f'完成: {out} ({os.path.getsize(out) / 1024 / 1024:.1f} MB)')
|
||||
|
||||
|
||||
def main():
|
||||
build_console = '--debug' in sys.argv
|
||||
ensure_pyinstaller()
|
||||
# 常规版: 双击即用, 无黑框
|
||||
build('EDID-Viewer', 'False')
|
||||
if build_console:
|
||||
# 调试版: 带控制台, 异常栈可见
|
||||
build('EDID-Viewer-console', 'True')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user