问题:
我有一个tkinter窗口,其中带有matplotlib图,其中显示了一些行(是一个更大的程序的一部分)。我会定期添加新行,删除最旧的行以限制显示的行数。不幸的是,程序的大小不断增加。我已经在Windows和Linux上看到了这一点。
问题:
我应该如何编写一个长时间运行的图形框架以显示最后的n行,这些行不会耗尽我的全部内存?最好通过调整下面的代码,但是如果需要的话,我已经准备好进行完整的重写。
我已经删除了有问题的部分,并进行了一些测试。
关键呼叫似乎是canvas.draw()
。即使未在图中添加任何行,所使用的内存也会随着时间逐渐增加。没有此调用,内存不会增加。当图中有更多行时,内存使用会更快增加。如果未创建轴(即没有fig.add_subplot(1, 1, 1)
),则内存不会增加。
之前已经讨论了从matplotlib中删除行并回收内存(How to remove lines in a Matplotlib plot)。对要删除的行的weakref确认已将其删除,并且由于即使在未绘制任何行的情况下也存在问题stil,所以我怀疑这不是根本原因。
最接近的类似问题似乎是Declaration of FigureCanvasTkAgg causes memory leak,在该问题上调用绘图轴(而不是pyplot)解决了该问题。在这种情况下,这似乎并不适用-我没有使用pyplot进行绘图,如果直接在轴(ax.plot(x, y, '.-')
上进行绘图,我仍然看到内存增加,并且再次出现时,我仍然看到问题没有画线。
使用mprof监视内存使用情况,并且我还使用Windows7上的系统诊断程序观看了它的发生。 mprof看到的内存使用示例:mprof plot of memory usage increasing
这是我目前得到的代码。与最初的用例相比,这已经大大减少了(_refresh_plot
通常不会做任何事情,除非已将新数据从另一个线程添加到队列中,等等),但仍然显示出了问题。
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib as mpl
import weakref
class PlotFrame:
def __init__(self, parent):
self.fig = mpl.figure.Figure()
self.canvas = FigureCanvasTkAgg(self.fig, master=parent)
self.canvas.get_tk_widget().pack()
self.ax = self.fig.add_subplot(1, 1, 1)
self.ax.set_ylim((0, 150))
self.ax.set_xlim((0, 12))
self.counter = 0
timer = self.fig.canvas.new_timer(interval=20)
timer.add_callback(self._refresh_plot, ())
timer.start()
def _refresh_plot(self, arg):
if True: # True to plot 1 or more lines, False for no lines at all
if len(self.ax.lines) > 2: # Remove the oldest line
existing_lines = self.ax.get_lines()
# r = weakref.ref(existing_lines[0]) # weakref to check that the line is being correctly removed
old_line = existing_lines.pop(0)
old_line.remove()
del old_line, existing_lines
# print('Old line dead? {}'.format(r() is None)) # Prints Old line dead? True
# Define a line to plot
x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
y = [(v - abs(len(x) - self.counter % (2 * len(x)))) ** 2 for v in x]
self.counter += 1
new_line = mpl.lines.Line2D(x, y, linestyle='-', marker='.')
self.ax.add_line(new_line)
del new_line, x, y
self.canvas.draw()
if __name__ == "__main__":
root = tk.Tk()
cpp = PlotFrame(root)
root.mainloop()
长帖子的道歉。我一直在盯着这个眼睛已经很久了,以至于对我来说这可能是一个愚蠢的错误。