简体   繁体   English

如果在 function 中创建,为什么 Tkinter 图像不显示?

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

This code works:此代码有效:

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()

It shows me the image.它向我展示了图像。

Now, this code compiles but it doesn't show me the image, and I don't know why, because it's the same code, in a class:现在,这段代码可以编译但没有显示图像,我不知道为什么,因为它是相同的代码,在 class 中:

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()

The variable photo is a local variable which gets garbage collected after the class is instantiated.变量photo是一个局部变量,它在类实例化后收集垃圾。 Save a reference to the photo, for example:保存对照片的引用,例如:

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

If you do a Google search on "tkinter image doesn't display", the first result is this:如果您在“tkinter image doesn't display”上进行谷歌搜索,第一个结果是这样的:

Why do my Tkinter images not appear? 为什么我的 Tkinter 图像没有出现? (The FAQ answer is currently not outdated) (常见问题解答目前尚未过时)

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() 

Just add global to the img definition and it will work只需将 global 添加到 img 定义中即可

The problem is Python automatically deletes the references to the variable by a process known as Garbage Collection .问题是 Python 通过称为垃圾收集的过程自动删除对变量的引用。 The solution is to save the reference or to create a new reference.解决方案是保存参考或创建新参考。

The following are the ways:以下是方法:

  1. Using self to increase the reference count and to save the reference.使用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. Saving it to a list to increase the reference count and to save the reference.将其保存到列表以增加引用计数并保存引用。
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()

While using method 2, you can either make a global list as i did or use list inside the class.使用方法 2 时,您可以像我一样制作一个全局列表,也可以在类中使用列表。 Both would work.两者都会起作用。

Some useful links:一些有用的链接:

As a rule of thumb, whenever you create your image in an indented block of code you need to safe a reference to that image .根据经验,无论何时在缩进代码块中创建图像,都需要保护对该图像的引用 This is because of the python's automated garbage collection and it collects everything with a refcount of 0 when it destroys/leaves that frame/page/indented block of code.这是因为 python 的自动垃圾收集,当它销毁/离开该框架/页面/缩进代码块时,它会收集引用计数为 0 的所有内容。


The canonical way to deal with it is to have a list of images somewhere in the global namespace and add your image-references to that list.处理它的规范方法在全局命名空间的某处有一个图像列表,并将您的图像引用添加到该列表。 This is convenient but not very efficient and should be used for small applications.这很方便但效率不高,应该用于小型应用程序。

import tkinter as tk

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

An more efficient way is to bound an attribute to your widget or class that holds that reference for you, as Bryan proposed in his answer.正如 Bryan 在他的回答中所建议的那样,一种更有效的方法是将一个属性绑定到为您保存该引用的小部件或类 It doesn't make a difference if you do self.image or widget.image that was assigned widget = tk.Widget(.. before. But this also might not the right approach if you want to use that image further even when the widget is destroyed and garbage collected.如果你做self.imagewidget.image之前分配的widget = 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)

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM