#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 把 hdmi_gui.py 打包成单文件 exe(工具名: HDMI-Tool) ================================ 使用 PyInstaller --onefile --noconsole: Python 解释器 + 标准库 + tkinter 全部打进一个 exe, 目标机器无需安装任何依赖。 用法: python build_exe.py 自动安装 PyInstaller(如缺)并打包 python build_exe.py --debug 额外打一个带控制台的变体(--dump 可用) 产物: release/HDMI-Tool.exe(及 --debug 时的 HDMI-Tool-console.exe) 首次运行 onefile exe 会先解压到临时目录, 启动比脚本慢一两秒, 属正常现象。 """ import os import subprocess import sys # 本脚本位于 src/, 打包产物输出到工具根的 release/(与 pattern_slots.json 同目录) SRC = os.path.dirname(os.path.abspath(__file__)) BASE = os.path.dirname(SRC) ICO = os.path.join(SRC, 'logos', 'logo_ph_orange.ico') 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, extra_args): dist = os.path.join(BASE, 'release') work = os.path.join(SRC, 'build') remove_stale(os.path.join(dist, name + '.exe')) cmd = [sys.executable, '-m', 'PyInstaller', '--onefile', '--clean', '--name', name, '--distpath', dist, '--workpath', work, '--specpath', work, ] + extra_args + [os.path.join(SRC, 'hdmi_gui.py')] if os.path.exists(ICO): # exe 文件图标 + 运行时窗口图标资源(从 _MEIPASS 读取) cmd += ['--icon', ICO, '--add-data', f'{ICO};.'] else: print('警告: 未找到 logos/logo_ph_orange.ico(先运行 logos/make_ico.py), 本次打包不带图标') print('\n>>', ' '.join(cmd)) subprocess.check_call(cmd) out = os.path.join(dist, name + '.exe') print(f'完成: {out} ({os.path.getsize(out) / 1024 / 1024:.1f} MB)') def main(): build_console = '--debug' in sys.argv ensure_pyinstaller() # 常规版: 双击即用, 无黑框(GUI/图案/自定义栏位全部可用, --dump 除外) build('HDMI-Tool', ['--noconsole']) if build_console: # 调试版: 带控制台, --dump / 异常栈可见 build('HDMI-Tool-console', []) if __name__ == '__main__': main()