写在前面:本人对linux系统操作不熟悉,由于项目需求,需要对树莓派上的多个.py文件进行打包,最终形成一个EXE可执行文件。过程非常曲折,历经九九八十一难可谓是打包成功了,走了很多弯路,Linux系统也确实有很多需要注意和踩坑的点,现把我的解决方法整理记录一下,以便后续遇到同样的问题,希望也能帮助到和那些我一样绝望的uu们。
本机Linux系统打包基本配置
- Linux系统内核版本号:Linux raspberrypi 5.15.84-v7l (v7l表示32位)
- 编译器版本信息:Raspbian 10.2.1
- python使用版本:python 3.9.2
- pyinstaller使用的版本:pyinstaller 5.5
- 不清楚如何查询版本号的参考:查看树莓派系统及版本
- 查询python版本:
python --version
- 查询pyinstaller版本同理,也可以在终端使用:
pyinstaller -v
演示:
这里要注意的一点:下载的pyinstaller版本需要支持你的python版本,否则打包不成功。官网(pyinstaller.org)最新的pyinstaller是5.8.0,但是它只支持python3.7及以上的打包,如果你的python版本过低,就会打包不成功。
使用pyinstaller打包成exe
阶段一:完成打包操作
可以使用python定位到pyinstaller打包也可以直接pyinstaller打包,我试过都可以打包成功。
方法一:
1.先定位到pyinstaller(我解压放在了桌面上)文件夹中的bootloader文件夹上,因为这里面有waf文件,方便后续打包失败彻底清除。
prototype2@raspberrypi:~s cd /home/prototype2/Desktop/pyinstaller/bootloader
这里我是直接cd 然后右击bootloader文件夹复制其路径粘上去,这样更方便
2.这里就可以开始打包了
prototype2@raspberrypi:~/Desktop/pyinstaller/bootloader s pyinstaller -F /home/prototype2/Desktop/version_0.0.4/breath_python/websocket_connect.py
这是直接使用pyinstaller打包,语法:pyinstaller -F main.py(主程序运行的绝对路径)
这就打包成功了,由于我们是在bootloader文件夹下打包的,所以最后会在该文件夹中生成两个文件夹,分别是dist和build文件夹,打开dist文件夹里面就生成了一个exe文件,如果到这里打开,你的程序就可以正常运行了,那么恭喜你,如果没有,请继续看下去。
方法二:
1.第一步同样定位到pyinstaller(我解压放在了桌面上)文件夹中的bootloader文件夹上。
prototype2@raspberrypi:~s cd /home/prototype2/Desktop/pyinstaller/bootloader
2.打包命令:
prototype2@raspberrypi:~/Desktop/pyinstaller/bootloader s python /home/prototype2/Desktop/pyinstaller/PyInstaller -F /home/prototype2/Desktop/version_0.0.4/breath_python/websocket_connect.py
这是python调用pyinstaller包进行打包,语法:python PyInstaller的绝对路径 -F main.py的绝对路径
这里PyInstaller(和bootloader在同一个目录下)是在我解压的pyinstaller文件夹中,我把这个文件夹放在了桌面上,所以它的路径如上述。
到这里,打包完成了,这个环节有几点要特别注意:
- 多个.py文件有一个主程序文件(我这里是websocket_connect.py),该主程序文件调用了很多其他.py文件,那么文件放置的位置一定要和程序里调用的.py文件路径一致,如果直接调用某个.py文件,那么主程序文件一定是和这个文件在同一个目录下,否则路径会错误找不到。
- 如果你在打包时告诉你权限不够,那就在程序的第一步提前加上
sudo -s
就可以了。 - 如果最终打包终端显示已经成功了,但是dist文件里面什么都没有,试试
sudo reboot
重启树莓派,它卡了,我出现过好几次这样的情况。 - 前提没有打包之前程序是可以正常运行的,否则打包后也无法成功。
阶段二 点击打包后的exe文件无法打开解决方法
故事从这里才真正开始,打包后遇到的第一个问题就是,打开dist文件后点击exe,程序无法执行,得到终端报错信息:
Couldn’t open res/image/background.png
Couldn't open res/image/background.png
其实,这个问题挺简单的,是路径问题,只不过我当时不清楚花了很长时间去解决,还把代码中的相对路径改成绝对路径等等,不推荐这样,尤其使用的资源特别多的时候。
如果生成的exe文件打开报错:Couldn’t open xxxx ,很大可能就是没有把需要的图片、音频等资源放在生成的exe同目录下,导致其无法打开该资源,这里还是要注意要与程序中调用路径相同。
解决:把res这个文件夹和其他主程序下要用到的资源一起拷贝到exe同目录下,相当于阶段一所说的第一点注意点。
补充:如果文件复制到exe所在的dist文件夹权限不够,可以使用命令cp复制,也可以直接修改该文件夹的权限sudo chmod 777 /home/prototype2/Desktop/pyinstaller/bootloader/dist
即sudo chmod 777 dist的绝对路径,这样就可以粘过去了。
至此,点击exe文件就可以打开界面了,其他功能都能正常运行,但是,pygame模块,一点击游戏开始终端闪退,页面显示连接不上。
阶段三 点击打包后的exe文件运行pygame游戏模块闪退解决方案
游戏一点击程序就闪退,几毫秒的事情,终端也退出了,页面也无法再次连接,通过录屏截取了程序报错信息:
Fatal Python error: pygame_parachute:(pygame_parachute) Segmentation Fault
Python runtime state: initialized
Traceback (most recent call last):
File “pygame/pkgdata.py”,line 67,in getResource
FileNotFoundError:[Errno 2] No such file or directory:‘/tmp/_MEIHEonzn/pygame/freesansbold.ttf’
Fatal Python error: pygame_parachute:(pygame_parachute) Segmentation Fault
Python runtime state: initialized
Traceback (most recent call last):
File "pygame/pkgdata.py",line 67,in getResource
FileNotFoundError:[Errno 2] No such file or directory:‘/tmp/_MEIHEonzn/pygame/freesansbold.ttf'
这一part走了很多弯路,尝试很多方法,试过搜索tmp文件夹和全盘搜索该.ttf文件,创建复制该文件夹,.ttf会出现感叹号不可用,也试过修改font(None,xx)为font(‘Arial’,xx)等等,依旧同样的问题,大致原因是pyinstaller有些资源是默认不打包进去的,所以就出现了不完整,找不到等情况。
解决方案:手动打包添加缺失文件 --add-binary<Src:Dest或者Src;Dest>
参数解释:
-
Src:打包缺失的文件在本机的位置
[ 最好使用绝对路径,我是在全机搜索的该文件,因为用的python3(之前和python3.9进行了软链接),所以使用的是…(安装python的位置,根据情况而定)/python3/dist-packages/pygame/freesansbold.ttf ] -
Dest: 文件运行时,需要动态拷贝到的相对目录
[ 如这里提示的是/tmp/_MEIHEonzn/pygame/freesansbold.ttf,其中/tmp/_MEIHEonzn/是每次打包执行的绝对路径开始的地方,_MEIHEonzn每次打包都会自动随机生成一个,所以Dest在这里只需要输入相对路径;对于上述,Dest应为pygame ] -
Linux系统分隔符用的是:
-
Windows系统分隔符用的是;
所以,本机添加的代码为`–add-binary=“/usr/lib/python3/dist-packages/pygame/freesansbold.ttf:pygame”
添加该参数代码目的:把本机的 freesansbold.ttf 文件打包到执行文件中,并且运行时动态释放到 相对目录 pygame 中,从而解决 错误提示 的文件找不到问题
更详细的说明参考python3 pyinstaller 打包后执行文件运行错误 No such file or directory 和 Cannot load native module 解决方法
接下来我就使用以下代码完成打包:
1.定位到bootloader文件夹
prototype2@raspberrypi:~s cd /home/prototype2/Desktop/pyinstaller/bootloader
2.进行打包并添加缺失文件
prototype2@raspberrypi:~/Desktop/pyinstaller/bootloader s pyinstaller -F /home/prototype2/Desktop/version_0.0.4/breath_python/websocket_connect.py --add-bianry"/usr/lib/python3/dist-packages/pygame/freesansbold.ttf:pygame"
到此,打开dist文件,别忘了把其他资源文件拷过来(阶段二),然后打开exe文件,程序其他模块正常,点击游戏模块也不闪退了,主程序也不关闭了,但是游戏窗口是黑屏,只有一个头部名称,吹数据在终端都有显示,就是游戏模块无法显示!!!
这过程非常曲折,每当我准备放弃的时候,又出来一线生机,让我忍不住跟下去。
阶段四 打包后pygame游戏界面无法显示解决方法
目前其他模块正常,游戏界面无显示,终端也能出数据,于是截取了报错信息:
TvpeError:expected str. bytes or os.PathLike obiect, not BytesIo
大致问题还是上述字体问题并没有彻底解决,系统找不到相应字体,这里直接附上我的解决方法:
思路:pygame游戏模块写的代码中用到了pygame .font .SysFont (None, xx)全部替换掉系统目前已经有的字体,这样就不会出现找不到的情况了。
- 查看树莓派系统中已有字体命令:
fc-list
截取了一部分:
由于我需要的是中文显示界面字体,所以使用fc-list :lang=zh
查看系统中安装的中文字体,中文字体需要自己下载,我之前下载过
从中随便选择一个字体,比如
前面/usr/…/wqy/是/wqy-zenhei.ttc的文件存储路径,可以从这个路径里找到需要字体.ttc/.ttf文件,后面从.ttc冒号后面到style冒号前面整个部分:文泉…,WenQuanYi Zen Hei是字体名称
2.将程序报错中的文件修改代码,替换None
修改哪些程序在报错信息里,按照上面的报错信息,我修改的是source/game.py和source/widgets.py和source/button.py因为这几个地方都用到了font,找到对应位置将None替换成你需要的字体名称即可。
到这步,程序就正常运行了,各模块运行正常,打包完成,不过唯一一点,英文按钮并没有转换为中文,因为虽然换了字体,代码中名称设置依旧为英文的,我又将页面显示的英文字如“Restart”“Exit”(这些都是在pygme自定义的名称)在程序中全手动修改成中文,最后显示出来才会是中文,部分代码修改如上图。
- 在widgets.py文件中将原来使用的字体文件路径替换成现在使用的字体路径,在上述字体路径找到复制到目前字体的目录上就可以使用相对路径,复制无权限上面有介绍,chmod 777 … ,也可以直接使用绝对路径,修改如下,不过这步不换也不影响的:
如果出现第三阶段找不到.ttf文件的问题时,可以直接用第四阶段的方法一定是可以的,后来又尝试不加–add-binary=“…:…”,直接修改None为系统字体,需要显示的改为中文命名,打包成功,所以完成第四阶段设置最后可直接用该命令打包:pyinstaller -F /home/.../main.py
补充:
每次打包失败,需要重新打包时,需要把生成的dist和build文件删除,同上也是定位到bootloader文件夹,并用里面的waf文件清理打包文件
rm -rf dist
rm -rf build
python ./waf distclean all
每次重新打包前都要操作一下
至此,打包工作结束!!!
如果有任何问题,欢迎大家交流指正,希望给打包的uu们提供一些帮助,upupup!!!
其他参考链接:https://www.cnblogs.com/aby321/p/13628330.html
https://blog.csdn.net/yuxingdeyun/article/details/120013000
https://github.com/pyinstaller/pyinstaller/tree/develop/PyInstaller