因个人需要尝试把pyqt开发的小软件打包成exe给别人使用,网上搜了下打包很简单,就一行代码,但是暗坑比较多,特此记录。
首先pip install pyinstaller,一定要安装在软件依赖的环境中
pyinstaller [参数] [要打包的程序.py]
参数说明:
-icon=图标路径
-F 打包成一个exe文件
-w 使用窗口,无控制台
-c 使用控制台,无窗口
-D 创建一个目录,里面包含exe以及其他一些依赖性文件
pyinstaller -h 来查看参数
比如我的pyqt软件,pyinstaller -Fw D:\xxx.py,会在当前目录(不一定是py文件所在目录)下创建一个dist文件夹,里面包含生成的exe文件
软件初步开发完后尝试打包,一次成功,29M。但是无法打开,在命令行执行后显示
ImportError:No module named 'sip'
网上查阅说需要在代码里加一行
import sip
提示找不到sip,于是通过pip安装。
解决之后又报了另一个错
需要去python环境里把platforms文件夹复制过来
注:出现以上问题可能是PyQt版本比较老的原因,重新安装之后没有出现错误
后续开发中又调了一个第三方库,打包时提示找不到nltk
Unable to find "nltk_data" when adding binary and data files
nltk是一个自然语言处理库,看了一下第三方库源码,确实有import nltk,于是下载nltk_data(最好从别人网盘下载,速度快),然后通过下面一行代码查看路径(已经pip安装过nltk),把下载的nltk_data放到任意一个路径即可。
from nltk.book import *
之后仍然不能解决问题,还需要到pyinstaller安装位置中修改hook-nltk.py,例如我的在D:\Anaconda3\envs\py36\Lib\site-packages\PyInstaller\hooks。将其中的
for p in nltk.data.path:
datas.append((p, "nltk_data"))
修改为nltk_data实际路径
datas.append((nltk_data实际路径, "nltk_data"))
nltk_data的实际路径也可以通过以下两行代码找到
from nltk import data
print(data.find('.'))
之后成功打包,一看大小傻眼了,600M!!!
打开速度巨慢,而且出现了warning
warning不影响运行,没有管。网上有类似文章,没有尝试。比如
http://www.classnotes.cn/3647.html
https://www.dazhuanlan.com/2020/01/18/5e231d41ca9dd/
考虑到我的程序在没下载nltk_data之前也能正常运行,说明nltk并不是必须的。查看调用的第三方库源码发现,nltk是在函数内部import的,而该函数我并不会调用到,于是修改第三方库源码把nltk部分注释掉,但是打包之后仍然是600M。一气之下pip uninstall nltk卸载,仍然能成功打包,大小减小到了200M+
检查打包过程输出的信息发现,pandas也被打包到了exe中,而我压根就没有用到。这说明,PyInstaller在打包时会把一些环境中存在但是程序没有调用的包一起打包进去。网上查资料也证实了我的猜想。
普遍推荐的做法是:为需要打包的程序单独创建一个不包含任何多余包的虚拟环境。用pipenv也好,virtualenv也好,anaconda也好,首先创建一个纯净的python环境,然后只安装需要的包以及PyInstaller
我用的anaconda,打包之后的程序仅有37M!!!虽然对于一个小软件来说仍然有点大,但是与原来的600M相比,已经足够使人感动了。而且没有任何error和warning(可能与新版本PyQt有关)
还遇到一个文件路径问题。调用的第三方库需要读取本地文件,代码中使用的是os.path.dirname(__file__)指定的相对路径,PyInstaller打包时会修改为以_MEI开头的缓存文件夹路径,因此会报找不到文件的错误,类似于
No such or directory: C:\user\xxx\AppData\Local\Temp\_MEI11023\xxx.txt
解决方法是用os.path.dirname(sys.executable)替换
sys.executable是当前运行目录,就是打包好的exe所在目录,因此把所需文件拷贝过来就可以正常读取
(在用python开发过程中,sys.executable是python解释器所在路径)