Pyinstaller 打包指南:从通用模板到自主调试
PyInstaller 通用打包指南
适用: Python 3.8+ / Windows 10/11
第一章 概述
PyInstaller 可将 Python 项目打包为独立的可执行程序,用户无需安装 Python 即可运行。
支持两种输出模式:
| 模式 | 描述 | 优点 | 缺点 |
|---|---|---|---|
| onedir | 目录模式,生成一个文件夹,包含 EXE 和所有依赖文件 | 启动快、稳定、易于调试 | 文件多,需整体分发 |
| onefile | 单文件模式,所有内容压缩为一个 EXE | 单个文件,分发方便 | 启动慢(需解压到临时目录)、大文件可能解压失败 |
第二章 快速开始
注意: 本章所有命令均在 cmd 或 PowerShell 中运行。
启动方式: Win+R → 输入 cmd → 回车(或VS code或Pycharm终端)
2.1 安装 PyInstaller
方法A: pip 安装(推荐)
pip install pyinstaller
方法B: 指定版本(遇到兼容性问题时)
pip install pyinstaller==5.13.2
★ 如何判断应安装哪个版本:
- 首先确认你的 Python 版本:
python --version
- 对照 PyInstaller 版本兼容表:
| Python 版本 | 推荐 PyInstaller 版本 |
|---|---|
| Python 3.8 - 3.11 | 5.13.2(最稳定) |
| Python 3.12 | 6.10.0+ |
| Python 3.13+ | 6.20.0+ |
| 错误信息 | 解决方案 |
|---|---|
AttributeError: 'compat' has no attribute 'is_py314'(Python 3.8-3.9 常见) |
PyInstaller 太新,降级到 5.13.2 |
build\...\hook-_ctypes.py 中报错 |
同上,降级 |
No module named 'importlib.metadata'(Python 3.12+ 常见) |
PyInstaller 太旧,升级到最新版 |
pip index versions pyinstaller # 列出所有版本
-
在 PyPI 官网查看各版本发布时间和说明:
https://pypi.org/project/pyinstaller/#history -
简单原则:
| Python 版本 / 场景 | 推荐操作 |
|---|---|
| Python 3.11 及以下 | 使用 5.13.2 |
| Python 3.12 及以上 | 使用最新版(pip install pyinstaller) |
| 打包出错 | 降级到 5.13.2 试试 |
检查安装
pyinstaller --version
2.2 最简单的打包命令
# cmd 终端执行 (onedir 模式)
cd /d 你的项目目录
pyinstaller --noconfirm 你的入口文件.py
# 单文件模式
pyinstaller --noconfirm --onefile 你的入口文件.py
# 带图标的单文件
pyinstaller --noconfirm --onefile --icon=app.ico 你的入口文件.py
输出位置: dist\你的入口文件\ (onedir) 或 dist\你的入口文件.exe (onefile)

文件打包完成后可删除build文件夹(打包过程中不要删)
2.3 常用命令行参数
| 参数 | 短参数 | 说明 |
|---|---|---|
--onefile |
-F |
生成单个 EXE 文件 |
--onedir |
-D |
生成目录(默认) |
--noconfirm |
-y |
覆盖输出目录不询问(需要确认时自动回答 yes) |
--name NAME |
-n NAME |
指定输出名称 |
--icon FILE |
-i FILE |
指定 EXE 图标(.ico) |
--console |
-c |
显示控制台窗口(默认) |
--windowed |
-w |
隐藏控制台窗口(GUI 程序用) |
--add-data SRC;DST |
— | 打包额外数据文件 |
--add-binary SRC;DST |
— | 打包额外二进制文件 |
--hidden-import MOD |
— | 添加隐藏导入 |
--exclude-module MOD |
— | 排除模块 |
--clean |
— | 清理缓存后构建 |
--noupx |
— | 禁用 UPX 压缩 |
第三章 Spec 文件详解
3.1 生成 Spec 文件
pyi-makespec --windowed --icon=app.ico --name=MyApp main.py
这会生成 MyApp.spec,之后可反复使用:
pyinstaller MyApp.spec --noconfirm
3.2 Spec 文件模板(通用版)
# -*- mode: python ; coding: utf-8 -*-
# ↑ magic comment(魔法注释): mode=python → 编辑器语法高亮 coding=utf-8 → 文件使用utf-8编码
import os
from PyInstaller.utils.hooks import collect_data_files, collect_submodules
# ============================================================
# 路径配置(修改为你的实际路径)
# ============================================================
PROJECT_ROOT = r"你的项目根目录绝对路径" # 项目根目录
SRC_DIR = os.path.join(PROJECT_ROOT, "src") # 拼接源码目录路径(如果项目有 src 文件夹)
ENTRY_SCRIPT = os.path.join(PROJECT_ROOT, "main.py") # 拼接入口脚本的完整路径
# ============================================================
# 数据文件: 打包图片、模型、配置文件等非 Python 文件
# 格式: (源文件路径, 打包后的目标文件夹名)
# ============================================================
added_files = [ # 手动指定的额外数据文件列表
("assets/images/logo.png", "assets/images"), # 示例: 将 logo.png 打包到 assets/images/
("config/settings.json", "config"), # 示例: 将 settings.json 打包到 config/
("data/model.pkl", "data"), # 示例: 将 model.pkl 打包到 data/
] # 没有额外文件就留空: added_files = []
# ============================================================
# 二进制文件: 补充 PyInstaller 找不到的 DLL/动态库
# 如果打包后运行报 "DLL not found",需要在此手动添加
# ============================================================
added_binaries = [] # 手动添加的二进制文件列表,格式同上
# 示例: 收集某个目录下所有 DLL 文件
# import os as _os # 导入 os 模块
# _dll_dir = r"C:\path\to\dll\folder" # DLL 所在的目录
# for _f in _os.listdir(_dll_dir): # 遍历目录中所有文件
# if _f.endswith(".dll"): # 只收集 .dll 文件
# added_binaries.append((_os.path.join(_dll_dir, _f), "."))# 添加到列表,"." 表示放在 exe 同级目录
# ============================================================
# 隐藏导入: 代码中通过 __import__() 或 importlib 动态导入的模块
# PyInstaller 无法自动检测这些,需要在此声明
# ============================================================
hidden_imports = [ # 手动声明的隐藏导入模块列表
"sklearn.utils._typedefs", # 示例: sklearn 内部模块
"sklearn.utils._vector_sentinel", # 示例: sklearn 内部模块
"pandas._libs.tslibs", # 示例: pandas C 扩展
] # 没有隐藏导入就留空: hidden_imports = []
# ============================================================
# 收集第三方库的数据文件(如字体、配置、模板等)
# 有些库的配置文件不在 Python 代码中,需要专门收集
# 可替换为自己项目中第三方库的数据文件
# ============================================================
datas_extra = [] # 自动收集的数据文件汇总列表
# 对每个有外部数据文件的库,用 collect_data_files 自动收集:
try:
datas_extra += collect_data_files("matplotlib", subdir="mpl-data") # 收集 matplotlib 的字体文件
except: # 如果 matplotlib 未安装就跳过
pass
try:
datas_extra += collect_data_files("ultralytics") # 收集 ultralytics 的 cfg/*.yaml 配置文件
except:
pass
try:
datas_extra += collect_data_files("pandas") # 收集 pandas 的数据文件
except:
pass
# ============================================================
# Analysis: 核心步骤 —— 分析脚本的所有依赖,生成依赖图
# 这一步决定了哪些文件会被打包
# ============================================================
a = Analysis( # 创建 Analysis 对象(依赖分析器)
[ENTRY_SCRIPT], # 入口脚本列表(可以有多个)
pathex=[SRC_DIR, PROJECT_ROOT], # 额外的模块搜索路径
binaries=added_binaries, # 传入手动添加的二进制文件
datas=added_files + datas_extra, # 传入所有数据文件(手动 + 自动收集)
hiddenimports=hidden_imports, # 传入所有隐藏导入模块
hookspath=[], # 自定义 hook 目录(一般留空)
runtime_hooks=[], # 运行时 hook 路径(一般留空)
excludes=[ # 排除不需要的模块,减小打包体积
"tkinter", # 排除 tkinter GUI 库
"unittest", # 排除单元测试框架
"test", # 排除测试模块
"pytest", # 排除 pytest 测试框架
"IPython", # 排除 IPython 交互环境
"jupyter", # 排除 Jupyter 相关
],
noarchive=False, # False=允许压缩, True=不压缩(调试用)
)
# ============================================================
# PYZ: 将所有纯 Python 源码(.pyc)打包为一个压缩文件
# 类似于一个不含二进制文件的 ZIP
# ============================================================
pyz = PYZ( # 创建 PYZ 对象
a.pure, # 纯 Python 模块(不含 C 扩展)
a.zipped_data, # 压缩后的数据文件
)
# ============================================================
# EXE: 生成 Windows 可执行文件 (.exe)
# ============================================================
exe = EXE( # 创建 EXE 对象
pyz, # 上面生成的 PYZ 压缩包
a.scripts, # 脚本模块
[], # onedir 模式设为空列表(二进制在 COLLECT 中收集)
exclude_binaries=True, # onedir: True(二进制单独放)/ onefile: False(全打入exe)
name="MyApp", # 生成的 EXE 文件名(不含 .exe 后缀)
debug=False, # False=正式版 / True=调试版(含调试信息)
strip=False, # False=不裁剪 / True=移除符号表减体积
upx=False, # False=禁用UPX / True=启用UPX压缩(建议False,避免DLL损坏)
console=False, # False=隐藏控制台(GUI程序) / True=显示控制台
icon=os.path.join(PROJECT_ROOT, "app.ico"), # EXE 图标路径(.ico 格式)
)
# ============================================================
# COLLECT: 【仅 onedir 模式】将所有文件收集到 dist 输出目录
# 如果使用 onefile 单文件模式,删除下面整个 COLLECT 块
# ============================================================
coll = COLLECT( # 创建 COLLECT 对象(文件收集器)
exe, # 上面生成的 EXE 文件
a.binaries, # 所有二进制文件(DLL、.pyd 等)
a.zipfiles, # 所有依赖的压缩包
a.datas, # 所有数据文件(图片、模型、配置等)
strip=False, # 是否裁剪符号表
upx=False, # 是否 UPX 压缩
name="MyApp", # 输出文件夹名称(dist/MyApp/)
)
# ============================================================
# 如果要用 onefile 单文件模式(生成单个 .exe),请:
# 1. 删除上面整个 COLLECT(...) 块
# 2. 修改 EXE(...) 中 exclude_binaries=False
# 3. 将 EXE 中的 [] 改为 a.binaries,参考以下写法:
# ============================================================
# exe = EXE( # onefile 版本的 EXE
# pyz,
# a.scripts,
# a.binaries, # onefile: 把二进制也打入 EXE
# a.zipfiles, # onefile: 把压缩包也打入 EXE
# a.datas, # onefile: 把数据文件也打入 EXE
# name="MyApp",
# debug=False,
# strip=False,
# upx=False, # 强烈建议 False,UPX 易损坏大文件
# console=False, # GUI 应用设为 False 隐藏控制台
# icon=os.path.join(PROJECT_ROOT, "app.ico"),
# )
第四章 常见问题与解决方案
4.1 打包后运行闪退
原因: 缺少依赖文件或模块导入失败
解决方案:
A) 先用控制台模式运行查看错误:
pyinstaller --console --onefile main.py
在 cmd 中运行 dist\main.exe,观察报错信息
B) 常见缺失:
- 第三方库的配置文件(YAML、JSON 等)
- 动态导入的模块
- 数据文件(模型、图片、字体等)
C) 添加缺失内容:
--hidden-import 模块名
--add-data "源文件;目标目录"
--collect-data 包名 (收集包的所有数据文件)
--collect-binaries 包名 (收集包的所有二进制文件)
4.2 启动后白屏/黑屏
原因: GUI 框架初始化失败
常见于: PyQt5/PySide6/tkinter 等
解决方案:
A) PyQt5: 确保隐藏导入
--hidden-import PyQt5.QtCore
--hidden-import PyQt5.QtGui
--hidden-import PyQt5.QtWidgets
B) matplotlib: 确保后端正确
--hidden-import matplotlib.backends.backend_qt5agg
收集 matplotlib 数据: --collect-data matplotlib
C) 检查 Qt 插件是否完整:
确认 dist/ 目录下有 platforms/qwindows.dll
4.3 文件体积过大
原因: 大型库如 torch/numpy/scipy 被打包
解决方案:
A) 排除不需要的模块:
--exclude-module tkinter
--exclude-module test
--exclude-module unittest
B) 使用虚拟环境:
创建只包含必要依赖的干净虚拟环境后再打包
C) 使用 onedir 而非 onefile:
onedir 模式文件更小,且升级时只需替换变化的文件
D) 禁用 UPX(避免压缩损坏同时省时间):
--noupx
4.4 找不到 DLL 文件
常见警告:
WARNING: lib not found: xxx.dll dependency of xxx.pyd
解决方案:
A) 定位 DLL 位置:
where libopenblas*.dll (cmd)
Get-ChildItem -Recurse -Filter "*.dll" | Select FullName (PowerShell)
B) 在 spec 中添加:
added_binaries.append(("C:/path/to/missing.dll", "."))
常见缺失 DLL:
| 库 | 缺失 DLL | 位置 |
|---|---|---|
| scipy | libopenblas*.dll |
scipy.libs 目录 |
| shapely | geos_c*.dll |
shapely.libs 目录 |
| matplotlib | msvcp140.dll |
VC++ 运行时 |
| PyQt5 | Qt5*.dll |
PyQt5/Qt5/bin 目录 |
4.5 ModuleNotFoundError / ImportError
原因: PyInstaller 无法自动检测某些导入
常见场景:
A) import() 动态导入
B) importlib.import_module()
C) 插件式架构
D) 字符串形式的模块名
解决方案: 手动添加隐藏导入
--hidden-import 包名.模块名
或在 spec 中:
hiddenimports=["包名.模块名", ...]
4.6 启动非常慢(>30 秒)
原因: onefile 模式需要解压全部文件到临时目录
解决方案:
A) 改用 onedir 模式(启动快)
B) 减少不必要的依赖
C) 排除大型可选库
4.7 中文路径/文件名乱码
解决方案:
A) spec 文件开头添加:
# -*- coding: utf-8 -*-
B) 避免路径中含中文(推荐纯英文路径)
C) 处理中文资源时使用 UTF-8:
with open(path, "r", encoding="utf-8") as f:
...
4.8 打包后资源文件路径错误
原因: 代码中使用相对路径,打包后路径发生变化
解决方案: 在代码中使用以下函数获取资源路径,同时把项目源代码路径改为相对路径:
import sys, os
def resource_path(relative_path):
"""获取资源的绝对路径,适配开发/打包两种模式"""
try:
base_path = sys._MEIPASS # PyInstaller 临时目录
except AttributeError:
base_path = os.path.dirname(os.path.abspath(__file__))
return os.path.join(base_path, relative_path)
# 使用示例
icon_path = resource_path("assets/icon.png")
model_path = resource_path("models/model.pt")
4.9 某些包的数据文件未被打包
解决方案: 使用 PyInstaller 的 collect 函数
在 spec 文件中:
from PyInstaller.utils.hooks import collect_data_files
# 收集整个包的数据文件
datas_extra += collect_data_files("包名")
# 收集子目录
datas_extra += collect_data_files("包名", subdir="子目录")
# 收集子模块
from PyInstaller.utils.hooks import collect_submodules
hidden_imports += collect_submodules("包名")
4.10 multiprocessing 多进程问题
原因: onefile 模式下子进程无法找到自身
解决方案:
import multiprocessing
multiprocessing.freeze_support()
在 if name == "main": 块中调用
第五章 按框架分类的打包要点
5.1 PyQt5 / PySide6 项目
隐藏导入:
--hidden-import PyQt5.QtCore
--hidden-import PyQt5.QtGui
--hidden-import PyQt5.QtWidgets
--hidden-import PyQt5.QtSvg
如果用 WebEngine:
--hidden-import PyQt5.QtWebEngineWidgets
--hidden-import PyQt5.QtWebEngine
--hidden-import PyQt5.QtWebEngineCore
GUI 模式(无控制台):
--windowed
5.2 PyTorch / ultralytics 项目
核心要点:
--collect-data ultralytics # 收集 cfg/*.yaml
--hidden-import torch
--hidden-import torchvision
--hidden-import ultralytics.nn
--hidden-import ultralytics.nn.modules
注意: torch 体积很大(>1GB),建议 onedir 模式(实测onefile几乎要1分钟左右,而onedir仅需10s-30s)
5.3 OpenCV (cv2) 项目
--hidden-import cv2
--collect-binaries cv2
注意: opencv_videoio_ffmpeg*.dll 容易被 UPX 损坏,建议 --noupx
5.4 matplotlib 项目
隐藏导入:
--hidden-import matplotlib.backends.backend_qt5agg
--hidden-import matplotlib.backends.backend_svg
收集数据和字体:
--collect-data matplotlib
5.5 pandas / numpy / scipy 项目
pandas:
--hidden-import pandas._libs.tslibs
scipy:
需要收集 openblas DLL(见 4.4 节)
numpy:
需要收集 openblas DLL
5.6 Flask / FastAPI Web 项目
--hidden-import jinja2
--hidden-import jinja2.ext
--collect-data jinja2
--add-data "templates;templates"
--add-data "static;static"
第六章 调试技巧
6.1 分步调试流程
- 先用 --console 打包,查看错误输出
pyinstaller --console --onefile main.py
- 运行 EXE,记录错误信息
cmd> dist\main.exe
-
根据错误信息添加 --hidden-import 或 --add-data
-
逐个修复后重新打包
6.2 使用 --debug 参数
pyinstaller --debug=all --console main.py #项目主文件
可看到详细的导入过程,帮助定位缺失的模块
6.3 检查 dist 目录内容
:: 列出 dist 目录下的文件
dir /s dist\MyApp
:: 确认关键文件是否存在
dir dist\MyApp\关键文件
6.4 在开发环境验证导入
打包前先在 Python 中测试所有导入是否成功:
python -c "import 你的模块; print('OK')"
逐模块测试,确保没有隐藏依赖
第七章 最佳实践
7.1 推荐工作流
- 创建干净的虚拟环境
python -m venv build_env
build_env\Scripts\activate
- 只安装项目必需的依赖
pip install 你的依赖包
pip install pyinstaller
- 用 pyi-makespec 生成 spec 文件
pyi-makespec --windowed --name=MyApp main.py
-
编辑 spec 文件,添加缺失的数据和隐藏导入
-
用 spec 文件打包
pyinstaller MyApp.spec --noconfirm --clean
- 测试打包结果
dist\MyApp\MyApp.exe
- 修复问题 → 重复步骤 4-6
7.2 推荐设置
| 参数 | 说明 |
|---|---|
--noconfirm |
自动覆盖,无需人工确认 |
--windowed |
GUI 应用使用,隐藏控制台 |
--noupx |
禁用 UPX,避免压缩导致的 DLL 损坏 |
--clean |
每次打包前清理缓存(首次或遇到奇怪问题时使用) |
7.3 onedir vs onefile 选择
| 项目体积 / 场景 | 推荐模式 |
|---|---|
| < 100MB | 可使用 onefile |
| 100–500MB | 可尝试 onefile(注意测试) |
| > 500MB | 强烈推荐 onedir |
| 含 torch/tensorflow | 必选 onedir |
onedir 分发方式: 将整个输出目录打包为 ZIP 发给用户
7.4 减小体积的方法
- 使用纯净的虚拟环境
- 排除测试模块: --exclude-module test
- 排除不需要的 GUI 框架: --exclude-module tkinter
- 使用 --noupx 避免压缩开销(压缩后反而可能更大)
- 只在 onedir 模式下分发必要的文件
第八章 快速参考卡片
打包命令速查
| 操作 | 命令 |
|---|---|
| 生成 spec | pyi-makespec -w -i icon.ico -n App main.py |
| onedir 打包 | pyinstaller App.spec --noconfirm |
| onefile 打包 | pyinstaller -F -w -i icon.ico main.py |
| 查看帮助 | pyinstaller -h |
常见问题速查
| 问题 | 方案 |
|---|---|
| 闪退 | 用 --console 看错误 + 补 hidden-import |
| DLL 缺失 | 在 spec 中 added_binaries 添加 |
| 资源找不到 | 用 sys._MEIPASS 兼容路径 |
| 启动慢 | 换 onedir 模式 |
| UPX 错误 | --noupx |
| 体积大 | 排除不用的模块 + 干净虚拟环境 |
附录: 完整的通用 Spec 文件模板
将以下内容保存为 build.spec,修改路径后使用:
# -*- mode: python ; coding: utf-8 -*-
"""
通用 PyInstaller Spec 模板
使用: pyinstaller build.spec --noconfirm
"""
import os
from PyInstaller.utils.hooks import collect_data_files, collect_submodules
# ====== 修改此处 ======
PROJECT_ROOT = r"你的项目绝对路径"
ENTRY_SCRIPT = os.path.join(PROJECT_ROOT, "main.py")
APP_NAME = "MyApp" # 打包后文件名称
APP_ICON = os.path.join(PROJECT_ROOT, "app.ico")
USE_CONSOLE = False # GUI 应用设为 False
USE_ONEDIR = True # True=目录模式, False=单文件模式
# =====================
# 数据文件
added_files = [
# (os.path.join(PROJECT_ROOT, "data/file.dat"), "data"),
]
# 二进制 DLL
added_binaries = []
# 隐藏导入
hidden_imports = []
# 收集第三方库数据
datas_extra = []
for lib in ["matplotlib"]: # 添加需要收集数据的库
try:
datas_extra += collect_data_files(lib)
except:
pass
# 排除的模块
excludes = ["tkinter", "unittest", "test", "pytest"]
a = Analysis(
[ENTRY_SCRIPT],
pathex=[PROJECT_ROOT],
binaries=added_binaries,
datas=added_files + datas_extra,
hiddenimports=hidden_imports,
excludes=excludes,
)
pyz = PYZ(a.pure, a.zipped_data)
exe = EXE(
pyz,
a.scripts,
[] if USE_ONEDIR else a.binaries,
exclude_binaries=USE_ONEDIR,
name=APP_NAME,
debug=False,
strip=False,
upx=False,
console=USE_CONSOLE,
icon=APP_ICON,
)
if USE_ONEDIR:
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=False,
name=APP_NAME,
)
实例如下(笔者学校比赛时的项目):
# -*- mode: python ; coding: utf-8 -*-
"""PyInstaller spec for Grating Constant Measurement Platform."""
import os
# 项目根目录 — 使用绝对路径避免 SPECPATH 问题
PROJECT_ROOT = r"D:\py_files\ultralytics-main\Transmission-Grating-Constant-Measurement-Platform"
SRC_DIR = os.path.join(PROJECT_ROOT, "src")
# 需要打包的数据文件: (源绝对路径, 目标文件夹名)
ADDED_FILES = [
(os.path.join(PROJECT_ROOT, "assets", "images", "cover.jpg"), "assets/images"),
(os.path.join(PROJECT_ROOT, "assets", "images", "app.ico"), "assets/images"),
(os.path.join(PROJECT_ROOT, "assets", "models", "best.pt"), "assets/models"),
(os.path.join(PROJECT_ROOT, "docs", "theory.docx"), "docs"),
]
# 缺失的关键 DLL(conda 环境中的原生库)
CONDA_ENV = r"C:\Users\34203\miniconda3\envs\yolov8"
ADDED_BINARIES = []
# scipy 的 openblas DLL
_scipy_libs = os.path.join(CONDA_ENV, "Lib", "site-packages", "scipy.libs")
if os.path.isdir(_scipy_libs):
for _f in os.listdir(_scipy_libs):
if _f.endswith(".dll"):
ADDED_BINARIES.append((os.path.join(_scipy_libs, _f), "scipy.libs"))
# shapely 的 geos DLL
_shapely_libs = os.path.join(CONDA_ENV, "Lib", "site-packages", "shapely.libs")
if os.path.isdir(_shapely_libs):
for _f in os.listdir(_shapely_libs):
if _f.endswith(".dll"):
ADDED_BINARIES.append((os.path.join(_shapely_libs, _f), "shapely.libs"))
# numpy 的 openblas DLL
_numpy_libs = os.path.join(CONDA_ENV, "Lib", "site-packages", "numpy", ".libs")
if os.path.isdir(_numpy_libs):
for _f in os.listdir(_numpy_libs):
if _f.endswith(".dll"):
ADDED_BINARIES.append((os.path.join(_numpy_libs, _f), "numpy.libs"))
# MSVCP140 DLL(matplotlib 依赖)
for _dll in ["msvcp140.dll", "msvcp140_1.dll", "msvcp140_2.dll", "vcruntime140.dll", "vcruntime140_1.dll"]:
_p = os.path.join(CONDA_ENV, _dll)
if os.path.exists(_p):
ADDED_BINARIES.append((_p, "."))
# 隐藏导入
hiddenimports = [
# PyQt5 WebEngine(QWebEngineView 依赖)
"PyQt5.QtWebEngineWidgets",
"PyQt5.QtWebEngine",
"PyQt5.QtWebEngineCore",
"PyQt5.QtWebChannel",
# PyQt5 完整组件
"PyQt5.QtSvg",
"PyQt5.QtPrintSupport",
"PyQt5.QtNetwork",
# matplotlib
"matplotlib.backends.backend_qt5agg",
# torch & ultralytics
"torch",
"torchvision",
"ultralytics",
"ultralytics.nn",
"ultralytics.nn.modules",
"ultralytics.data",
"ultralytics.utils",
"ultralytics.engine",
"ultralytics.models",
# OpenCV
"cv2",
"numpy",
# Word COM 导出链
"win32com",
"win32com.client",
"pythoncom",
"fitz", # PyMuPDF
"PIL",
"PIL.Image",
"PIL.ImageDraw",
"PIL.ImageFont",
# 文档处理
"docx",
"lxml",
# AI 对话
"openai",
"dotenv",
"requests",
"urllib3",
# 其它
"markdown",
"yaml",
"scipy",
"shapely",
"onnxruntime",
"psutil",
]
# matplotlib 额外数据
from PyInstaller.utils.hooks import collect_data_files, collect_submodules
# 收集 matplotlib 的字体等数据文件
datas_extra = []
try:
datas_extra += collect_data_files("matplotlib", subdir="mpl-data")
except Exception:
pass
# 收集 ultralytics 的配置文件(cfg/*.yaml 等)
try:
datas_extra += collect_data_files("ultralytics")
except Exception:
pass
a = Analysis(
[os.path.join(PROJECT_ROOT, "src", "grating_app", "__main__.py")],
pathex=[SRC_DIR],
binaries=ADDED_BINARIES,
datas=ADDED_FILES + datas_extra,
hiddenimports=hiddenimports,
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[
"tkinter",
"IPython",
"jupyter",
"notebook",
"pytest",
],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=None,
noarchive=False,
)
pyz = PYZ(
a.pure,
a.zipped_data,
cipher=None,
)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name="GratingApp",
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=False,
console=False,
disable_windowed_traceback=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=os.path.join(PROJECT_ROOT, "assets", "images", "app.ico"),
)
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=False,
upx_exclude=[],
name="GratingApp",
)

点击其中.exe文件即可运行,若是单文件直接打开即可。

浙公网安备 33010602011771号