PyQtChart 使用 numpy 构建数据时发生内存泄漏

2018-10-15 11:07:43 +08:00
 XIVN1987

想用 PyQtChart 做绘图功能,于是在网上找到个例程,,运行了下功能能实现,,不过在任务管理器里发现这个程序占用的内存一直在增大,,应该是发生了内存泄漏

显示数据是由 series_to_polyline()构建的,里面用到了 numpy,,哪位大神知道这种情况怎么解决?

运行效果:

代码:

import os
import sys
import math
import array

from PyQt5.QtCore import Qt, QTimer, QPointF
from PyQt5.QtGui  import QPolygonF
from PyQt5.QtWidgets import QApplication, QMainWindow

from PyQt5.QtChart import QChart, QChartView, QLineSeries

import numpy as np

class DemoWindow(QMainWindow):
    def __init__(self, parent=None):
        super(DemoWindow, self).__init__(parent=parent)

        self.plotChart = QChart()
        self.plotChart.legend().hide()

        self.plotView = QChartView(self.plotChart)
        self.setCentralWidget(self.plotView)

        self.plotCurve = QLineSeries()
        self.plotCurve.setUseOpenGL(True)
        self.plotCurve.pen().setColor(Qt.red)
        self.plotChart.addSeries(self.plotCurve)

        self.plotChart.createDefaultAxes()
        self.plotChart.axisX().setLabelFormat('%g')

        self.RecvData = array.array('f')    # 存储接收到的传感器数据
        self.RecvIndx = 0

        self.tmrData = QTimer()             # 模拟传感器传送过来数据
        self.tmrData.setInterval(3)
        self.tmrData.timeout.connect(self.on_tmrData_timeout)
        self.tmrData.start()

        self.tmrPlot = QTimer()
        self.tmrPlot.setInterval(100)
        self.tmrPlot.timeout.connect(self.on_tmrPlot_timeout)
        self.tmrPlot.start()

    def on_tmrData_timeout(self):
        val = math.sin(2*3.14 / 500 * self.RecvIndx)
        self.RecvData.append(val)

        self.RecvIndx += 1

    def series_to_polyline(self, xdata, ydata):
        """Convert series data to QPolygon(F) polyline
        
        This code is derived from PythonQwt's function named 
        `qwt.plot_curve.series_to_polyline`"""
        size = len(xdata)
        polyline = QPolygonF(size)
        pointer = polyline.data()
        dtype, tinfo = np.float, np.finfo  # integers: = np.int, np.iinfo
        pointer.setsize(2*polyline.size()*tinfo(dtype).dtype.itemsize)
        memory = np.frombuffer(pointer, dtype)
        memory[:(size-1)*2+1:2] = xdata
        memory[1:(size-1)*2+2:2] = ydata

        return polyline    

    def on_tmrPlot_timeout(self):
        self.RecvData = self.RecvData[-1000:]

        plotData = self.series_to_polyline(range(len(self.RecvData)), self.RecvData)
        
        self.plotCurve.replace(plotData)
        self.plotChart.axisX().setMax(len(plotData))
        self.plotChart.axisY().setRange(min(self.RecvData), max(self.RecvData))


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = DemoWindow()
    window.show()
    window.resize(700, 400)
    sys.exit(app.exec_())
3221 次点击
所在节点    Python
12 条回复
laqow
2018-10-15 12:50:04 +08:00
你这个 self.RecvData.append(val)只进不出的内存不就越来越大
XIVN1987
2018-10-15 13:37:51 +08:00
@laqow
不是的,你看 on_tmrPlot_timeout()函数第一句
justou
2018-10-15 13:42:47 +08:00
我这里试了一下, 这一行 self.plotCurve.replace(plotData)导致内存持续增长, 注释掉就不会, 怀疑是 pyqt 有问题, 可以用 C++ Qt 再验证下...
XIVN1987
2018-10-15 13:59:48 +08:00
@justou
不是的,因为把数据构建方式改成下面这样子,其他不变,,就不会有内存泄漏

``` python
def series_to_polyline(self, xdata, ydata):
polyline = []
for i,d in enumerate(ydata):
polyline.append(QPointF(i,d))

return polyline
```
XIVN1987
2018-10-15 14:04:00 +08:00
@justou
不过这样一个点一个点构建数据,我觉可能会比较慢,,不如原来用 numpy 那种方式速度快
justou
2018-10-15 14:26:57 +08:00
原始的 polyline 是 Qt 的 QPolygonF, 你修改后的是 python 的 list, 你再试试在 series_to_polyline 里面完全不使用 numpy 对 QPolygonF 赋值, 内存一样一直涨, 所以排除是 numpy 的问题

def series_to_polyline(self, xdata, ydata):
size = len(xdata)
polyline = QPolygonF(size)

return polyline
XIVN1987
2018-10-15 15:08:02 +08:00
@justou
多谢指点
试了下,确实如此,,
看来跟 numpy 无关,就是使用 list[QPointF]作为绘图数据时能自动释放,使用 QPolygonF 作为绘图数据时无法自动释放内存,,我去搜下,看能不能手动释放 QPolygonF 占用的内存
justou
2018-10-15 19:46:25 +08:00
用 C++Qt 试了下, 没有问题. 画复杂图形的话, pyqt 直接嵌入 matplotlib 来作图更容易, 相比来说, pyqtchart 太难用了

https://gist.github.com/justou/1fe72187b21af835cf633a37b06e9d74

链接: https://pan.baidu.com/s/15sbdoFpUnbG9rYGnWvtxFg 提取码: cmgj
justou
2018-10-15 20:38:55 +08:00
补充下信息: 我用的 PyQt5 (PyQtChart) 5.11.3, C++Qt 5.10.1

5.9.0 之前这儿似乎是有内存泄露的: https://bugreports.qt.io/browse/QTBUG-58802
XIVN1987
2018-10-15 22:05:26 +08:00
@justou
多谢热心指点

首先,我觉得 matplotlib 不是用来做实时绘图用的,它首页说它用于“ produces publication quality figures ”

QPolygon 导致内存泄漏的问题,我想到一个办法:不每次都新建一个 QPolygon、而是只建一个重复使用,每次更新显示时更新 QPolygon 的内容,,结果试了下内存还是泄漏(@_@;)
tuduweb
2018-10-16 00:34:14 +08:00
楼主是做示波器吗…
XIVN1987
2018-10-16 09:01:28 +08:00
@tuduweb
不是示波器,是个低速的数据接收、波形显示小软件

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/497685

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX