为什么在函数中创建的 Tkinter 图像不显示?

Why does Tkinter image not show up if created in a function?

提问人:thomas.winckell 提问时间:5/8/2013 最后编辑:Bryan Oakleythomas.winckell 更新时间:10/11/2023 访问量:93946

问:

此代码有效:

import tkinter

root = tkinter.Tk()
canvas = tkinter.Canvas(root)
canvas.grid(row = 0, column = 0)
photo = tkinter.PhotoImage(file = './test.gif')
canvas.create_image(0, 0, image=photo)
root.mainloop()

它向我展示了图像。

现在,这段代码可以编译,但它没有向我显示图像,我不知道为什么,因为它是同一个代码,在一个类中:

import tkinter

class Test:
    def __init__(self, master):
        canvas = tkinter.Canvas(master)
        canvas.grid(row = 0, column = 0)
        photo = tkinter.PhotoImage(file = './test.gif')
        canvas.create_image(0, 0, image=photo)

root = tkinter.Tk()
test = Test(root)
root.mainloop()
python image tkinter tkinter-canvas

评论

3赞 maszoka 1/31/2021
effbot.org 已关闭。它的要点是图像是通过引用传递的。如果引用是局部变量,则引用的内存将被重用,并且引用将过时。存储图像的变量应与它所在的 Tk gui 对象位于相同的范围内(必须具有相同的生存期)。
0赞 martineau 10/5/2021
@maszoka:可能已关闭,但您仍然可以阅读链接 为什么我的 Tkinter 图像没有出现? 感谢 Internet Archive 的 wayback machineeffbot.org
1赞 martineau 2/26/2022
另请注意,在使用临时 s 的任何地方都可能出现相同的问题,例如在调用序列中,例如 。PhotoImagelabel = Label(image=ImageTk.PhotoImage(Image.fromarray(data)))

答:

121赞 Bryan Oakley 5/8/2013 #1

该变量是一个局部变量,在类实例化后获取垃圾回收。该解决方案涉及保存对照片的引用,例如:photo

self.photo = tkinter.PhotoImage(...)

如果你在谷歌上搜索“tkinter image doesn't display”,第一个结果是这样的:

为什么我的 Tkinter 图像没有出现?

评论

13赞 Tamas Hegedus 12/27/2020
哇。他们认为这是 tkinter 中的错误吗?他们应该这样做。
4赞 Tamas Hegedus 12/27/2020
我发现了一张非常旧的票,已经关闭了,没有修复:bugs.python.org/issue632323
2赞 Aru 1/20/2021
链接无法正常工作 atm,除了使用“全局”之外,还有其他方法吗?
3赞 Bryan Oakley 1/20/2021
@aru:此示例显示了如何在不使用全局的情况下执行此操作。
10赞 martineau 3/16/2021
@TamasHegedus:我同意这是一个错误,但显然在(目前)将近二十年之后,没有人费心去修复。已经记不清有多少次我看到关于它的问题仍然弹出。
0赞 Gabriel 3/1/2019 #2

只需添加为函数内的第一行即可。global photo

评论

8赞 Aran-Fey 6/20/2019
然后,您创建第二个实例,第一个实例丢失了其映像。祝贺。Test
5赞 Sylvester Kruin - try Codidact 10/29/2021
在课堂上绝对没有必要使用。 负责所有这些。globalself.
9赞 TIRTH SHAH 8/26/2020 #3
from tkinter import *
from PIL import ImageTk, Image

root = Tk()

def open_img():
    global img
    path = r"C:\.....\\"
    img = ImageTk.PhotoImage(Image.open(path))
    panel = Label(root, image=img)
    panel.pack(side="bottom", fill="both")
but1 = Button(root, text="click to get the image", command=open_img)
but1.pack()
root.mainloop() 

只需将全局添加到 img 定义中,它就可以工作了

评论

3赞 Sylvester Kruin - try Codidact 1/8/2022
对于只使用函数的程序来说,这个答案很好,但是如果像 OP 的情况一样,你使用一个类,那么就不是要走的路。global
8赞 Faraaz Kurawle 3/17/2022 #4

问题是 Python 通过称为垃圾回收的过程自动删除对变量的引用。解决方案是保存引用或创建新引用。

以下是方法:

  1. 用于增加引用计数并保存引用。self
import tkinter

class Test:
    def __init__(self, master):
        canvas = tkinter.Canvas(master)
        canvas.grid(row = 0, column = 0)
        self.photo = tkinter.PhotoImage(file = './test.gif') # Changes here
        canvas.create_image(0, 0, image=self.photo) # Changes here

root = tkinter.Tk()
test = Test(root)
root.mainloop()
  1. 将其保存到列表中以增加引用计数并保存引用。
import tkinter
l=[]
class Test:

    def __init__(self, master):
        canvas = tkinter.Canvas(master)
        canvas.grid(row = 0, column = 0)
        photo = tkinter.PhotoImage(file = './test.gif')
        l.append(photo)
        canvas.create_image(0, 0, image=photo)

root = tkinter.Tk()
test = Test(root)
root.mainloop()

使用方法 2 时,您可以像我一样创建全局列表,也可以在类中使用列表。两者都行得通。

一些有用的链接:

2赞 Thingamabobs 11/3/2022 #5

根据经验,每当在缩进代码块中创建图像时,都需要保护对该图像的引用。这是因为 python 的自动垃圾回收,当它销毁/离开该帧/页面/缩进的代码块时,它会收集 refcount 为 0 的所有内容。


处理它的规范方法是在全局命名空间的某处有一个图像列表,并将图像引用添加到该列表中。这很方便,但不是很有效,应该用于小型应用程序。

import tkinter as tk

global_image_list = []
global_image_list.append(tk.PhotoImage(file = 'test.png'))

更有效的方法是属性绑定到您的小部件或类,该小部件或类为您保存该引用,正如 Bryan 在他的回答中提出的那样。如果您这样做或之前分配过,这不会有什么区别。但是,如果您想进一步使用该图像,即使小部件被销毁并垃圾回收,这也可能不是正确的方法。self.imagewidget.imagewidget = tk.Widget(..

import tkinter as tk

root = tk.Tk()
label = tk.Label(root, text='test')
label.image = tk.PhotoImage(file = 'test.png')
label.configure(image=label.image)