tkinter,matplotlib:内存使用情况

时间:2017-05-31 11:40:32

标签: python python-2.7 memory matplotlib tkinter

我有一个使用tkinter创建GUI的简单程序。 GUI包含一个按钮,每次按下该按钮都会创建一个新的tkinter Toplevel窗口。每个顶层窗口都包含一个从自定义Figure类创建的matplotlib图。

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from Tkinter import *

class MainGUI(Tk):
    def __init__(self):
        Tk.__init__(self)
        Button(self, text="new_child", command=self.new_child).pack()
        self.mainloop()

    def new_child(self):
        self.tl = TL()

class TL(Toplevel):
    def __init__(self, **kw):
        Toplevel.__init__(self, **kw)
        self.data = "00000000000"*10000000  # just some data to analyze memory allocation 
        self._figure = MyFigure(self.data)
        self._canvas = FigureCanvasTkAgg(self._figure, master=self)
        self._canvas.get_tk_widget().grid(row=0, column=0, sticky="NSWE")

class MyFigure(Figure):
    def __init__(self, data):
        super(MyFigure, self).__init__()
        self._data = data

if __name__ == '__main__':
    MainGUI()

该程序按预期工作,唯一的问题是关闭一个窗口不会释放任何内存。

从顶层窗口中删除Figure时,已正确释放已用内存,因此我假设Figure导致内存泄漏。

我读到引用计数不适用于使用matplotlibs pyplot接口创建的Figure个对象,但这不适用于我的示例。 (有关详细信息,请参阅here

我不明白这里发生了什么,所以非常感谢任何帮助。

提前致谢。

修改

我忘了提到我已经尝试用gc.collect()手动调用垃圾收集器,但这没有用。

2 个答案:

答案 0 :(得分:0)

在顶层窗口关闭后尝试致电gc.collect()

def new_child(self):
    tl = TL()
    self.wait_window(tl)   # Wait for tl to be closed before continuing execution
    del tl
    gc.collect()

答案 1 :(得分:0)

感谢您提出这个问题,以及对我帮助很大的答案。我发现自己处于类似的情况,但 GUI 稍微复杂一些。尽管应用了此处给出的解决方案,但我还是遇到了内存泄漏。似乎将跟踪添加到 Tkinter 变量(例如 IntVar、DoubleVar 等)会阻止 Toplevel 窗口被正确破坏,并导致泄漏。 在销毁时手动删除痕迹可解决此问题。让我举一个发生泄漏的代码示例,以及我找到的修复程序。我希望它可以帮助其他人。

初步: 首先,使用 Josselin 给出的修正版 new_child

def new_child(self):
    tl = TL()
    self.wait_window(tl)   # Wait for tl to be closed before continuing execution
    del tl
    gc.collect()

然后,代码内存泄漏(由跟踪引起):

class TL(Toplevel):
    def __init__(self, **kw):
        Toplevel.__init__(self, **kw)
        self.number = IntVar()
        self.data = "00000000000"*10000000  # just some data to analyze memory allocation 
        self._figure = MyFigure(self.data)
        self._canvas = FigureCanvasTkAgg(self._figure, master=self)
        self._canvas.get_tk_widget().grid(row=0, column=0, sticky="NSWE")
        Spinbox(self, from_=0, to=10, textvariable=self.number).grid(row=1,column=0)
        self.number.trace('w', self.updateSomething)
    def updateSomething(self, *args): return

最后,修复(在销毁时手动删除痕迹):

class TL(Toplevel):
    def destroy(self):
        for var, s, trid in self.allTraces: var.trace_vdelete(s, trid) #manually removes traces. Otherwise: memory leak!
        super(TL, self).destroy()
    def __init__(self, **kw):
        Toplevel.__init__(self, **kw)
        self.allTraces = []
        self.number = IntVar()
        self.data = "00000000000"*10000000  # just some data to analyze memory allocation 
        self._figure = MyFigure(self.data)
        self._canvas = FigureCanvasTkAgg(self._figure, master=self)
        self._canvas.get_tk_widget().grid(row=0, column=0, sticky="NSWE")
        Spinbox(self, from_=0, to=10, textvariable=self.number).grid(row=1,column=0)
        trid = self.number.trace('w', self.updateSomething)
        self.allTraces.append((self.number, 'w', trid))
    def updateSomething(self, *args): return