赞
踩
pip install pyinstaller
cd xxxxxxxxxxx
Pyinstaller可以通过简单的命令进行python代码的打包工作,其基本的命令为:
pyinstaller -option xxx.py
假如项目启动入口为xxx.py,首先需要以管理员身份启动cmd,然后cd到xxx.py所在的文件夹
当我们使用命令行直接打包,即 pyinstaller -D xxx.py 时 :
1.生成默认的xxx.spec文件
```python
2.直接根据默认的main.spec文件进行执行pyinstaller -D xxx.spec完成默认的打包
-D 与 -F 相反用法,生成一个文件目录包含可执行文件和相关动态链接库和资源文件等,对于打包结果较大的项目,选用-D生成目录相比-F的打包方式,执行速度更快,但包含更加多的文件
-F 表示在 dist 文件夹下只生成单个可执行文件(内部包含所有依赖),不加默认会在 dist 生成一大堆依赖文件+可执行文件。
-w 表示去掉控制台窗口,如果你的程序是有界面的,可以不写这个参数,
-c 表示去掉窗框,使用控制台,推荐使用,会打印各种信息和log到控制台,加上这个参数生成的spec中的console=True
-p 表示自己定义需要加载的类路径,项目中包含多个自建模块的时候需要加上 -p aaa.py -p bbb.py -p ccc.py
-i 表示可执行文件的图标,后面跟图标的路径,可以自定义exe文件的图标,我尝试了好多次没成功
打包完毕后在 dist 文件夹下双击项目启动文件就可以执行了
参考:https://blog.csdn.net/weixin_42052836/article/details/82315118
以一个多文件和目录的Python项目为例,项目文件包含:1.Python源代码文件;2.图标资源文件;3.其它资源文件
以图中项目为例,Python源代码文件在多个目录下:bin, lib\app, lib\models, lib\views;图标资源文件在lib\icon目录下;其它资源文件在data目录下,包括文本文件,视频文件等等
为了实现自定义配置的打包:
第一步:需要生成启动文件默认的spec文件:(选择一个即可)
pyi-makespec -w xxx.py
pyi-makespec -c xxx.py # 生成的spec文件中console=True,即需要打印到控制台
第二步:打开生成的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也可以没有
我们修改默认生成的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',
第一个参数:Python中的资源文件等非py类型文件的路径
第二个参数:打包后路径,要和路径中的文件夹名称相同
import sys
sys.setrecursionlimit(5000)
excludes=['zmq']
pyinstaller -D xxx.spec
打包生成两个文件目录build和dist:
build为临时文件目录完成打包后可以删除;
dist中存放打包的结果,可执行文件和其它程序运行的关联文件都在这个目录下,dist文件夹里包含了整个项目所需的代码和环境,可在其他电脑中进行使用
当项目不使用默认的pip管理,采用更高级的pipenv管理的时候,是额外的一个虚拟环境,各种依赖都在虚拟幻境里,就需要切换到虚拟环境中操作,话不多说先上图:
该项目采用最新版 python 3.8.0
cd C:\Users\13154\Desktop\taibaorpa
pipenv install
pipenv会根据项目的pipfile自动安装其中记载的所有依赖
pipenv shell
进入后,路径最前面会出现虚拟环境
pipenv graph
pip install pyinstaller
pyi-makespec -c main.py # 生成的spec文件中console=True,即需要打印到控制台
修改后的spec
# -*- mode: python ; coding: utf-8 -*- import sys import os.path as osp sys.setrecursionlimit(5000) SETUP_DIR = 'C:\\Users\\13154\\Desktop\\taibaorpa\\' block_cipher = None a = Analysis(['main.py', SETUP_DIR+'asynctask.py', SETUP_DIR+'errors.py', SETUP_DIR+'file.py', SETUP_DIR+'msg.py', SETUP_DIR+'msg_pattern.py', SETUP_DIR+'pageon.py', SETUP_DIR+'report.py', SETUP_DIR+'script.py', SETUP_DIR+'settings.py', SETUP_DIR+'taibaorpa.py', SETUP_DIR+'taiboweb.py', SETUP_DIR+'webdriverhelper.py', SETUP_DIR+'wechat.py',], pathex=['C:\\Users\\13154\\Desktop\\taibaorpa'], binaries=[], datas=[(SETUP_DIR+'driver\\chrome','driver\\chrome'), (SETUP_DIR+'log\\screenshot_taiboweb','log\\screenshot_taiboweb'), (SETUP_DIR+'log\\screenshot_wechat','log\\screenshot_wechat'), (SETUP_DIR+'log\\wechat_files','log\\wechat_files'), (SETUP_DIR+'report','report'), (SETUP_DIR+'res\\img','res\\img'), (SETUP_DIR+'res\\vid','res\\vid'), (SETUP_DIR+'README.md','.')], hiddenimports=[], hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher, noarchive=False) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE(pyz, a.scripts, [], exclude_binaries=True, name='main', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, console=True ) coll = COLLECT(exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=True, upx_exclude=[], name='main')
pyinstaller -D main.spec
然后打开dist/main文件夹,里面的main.exe就是执行文件,双击能正确启动则打包成功
程序调用的很多包,在打包时候可能会出现一些问题,针对这写问题需要做一些处理才能保证打包的程序正常执行。
1.PyQt plugins缺失
使用PyQt编写UI交互界面的python代码在进行打包时可能会出现一些特别的问题。
执行使用了PyQt的打包程序,常会出现这样的错误,提示缺少Qt platfrom plugin “windows”,如下图
打包后程序运行后,使用png格式的图标可以正常显示,但使用的ico格式图标不显示(对于所有图标和关联文件都无法使用的情况涉及到路径问题,后文会另外解释)。
这两个错误产生的问题都是因为打包时没有将PyQt相关的动态链接库目录生成到打包目录下,因此可以通过将这些需要的文件目录拷贝到打包生成目录下,解决plugin缺失问题。以使用PyQt5编写的python软件打包为例,完成打包后的结果目录下包含PyQt5文件夹,将PyQt5\Qt\plugins下的所有内容(如下图)拷贝到打包结果目录。这样就可以解决PyQt plugins缺失的问题。
2.动态链接库缺失,执行程序出错:ImportError: DLL load failed: 找不到指定的模块
在打包过程中一般会有与此相关的warning提示(lib not found)无法找到这些动态链接库。例如在32位版本的打包中,可能会出现scipy模块相关的dll文件无法找到。
这时就需要在打包的spec文件中指定动态链接库路径,使其关联到打包后的路径中。
binaries=[(‘C:\Program Files\Python36-32\Lib\site-packages\scipy\extra-dll’,’.’)]
Analysis下的binaries是为打包文件添加二进制文件,缺失的动态链接库可以通过这种方式自动加入到打包路径中。
3.窗体风格变化问题
在某些情况下,如在精简环境下的python程序打包中,执行打包后的程序会出现窗体风格变为老式的win风格,这是由于打包时候PyQt的styles动态库没有找到。因此只需要在Python 目录下找到 Lib\site-packages\PyQt5\Qt\plugins\styles,将styles整个目录复制到打包结果目录。
4.当打包时出现:UnicodeDecodeError: ‘utf-8’ codec can’t decode byte 0xce in position 122
可在打包的命令行中输入chcp 65001设置命令行显示utf-8字符,然后再执行打包命令。或者,修改pyinstaller包下的compat.py,根据报错对应的行将
out = out.decode(encoding)
改为
out = out.decode(encoding, ‘replace’)
5.执行打包命令时报错 ImportError: No module named ‘queue’
原因:尚不清楚
解决方法:如果该模块你用不到,可以在执行打包命令时用 --hidden-import 不打包进去。如果程序中需要该模块,在主文件最上面写上 improt queue
6.打包命令执行成功,但双击可执行程序弹出报错窗口failed to excute script xxx
原因:打包时内部缺少了某个依赖,这时需要看看控制台打印了什么报错信息,打包时加了 -w 参数的请再打包一次记得去掉 -w以便于查看log信息
现象:基本都是在控制台上发现报错 No module named ‘xxxxx’,如 No module named ‘queue’ 或者 ModuleNotFoundError: No module named ‘PyQt5.sip’
解决方法1:同2,如果该模块你用不到,可以在执行打包命令时用 --hidden-import 不打包进去。如果程序中需要该模块,在主文件最上面写上 improt xxxxx。如 import queue 或 import PyQt5.sip
解决方法2:我用pipenv管理的项目,但是在真实环境下打包,打包好后运行出现这个,重新走 第二章 中流程,成功打包
7.冻结打包路径
执行打包后的程序,经常会出现程序使用的图标无法显示,程序使用的关联文件无法关联。或者,在打包的本机上运行正常,但是将打包后的程序放到其它机器上就有问题。这些现象都很有可能是由程序使用的文件路径发生改变产生的,因此在打包时候我们需要根据执行路径进行路径“冻结”。
1.使用绝对路径
在python代码中使用绝对路径调用外部文件可以保证打包时候路径可追溯,因此在本机上运行打包后程序基本没问题。但是当本机上对应路径的资源文件被改变,或者将打包程序应用到别的机器,都会出现搜索不到资源文件的问题。这种方式不是合适的打包发布python软件的方式。
2.使用冻结路径
增加一个py文件,例如叫frozen_dir.py
“”"
Created on Sat Aug 25 22:41:09 2018
frozen dir
@author: yanhua
“”"
import sys
import os
def app_path():
“”“Returns the base application path.”""
if hasattr(sys, ‘frozen’):
# Handles PyInstaller
return os.path.dirname(sys.executable)
return os.path.dirname(file)
其中的app_path()函数返回一个程序的执行路径,为了方便我们将此文件放在项目文件的根目录,通过这种方式建立了相对路径的关系。
源代码中使用路径时,以app_path()的返回值作为基准路径,其它路径都是其相对路径。以本文中使用的python项目打包为例,如下所示
import frozen_dir
SETUP_DIR = frozen_dir.app_path()
FONT_MSYH = matplotlib.font_manager.FontProperties(
fname = SETUP_DIR + r’\data\fonts\msyh.ttf’,
size = 8)
DIR_HELP_DOC = SETUP_DIR + r’\data\docs’
DIR_HELP_VIDEO = SETUP_DIR + r’\data\videos’
通过冻结路径,使用了基准目录下的data目录下的fonts, docs, videos。
主程序中也做了类似的调整,改变其设置路径方法
import frozen_dir
SETUP_DIR = frozen_dir.app_path()+r’\lib’
sys.path.append(SETUP_DIR)
使用这样的方法进行打包,打包后的可执行程序就可以在其它机器上运行。
五 其它问题
由于操作系统和运行环境的不同,pyinstaller打包中还可能遇到很多其它问题,最后总结一些我在打包中遇到的其它坑:
1.权限问题
通常时在打包时出现的某些文件拒绝访问或没有权限执行某些操作等。解决这个的方法一般有这几个方面:
a)使用管理员权限运行cmd或其它命令行窗口
b)关闭杀毒软件
c)使用完全权限的管理员账户
2.中文路径
pyinstaller打包后的路径使用中文没有问题,不过为了减少打包时候出错的可能,尽量将打包使用的资源文件和代码文件路径设置为英文。
3.打包后文件的大小
通常python打包为可执行文件都会得到一个较大的包,这是无法避免的,但是我们还是可以通过一些方法来尽量精简打包后的执行程序:
a)在代码中减少不必要的import,如from xxx import *
b)在精简的运行环境(如原生python环境)下打包,缺什么包就下什么包,避免不必要的python包被打包入程序。尤其是anaconda这样的集成环境下打包的结果会大很多。
c)使用UPX
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。