PyQt结合Matplotlib实现实时动态绘图与数据刷新优化方案
本文详解如何在PyQt5环境中使用Matplotlib实现基于实时数据的动态绘图。针对x、y轴数据动态增加的场景,通过修改_update_canvas函数实现数据累积绘制。此外,补充了pyqtgraph在高频更新中可能出现的界面卡顿问题,对比了移除重绘与QTimer定时器两种解决方案,推荐使用定时器法以保障程序响应性。
需求分析
在项目开发中,需要根据实时采集到的数据在图形界面上进行动态绘制展示。
运行环境
Python 3.7 + Matplotlib 3.0.2 + PyQt 5
Matplotlib官方示例参考
import sys
import time
import numpy as np
from matplotlib.backends.qt_compat import QtCore, QtWidgets, is_pyqt5
if is_pyqt5():
from matplotlib.backends.backend_qt5agg import (
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
else:
from matplotlib.backends.backend_qt4agg import (
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
layout = QtWidgets.QVBoxLayout(self._main)
static_canvas = FigureCanvas(Figure(figsize=(5, 3)))
layout.addWidget(static_canvas)
self.addToolBar(NavigationToolbar(static_canvas, self))
dynamic_canvas = FigureCanvas(Figure(figsize=(5, 3)))
layout.addWidget(dynamic_canvas)
self.addToolBar(QtCore.Qt.BottomToolBarArea,
NavigationToolbar(dynamic_canvas, self))
self._static_ax = static_canvas.figure.subplots()
t = np.linspace(0, 10, 501)
self._static_ax.plot(t, np.tan(t), ".")
self._dynamic_ax = dynamic_canvas.figure.subplots()
self._timer = dynamic_canvas.new_timer(
100, [(self._update_canvas, (), {})])
self._timer.start()
def _update_canvas(self):
self._dynamic_ax.clear()
t = np.linspace(0, 10, 101)
# Shift the sinusoid as a function of time.
self._dynamic_ax.plot(t, np.sin(t + time.time()))
self._dynamic_ax.figure.canvas.draw()
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
qapp.exec_()

官方示例中,上方图表显示为静止散点,下方图表则呈现类似行波的动态效果,这是因为使用了**self._dynamic_ax.plot(t, np.sin(t + time.time()))**函数。然而,该示例并非典型的实时数据绘图场景,其x轴和y轴的数据点数量是固定的。在实际项目中,我们需要根据生成的数据实时绘图,这意味着x轴和y轴的元素个数会随着时间逐渐增加。
通过阅读上述_update_canvas函数代码以及dynamic_canvas.new_timer机制,可以实现每次调用_update_canvas时,x轴和y轴的元素数量逐步增加。修改后的代码如下:
import sys
import time
import numpy as np
from matplotlib.backends.qt_compat import QtCore, QtWidgets, is_pyqt5
if is_pyqt5():
from matplotlib.backends.backend_qt5agg import (
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
else:
from matplotlib.backends.backend_qt4agg import (
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
layout = QtWidgets.QVBoxLayout(self._main)
static_canvas = FigureCanvas(Figure(figsize=(5, 3)))
layout.addWidget(static_canvas)
self.addToolBar(NavigationToolbar(static_canvas, self))
dynamic_canvas = FigureCanvas(Figure(figsize=(5, 3)))
layout.addWidget(dynamic_canvas)
self.addToolBar(QtCore.Qt.BottomToolBarArea,
NavigationToolbar(dynamic_canvas, self))
self._static_ax = static_canvas.figure.subplots()
t = np.linspace(0, 10, 501)
self._static_ax.plot(t, np.tan(t), ".")
self.x = [] #建立空的x轴数组和y轴数组
self.y = []
self.n = 0
self._dynamic_ax = dynamic_canvas.figure.subplots()
self._timer = dynamic_canvas.new_timer(
100, [(self._update_canvas, (), {})])
self._timer.start()
def _update_canvas(self):
self.n += 1
if self.n == 200: #画200个点就停止,根据实际情况确定终止条件
self._timer.stop()
self._dynamic_ax.clear()
self.x.append(np.pi/100*self.n) #x加入一个值,后一个值比前一个大pi/100
xx = np.array(self.x)
# t = np.linspace(0, 10, 101)
# Shift the sinusoid as a function of time.
self._dynamic_ax.plot(xx, np.sin(xx))
self._dynamic_ax.set_xlim(0,7)
self._dynamic_ax.set_ylim(-1,1)
self._dynamic_ax.figure.canvas.draw()
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
qapp.exec_()

修改后,上图依然保持静止,而下图可以实时显示动态变化。

补充说明:pyqtgraph实时绘图时可能遇到的刷新问题
在使用pyqtgraph进行实时绘图时,偶尔会出现图表无法及时刷新的情况。
while True: ...... update() # 通过 plotitem.setData()更新数据 ......
这通常是因为使用了while循环来不断更新数据,导致绘图刷新和操作响应(如窗口最小化)概率性失效。
解决方法一
若使用的是PlotWidget,可以通过先remove再addwidget的方式,然后重新绘制图表来解决。
解决方法二
避免使用while循环,改用QTime定时器来触发数据更新。
t = QTimer() t.timeout.connect(self.update) t.start(10)
上述两种方法均可解决问题,但推荐使用方法二。
有观点认为,若必须使用while循环,需在更新数据后调用pg.QtGui.QApplication.processEvents()以确保正常显示。但经本人测试,该方法在本环境中无效,可能是环境差异所致。
以上为个人经验分享,希望能为大家提供参考。欢迎大家支持本站。如有错误或考虑不周之处,敬请指正。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













