问题描述
在pyqt或tkinter里面用到图片的时候,pyinstaller打包的程序不能把图片资源弄上,也就是图片无法显示。
解决思路
(Demo https://github.com/Duola-li/PyinstallerWithPicture)
有些童鞋发邮件问我详细,我干脆在这里重新详细说一下吧。
把图片用base64编码转换成.py文件里面的变量,程序在使用图片前先把相关变量解码,并保存为本地的临时图片,然后即可正常在程序中show,程序加载后就可以删除本地的临时图片。
这是我的原话,再详细一点。
pyinstaller打包不打图片,但是.py文件肯定会打。那么把图片用.py文件保存就是解决问题的核心。也就是把图片用base64编码保存在文件里面。
首先,base64编码是一个用途很广编码方式,它是把二进制数据(0101010,计算机存的东西都是二进制数据),编码成一串可读的字符串。具体可以百度一下。我们这里把一张图片one.png,读取字节流(二进制流)然后base64编码,让图片变成了一串字符串iVBOR..........。很长,一般来说编码之后的大小是之前的3/4倍。这张图片3kb,然后把它base64编码保存到img变量,写出one_png.py文件,大小为4kb。如下图所示。
那么怎么把图片编码呢?可以直接用我下文中给出的Pic2py.py文件。
接下来就是在文件中导入你刚刚生成的one_png.py文件,因为每个图片文件里面都是img变量,所以导入的时候可以用别名,这是python的基本用法了,不再多说。
from one_png import img as png1
from two_png import img as png2
from com_png import img as com
from socket_png import img as socket
from win_png import img as win
然后在主程序中,先把图片编码变量解码保存为图片(参考代码见具体方法里面的第二步,在程序中引用)。这时候图片就有了,你想存在哪里就存在哪里,想怎么用就怎么用,用完想删除就删除。
pyqt的解决方法2:一般pyqt都是用desinger来设计界面,哪里可以把图片生产资源文件,也就是qrc,然后用python自带的工具pyrcc 把他转换成py文件调用即可。详见https://blog.csdn.net/wn0112/article/details/47973953。
具体方法
1 Pic2py.py : 图片转为.py
假设我们需要用的图片是 :"one.png", "two.png", "com.png", "socket.png", "win.png"。现在我们用下面这个Pic2py.py工具,把图片转换成pyinstaller能够识别的 .py 后缀文件。
图片放到Pic2py.py的同级目录下
# -*- coding: utf-8 -*-
# @Time : 2018/6/6 18:29
# @Author : Octan3
# @Email : Octan3@stu.ouc.edu.cn
# @File : Pic2py.py
# @Software: PyCharm
import base64
def pic2py(picture_name):
"""
将图像文件转换为py文件
:param picture_name:
:return:
"""
open_pic = open("%s" % picture_name, 'rb')
b64str = base64.b64encode(open_pic.read())
open_pic.close()
# 注意这边b64str一定要加上.decode()
write_data = 'img = "%s"' % b64str.decode()
f = open('%s.py' % picture_name.replace('.', '_'), 'w+')
f.write(write_data)
f.close()
if __name__ == '__main__':
pics = ["one.png", "two.png", "com.png", "socket.png", "win.png"]
for i in pics:
pic2py(i)
print("ok")
运行完后,我们的到了每个图片对应的py文件
"one_png.py", "two_png.py".....
每个文件里面内容都是一个img变量
img = xxxxxxxx(图片的base64编码)
2 在程序中引用
现在我们在文件中引用这个py图片。加入我们需要用到 one.png 这张图片,那么我们导入相应的one_png.py文件(咳咳,python的包导入不清楚的百度一下)
然后在程序运行后,需要用到这个图片的时候,我们把图片搞出来。解码,写入文件。
就像是我们带着干粮,随时生火做饭。
from one_png import img as one #引入img变量,赋别名为one
...
tmp = open('one.png', 'wb') #创建临时的文件
tmp.write(base64.b64decode(one)) ##把这个one图片解码出来,写入文件中去。
tmp.close()
#现在就能用了,用完(加载到程序里之后)删了就好
#xxxxxx #这里one.png 就已经拿出来了,可以用来。下面就可以对此进行你想要的操作了。
do_something_with(one.png) #做任何你想做的
#xxxxxx
os.remove('one.png') #用完可以删除这个临时图片
这样程序打包的时候,就不需要带着one.png, 而是把one_png.py打包,用的时候程序自己把图片写入文件。
3 打包即可
pyinstaller -F -w xxx.py
4 Demo 实例
说了半天,不如来个实例代码跑一跑有效果。我把最最最简单的demo整理了一下,大家可以参考代码进行修改学习:https://github.com/Duola-li/PyinstallerWithPicture。