Почему изображение Tkinter не отображается, если оно создано в функции?

#python #tkinter

#питон #изображение #tkinter #tkinter-canvas

Вопрос:

Этот код работает:

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

Комментарии:

1. effbot.org находится внизу. Суть в том, что изображение передается по ссылке. Если ссылка относится к локальной переменной, память, на которую ссылается ссылка, используется повторно, и ссылка становится устаревшей. Переменная, хранящая изображение, должна находиться в той же области (должна иметь то же время жизни), что и объект Tk gui, в котором он отображается.

2. @maszoka: effbot.org может быть отключено, но вы все равно можете прочитать ссылку Почему мои изображения Tkinter не отображаются? благодаря машине обратного доступа Internet Archive.

3. Также обратите внимание, что та же проблема может возникнуть везде, где используются временные PhotoImage s, например, в вызывающей последовательности, такой как label = Label(image=ImageTk.PhotoImage(Image.fromarray(data))) .

Ответ №1:

Переменная photo — это локальная переменная, которая собирает мусор после создания экземпляра класса. Сохраните ссылку на фотографию, например:

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

Если вы выполните поиск в Google по запросу «изображение tkinter не отображается», первый результат будет таким:

Почему мои изображения Tkinter не отображаются?(Ответ на часто задаваемые вопросы в настоящее время не устарел)

Комментарии:

1. Вау. Считают ли они это ошибкой в tkinter? Они должны.

2. Я нашел очень старый билет, уже закрытый без исправления: bugs.python.org/issue632323

3. Ссылка не работает atm, есть ли другой способ, кроме использования «global»?

4. @aru: В этом примере показано, как это сделать без использования глобального.

5. @TamasHegedus: Я согласен, что это ошибка, но, по-видимому, никто не удосужился исправить ее после (в настоящее время) почти двух десятилетий. Потерял счет, сколько раз я вижу, что вопрос, касающийся его, все еще всплывает.

Ответ №2:

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

Просто добавьте global в определение img, и оно будет работать

Комментарии:

1. Этот ответ подходит для программы, которая просто использует функции, но если, как в случае с OP, вы используете класс, global это не тот путь.

Ответ №3:

Проблема в том, что 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 вы можете либо создать глобальный список, как я, либо использовать list внутри класса. И то, и другое сработало бы.

Некоторые полезные ссылки:

Ответ №4:

Просто добавьте global photo в качестве первой строки внутри функции.

Комментарии:

1. А затем вы создаете 2-й Test экземпляр, и первый экземпляр теряет свое изображение. Поздравляю.

2. Определенно нет необходимости использовать global его в классе. self. позаботится обо всем этом.