pyinstaller 完美解决打包图片等资源的问题 您所在的位置:网站首页 tkinter程序打包 pyinstaller 完美解决打包图片等资源的问题

pyinstaller 完美解决打包图片等资源的问题

2023-11-10 02:14| 来源: 网络整理| 查看: 265

概要: 很多人用pyinstaller来打包python程序,但是都头疼图片等非py文件如何打包,或者打包后用原来的相对路径却加载不到。本文提供一种完美解决此类问题的方案,亲测可用且稳定。

pyinstaller可以帮我们的python代码生成单个的可执行文件,方便分享,而不是发几个 py 文件让别人去执行。但是默认情况下,pyinstaller只打包 *.py 文件,对于其它文件,如图片、文本文件等,需要添加额外的参数手动指定加入打包行列,或者更改特定的 spec 文件。

首先,你需要用 pip 安装 pyinstaller

[Can:]$ python3 -m pip install --user pyinstaller

正常的打包命令(-F 参数会只生成一个单独的文件,方便发给别人):

[Can:]$ pyinstaller -F alien_invasion.py

每次用类似命令打包的开始,pyinstaller都会在当前目录生成一个 xxx.spec 文件。这是一个配置文件,本质上跟用命令行没什么差别。如果你用的是 pyinstaller xxx.py 的命令,这个 xxx.spec 文件就会根据当前命令的参数重新生成。如果你先修改这个 xxx.spec 文件,再用 pyinstaller xxx.spec 的命令去执行,就会采用配置文件里的内容。由于 xxx.spec 文件很容易被自动重新生成,所以除非你需要频繁重复打包,那可能会节省一些你输入命令的时间,否则我个人的感觉是直接用命令更方便一些。

那如何才能指定让 图片文件 也可以打包进去呢?我的当前目录下,有两个图片文件:

images/alien.bmp images/ship.bmp

pyinstaller的官方提供了两种方式用来加入额外的文件: pyinstaller官方参数 其中 --add-data 用于普通文件, --add-binary 用于二进制文件。我们的bmp文件要用前面这个哦,后面这个主要用于dll之类的第三方库。

官方甚至给了几个例子: pyinstaller参数使用官方举例 用法的注意事项有几个:

--add-data后面可以加=,也可以直接空格,效果一样。--add-data后面的参数值里,有两部分,用 : 或者 ; 隔开,前面是指打包前文件所在的位置,后面是指打包后你希望文件所在的位置。比如样例里的: --add-data="image1.png:img" 的意思是把当前目录里一个叫 “image1.png” 的文件打包进去,但是放在打包后的 “img” 目录下,也就是变成 img/image1.png,文件名不变。--add-data 可以用好多次,也就是可以一个文件一个文件地加。但也可以整个文件夹一起加,这个官方文档没有说,我自己试过后可以这么用: --add-data 'images:images' 也就是把当前目录下 images 文件夹里的文件都打包进去,打包后的目录也是 images 一样的文件夹下。

最终的命令看起来是这个样子:

[Can:]$ pyinstaller --add-data 'images:images' -wF alien_invasion.py

其中这里的w是把运行的时候的命令行窗口隐藏,这个官方文档里有说,就不多解释了。

然而这就完事了吗?并没有! (如果你这就高高兴兴地带着生成的 *.app 或者 *.exe 去运行的话,大概率还是会出现图片文件没有找到的情况)

原因是当app运行的时候,会先把资源解压到一个系统的临时目录,包括打包进去的 images文件等,在MAC系统上,会是类似于这样的目录:

/var/folders/gq/scyg5_zj2zz7vmmdr5v6tj2w0000gn/T/_MEI0ORo7m/

里面的内容大概是这样子的:

pygame/ numpy/ images/ include/ lib/ build/ dist/ alien_invasion.spec base_binary.zip 无数个 *.so 文件 好多个 *.dylib 文件

基本上都是依赖库或者半编译的中间文件,这些我们都不关心,我们关心的是 images 文件夹的确在这里,里面的文件也都在。但是坑爹的是,此时的代码并不能直接通过 “images/alien.bmp” 这样的相对路径来得到文件(应该是因为本身程序就不在这里,所以自然引用不到)。

于是,遭受挫折的你,不得不回去改代码,这事已经不是通过命令加参数能够解决的了,至少 pyinstaller 不行。

这时需要用到 sys 的一个半私有的属性 sys._MEIPASS,根据官方的说明,它是 pyinstaller 运行时创建的临时目录的绝对路径。需要在程序运行时判断当前是不是有这个属性,如果有的话,生成临时目录下图片的绝对路径,如果没有的话,那就直接返回当前程序的绝对路径:

def get_resource_path(relative_path): if hasattr(sys, '_MEIPASS'): return os.path.join(sys._MEIPASS, relative_path) return os.path.join(os.path.abspath("."), relative_path)

然后在真正加载图片的代码里:

self.image = pygame.image.load(get_resource_path('images/ship.bmp'))

然后重新用pyinstaller打包一次就可以了:

[Can:]$ pyinstaller --add-data 'images:images' -wF alien_invasion.py

就可以了,这样不管是在什么环境下,都能够正常加载到图片资源,管理起来的时候也可以仍然按照相对路径去管理。

唯一让强近症者抓狂的可能就是,由于是访问了半私有的属性,pycharm会有个warning。这个目前也没有更好的方法。(照道理来说,用 __file__ 也可以解决问题,但本质上没什么不同,我没试,看官方文档应该也是可以的,好处是 warning 可能可以消掉)

如果有更漂亮的解决方案,可以留言,谢谢!

参考资料: https://pyinstaller.readthedocs.io/en/stable/usage.html https://pyinstaller.readthedocs.io/en/stable/runtime-information.html



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有