0%

Pyinstaller打包教程

由于用户使用Python脚本的时候可能没有运行环境,所以需要打包。记录下碰到的问题。

安装

1
2
3
4
5
6
7
# 安装
pip install --upgrade pyinstaller

# 安装最新开发版
pip install https://github.com/pyinstaller/pyinstaller/tarball/develop
# 验证安装
pyinstaller -v

基本使用方法

Pyinstaller可以通过简单的命令进行python代码的打包工作,其基本的命令为:

1
pyinstaller -option xxx.py
选项 功能
-D, --onedir Create a one-folder bundle containing an executable (default)
-F, --onefile Create a one-file bundled executable.

高级使用方法

以一个多文件和目录的Python项目为例,项目文件包含:1.Python源代码文件;2.图标资源文件;3.其它资源文件。

1. spec文件生成

为了进行自定义配置的打包,首先需要编写打包的配置文件.spec文件。当使用pyinstaller -D/F xxx.py时候会生成默认的xxx.spec文件进行默认的打包配置。通过配置spec脚本,并执行pyinstaller xxx.spec完成自定义的打包。

通过生成spec文件的命令,针对代码的主程序文件生成打包对应的spec文件

1
pyi-makespec xxx.py

打开生成的spec文件,修改其默认脚本,完成自定义打包需要的配置。spec文件是一个python脚本,其默认的结构如下例所示

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
# -*- mode: python -*-

block_cipher = None


a = Analysis(['fastplot.py'],
pathex=['D:\\install_test\\DAGUI-0.1\\bin'],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
exclude_binaries=True,
name='fastplot',
debug=False,
strip=False,
upx=True,
console=False )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='fastplot')

spec文件中主要包含4个class: Analysis, PYZ, EXE和COLLECT.

  • Analysis以py文件为输入,它会分析py文件的依赖模块,并生成相应的信息
  • PYZ是一个.pyz的压缩包,包含程序运行需要的所有依赖
  • EXE根据上面两项生成
  • COLLECT生成其他部分的输出文件夹,COLLECT也可以没有

2. spec文件配置

首先给出举例python项目的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
# -*- mode: python -*-
import sys
import os.path as osp
sys.setrecursionlimit(5000)

block_cipher = None


SETUP_DIR = 'D:\\install_test\\FASTPLOT\\'

a = Analysis(['fastplot.py',
'frozen_dir.py',
'D:\\install_test\\FASTPLOT\\lib\\app\\start.py',
'D:\\install_test\\FASTPLOT\\lib\\models\\analysis_model.py',
'D:\\install_test\\FASTPLOT\\lib\\models\\datafile_model.py',
'D:\\install_test\\FASTPLOT\\lib\\models\\data_model.py',
'D:\\install_test\\FASTPLOT\\lib\\models\\figure_model.py',
'D:\\install_test\\FASTPLOT\\lib\\models\\time_model.py',
'D:\\install_test\\FASTPLOT\\lib\\models\\mathematics_model.py',
'D:\\install_test\\FASTPLOT\\lib\\views\\constant.py',
'D:\\install_test\\FASTPLOT\\lib\\views\\custom_dialog.py',
'D:\\install_test\\FASTPLOT\\lib\\views\\data_dict_window.py',
'D:\\install_test\\FASTPLOT\\lib\\views\\data_process_window.py',
'D:\\install_test\\FASTPLOT\\lib\\views\\data_sift_window.py',
'D:\\install_test\\FASTPLOT\\lib\\views\\mathematics_window.py',
'D:\\install_test\\FASTPLOT\\lib\\views\\para_temp_window.py',
'D:\\install_test\\FASTPLOT\\lib\\views\\mainwindow.py',
'D:\\install_test\\FASTPLOT\\lib\\views\\paralist_window.py',
'D:\\install_test\\FASTPLOT\\lib\\views\\plot_window.py'],
pathex=['D:\\install_test\\FASTPLOT'],
binaries=[],
datas=[(SETUP_DIR+'lib\\icon','lib\\icon'),(SETUP_DIR+'data','data'),('policy_check_rule.xlsx','.') ],
hiddenimports=['pandas','pandas._libs','pandas._libs.tslibs.np_datetime','pandas._libs.tslibs.timedeltas',
'pandas._libs.tslibs.nattype', 'pandas._libs.skiplist','scipy._lib','scipy._lib.messagestream'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher)


pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
exclude_binaries=True,
name='fastplot',
debug=False,
strip=False,
upx=True,
console=True , icon='myicon.ico')
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='fastplot')

a). py文件打包配置

针对多目录多文件的python项目,打包时候需要将所有相关的py文件输入到Analysis类里。Analysis类中的pathex定义了打包的主目录,对于在此目录下的py文件可以只写文件名不写路径,子目录中py文件必须使用绝对路径。如上的spec脚本,将所有项目中的py文件路径以列表形式写入Analysis,这里为了说明混合使用了绝对路径和相对路径。

b). 资源文件打包配置

资源文件包括打包的python项目使用的相关文件,如图标文件,文本文件等。对于此类资源文件的打包需要设置Analysis的datas,如例子所示datas接收元组:datas=[(SETUP_DIR+'lib\icon','lib\icon'),(SETUP_DIR+'data','data')]。元组的组成为(原项目中资源文件路径,打包后路径),例子中的(SETUP_DIR+'lib\icon','lib\icon')表示从D:\install_test\FASTPLOT\lib\icon下的图标文件打包后放入打包结果路径下的lib\icon目录。例子中('policy_check_rule.xlsx','.')表示从源码目录打包文件直接放入结果路径下。

c). ICON 图标配置

图标文件在EXE类中添加icon='myicon.ico'属性进行设置。

d). Hidden import配置

pyinstaller在进行打包时,会解析打包的python文件,自动寻找py源文件的依赖模块。但是pyinstaller解析模块时可能会遗漏某些模块(not visible to the analysis phase),造成打包后执行程序时出现类似No Module named xxx。这时我们就需要在Analysis下hiddenimports中加入遗漏的模块,如例子中所示。

e). 递归深度设置

在打包导入某些模块时,常会出现"RecursionError: maximum recursion depth exceeded"的错误,这可能是打包时出现了大量的递归超出了python预设的递归深度。因此需要在spec文件上添加递归深度的设置,设置一个足够大的值来保证打包的进行,即

1
2
import sys
sys.setrecursionlimit(5000)

f). 去除不必要的模块import

有时需要让pyinstaller不打包某些用不到的模块,可通过在excludes=[]中添加此模块实现,如

1
excludes=['zmq']

打包后执行程序提示找不到文件

注意该案例只适用打包成单个目录中多个文件,不适用于打包成单个exe文件。

打包完毕之后执行程序提示找不到文件:

1
2
3
4
5
6
7
Please input configuration file or file path and name:
Traceback (most recent call last):
File "main.py", line 180, in <module>
File "main.py", line 118, in main
File "main.py", line 80, in neteye_cfg_file_read
FileNotFoundError: [Errno 2] No such file or directory: '.\\cfg_file\\'
[17792] Failed to execute script main

这十有八九和你Python程序书写有BUG相关,因为你再语句中使用了相对路径。但是当执行程序的时候所处的目录却并非程序文件所在路径,所以要使用os.chdir()方法变更当前工作目录

1
2
3
# 获取程序所处目录,并且变更当前工作目录
cwd = os.path.dirname(os.path.realpath(sys.argv[0]))
os.chdir(cwd)

参考:

pyinstaller使用方法

https://www.jb51.net/article/187806.htm

https://blog.csdn.net/wanzheng_96/article/details/108556950

https://stackoverrun.com/cn/q/9124269

pyinstaller文件路径获取

https://blog.csdn.net/qq_31801903/article/details/81666124

https://blog.csdn.net/Iv_zzy/article/details/107407167

https://www.jb51.net/article/184736.htm

https://www.jb51.net/article/52410.htm

https://www.jb51.net/article/163664.htm

一些其他的BUG记录

判断文件是否存在,但是直接回车提示错误。因为直接回车当前目录是存在的,但是只能读取文件所以报错。判断一下是否文件即可解决。

1
if(os.path.exists(file_name) and os.path.isfile(file_name)):