tkinter的按钮、标签等都可以用图标显示,但是可能存在不显示的问题。当然包括多种原因,比如路径不对、图片格式不对等。这些都不是本文要解决的。本文所说的不显示具体指,
1.在一个函数内生成图标
def createImageProcess():
scan1 = icon("skip_forward_16x16.gif").get()
btn1 = tk.Button(master,image = scan1, text="new")
(请暂且忽略icon,这是我自己定义的一个图片导入类。)
2.在一个类的方法内生成图标
class A:
def createImageProcess(self):
scan1 = icon("skip_forward_16x16.gif").get()
btn1 = tk.Button(master,image = scan1, text="new")
3.这一种最为诡异。大概的情况是我已经生成了有图标的按钮,然后再在界面上修改时,原有的图标——嗖——不见了。就像下面这样
1
2
在图1中图标还在,等生成第二个tab的时候,图标不见了(请忽略图2中的蓝色,那是我解决问题以后出现的)
对于前两种情况产生的原因已经有一些博文解释了,也给出了解决方案。原因是python的垃圾回收机制。当函数运行结束后,其中的局部变量被回收了,图片被销毁。所以,解决方案就是阻止图片被销毁。对于第一种,将图片提到函数外面
scan1 = icon("skip_forward_16x16.gif").get()
def createImageProcess():
btn1 = tk.Button(master,image = scan1, text="new")
第二种,将图片写成类属性
class A:
def createImageProcess(self):
self.scan1 = icon("skip_forward_16x16.gif").get()
btn1 = tk.Button(master,image = self.scan1, text="new")
但是对于第三种,怕是比较诡异了。我是头一次见到,大概你也是吧。我猜想也还是被回收了。
如果是小一点的程序,用上边的方法应该够了。但是对于一个复杂的程序,上边的方法始终感觉有些不完美。所以,完美解决这个问题也就成了本文的主旨。
要想阻止被回收,一种方法是定义全局变量。当然这绝对不是完美的方案。还有一种就是用一个单例。单例是替换全局变量的一个套路解法。但是这里还有一些变化。因为我们的图片有很多个。所以需要修改一下。先上代码
import os
import tkinter as tk
class icon(object):
_instance = {}
def __init__(self, name):
self.path = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "icons",
name)
@classmethod
def instance(cls, name):
if name not in icon._instance.keys():
i = icon(name)
icon._instance[name] = i.get()
return icon._instance[name]
def get(self):
return tk.PhotoImage(file = self.path)
这是一个基础版本的单例写法。不同之处在于instance方法中传入了图片名称。用一个公有变量_instance保存了所有已导入的图片。当已经导入时,直接返回。没有导入时,才用tk.PhotoImage导入。
这个方案用起来很方便。所有的图片保存在icons文件夹下。导入的时候只需要
tk.Button(master,
image = icon.instance("file_(add)_16x16.gif"),
text="new")
随便在哪个函数、哪个类的方法里用,都不存在问题了。
要说这个方案也存在一点瑕疵。在多线程的时候图片可能重复导入。要修改得加线程锁。但是,我们只是读取图片。重复导入一张图片也没什么大碍。简简单单的挺美。
Beautiful!用你肉嘟嘟的小手给我点个赞吧。
网友评论