PyQtChart 使用 numpy 构建数据时发生内存泄漏如何解决?

想用 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_())


PyQtChart 使用 numpy 构建数据时发生内存泄漏如何解决?

13 回复

你这个 self.RecvData.append(val)只进不出的内存不就越来越大


PyQtChart 使用 numpy 数组时内存泄漏通常是因为数据没有被正确释放。核心问题是 numpy 数组和 Qt 内部数据结构之间的生命周期管理不当。

主要解决方案是确保在更新图表数据时,显式地清理旧的 QLineSeries 或 QScatterSeries 等系列对象,并让 Python 的垃圾回收机制能够正常工作。下面是一个完整的示例:

import sys
import numpy as np
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
from PyQt5.QtChart import QChart, QChartView, QLineSeries
from PyQt5.QtCore import Qt

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.chart = QChart()
        self.chart_view = QChartView(self.chart)
        self.setCentralWidget(self.chart_view)
        
        # 初始数据
        self.update_chart_data()
        
    def update_chart_data(self):
        # 清除现有系列
        self.chart.removeAllSeries()
        
        # 创建新数据
        x = np.linspace(0, 10, 1000000)  # 大量数据点
        y = np.sin(x)
        
        # 创建新系列
        series = QLineSeries()
        
        # 关键:使用 Python 列表而不是 numpy 数组直接传递
        # 这样可以避免 Qt 内部持有对 numpy 数组的引用
        series.append([(float(x[i]), float(y[i])) for i in range(len(x))])
        
        # 添加到图表
        self.chart.addSeries(series)
        self.chart.createDefaultAxes()

def main():
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

关键点:

  1. 在更新数据前调用 removeAllSeries() 确保旧系列被完全移除
  2. 将 numpy 数组转换为 Python 列表的元组列表,避免 Qt 内部持有 numpy 数组引用
  3. 确保系列对象在函数作用域结束后能被垃圾回收

如果数据量特别大,考虑使用 QScatterSeries 并设置 useOpenGL(True) 来利用 GPU 加速,但这需要你的系统支持 OpenGL。

总结:确保正确清理旧系列并断开 numpy 引用。


不是的,你看 on_tmrPlot_timeout()函数第一句

我这里试了一下, 这一行 self.plotCurve.replace(plotData)导致内存持续增长, 注释掉就不会, 怀疑是 pyqt 有问题, 可以用 C++ Qt 再验证下…


不是的,因为把数据构建方式改成下面这样子,其他不变,,就不会有内存泄漏

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


不过这样一个点一个点构建数据,我觉可能会比较慢,,不如原来用 numpy 那种方式速度快

原始的 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


多谢指点
试了下,确实如此,,
看来跟 numpy 无关,就是使用 list[QPointF]作为绘图数据时能自动释放,使用 QPolygonF 作为绘图数据时无法自动释放内存,,我去搜下,看能不能手动释放 QPolygonF 占用的内存

用 C++Qt 试了下, 没有问题. 画复杂图形的话, pyqt 直接嵌入 matplotlib 来作图更容易, 相比来说, pyqtchart 太难用了

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

链接: https://pan.baidu.com/s/15sbdoFpUnbG9rYGnWvtxFg 提取码: cmgj

补充下信息: 我用的 PyQt5 (PyQtChart) 5.11.3, C++Qt 5.10.1

5.9.0 之前这儿似乎是有内存泄露的: https://bugreports.qt.io/browse/QTBUG-58802


多谢热心指点

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

QPolygon 导致内存泄漏的问题,我想到一个办法:不每次都新建一个 QPolygon、而是只建一个重复使用,每次更新显示时更新 QPolygon 的内容,,结果试了下内存还是泄漏(@_@;)

楼主是做示波器吗…


不是示波器,是个低速的数据接收、波形显示小软件

回到顶部