前言

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/

Last modification:April 13th, 2020 at 01:33 pm