В tkinter это хороший способ избежать нежелательной сборки мусора? - PullRequest
0 голосов
/ 10 ноября 2019

В Tkinter нам иногда приходится создавать ссылку на преобразованное изображение (например), чтобы оно не было уничтожено при выходе из области видимости.

Обычный способ - добавить переменнуюк его экземпляру виджета. Примером этого является:

    bard = Image.open("bardejov.jpg")
    bardejov = ImageTk.PhotoImage(bard)
    label1 = Label(self, image=bardejov)
    label1.image = bardejov #<<<<<<<<<<<<<<<<<<<<<
    label1.place(x=20, y=20)

Это часть примера, опубликованного Яном Боднаром из Zetcode, с моей пометкой этого примера. bardejov - это локальная переменная в функции, и если вы закомментируете отмеченную строку, вы не получите изображение, потому что оно уничтожается, когда функция возвращается, а метка просто видит «none».

IЯ новичок в Tkinter, и это меня скорее беспокоило, добавив новые свойства в системный код, и кто-то предложил это:

class S():
    # To make an object 'accessible', and thus save 
    # it from garbage collection.
    fred = []
    @classmethod
    def save(cls, x):
        cls.fred.append(x)

Это, безусловно, сработало в примере Яна:

bard = Image.open("xxxx.jpg")
bardejov = ImageTk.PhotoImage(bard)
label1 = Label(self, image=bardejov)
#label1.image = bardejov
S.save(bardejov)

НоЭто нормально? Есть ли нежелательные побочные эффекты?

1 Ответ

1 голос
/ 10 ноября 2019

Вопрос : хороший способ избежать нежелательного сбора мусора?

Это не вопрос, хорошо это или нет, вам нужно это сделать.
Вместоделая это снова и снова, вы можете определить свой собственный PhotoImageLabel.

Например:

import tkinter as tk
from PIL import Image, ImageTk


class PhotoImageLabel(tk.Label):
    def __init__(self, parent, **kwargs):
        image = Image.open(kwargs['image'])
        self._image = ImageTk.PhotoImage(image)

        kwargs['image'] = self._image
        super().__init__(parent, **kwargs)

Использование :

class App(tk.Tk):
    def __init__(self):
        super().__init__()

        self.photo_label = PhotoImageLabel(self, image="xxxx.jpg")
        self.photo_label.grid()


if __name__ == '__main__':
    App().mainloop()

Комментарий : однажды я думаю, что сам виджет решит проблему

Это маловероятно, поскольку ImageTk.PhotoImageне является частью tkinter. Вы можете расширить PhotoImage, чтобы вести себя как объект tkinter, привязав объект изображения к виджету:

Например:

class PhotoImage(ImageTk.PhotoImage):
    def __init__(self, parent, **kwargs):
        image = kwargs['image']

        # PIL.Image => .PhotoImage
        super().__init__(image)

        # Update <widget>.children with [self.__photo.name] = self
        self._ref = parent.children
        self._ref[self.__photo.name] = self

    def destroy(self):
        # This gets called on `.destroy()` the parent
        # Delete the reference in parent.children
        del self._ref[self.__photo.name]

Использование:

class PhotoImageLabel(tk.Label):
    def __init__(self, parent, **kwargs):
        image = Image.open(kwargs['image'])

        # Get a PhotoImage object which is bound to 'self'
        kwargs['image'] = PhotoImage(self, image=image)

        super().__init__(parent, **kwargs)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...