在 tkinter 中,这是避免不必要的垃圾收集的好方法吗? [英] In tkinter, is this a good way of avoiding unwanted garbage collection?

查看:29
本文介绍了在 tkinter 中,这是避免不必要的垃圾收集的好方法吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 Tkinter 中,我们有时必须创建对转换后的图像(例如),以便它在超出范围时不会被销毁.

一种常见的方法是向其小部件实例添加一个变量.一个例子是:

 bard = Image.open("bardejov.jpg")bardejov = ImageTk.PhotoImage(bard)标签1 = 标签(自我,图像=bardejov)label1.image = bardejov #<<<<<<<<<<<<<<<<<<label1.place(x=20, y=20)

这是 Zetcode 的 Jan Bodnar 发布的示例的一部分,我对该示例进行了标记.bardejov 是函数中的局部变量,如果注释掉标记的那行就得不到图像,因为函数返回时它被销毁了,标签只看到'none'.

我是 Tkinter 的新手,这让我很担心,向系统代码添加新属性,有人建议这样做:

类 S():# 使对象可访问",从而保存# 它来自垃圾收集.弗雷德 = []@类方法定义保存(cls,x):cls.fred.append(x)

这在 Jan 的例子中确实有效:

bard = Image.open("xxxx.jpg")bardejov = ImageTk.PhotoImage(bard)标签1 = 标签(自我,图像=bardejov)#label1.image = bardejovS.save(bardejov)

但是可以吗?有什么不良副作用吗?

解决方案

问题:避免不必要的垃圾收集的好方法?

这不是问题,好不好,你必须去做.
您可以定义自己的 PhotoImageLabel,而不是一遍又一遍地重复.

例如:

导入 tkinter 作为 tk从 PIL 导入 Image、ImageTk类 PhotoImageLabel(tk.Label):def __init__(self, parent, **kwargs):image = Image.open(kwargs['image'])self._image = ImageTk.PhotoImage(image)kwargs['image'] = self._imagesuper().__init__(parent, **kwargs)

<块引用>

用法:

类应用程序(tk.Tk):def __init__(self):super().__init__()self.photo_label = PhotoImageLabel(self, image="xxxx.jpg")self.photo_label.grid()如果 __name__ == '__main__':应用程序().主循环()

<小时><块引用>

评论:总有一天我想小部件本身会解决这个问题

这不太可能,因为 ImageTk.PhotoImage 不是 tkinter 的一部分.您可以通过将图像对象绑定到小部件来扩展 PhotoImage 使其表现得像一个 tkinter 对象:

例如:

class PhotoImage(ImageTk.PhotoImage):def __init__(self, parent, **kwargs):图像 = kwargs['图像']# PIL.Image =>.PhotoImagesuper().__init__(image)# 更新 <widget>.children 为 [self.__photo.name] = selfself._ref = parent.childrenself._ref[self.__photo.name] = selfdef销毁(自我):# 这在父级`.destroy()` 上被调用# 删除parent.children中的引用del self._ref[self.__photo.name]

<块引用>

用法:

class PhotoImageLabel(tk.Label):def __init__(self, parent, **kwargs):image = Image.open(kwargs['image'])# 获取一个绑定到'self'的PhotoImage对象kwargs['image'] = PhotoImage(self, image=image)super().__init__(parent, **kwargs)

In Tkinter, we sometimes have to create a reference to a converted image (for example), so that it will not be destroyed when it goes out of scope.

A common way is to add a variable to its widget instance. An example of this is:

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

This is part of an example published by Jan Bodnar of Zetcode, with my flagging of the example. bardejov is a local variable in a function, and if you comment out the marked line you don't get the image, because it is destroyed when the function returns, and the label just sees 'none'.

I'm new to Tkinter, and this worried me rather, adding new properties to system code, and someone suggested this:

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

This certainly worked in Jan's example:

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

But is it OK? Any undesirable side effects?

解决方案

Question: good way of avoiding unwanted garbage collection?

It's not the question, good or not, you have to do it.
Instead of doing it over and over again you can define your own PhotoImageLabel.

For example:

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)

Usage:

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


Comment: One day I guess the widget itself will fix the problem

That is unlikely, because ImageTk.PhotoImage is not part of tkinter. You can extend PhotoImage to behave it like a tkinter object, by binding the image object to the widget:

For example:

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]

Usage:

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)

这篇关于在 tkinter 中,这是避免不必要的垃圾收集的好方法吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆