如何清理tk.createcommand?

How to clean up tk.createcommand?

以下示例将由于 tk.createcommand:

而造成内存泄漏
import tkinter
import gc

class Foo:

    def __init__(self):
        self.tk = tkinter.Tcl()
        self.tk.createcommand("someCommand", self.someCommand)

    def someCommand(self):
        pass

if __name__ == "__main__":

    while 1:
        f = Foo()
        # f.tk.deletecommand("someCommand")
        # del f
        # gc.collect()

如果删除 tk.createcommand 一切正常,del fgc.collect() 无效,tk.deletecommand 将抛出 _tkinter.TclError: can't delete Tcl command

有什么方法可以删除创建的命令或清理内存吗?我使用 tkinter GUI,我需要它来调用一些遗留的 tcl 代码。

python                                                                                                                         
Python 3.7.3 (default, Mar 26 2019, 21:43:19) 
[GCC 8.2.1 20181127] on linux

虽然在完整的 Tcl 环境中解释器是完全可销毁的(如果不是廉价的)对象,Tkinter 自己的参考 Tcl 解释器(一个相当重量级的实体)似乎不是一个容易删除的对象;它与底层 C _tkinter 对象的生命周期有关,这有点复杂……以至于我很难判断删除它和垃圾收集它是否会导致事情变得可重新加载或者你是否会只是为未来的怪异做好准备。它 可能 可以工作,前提是你没有加载 Tk(因为 Tk 有点挑剔)。终于。

因此,真的如果你能避免在不需要时重新加载它是最好的,而且如果你不创建新实例也是最好的Tcl() 如果您实际上不需要它们。毕竟这有点像说制作一个新的 Python 解释器......所以它可能不应该在普通代码中的紧密循环中完成。

您已经找到了deletecommand;命令设计成创建和销毁成本低廉。


是否可以 运行 子进程中的代码?没有 Tk about,那应该相当简单,并且进程被 OS 清理得非常好。有很多方法可以做到这一点;哪一个最好将取决于您正在做的事情的细节(尤其是有多少交互性以及您要移动多少数据)。

我找到了一个解决方案,尽管这个解决方案不适用于我的情况。不知道为什么,但我们做的不仅仅是这个小演示代码,所以我切换到 multiprocessing

您必须明确调用 deletecommand

import tkinter
import gc

class Foo:

    def __init__(self):
        self.tk = tkinter.Tcl()
        self.tk.createcommand("someCommand", self.someCommand)
        print("Foo intialized")
        # self.tk.eval('rename someCommand ""')

    def someCommand(self):
        pass

    def __del__(self):
        print("DESTRUCTOR called!")

if __name__ == "__main__":

    while 1:
        f = Foo()
        f.tk.tk.deletecommand("someCommand")
        del f
        gc.collect()

小心!将调用放在 __del__ 中将不起作用,因为由于内存泄漏,析构函数将永远不会被调用:

import tkinter
import gc

class Foo:

    def __init__(self):
        self.tk = tkinter.Tcl()
        self.tk.createcommand("someCommand", self.someCommand)
        print("Foo intialized")
        # self.tk.eval('rename someCommand ""')

    def someCommand(self):
        pass

    def __del__(self):
        f.tk.tk.deletecommand("someCommand")
        print("DESTRUCTOR called!")

if __name__ == "__main__":

    while 1:
        f = Foo()
        # f.tk.tk.deletecommand("someCommand")
        del f
        gc.collect()