使用PyInstaller 打包Python项目是一个常见的需求,它可以将Python程序及其所有依赖项打包成一个独立的可执行文件或者安装文件,方便在没有安装Python环境的机器上运行。本随笔介绍WxPython跨平台开发框架中使用PyInstaller 进行打包处理,包括在WIndow平台下生成独立的exe文件,松散结构的exe文件和目录,以及在MacOS上生成安装包的处理过程。
1. 安装和使用 PyInstaller 进行程序打包处理
PyInstaller是目前最流行的Python打包工具之一。它可以将Python脚本打包成独立的可执行文件,支持Windows、Linux和macOS平台。
打包后的可执行文件可以在没有 Python 环境的机器上运行。PyInstaller 会自动分析程序的依赖关系,并将所有必要的库和资源打包到一个文件或者一个文件夹中。
cx_Freeze是另一个常用的Python打包工具,同样支持跨平台。
Py2exe是专门用于将Python脚本打包成Windows平台可执行文件的工具。
首先,确保您的Python环境已经安装。然后,通过pip安装PyInstaller。在VSCode或者其他编辑器的命令行(终端)中执行以下命令:
pip install pyinstaller
在项目目录下,使用PyInstaller命令来打包您的Python脚本。假设您的主程序文件名为main.py
,则可以使用以下命令:
pyinstaller --onefile main.py
这里的--onefile
选项指示PyInstaller生成一个单独的可执行文件,而不是一个包含多个文件的文件夹。PyInstaller还支持许多其他选项,如--icon
来指定应用程序的图标,--windowed
或--noconsole
来避免在Windows上打开命令行窗口等。
PyInstaller完成打包后,会在dist
目录下生成可执行文件(或文件夹,如果您没有使用--onefile
选项)。进入dist
目录,您应该能看到一个名为main
(或您指定的名称,如果使用了--name
选项)的可执行文件。
如果一切正常,您的Python程序现在应该会在没有Python环境的情况下运行。
使用PyInstaller打包Python项目是一个简单而强大的方法,可以让您的程序更加便携和易于分发。通过遵循上述步骤,您可以轻松地将您的Python项目打包成一个可执行文件。
常用选项
PyInstaller 提供了很多命令行选项,用于定制打包过程。以下是一些常用的选项:
--onefile
生成单个可执行文件。默认情况下,PyInstaller 会生成一个文件夹,里面包含了可执行文件和所有相关的依赖项。如果你希望打包成一个独立的可执行文件(可能会更大),可以使用 --onefile
选项。
--noconsole
如果你正在创建一个 GUI 应用程序(如使用 wxPython 或 PyQt),并且不希望显示控制台窗口,可以使用 --noconsole
选项。此选项会隐藏控制台窗口,适用于 Windows 和 macOS。
--icon
为生成的可执行文件指定一个图标。图标文件可以是 .ico
(Windows)或 .icns
(macOS)格式。
--onedir
生成一个包含可执行文件和所有依赖项的文件夹,而不是单个文件。你可以将整个文件夹分发给其他用户。
build
文件夹被PyInstaller用来收集和准备捆绑的文件,它包含分析的结果和一些额外的日志。在大多数情况下,你可以忽略这个文件夹的内容,除非你想调试问题。
dist
(代表 "分发")文件夹包含要分发的文件。这包括你的应用程序,捆绑成一个可执行文件,以及任何相关的库(例如WxPython、PyQt6 等)和二进制.so
文件。
2、使用.spec文件进行定制打包处理
打包过程中,PyInstaller 会生成一个 .spec
文件。这个文件包含了 PyInstaller 的配置信息,其中包含了构建过程的所有配置信息。你可以修改这个文件来定制打包过程。
如果我们执行下面代码
pyinstaller main.py
或者指定更多的参数的代码
pyinstaller --onefile --icon=your_icon.ico main.py
PyInstaller 都会生成一个 .spec
文件,然后可以编辑 main.spec
文件,例如:
# main.spec # -*- mode: python ; coding: utf-8 -*- block_cipher = None a = Analysis(['main.py'], pathex=['/path/to/your/script'], # 项目的路径 binaries=[], datas=[('data/*.txt', 'data')], # 资源文件 hiddenimports=['some_module'], # 隐藏导入模块 hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False) pyz = PYZ(a.pure) exe = EXE(pyz, a.scripts, a.binaries, a.zipfiles, a.datas, name='main', debug=False, strip=False, upx=True, console=False) # 设置是否显示控制台
一般情况下,我们的.spec文件会比这个更多的内容,虽然原则上.spec文件支持跨平台的配置,不过我们在实际中往往根据不同的平台配置特定的.spec文件。
你可以手动修改 .spec
文件来添加资源文件、修改导入模块、定制输出路径等。
你可以通过编辑.spec
文件,在EXE、COLLECT和BUNDLE块下添加一个name=
,为PyInstaller提供一个更好的名字,以便为应用程序(和dist
文件夹)使用。
EXE下的名字是可执行文件的名字,BUNDLE下的名字是应用程序包的名字。
修改完成后,执行以下命令来重新打包:
解决常见问题
-
缺少依赖库:如果打包后运行时出现缺少模块的错误,可以尝试将缺少的模块加入到
hiddenimports
中,或者通过--hidden-import
选项指定: - 大文件:如果使用
--onefile
时打包后的文件太大,考虑使用--onedir
或通过压缩文件等方法进行优化。 - 处理资源文件:如果你的应用程序包含非 Python 代码的资源(如图像、配置文件、数据文件等),你需要通过
--add-data
选项指定资源文件的路径,或者在.spec
文件中修改datas
选项。 -
动态链接库,如果你的应用程序依赖于特定的动态链接库(如 DLL 文件或
.so
文件),你需要将这些库包含到打包中。可以在.spec
文件的binaries
选项中指定: -
多平台支持:PyInstaller 支持 Windows、Linux 和 macOS 等多个平台,但需要在相应的平台上打包。例如,如果你要为 Windows 用户创建可执行文件,最好在 Windows 上运行 PyInstaller 来生成 Windows 的
.exe
文件。如果在 macOS 上打包,生成的文件只能在 macOS 上运行。
PyInstaller 有丰富的文档,提供了详细的使用说明和常见问题解答,你可以通过以下链接访问:
- PyInstaller 官方文档:https://pyinstaller.readthedocs.io
- GitHub 代码库:https://github.com/pyinstaller/pyinstaller
这些文档和资源能帮助你深入了解 PyInstaller 的使用方式,并解决在打包过程中可能遇到的问题。
3、WxPython跨平台开发框架中定制不同平台的 .spec文件
前面介绍了,我们推荐使用修改配置.spec文件的方式生成打包文件,并且可以通过定制不同平台的.spec文件,以便实现更好的处理,避免冲突。
对于不同平台的图标处理,我们可以使用代码来判断,.spec文件本身可以编写python的代码的。
import sys import os from pathlib import Path if sys.platform == "win32": icon = "app/images/app.ico" elif sys.platform == "darwin": icon = "app/images/app.icns"
另外我们的业务窗体是动态根据路径解析的,因此在打包的时候,会漏掉的,为了解决这个问题,需要手动把视图部分的模块加载到hidden-import集合中,如下所示。
为了避免手工填写出错和麻烦,使用python函数递归处理目录的视图模块。
def collect_modules(directory): modules = [] for root, dirs, files in os.walk(directory): for file in files: if file.endswith(".py") and file != "__init__.py": # 排除 __init__.py 文件 module_path = os.path.relpath(os.path.join(root, file), directory) module_name = module_path.replace(os.path.sep, ".").replace(".py", "") modules.append(module_name) return modules # 使用动态加载目录下的模块方式 hiddenimports = collect_modules("app/views") hiddenimports = [f"views.{module}" for module in hiddenimports]
这样在修改里面的变量即可。
# Analysis: PyInstaller Analysis object a = Analysis( ["app/main.py"], pathex=[], binaries=[], datas=[ ("app/images/splash.png", "images"), ("app/.env", "."), ("app/settings.ini", "."), ], hiddenimports=hiddenimports, hookspath=[], hooksconfig={}, runtime_hooks=[], excludes=[], noarchive=False, optimize=0, )
1)WIndows平台的打包文件生成
然后指定EXE(用于Window的处理打包)
# PYZ: PyInstaller PYZ object pyz = PYZ(a.pure) # EXE: PyInstaller EXE object exe = EXE( pyz, a.scripts, a.binaries, a.datas, [], name="wxpython_project", debug=False, bootloader_ignore_signals=False, strip=False, upx=True, upx_exclude=[], runtime_tmpdir=None, console=False, # True启用/False禁用命令行窗口 onefile=False, # <-- False取消、True使用 onefile 模式 icon=icon, # <-- 图标路径 disable_windowed_traceback=False, argv_emulation=False, target_arch=None, codesign_identity=None, entitlements_file=None, )
最后在dist目录上生成一个文件,如下所示。
本文件用于Window平台下打包整个项目,生成一个独立的exe文件,包含了所有的依赖和资源文件,文件运行后会在临时目录中解压出来一个文件夹,程序退出后,该文件夹自动移除。
临时目录一般在C:UsersAdministratorAppDataLocalTemp。
如果我们想在Windows平台生成的dist目录中生成一个启动exe,和其他相关的Lib依赖库目录,那么我们可以适当调整下.spec文件,让它可以生成松散结构的文件目录包。
# PYZ: PyInstaller PYZ object pyz = PYZ(a.pure) exe = EXE( pyz, a.scripts, [], exclude_binaries=True, name="wxpython_project", debug=False, bootloader_ignore_signals=False, strip=False, upx=True, console=False, # True启用/False禁用命令行窗口 icon=icon, # <-- 图标路径 disable_windowed_traceback=False, argv_emulation=False, target_arch=None, codesign_identity=None, entitlements_file=None, ) # COLLECT: PyInstaller COLLECT object coll = COLLECT( exe, a.binaries, a.datas, strip=False, upx=True, upx_exclude=[], name="main", )
相当于之前在exe包中的a.binaries 和 a.datas从EXE 构造函数中移到了Collect的构造函数里面了。这样会生成下面的目录结构。
其中_internal目录包含程序的相关依赖包和文件资源。
如果我们需要使用第三方安装包制作工具(如SetupFactory ),那么整合这些文件即可实现安装程序的处理。
2)MacOs下的打包文件生成
如果在MacOS或者Linux下的打包处理,会有所不同。
# 使用 BUNDLE 创建 macOS 应用包 app = BUNDLE( coll, name="wxpython_project.app", icon=icon, bundle_identifier="com.iqidi.wxpython_project", # 推荐设置唯一的 bundle identifier info_plist={ "NSPrincipalClass": "NSApplication", "NSHighResolutionCapable": "True", }, )
生成的 wxpython_project.app 应用程序捆绑包,实际上只是一个特殊的文件夹。
虽然macOS将其显示为一个应用程序,但如果你试图分享它,你实际上将分享数百个单独的文件。为了正确分发应用程序,我们需要某种方式将其打包成一个单一的文件。
如果你以前安装过macOS应用程序,通常情况下你会得到一个磁盘镜像 .dmg
,打开后会显示应用程序包,以及一个指向应用程序文件夹的链接。要安装该应用程序,你只需将其拖到目标上。
为了创建我们的磁盘镜像,我们将使用create-dmg工具。这是一个命令行工具,它提供了一个简单的方法来自动建立磁盘镜像。如果你使用Homebrew,你可以用以下命令安装 create-dmg。
brew install create-dmg
create-dmg
是一个用于 macOS 的命令行工具,用于创建 .dmg
(Disk Image) 文件,这些文件通常用于分发 macOS 应用程序。它简化了创建 .dmg
文件的过程,并允许你自定义磁盘映像的外观和行为。以下是 create-dmg
的一些主要功能和使用方法:
主要功能:
- 简单易用:通过命令行参数,快速创建
.dmg
文件。 - 自定义背景图片:可以为磁盘映像设置自定义背景图片。
- 应用程序放置位置:可以指定应用程序图标在磁盘映像中的位置。
- 图标大小和排列:自定义图标的大小和排列方式。
- 自动挂载和卸载:创建
.dmg
文件时自动挂载和卸载磁盘映像。
以下是一个简单的示例,用于创建一个 .dmg
文件:
create-dmg 'path/to/your/app' 'path/to/output/directory'
常用参数:
create-dmg
提供了多种参数来定制 .dmg
文件。以下是一些常用参数:
--overwrite
:如果输出目录已经存在相同名称的.dmg
文件,使用此参数将其覆盖。--background
:指定.dmg
文件的背景图片。--icon
:设置应用程序图标的位置,格式为name:x,y
,例如:--icon "YourAppName:150,150"
.--icon-size
:设置图标的大小,默认值为 80。--window-size
:设置.dmg
窗口的大小,格式为width,height
,例如:--window-size 600,400
.--title
:设置.dmg
窗口的标题。
以下是一个更复杂的示例,展示了如何使用多个参数来自定义 .dmg
文件:
create-dmg 'path/to/your/app' 'path/to/output/directory' --overwrite --background 'path/to/background.png' --icon 'YourAppName:150,150' --icon-size 100 --window-size 800,600 --title 'YourAppName Installer'