由于用户使用Python脚本的时候可能没有运行环境,所以需要打包。记录下碰到的问题。
安装
# 安装
pip install --upgrade pyinstaller
# 安装最新开发版
pip install https://github.com/pyinstaller/pyinstaller/tarball/develop
# 验证安装
pyinstaller -v
基本使用方法
Pyinstaller可以通过简单的命令进行python代码的打包工作,其基本的命令为:
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文件
pyi-makespec xxx.py
打开生成的spec文件,修改其默认脚本,完成自定义打包需要的配置。spec文件是一个python脚本,其默认的结构如下例所示
# -*- 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文件配置
注意注意:对于在源目录下的文件或目录可以只写文件名不写路径,子目录必须使用绝对路径
# -*- 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文件上添加递归深度的设置,设置一个足够大的值来保证打包的进行,即
import sys
sys.setrecursionlimit(5000)
f). 去除不必要的模块import
有时需要让pyinstaller不打包某些用不到的模块,可通过在excludes=[]中添加此模块实现,如
excludes=['zmq']
打包后执行程序提示找不到文件
注意:该案例只适用打包成单个目录中多个文件,不适用于打包成单个exe文件。
打包完毕之后执行程序提示找不到文件:
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()方法变更当前工作目录。
# 获取程序所处目录,并且变更当前工作目录
cwd = os.path.dirname(os.path.realpath(sys.argv[0]))
os.chdir(cwd)
一些其他的BUG记录
判断文件是否存在,但是直接回车提示错误。因为直接回车当前目录是存在的,但是只能读取文件所以报错。判断一下是否文件即可解决。
if(os.path.exists(file_name) and os.path.isfile(file_name)):