python tkinter图标不显示的原因及完美解决方案

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!用你肉嘟嘟的小手给我点个赞吧。

你可能感兴趣的:(python tkinter图标不显示的原因及完美解决方案)