Pyqt5软件打包全流程以及问题汇总
文章目录
引言
在软件开发中,将应用程序打包成可执行文件是发布软件的重要一步。对于使用Python和PyQt框架开发的应用程序来说,PyInstaller是一个流行的选择。本文将详细介绍如何使用PyInstaller将PyQt应用程序打包成可执行文件,并阐述打包软件的目的。
Pyqt 简介
PyQt 是一个跨平台的图形用户界面(GUI)应用程序开发框架,它提供了Qt库的Python绑定。Qt 是一个广泛使用的C++库,用于创建具有丰富用户界面的应用程序。PyQt 使得开发者可以使用Python语言来访问Qt的所有功能,包括窗口、按钮、菜单、图形、网络编程等。
Pyinstaller
PyInstaller 够将Python脚本及其所有依赖项打包成一个独立的可执行文件。这使得开发者可以轻松地将Python应用程序分发给用户,而无需用户安装Python解释器或任何依赖库。PyInstaller 支持跨平台打包,可以在Windows、macOS和Linux等操作系统上运行。
打包软件的几个主要目的:
- 方便分发:将应用程序打包成可执行文件,用户无需了解Python或任何依赖库,即可在不同操作系统上运行。
- 提高安全性:打包后的应用程序隐藏了源代码,减少了被恶意修改的风险。
- 简化安装:用户无需手动安装Python环境和依赖库,只需运行一个安装程序即可完成部署。
- 提升用户体验:提供单一的可执行文件,简化了用户的使用流程,提高了应用程序的专业度。
- 便于维护:开发者可以通过打包后的日志和错误报告,更有效地监控和维护应用程序。
软件打包流程
软件打包可以通过以下两种方式进行:
一、编写pack.py打包脚本
1.准备工作
-
安装PyInstaller: 确保您的conda环境已经安装了PyInstaller。如果尚未安装,可以通过运行以下命令来安装:
conda install -c conda-forge pyinstaller
-
切换到相应的conda环境: 在开始打包之前,确保您已经切换到了包含您项目所有依赖的conda环境中。通过以下命令切换:
conda activate your_env_name
2.打包过程
-
准备打包文件: 确保
main.py
、path.py
、ui.py
以及QSS
和win
子文件夹位于项目的根目录下。如果有特定的资源文件或配置文件也需要包含在打包文件中,请确保它们也位于正确的位置。 -
编写打包脚本: 由于您需要包含特定的子文件夹和文件,建议在项目根目录下创建一个打包脚本
pack.py
。这个脚本将使用PyInstaller的API来定义打包的细节。以下是一个基本示例:
# -*- mode: python -*-
from PyInstaller.__main__ import run
import os
# 主程序文件
main_script = 'Main_win.py'
# 直接指定需要包含的文件和文件夹路径
files_and_folders = [
'Main_Win.py', # 主程序文件
'path.py', # 其他Python文件
'Ui.py', # 其他Python文件
'Thread.py',
'tools/', # tools文件夹
'QSS/', # QSS文件夹
]
# 转换为PyInstaller需要的格式
datas = []
for item in files_and_folders:
if os.path.isdir(item):
# 对于文件夹,添加整个文件夹
datas.append((item + os.sep, item))
else:
# 对于文件,只添加文件本身
datas.append((item, '.'))
# 构建PyInstaller命令
pyinstaller_command = [
main_script, # 主程序
'--onefile', # onefile模式
'--windowed', # 防止弹出命令行窗口
'--icon=app.ico', # 使用app.ico作为软件图标
'--name=app', # 设置打包后的文件名为app
'--clean', # 清理打包前的缓存和临时文件
'--runtime-tmpdir=Temp', # 设置运行时的临时目录为项目目录下的Temp文件夹
]
# 添加数据文件
for data in datas:
pyinstaller_command.extend(['--add-data', f'{data[0]};{data[1]}'])
# 执行PyInstaller命令
run(pyinstaller_command)
二、通过.spec文件打包
1. 准备工作
在开始打包之前,请确保开发环境已安装以下组件:
- Python
- PyQt5
- PyInstaller
可以通过以下命令安装这些组件(如果尚未安装):
pip install pyqt5 pyinstaller
2. 创建 .spec
文件
PyInstaller 需要一个 .spec
文件来定义打包的配置。可以通过运行以下命令自动生成一个基本的 .spec
文件(还没有完成打包的全部配置):
pyinstaller --name=YourAppName --windowed --icon=app_icon.ico your_app.py
YourAppName
是生成的可执行文件的名称,app_icon.ico
是应用程序的图标文件,your_app.py
是主应用程序脚本。
3. 编辑 .spec
文件
生成的 .spec
文件需要根据您的应用程序进行一些调整。打开该文件,您可能会看到类似以下内容:
block_cipher = None
a = Analysis(['Main_win.py'],
pathex=[],
binaries=[],
datas=[('Main_Win.py', '.'), ('path.py', '.'), ('Ui.py', '.'), ('Thread.py', '.'), ('tools/\\', 'tools/'), ('QSS/\\', 'QSS/'),
hiddenimports=[],
hookspath=[],
hooksconfig={},
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,
a.binaries,
a.zipfiles,
a.datas,
[],
name='app',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir='Temp',
console=False,
disable_windowed_traceback=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None , icon='app.ico')
4. 添加必要的data参数和钩子
如果应用程序依赖于数据文件或需要特定的钩子来处理PyQt的特定组件,您需要在 .spec
文件中添加它们。例如:
a = Analysis(['your_app.py'],
pathex=['/path/to/your/app'],
binaries=[],
datas=[('path/to/datafile', 'data'), ('path/to/image.png', '.')],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
(‘path/to/datafile’, ‘data’) 表示将 datafile 复制到打包应用程序的 data 文件夹中,而 (‘path/to/image.png’, ‘.’) 表示将 image.png 复制到应用程序的根目录。
a = Analysis(['your_app.py'],
hookspath=['path/to/custom_hooks'],
runtime_hooks=['path/to/runtime_hooks.py'],
# ... 其他参数
)
PyInstaller 允许你为特定的库或框架编写钩子,这些钩子可以自动检测并包含必要的文件。如果你使用的是 PyQt,PyInstaller 已经内置了钩子来处理常见的 PyQt 文件。如果你需要自定义钩子,可以在 .spec 文件中指定 hookspath 和 runtime_hooks。
5. 打包应用程序
保存修改后的 .spec
文件,并在命令行中运行 PyInstaller,指向您的 .spec
文件:
pyinstaller -F --onefile YourAppName.spec
这将开始打包过程,PyInstaller 将根据 .spec
文件中的指令创建可执行文件。
-
pyinstaller
:这是调用 PyInstaller 程序的命令。 -
-F
:这个选项告诉 PyInstaller 创建一个单个文件的打包程序。这意味着所有的依赖项和应用程序代码都会被打包进一个可执行文件中。 -
--onefile
:这个选项与-F
相同,也是用来创建一个单个文件的打包程序。在 PyInstaller 的早期版本中,-F
和--onefile
是分开的选项,但在最新版本中,-F
已经包含了--onefile
的功能,所以通常只需要-F
。 -
YourAppName.spec
:这是 PyInstaller 的配置文件(也称为.spec
文件),它包含了打包应用程序所需的所有信息和指令,比如应用程序的入口点、包含的文件、排除的模块等。
简而言之,这条命令是用来将名为 MyTcpServer
的应用程序打包成一个独立的可执行文件,用户可以直接运行这个文件而不需要安装 Python 或其他依赖项。
注意:在 PyInstaller 的较新版本中,--onefile
选项是默认的,所以通常不需要显式指定。如果你使用的是较新版本的 PyInstaller,只需要 -F
就足够了。
6. 测试打包的应用程序
打包完成后,在软件项目文件夹的 dist
目录下找到生成的可执行文件。运行该文件以确保应用程序正常工作,没有错误或警告。
遇到的问题
1.WARNING:file already exists but should not:_C.cp37-win_amd64:
在使用 PyInstaller 打包基于 PyQt 的应用程序时,我遇到了一个bug。启动打包后的程序时,程序会弹出一个警告信息:
WARNING: file already exists but should not: F:\app\Temp\_MEI46802\torch\_C.cp36-win_amd64.pyd
这个警告表明在打包过程中,PyInstaller 尝试创建一个已经存在的文件,这通常是由于打包配置不正确或者依赖管理问题导致的。
解决方案
报错内容可能不同,但都是xxx已存在,问题的原因是pyinstaller打包时多打了一次,所以会报已经存在了。
这个解决方案就是把多余的去掉。
在自动生成的.spec中,在 a 和 PYZ 中间添加如下代码,去掉多余依赖项
for d in a.datas:
if '_C.cp36-win_amd64' in d[0]:
a.datas.remove(d)
break
在自动生成的.spec中,在 a 和 PYZ 中间添加上述代码,去掉多余依赖项
添加后.spec文件如下所示:
a = Analysis(['Main_win.py'],
pathex=[],
binaries=[],
datas=[('Main_Win.py', '.'), ('path.py', '.'), ('Ui_detection.py', '.'), ('DetThread.py', '.'), ('EvalThread.py', '.'), ('TrainThread.py', '.'), ('imageBox.py', '.'), ('tools/\\', 'tools/'), ('QSS/\\', 'QSS/'), ('mmdet/\\', 'mmdet/'), ('C:\\Program Files\\Python36\\Lib\\site-packages\\mmcv', 'mmcv')],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
for d in a.datas:
if '_C.cp36-win_amd64' in d[0]:
a.datas.remove(d)
break
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
注意!!!注意把中间需要去掉的文件名改成与报错相同的名称!!
参考
Pyinstaller --onefile warning file already exists but should not
pyinstaller系列之七:打包各种问题汇总
WARNING: file already exists but should not: C:\Users\workAI\AppData\Local\Temp_MEI132522\torch_C