众所周知,pyinstaller常规的打包方法为
pyinstaller -w -F xxx.py
其中,-w 表示在执行文件时不打开命令行;-F 表示生成单个文件(file)。不加这两个参数,会默认为打开命令行,并把程序生成在一个文件夹中。
通过pyinstaller -F 打包程序有以下几个问题
- 生成文件的图标是固定的,打开文件后窗口的图标也是固定的,而且这两个图标并不相同
- 生成文件过大,几百兆起步
- 生成单文件,则启动速度很慢,几秒钟起步
- 如果使用窗口程序的同时还用了多进程,会使得每个进程都创建一个窗口
接下来,对这四个问题逐一解决。
图标问题
pyinstaller提供了-i 参数,可以用自己的图标来替换
pyinstaller -F -w -i ./yyy.ico xxx.py
但这个图标仅仅表示生成可执行文件的图标,而打开软件后,窗口左上角的图标并非pyinstaller 规定的,而是Python所调用的窗口模块所决定的。以tkinter 为例,可通过调用iconbitmap 来规定
win.iconbitmap("yyy.ico")
在使用pyinstaller 打包之后,尽管将图标文件放在了可执行文件所在的文件夹里,但并没有什么卵用。换言之,打包之后的程序可能找不到ico 文件,所以一个解决方案是,将ico 文件转换为其他可以找到的文件。
具体方法是,将ico 文件编码为字符
import base64
with open("yyy.ico","rb") as i:
b64str = base64.b64encode(i.read())
with open("ico.txt","ab+") as f:
f.write(b64str)
然后在Python的窗口程序中,将ico.txt 打开,复制其中的内容,将其作为字符串赋给一个变量,设这个变量为ICON_TXT ,然后将
import tkinter as tk
from icon import Icon
import base64
with open("ico.txt","rb") as tmp:
ICON_TXT = tmp.read()
root = tk.Frame()
with open('tmp.ico','wb') as tmp:
tmp.write(base64.b64decode(ICON_TXT))
root.iconbitmap('tmp.ico')
os.remove('tmp.ico')
生成文件过大的问题
原因很简单,是因为打包的过程中将大量不需要的模块一同打进去了。网上流传的解决方案几乎都是用conda 创建一个Python环境,从而只下载自己想要的包。
但网传的大部分见解都语焉不详,就算用conda 新建一个python环境,同样也会在创建过程中载入大量不需要的模块,所以问题的关键是创建一个纯净的Python环境。
conda 当然是可以做到的,其命令格式为
conda create --name envname --file env.txt
其中,envname 为想要创建的虚拟环境的名字;--file 说明conda将遵照一个文件来创建Python环境,后面跟着的env.txt 就是这个文件,其内容为
@EXPLICIT
https://repo.anaconda.com/pkgs/main/win-64/python-3.9.7-h6244533_1.tar.bz2
https://repo.anaconda.com/pkgs/main/win-64/pip-22.1.2-py39haa95532_0.tar.bz2
这两个网址分别是python3.9 和其对应的pip 的网址,如果想安装其他模块或者不同的版本,当然也可以修改。
如果将要打包的项目需要用到其他模块,需要在conda环境中,用pip来进行安装。
> conda activate envname
> pip install numpy
> pip install pyinstaller
而且,务必用pip 重新安装一次pyinstaller ,这样保证在这个纯净的python环境中调用的也是一个纯净的pyinstaller。
对比之下,使用普通conda环境打包的独立可执行文件233MB,使用纯净conda环境输出独立可执行文件22MB,可以说效果相当明显了。
打开速度过慢问题
所谓单文件版,可以简单地理解为把程序做了一个压缩,然后在点击这个可执行文件后,将压缩好的这些文件解压出来,解压之后再正常使用。
所以打开速度过慢的主要原因就是文件太大了,一个几百兆的文件和一个几十兆的文件,解压速度不可同日而语。在这个意义上来讲,解决了生成文件过大的问题,也就相当于解决了打开速度慢的问题。
那么,有没有更快的办法呢?
答案当然是有滴,只要找到一种比较快的解压方法就可以了,而封包工具还是比较多的,比如enigama virtualbox。
首先,用pyinstaller -D 的方法,将python代码以文件夹的形式打包。
pyinstaller -w -D test.py
然后,打开enigma virtualbox,设置好输入和输出的可执行文件,把pyinstaller生成的文件夹递归加入,然后点击打包,就可以生成单文件了。
最后尽管输出的文件比pyinstaller打包的单文件要稍大一些,但启动时的速度快了很多。
多线程问题
这个问题反而很好解决,只需在程序入口添加一条语句
import multiprocessing
if __name__ == '__main__':
multiprocessing.freeze_support()
关于freeze_support 的用法,在Python的官方文档上有写,是专门用于冻结multiprocessing 程序生成Windows可执行文件的。
而且标准库文档还特意强调,在 py2exe, PyInstaller 和 cx_Freeze 上测试通过。
|