简体   繁体   English

在PyQt GUI中嵌入和更新matplotlib图时内存泄漏

[英]Memory leak when embedding and updating a matplotlib graph in a PyQt GUI

I am trying to embed a matplotlib graph that updates every second into a PyQt GUI main window. 我试图嵌入一个matplotlib图,每秒更新一次PyQt GUI主窗口。

In my program I call an update function every second using threading.Timer via the timer function shown below. 在我的程序中,我每秒使用threading.Timer通过下面显示的timer函数调用更新函数。 I have a problem: my program grows bigger every second - at a rate of about 1k every 4 seconds. 我有一个问题:我的程序每秒都会变大 - 每4秒钟大约1k。 My initial thoughts are that the append function (that returns a new array in update_figure ) does not delete the old array? 我最初的想法是append函数(在update_figure中返回一个新数组)不会删除旧数组? Is it possible this is the cause of my problem? 这可能是我问题的原因吗?

def update_figure(self):
    self.yAxis = np.append(self.yAxis, (getCO22()))
    self.xAxis = np.append(self.xAxis, self.i)
    # print(self.xAxis)
    if len(self.yAxis) > 10:
        self.yAxis = np.delete(self.yAxis, 0)

    if len(self.xAxis) > 10:
        self.xAxis = np.delete(self.xAxis, 0)

    self.axes.plot(self.xAxis, self.yAxis, scaley=False)
    self.axes.grid(True)

    self.i = self.i + 1

    self.draw()

This is my timer function - this is triggered by the click of a button in my PyQt GUI and then calls itself as you can see: 这是我的计时器功能 - 这是通过点击我的PyQt GUI中的按钮触发,然后调用自己,如您所见:

def timer(self):
    getCH4()
    getCO2()
    getConnectedDevices()
    self.dc.update_figure()
    t = threading.Timer(1.0, self.timer)
    t.start()

EDIT: I cant post my entire code because it requires a lot of .dll includes. 编辑:我无法发布我的整个代码,因为它需要很多.dll包含。 So i'll try to explain what this program does. 所以我将尝试解释这个程序的功能。

In my GUI I want to show the my CO 2 value over time. 在我的GUI中,我想显示我的CO 2值随着时间的推移。 My get_co22 function just returns a float value and I'm 100% sure this works fine. 我的get_co22函数只返回一个浮点值,我100%肯定这个工作正常。 With my timer, shown above, I want to keep append a value to a matplotlib graph - the Axes object is available to me as self.axes . 使用我的计时器,如上所示,我想继续为matplotlib图添加一个值 - Axes对象可以作为self.axes I try to plot the last 10 values of the data. 我尝试绘制数据的最后10个值。

EDIT 2: After some discussion in chat , I tried putting the call to update_figure() in a while loop and using just one thread to call it and was able to make this minimal example http://pastebin.com/RXya6Zah . 编辑2: 在聊天中进行了一些讨论之后,我尝试在一个while循环中调用update_figure()并使用一个线程来调用它,并且能够创建这个最小的例子http://pastebin.com/RXya6Zah This changed the structure of the code to call update_figure() to the following: 这改变了代码的结构,将update_figure()调用到以下内容:

def task(self):
    while True:
        ui.dc.update_figure()
        time.sleep(1.0)

def timer(self):
    t = Timer(1.0, self.task())
    t.start()

but now the program crashes after 5 iterations or so. 但现在程序在5次迭代后崩溃了。

The problem is definitely not with how you are appending to your numpy array, or truncating it. 问题绝对不在于如何附加到numpy数组或截断它。

The problem here is with your threading model. 这里的问题是您的线程模型。 Integrating calculation loops with a GUI control loop is difficult. 将计算循环与GUI控制循环集成很困难。

Fundamentally, you need your GUI threading to have control of when your update code is called (spawning a new thread to handle it if necessary) - so that 从根本上说,您需要使用GUI线程来控制何时调用更新代码(如果需要,生成一个新线程来处理它) - 这样

  1. your code does not block the GUI updating, 您的代码不会阻止GUI更新,
  2. the GUI updating does not block your code executing and GUI更新不会阻止您的代码执行和
  3. you don't spawn loads of threads holding multiple copies of objects (which might be where your memory leak comes from). 你不会产生大量持有多个对象副本的线程(这可能是你的内存泄漏的来源)。

In this case, as your main window is controlled by PyQt4 , you want to use a QTimer (see a simple example here ) 在这种情况下,由于您的主窗口由PyQt4控制,您想要使用QTimer (请参阅此处简单示例

So - alter your timer code to 所以 - 改变您的timer代码

def task(self):
    getCH4()
    getCO2()
    getConnectedDevices()
    self.dc.update_figure()

def timer(self):
    self.t = QtCore.QTimer()
    self.t.timeout.connect(self.task)
    self.t.start(1000)

and this should work. 这应该工作。 Keeping the reference to the QTimer is essential - hence self.t = QtCore.QTimer() rather than t = QtCore.QTimer() , otherwise the QTimer object will be garbage collected. 保持对QTimer的引用是必不可少的 - 因此self.t = QtCore.QTimer()而不是t = QtCore.QTimer() ,否则QTimer对象将被垃圾收集。


Note: 注意:

This is a summary of a long thread in chat clarifying the issue and working through several possible solutions. 这是聊天中的一个长线程的摘要,用于澄清问题并解决几个可能的解决方案。 In particular - the OP managed to mock up a simpler runnable example here: http://pastebin.com/RXya6Zah 特别是 - OP设法在这里模拟一个更简单的可运行示例: http//pastebin.com/RXya6Zah

and the fixed version of the full runnable example is here: http://pastebin.com/gv7Cmapr 完整的runnable示例的固定版本在这里: http//pastebin.com/gv7Cmapr

The relevant code and explanation is above, but the links might help anyone who wants to replicate / solve the issue. 相关的代码和解释如上所述,但链接可能有助于任何想要复制/解决问题的人。 Note that they require PyQt4 to be installed 请注意,它们需要安装PyQt4

if you are creating a new figure for every time this is quite common. 如果你每次创建一个新的数字这很常见。

matplotlib do not free the figures you create, unless you ask it, somethink like: matplotlib不会释放你创建的数字,除非你问它,有些想法:

pylab.close() 

see How can I release memory after creating matplotlib figures 请参阅创建matplotlib数据后如何释放内存

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM