如何清理tk.createcommand?

时间:2019-04-25 14:29:00

标签: python tkinter tcl

以下示例将由于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

2 个答案:

答案 0 :(得分:1)

尽管在完整的Tcl环境中,解释器是完全可破坏的(如果不是便宜的)对象,但Tkinter自己的参考Tcl解释器(相当重的实体)似乎并不容易删除;它与基础C _tkinter对象的寿命相关,并且有点复杂……到这一点,我很难判断删除该对象并对其进行垃圾回收会导致事情变得可重载,或者您是否愿意只是为将来的怪异做好准备。如果您也不必加载Tk,则可能有效(因为Tk有点麻烦)。最终。

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

您已经找到deletecommand;命令 被设计为廉价地创建和销毁。


是否可以在子进程中运行代码?如果没有Tk,这应该相当简单,并且操作系统可以很好地清理进程。有很多方法可以做到这一点。哪一个最好取决于您正在执行的操作的细节(尤其是交互性和移动的数据量)。

答案 1 :(得分:0)

我找到了一个解决方案,尽管此解决方案在我的情况下不起作用。不知道为什么,但是我们要做的只是这个小的演示代码,所以我切换到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()