合并
This commit is contained in:
181
etl_billiards/build_exe.py
Normal file
181
etl_billiards/build_exe.py
Normal file
@@ -0,0 +1,181 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
ETL GUI 打包脚本
|
||||
|
||||
使用 PyInstaller 将 GUI 应用打包为 Windows EXE
|
||||
|
||||
用法:
|
||||
python build_exe.py [--onefile] [--console] [--clean]
|
||||
|
||||
参数:
|
||||
--onefile 打包为单个 EXE 文件(默认为目录模式)
|
||||
--console 显示控制台窗口(调试用)
|
||||
--clean 打包前清理旧的构建文件
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def get_project_root() -> Path:
|
||||
"""获取项目根目录"""
|
||||
return Path(__file__).resolve().parent
|
||||
|
||||
|
||||
def clean_build():
|
||||
"""清理旧的构建文件"""
|
||||
project_root = get_project_root()
|
||||
|
||||
dirs_to_clean = [
|
||||
project_root / "build",
|
||||
project_root / "dist",
|
||||
]
|
||||
|
||||
files_to_clean = [
|
||||
project_root / "etl_gui.spec",
|
||||
]
|
||||
|
||||
for d in dirs_to_clean:
|
||||
if d.exists():
|
||||
print(f"清理目录: {d}")
|
||||
shutil.rmtree(d)
|
||||
|
||||
for f in files_to_clean:
|
||||
if f.exists():
|
||||
print(f"清理文件: {f}")
|
||||
f.unlink()
|
||||
|
||||
|
||||
def build_exe(onefile: bool = False, console: bool = False):
|
||||
"""构建 EXE"""
|
||||
project_root = get_project_root()
|
||||
|
||||
# 主入口
|
||||
main_script = project_root / "gui" / "main.py"
|
||||
|
||||
# 资源文件
|
||||
resources_dir = project_root / "gui" / "resources"
|
||||
database_dir = project_root / "database"
|
||||
|
||||
# 构建 PyInstaller 命令
|
||||
# 使用 ASCII 名称避免 Windows 控制台编码问题
|
||||
cmd = [
|
||||
sys.executable, "-m", "PyInstaller",
|
||||
"--name", "ETL_Manager",
|
||||
"--noconfirm",
|
||||
]
|
||||
|
||||
# 单文件或目录模式
|
||||
if onefile:
|
||||
cmd.append("--onefile")
|
||||
else:
|
||||
cmd.append("--onedir")
|
||||
|
||||
# 窗口模式
|
||||
if not console:
|
||||
cmd.append("--windowed")
|
||||
|
||||
# 添加数据文件
|
||||
# 样式表
|
||||
if resources_dir.exists():
|
||||
cmd.extend(["--add-data", f"{resources_dir};gui/resources"])
|
||||
|
||||
# 数据库 SQL 文件
|
||||
if database_dir.exists():
|
||||
for sql_file in database_dir.glob("*.sql"):
|
||||
cmd.extend(["--add-data", f"{sql_file};database"])
|
||||
|
||||
# 隐式导入
|
||||
hidden_imports = [
|
||||
"PySide6.QtCore",
|
||||
"PySide6.QtGui",
|
||||
"PySide6.QtWidgets",
|
||||
"psycopg2",
|
||||
"psycopg2.extras",
|
||||
"psycopg2.extensions",
|
||||
# GUI 模块
|
||||
"gui.models.task_model",
|
||||
"gui.models.schedule_model",
|
||||
"gui.utils.cli_builder",
|
||||
"gui.utils.config_helper",
|
||||
"gui.utils.app_settings",
|
||||
"gui.workers.task_worker",
|
||||
"gui.workers.db_worker",
|
||||
"gui.widgets.settings_dialog",
|
||||
]
|
||||
for imp in hidden_imports:
|
||||
cmd.extend(["--hidden-import", imp])
|
||||
|
||||
# 排除不需要的模块(减小体积)
|
||||
excludes = [
|
||||
"matplotlib",
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scipy",
|
||||
"PIL",
|
||||
"cv2",
|
||||
"tkinter",
|
||||
]
|
||||
for exc in excludes:
|
||||
cmd.extend(["--exclude-module", exc])
|
||||
|
||||
# 工作目录
|
||||
cmd.extend(["--workpath", str(project_root / "build")])
|
||||
cmd.extend(["--distpath", str(project_root / "dist")])
|
||||
cmd.extend(["--specpath", str(project_root)])
|
||||
|
||||
# 主脚本
|
||||
cmd.append(str(main_script))
|
||||
|
||||
print("执行命令:")
|
||||
print(" ".join(cmd))
|
||||
print()
|
||||
|
||||
# 执行打包
|
||||
result = subprocess.run(cmd, cwd=str(project_root))
|
||||
|
||||
if result.returncode == 0:
|
||||
print()
|
||||
print("=" * 50)
|
||||
print("打包成功!")
|
||||
print(f"输出目录: {project_root / 'dist'}")
|
||||
print("=" * 50)
|
||||
else:
|
||||
print()
|
||||
print("打包失败,请检查错误信息")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="ETL GUI 打包工具")
|
||||
parser.add_argument("--onefile", action="store_true", help="打包为单个 EXE")
|
||||
parser.add_argument("--console", action="store_true", help="显示控制台窗口")
|
||||
parser.add_argument("--clean", action="store_true", help="打包前清理")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# 检查 PyInstaller
|
||||
try:
|
||||
import PyInstaller
|
||||
print(f"PyInstaller 版本: {PyInstaller.__version__}")
|
||||
except ImportError:
|
||||
print("错误: 未安装 PyInstaller")
|
||||
print("请运行: pip install pyinstaller")
|
||||
sys.exit(1)
|
||||
|
||||
# 清理
|
||||
if args.clean:
|
||||
clean_build()
|
||||
|
||||
# 构建
|
||||
build_exe(onefile=args.onefile, console=args.console)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user