Python3中采用PyInstaller打包工程项目
2020-12-26 13:28
标签:www 表示 site toms scripts -name 存在 The 说明 对自己完成的工程项目进行打包,因为是第一次尝试,踩了各种各样的坑。以防下次继续踩坑,把相关问题以及解决办法记录下来。此次打包采用Python3.6,PyInstaller3.6,Windows64位系统。接下来就是整篇文章的精华了。 一、PyInstaller安装 PyInstaller包的安装可以在Anaconda环境下以conda install pyinstaller进行安装,在PyCharm中可以通过pip install pyinstaller进行安装。安装成功后就可以着手进行打包了。当然打包需要用到以下一些相关命令了。 常用到的命令为-F、-D、-i、-p、-w等,其中-i用于指定生成项目的图标,需要使用绝对路径。对于打包结果较大的项目,选用-d生成目录相比单可执行文件的打包方式,执行速度更快,但包含更加多的文件。本文的例子选中-D方式打包。 二、Python项目打包 打包分为单文件打包以及工程文件打包,单文件打包直接在命令窗口中采用pyinstaller -D filename.py,具体命令的添加可以参考表格中的命令。工程文件打包稍微有一些麻烦,主要是先生成主窗口的.spec文件,并修改相应的内容,最终执行.spec文件就可以逐步实现打包。以下是本次打包的工程目录,主要文件均在moleculeSystem,还包括img文件夹,tempFile文件夹等众多文件。 1.SPEC文件的生成 通过使用命令pyi-makespec -w xxx.py能够生成相应的xxx.spec文件,具体如下: spec文件中主要包含4个class: Analysis, PYZ, EXE和COLLECT. 首先给出举例项目的spec文件: 1)py文件打包配置 针对多目录多文件的python项目,打包时候需要将所有相关的py文件输入到Analysis类里。Analysis类中的pathex定义了打包的主目录,对于在此目录下的py文件可以只写文件名不写路径。如上的spec脚本,将所有项目中的py文件路径以列表形式写入Analysis,这里为了说明混合使用了绝对路径和相对路径。 2) 资源文件打包配置 资源文件包括打包的python项目使用的相关文件,如图标文件,文本文件等。对于此类资源文件的打包需要设置Analysis的datas,如例子所示datas接收元组:datas=[(SETUP_DIR+‘img‘,‘img‘)]。元组的组成为(原项目中资源文件路径,打包后路径),例子中的(SETUP_DIR+‘img‘,‘img‘)表示从D:\Python\untitled1\下的img文件夹文件打包后放入打包结果路径下的img目录。 3)Hidden import配置 pyinstaller在进行打包时,会解析打包的python文件,自动寻找py源文件的依赖模块。但是pyinstaller解析模块时可能会遗漏某些模块(not visible to the analysis phase),造成打包后执行程序时出现类似No Module named xxx。这时我们就需要在Analysis下hiddenimports中加入遗漏的模块,如例子中所示。 4)递归深度设置 在打包导入某些模块时,常会出现"RecursionError: maximum recursion depth exceeded"的错误,这可能是打包时出现了大量的递归超出了python预设的递归深度。因此需要在spec文件上添加递归深度的设置,设置一个足够大的值来保证打包的进行,即 5)去除不必要的模块import 有时需要让pyinstaller不打包某些用不到的模块,可通过在excludes=[]中添加此模块实现 打包生成两个文件目录build和dist,build为临时文件目录完成打包后可以删除;dist中存放打包的结果,可执行文件和其它程序运行的关联文件都在这个目录下。 三、打包出现的问题 1.PyQt plugins缺失 使用PyQt编写UI交互界面的python代码在进行打包时可能会出现一些特别的问题。 执行使用了PyQt的打包程序,常会出现这样的错误,提示缺少Qt platfrom plugin “windows”,如下图 打包后程序运行后,使用png格式的图标可以正常显示,但使用的ico格式图标不显示。这两个错误产生的问题都是因为打包时没有将PyQt相关的动态链接库目录生成到打包目录下,因此可以通过将这些需要的文件目录拷贝到打包生成目录下,解决plugin缺失问题。以使用PyQt5编写的python软件打包为例,完成打包后的结果目录下包含PyQt5文件夹,将PyQt5\Qt\plugins下的所有内容(如下图)拷贝到打包结果目录。这样就可以解决PyQt plugins缺失的问题。 2.动态链接库缺失问题 一般的,打包后可能会缺失某些动态链接库,造成执行程序出错,如 3.Failed to execute script pyi_rth_pkgres 打包运行xxx.exe文件出现上述问题,是有可能因PyInstaller版本不是最新的造成的,可以通过GitHub网站https://github.com/pyinstaller/pyinstaller/archive/develop.zip下载,下载后解压到某一文件夹中,采用命令行的方式进入解压后的文件夹中,使用命令python setup.py install进行安装。 4.Failed to execute script pyi_rth_certifi 出现此错误的原因是缺少了ssl证书,因此可以从Python官网上下载相应python版本的压缩包,解压后将解压包中的_ssl.pyd(复制到Anaconda目录下DLLs下覆盖原文件),将 libcrypto-1_1.dll、libssl-1_1.dll(复制到Anaconda根目录)即可解决问题。 5.no modules named xxxx 出现该问题是打包后的根目录中没有所需要的模块,因此可以将对应安装库中的文件复制到根目录中,此错误就不再出现。或者可以在hiddenimports中加入缺失的包。或者在datas中作同样的处理。 6.Failed to execute script xxxx 出现该问题是在引用同级目录中的文件时出现,在有python文件出增加__init__.py文件即可解决,该文件可什么都不写。 7.路径冻结 增加一个py文件,例如叫frozenPath.py 其中的app_path()函数返回一个程序的执行路径,为了方便我们将此文件放在项目文件的根目录,通过这种方式建立了相对路径的关系。源代码中使用路径时,以app_path()的返回值作为基准路径,其它路径都是其相对路径。以本文中使用的python项目打包为例,如下所示: 这样不论打包的文件放在哪台电脑上运行都不会出现路径错误。 8.始终还有问题存在 可以采用以下命令解决 9.顺便再记个PyQt5中QSplitter部件无法显示其他窗口的背景 原因在于主窗口部件无法显示背景,子窗口部件能够显示,基于此可以解决这种问题。 10.安装PyQt5 使用豆瓣能够告诉下载 希望这些能够对还在打包程序路上挣扎的同志们有所帮助。 Python3中采用PyInstaller打包工程项目 标签:www 表示 site toms scripts -name 存在 The 说明 原文地址:https://www.cnblogs.com/qhu-hjx/p/13036551.html
-h,--help
查看该模块的帮助信息
-F,-onefile
产生单个的可执行文件
-D,--onedir
产生一个目录(包含多个文件)作为可执行程序
-a,--ascii
不包含 Unicode 字符集支持
-d,--debug
产生 debug 版本的可执行文件
-w,--windowed,--noconsolc
指定程序运行时不显示命令行窗口(仅对 Windows 有效)
-c,--nowindowed,--console
指定使用命令行窗口运行程序(仅对 Windows 有效)
-o DIR,--out=DIR
指定 spec 文件的生成目录。如果没有指定,则默认使用当前目录来生成 spec 文件
-p DIR,--path=DIR
设置 Python 导入模块的路径(和设置 PYTHONPATH 环境变量的作用相似)。也可使用路径分隔符(Windows 使用分号,Linux 使用冒号)来分隔多个路径
-n NAME,--name=NAME
指定项目(产生的 spec)名字。如果省略该选项,那么第一个脚本的主文件名将作为 spec 的名字
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis([‘mainWin.py‘],
pathex=[‘D:\\Python\\untitled1\\moleculeSystem‘],
binaries=[],
datas=[],
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=‘mainWin‘,
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=‘mainWin‘)
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
SETUP_DIR = ‘D:\\Python\\untitled1\\‘
a = Analysis([‘mainWin.py‘],
pathex=[‘D:\\Python\\untitled1\\moleculeSystem‘],
binaries=[],
datas=[(SETUP_DIR+‘img‘,‘img‘),(SETUP_DIR+‘temp_img‘,‘temp_img‘),(SETUP_DIR+‘mol\\totalFile‘,‘mol\\totalFile‘),(SETUP_DIR+‘tempFile‘,‘tempFile‘),(‘D:\\Anaconda\\Anaconda3\\Lib\\site-packages\\vtkmodules‘,‘vtkmodules‘),(SETUP_DIR+‘AtomsInfo.txt‘,‘AtomsInfo.txt‘),(SETUP_DIR+‘BondInfos.txt‘,‘BondInfos.txt‘)],
hiddenimports=[‘pkg_resources‘],
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=‘mainWin‘,
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=‘mainWin‘)
import sys
sys.setrecursionlimit(5000)
3.使用spec文件打包pyinstaller -D xxx.spec
ImportError: DLL load failed: 找不到指定的模块
在打包过程中一般会有与此相关的warning提示(lib not found)无法找到这些动态链接库。例如在32位版本的打包中,可能会出现scipy模块相关的dll文件无法找到。这时就需要在打包的spec文件中指定动态链接库路径,使其关联到打包后的路径中。
binaries=[(‘路径‘,‘.‘)]
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__)
import frozenPath
# 根目录路径
appPath = frozenPath.app_path()
background = QtGui.QPixmap(appPath+"/img/1.png")
pyinstaller --hidden-import=pkg_resources -F xxxx.py
# 设置背景图片
background = QtGui.QPixmap(appPath+"/img/1.png")
palette1 = QtGui.QPalette()
palette1.setBrush(self.backgroundRole(),QtGui.QBrush(background))
self.handle.setPalette(palette1)
self.handle.setAutoFillBackground(True)
# 设置背景透明
self.main_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground)
下载PyQt5
pip install PyQt5 -i https://pypi.douban.com/simple
下载PyQt5-tools
pip install PyQt5-tools -i https://pypi.douban.com/simple