前言
python打包成exe程序的方法有不少,但最常用的还是pyinstaller,打包python程序主要是为了给那些不懂python的人用的,而且如果是像pyqt一类的图形界面程序,打包一下也会变得更易用。
安装
建议被打包的程序都放在一个虚拟环境中,不论是用venv,pipenv还是conda都是可以的,然后在虚拟环境中安装pyinstaller。
pip install pyinstaller
使用
命令行进入虚拟环境,使用pyinstaller就是一条命令行的事,命令的格式如下:pyinstaller [选项] xxx.py
其中选项可以用命令 pyinstaller -h
查看,常用命令如下:
选项 | 功能 |
---|---|
-h, --help | 帮助 |
--distpath DIR | 生成exe程序的位置,默认在当前目录的dist文件下 |
--workpath WORKPATH | 生成临时文件的位置,默认在当前目录的build文件下 |
--clean | 清除生成的文件(重新生成前先清除) |
-D, --onedir | 打包成一个包含exe程序的文件夹 |
-F, --onefile | 打包成一个独立的exe程序(运行起来会慢很多) |
--hidden-import MODULENAME | 有些依赖包是动态导入的需手动导入告诉pyinstaller |
-d {all,imports,bootloader,noarchive} | 生成debug版本 |
-w, --windowed, --noconsole | 隐藏命令行窗口 |
-i <FILE.ico or FILE.exe,ID or FILE.icns> | 为程序添加ico图标 |
-n NAME, --name NAME | 为exe程序指定名称,默认和py程序名一样 |
打包完成后会生成三个文件(夹):build,dist,以及一个.spec文件,这个文件记录你的打包参数,可以直接在里面修改参数,然后执行以下命令直接打包,对于依赖复杂的程序这么做更方便,毕竟谁也不想每次打包都输那么长的命令行。pyinstaller xxx.spec
至于spec文件具体怎么用参考官方文档。
一些坑
打包pyqt程序
虽然理论上程序打包很简单,但实际上可能会遇到各种问题,在打包pyqt程序时如果用的是动态导入ui文件,那么就得加上
--hidden-import PySide2.QtXml
并且把ui文件也一同放入exe文件夹内,因为程序并不会打包ui文件;如果程序用到了什么像图标,图片之类的静态文件,也需要都放到exe文件夹下。单个程序还是文件夹
程序打包默认打包成一个文件夹,使用
-F
参数可以打包成一个单独的exe程序,但经测试,单独一个程序启动速度起码慢了10倍不止,所以建议还是默认文件夹方式,但文件夹太乱了怎么办,可以采取后面的方法整理一下。另外我发现我的程序在打包成一个独立的exe的情况下,隐藏命令行窗口的话,程序中间有个命令无法正常运行,但是打包成文件夹就可以正常运行,由此可见打包成文件夹的容错率更大。
新建一个runtimehook.py文件:
import sys
import os
currentdir = os.path.dirname(sys.argv[0])
libdir = os.path.join(currentdir, "lib")
print(currentdir)
sys.path.append(libdir)
os.environ['path'] += ';./lib'
然后打包时加上参数 --runtime-hook="runtimehook.py"
,这样就可以把除了下面的几个文件外的库文件放到一个lib
文件夹中了。
base_library.zip
xxxx.exe
xxxx.exe.manifest
python37.dll
这个方法摘自白月黑羽,注意文件夹不能放到lib里。
新建一个快捷方式或者符号链接,可以参照我另一篇文章。不过在自己电脑新建的快捷方式不能打包发给别人,因为绝对路径不一样,如果用符号链接的话在打成压缩包时会被打回原型。
所以我们可以仿照某些软件安装包的样子做一个自解压的压缩包,在解压软件安装时加上生成桌面快捷方式的选项。打包可以用一些打包工具(比如某些压缩工具就带这个功能),直接打包生成自解压的exe文件然后还带桌面快捷方式,这样显得更高大上。
隐藏命令行窗口
- 如果你是gui程序自然不希望出现命令行,那么加上
-w
参数就行了。 - 另外隐藏命令行的话,就无法查看报错信息,那么只能在程序里捕捉异常然后通过gui的方法把错误显示出来。但这种错误显示通常不能获取多少有用信息,只是给用户看的。
- 有时候程序隐藏了窗口,但运行时总有黑黑的命令行窗口不时的闪现出来,网上查找到的弹窗多半是因为程序中引用了
os.system
引起的,但其实任何可能调用命令行窗口的程序命令都有可能导致弹窗。 - 比如我的程序因为黑窗口闪现太快肉眼无法捕捉,为了看清这个窗口究竟是什么,我利用录屏一帧帧查看发现是ffmpeg.exe的窗口,好吧,我的程序的pydub库的确用到了它,但怎么让它消失呢,我找到了一篇参考文章。因为pydub库的源码是直接使用subprocess调用ffmpeg.exe和ffprobe.exe的,只需要在audio_segment.py和utils.py文件中调用subprocess.Popen()时指定参数 shell=True, stdin=subprocess.PIPE 即可。
参考
Pyinstaller文档:https://pyinstaller.readthedocs.io/en/stable/
白月黑羽教Python:http://www.python3.vip/doc/tutorial/python/level2/gui/qt_04/
此处评论已关闭