Pyinstaller打包指南

pyinstaller可将python程序打包成可执行文件,无需配置python环境,提升用户体验、便于分发。

目录

快速开始

进阶配置

常见问题


快速开始

准备工作

安装 PyInstaller

打开终端(CMD 或 PowerShell),输入:

1
pip install pyinstaller

一键打包命令

场景1:简单脚本(无外部资源):

适合只有一个 .py 文件,不依赖外部配置、图片、模型文件的程序。

1
pyinstaller -F -w your_script.py   # your_script.py 为需打包的脚本文件

参数说明:

参数 全称 说明 适用场景
-F --onefile 打包成单个 exe 文件 简单脚本,文件体积小
-w --windowed 无控制台窗口 GUI 程序(PyQt5、Tkinter)

输出结果:

1
2
dist/
└── your_script.exe ← 可执行文件

场景2:包含资源文件的项目

适合包含模型、配置文件、图片等资源的复杂项目。

1
2
3
4
5
6
7
8
pyinstaller 主入口脚本.py \
--name="生成的软件名称" \
--onedir \
--add-data "源文件路径;目标路径" \
--hidden-import=需手动导入的库名 \
--noconsole \
--clean \
-y

参数详解:

参数 说明 示例
--name 自定义输出名称 --name="检测程序"
--onedir 文件夹模式(推荐) 生成 MyApp/ 文件夹
--add-data 添加资源文件 "源路径;目标路径"
--hidden-import 强制导入模块 动态导入的库
--noconsole 隐藏控制台(同 -w GUI 程序使用
--clean 清理缓存 避免旧文件干扰
-y 自动确认覆盖 跳过提示
--runtime-tmpdir 指定临时文件解压位置 自定义运行时目录
--exclude-module 排除不需要的模块 减小体积

Windows 路径分隔符说明:

1
2
3
4
5
# Windows 使用分号 ;
--add-data "models;models"

# Linux/macOS 使用冒号 :
--add-data "models:models"

完整示例(PyQt5 检测程序):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pyinstaller detection_app.py \
--name="智能检测系统" \
--onedir \
--add-data "models;models" \
--add-data "config;config" \
--add-data "weights;weights" \
--add-data "class_json;class_json" \
--hidden-import=PyQt5 \
--hidden-import=cv2 \
--hidden-import=torch \
--hidden-import=numpy \
--icon="logo.ico" \
--noconsole \
--clean \
-y

输出结果:

1
2
3
4
5
6
7
8
dist/
└── 智能检测系统/
├── 智能检测系统.exe ← 主程序
└── _internal/ ← 依赖文件
├── models/
├── config/
├── weights/
└── ...

注意:至此,基本打包已完成!以下为复杂任务的打包进阶配置流程。


进阶配置

对于包含模型文件、图片、配置文件或多个代码文件的复杂项目,推荐使用 .spec 配置文件进行打包。

打包模式的选择

PyInstaller 有两种主要模式:

  • 单文件模式 (-F)
  • 文件夹模式 (-D) (默认)

模式对比:

特性 文件夹模式 (-D) 单文件模式 (-F)
输出 文件夹 + exe 单个 exe
打包速度 快(3-5分钟) 慢(10-30分钟)
启动速度 快(直接运行) 慢(需先解压)
体积 适中(150-500MB) 大(300MB-2GB)
调试难度 容易 困难
兼容性 一般
分发 需打包成 ZIP 单文件直接分发
数据文件 可单独更新 无法单独更新
推荐度 推荐 不推荐

步骤 1: 生成.spec配置文件

在项目根目录下运行:

1
pyinstaller your_script.py --name="MyProject"

运行后,会发现当前目录下多了一个 MyProject.spec 文件。

可选参数:

1
2
3
4
5
6
pyinstaller your_script.py \
--name="MyProject" \
--distpath ./dist \
--workpath ./build \
--specpath . \
-y
参数 说明
--distpath 指定输出目录(默认 ./dist
--workpath 指定构建缓存目录(默认 ./build
--specpath 指定 spec 文件保存位置(默认当前目录)
-y 自动覆盖已存在的文件

步骤 2: 编辑 .spec 文件

打开MyProject.spec,按需修改:

文件夹模式示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# -*- mode: python ; coding: utf-8 -*-
import os

# ========== 1. 定义项目路径 ==========
project_root = os.getcwd() # 当前目录
# 或者指定绝对路径:
# project_root = r'D:\my_projects\detection_app'

# ========== 2. Analysis 配置 ==========
a = Analysis(
# 主程序入口文件
[os.path.join(project_root, 'main.py')],

# Python 搜索路径
pathex=[project_root],

# 二进制文件(通常为空)
binaries=[],

# 【关键】添加数据文件(左边=源路径,右边=打包后路径)
datas=[
(os.path.join(project_root, 'models'), 'models'), # 模型文件夹
(os.path.join(project_root, 'config'), 'config'), # 配置文件夹
(os.path.join(project_root, 'weights'), 'weights'), # 权重文件
(os.path.join(project_root, 'images'), 'images'), # 图片资源
(os.path.join(project_root, 'fonts'), 'fonts'), # 字体文件
# 单个文件示例:
# (os.path.join(project_root, 'config.json'), '.'), # 复制到根目录
],

# 【关键】隐藏导入(运行时报错 "No module named xxx" 时添加)
hiddenimports=[
'cv2', # OpenCV
'PIL', # Pillow
'PIL.Image',
'numpy',
'scipy',
'matplotlib',
'pandas',
'torch', # PyTorch
'torchvision',
'PyQt5', # PyQt5
'PyQt5.QtWidgets',
'PyQt5.QtGui',
'PyQt5.QtCore',
'PyQt5.sip',
# 根据实际情况添加
],

# 其他配置(通常保持默认)
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[], # 排除的模块
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=None,
noarchive=False,
)

# ========== 3. PYZ 配置(打包 Python 模块)==========
pyz = PYZ(a.pure, a.zipped_data, cipher=None)

# ========== 4. EXE 配置(生成可执行文件)==========
exe = EXE(
pyz,
a.scripts,
[], # 空列表表示使用文件夹模式
exclude_binaries=True, # 【关键】排除二进制文件(文件夹模式)
name='MyProject', # exe 文件名
debug=False, # 调试模式(一般设为 False)
bootloader_ignore_signals=False,
strip=False,
upx=True, # 使用 UPX 压缩(减小体积)
console=True, # True=显示控制台,False=隐藏控制台
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon='logo.ico', # 程序图标(.ico 格式)
)

# ========== 5. COLLECT 配置(收集所有文件到文件夹)==========
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='MyProject', # 输出文件夹名称
)

单文件模式配置:

如果坚持使用单文件模式,只需要修改EXE部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
exe = EXE(
pyz,
a.scripts,
a.binaries, # ← 添加
a.zipfiles, # ← 添加
a.datas, # ← 添加
[],
name='MyProject',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon='logo.ico',
)

# 注意:单文件模式不需要 COLLECT 部分,删除即可

步骤 3: 使用.spec文件打包

编辑保存.spec后,运行:

1
pyinstaller MyProject.spec --clean -y

打包输出结构:

文件夹模式输出:
1
2
3
4
5
6
7
8
9
10
11
12
your_project/
├── MyProject.spec
├── dist/
│ └── MyProject/
│ ├── MyProject.exe ← 双击运行
│ └── _internal/ ← 依赖文件
│ ├── models/
│ ├── config/
│ ├── weights/
│ ├── python310.dll
│ ├── ...
└── build/ ← 临时构建文件(可删除)
单文件模式输出:
1
2
3
4
5
your_project/
├── MyProject.spec
├── dist/
│ └── MyProject.exe ← 唯一文件
└── build/ ← 临时构建文件(可删除)

常见问题

问题 1: “ModuleNotFoundError: No module named ‘xxx’”

原因: 某个模块没有被 PyInstaller 自动检测到

解决方案:

.spec 文件的 hiddenimports 中添加缺失的模块名:

1
2
3
4
hiddenimports=[
'cv2', # 如果找不到cv2
...
]

问题 2: 数据文件未被包含(如 weights、utils)

原因: datas 配置不正确或路径错误。

解决方案:

方法 1:检查 .spec 配置

1
2
3
4
datas=[
('models', 'models'), # 确保源路径存在
('config', 'config'),
]

方法 2:使用绝对路径

1
2
3
4
5
6
7
8
import os

project_root = r'D:\projects\my_app'

datas=[
(os.path.join(project_root, 'models'), 'models'),
(os.path.join(project_root, 'config'), 'config'),
]

方法 3:检查打包后的文件

dist/MyApp/_internal/ 目录下,确认数据文件是否存在。

问题 3: exe 文件很大(>1GB)

原因: 包含了过多的依赖库(如 torch、opencv)

解决方案:

1
2
3
4
5
6
7
8
9
# 方法1. 排除不必要的模块
excluded=[
'matplotlib.backends.backend_tkagg',
'tkinter',
],

# 方法2. 过滤特定数据文件
# 在 Analysis 后添加
a.datas = [d for d in a.datas if not d[0].startswith('matplotlib')]

问题4:打包时提示错误struct.error: argument out of range

原因:打包的文件总大小超过了 PyInstaller 的限制(单文件模式)。

解决方案:

方法 1:使用文件夹模式

1
2
# 将 -F 改为 -D
pyinstaller -D main.py

方法 2:精确指定需要的文件

1
2
3
4
5
# 使用绝对路径指定每个必需的文件
datas=[
(r'D:\projects\app\models\yolov8.pt', 'models'),
(r'D:\projects\app\config\settings.json', 'config'),
]

方法 3:分离大文件

将大型模型文件放在程序外,运行时从外部加载:

1
2
# 不打包进 exe,运行时从外部加载
model_path = './models/yolov8.pt' # 用户自己下载模型

问题 5: 程序运行时找不到数据文件

原因: 代码中使用相对路径,打包后路径发生变化。

解决方案:

在代码中添加路径处理函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import sys
import os

def get_resource_path(relative_path):
"""获取资源文件的正确路径"""
if getattr(sys, 'frozen', False):
# 打包后运行
base_path = sys._MEIPASS # PyInstaller 临时目录
else:
# 开发时运行
base_path = os.path.dirname(__file__)

return os.path.join(base_path, relative_path)

# 使用方法
model_path = get_resource_path('models/yolov8.pt')
config_path = get_resource_path('config/settings.json')

适用于各种文件类型:

1
2
3
4
5
6
7
8
9
# 模型文件
model = load_model(get_resource_path('models/best.pt'))

# 配置文件
with open(get_resource_path('config/config.json'), 'r') as f:
config = json.load(f)

# 图片
image = cv2.imread(get_resource_path('images/logo.png'))

问题 6: 打包后 PyQt5 窗口无法显示

原因: PyQt5 plugin 未被正确包含

解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 在 spec 文件中确保添加了 PyQt5 相关的隐藏导入
hiddenimports=[
'PyQt5',
'PyQt5.QtWidgets',
'PyQt5.QtGui',
'PyQt5.QtCore',
'PyQt5.sip',
],

# 并添加 datas
datas=[
# ... 其他数据
(os.path.dirname(PyQt5.__file__), 'PyQt5'),
],

问题7:windows打包后报错0007错误

原因:缺少 Visual C++ 运行库或系统驱动问题

图例:

问题7

解决方案:

方法 1:安装 Visual C++ 运行库

下载并安装:

方法 2:更新系统驱动

  • 更新显卡驱动
  • 更新 Windows 系统

版本历史

  • 2025-06-08: 初始版本