我有一个使用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()
手动调用垃圾收集器,但这没有用。
答案 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