使用PyQtGraph实时绘制多传感器外部数据的问题求助
解决PyQtGraph多进程绘图无法更新的问题
看起来你遇到了多进程环境下PyQtGraph绘图更新的典型问题,核心原因是跨进程内存隔离和代码逻辑的小失误,下面一步步帮你解决:
问题根源分析
- 全局变量跨进程无效:多进程拥有独立的内存空间,你通过
globals()['DatExt1']试图获取主进程的队列是行不通的——子进程的全局命名空间和主进程完全隔离,DatExt1只是ShowData函数的局部变量,根本不在子进程的全局变量里。 - 代码笔误:
update2方法中更新910D绘图时,错误使用了self.data1的数据,应该用self.data2。 - 队列读取方式可能阻塞事件循环:直接用
empty()判断后get(),虽然不会死等,但不如用非阻塞读取更稳妥,避免影响Qt事件循环的响应。
修正后的完整代码
绘图模块(比如命名为PlotData.py)
from pyqtgraph.Qt import QtGui, QtCore import numpy as np import pyqtgraph as pg import sys import multiprocessing class Plot2D(): def __init__(self, queue_mpu, queue_910d): self.traces = dict() self.app = QtGui.QApplication([]) self.win = pg.GraphicsWindow(title="传感器数据绘图") self.win.resize(1000, 600) self.win.setWindowTitle('实时数据监控') pg.setConfigOptions(antialias=True) # 创建两个绘图区域 self.waveform_mpu = self.win.addPlot(title='MPU 数据', row=1, col=1) self.waveform_910d = self.win.addPlot(title='910D 数据', row=2, col=1) # 保存传入的队列(关键:直接关联到实例,不用全局变量) self.queue_mpu = queue_mpu self.queue_910d = queue_910d def start(self): # 直接用当前实例的QApplication执行事件循环,比instance()更可靠 if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): self.app.exec_() def set_plotdata(self, name, data_x, data_y): if name in self.traces: self.traces[name].setData(data_x, data_y) else: # 首次创建绘图轨迹 if name == '910D': self.traces[name] = self.waveform_910d.plot(pen='c', width=3) if name == 'MPU': self.traces[name] = self.waveform_mpu.plot(pen='c', width=3) def update_plot(self): # 非阻塞读取MPU队列数据,避免阻塞Qt事件循环 while not self.queue_mpu.empty(): try: data = self.queue_mpu.get_nowait() self.set_plotdata('MPU', data[0], data[1]) except multiprocessing.queues.Empty: break # 读取910D队列数据(修正了之前的笔误) while not self.queue_910d.empty(): try: data = self.queue_910d.get_nowait() self.set_plotdata('910D', data[0], data[1]) except multiprocessing.queues.Empty: break def start_animation(self): timer = QtCore.QTimer() timer.timeout.connect(self.update_plot) timer.start(60) # 每60ms更新一次绘图 self.start() def ShowData(Data1, Data2): # 直接把队列传给Plot2D实例,不用依赖全局变量 plotter = Plot2D(Data1, Data2) plotter.start_animation()
主进程代码(main.py)
import multiprocessing as mp import numpy as np import time import PlotData if __name__ == '__main__': # 创建两个进程间通信队列 queue_mpu = mp.Queue() queue_910d = mp.Queue() # 启动绘图子进程 plotting_process = mp.Process(target=PlotData.ShowData, args=(queue_mpu, queue_910d,)) plotting_process.start() # 模拟传感器数据生成 t = np.arange(-3.0, 2.0, 0.01) i = 0.0 try: while True: # 生成MPU模拟数据 mpu_data = np.sin(2 * 2 * np.pi * t) / (2 * np.pi * t + i) queue_mpu.put([t, mpu_data]) # 生成910D模拟数据(测试用) sensor_910d_data = np.cos(2 * np.pi * t + i) queue_910d.put([t, sensor_910d_data]) time.sleep(1) i += 0.1 except KeyboardInterrupt: # 捕获Ctrl+C,优雅终止绘图进程 plotting_process.terminate() plotting_process.join()
关键修改说明
- 队列直接传入绘图实例:不再依赖全局变量,把队列作为参数传递给
Plot2D的构造函数,确保子进程能直接访问到通信队列。 - 非阻塞队列读取:用
get_nowait()配合异常捕获,避免队列空时阻塞Qt的事件循环,保证GUI始终响应流畅。 - 修正数据更新笔误:更新
910D绘图时使用正确的队列数据,不再复用MPU的数据。 - 优雅终止进程:主进程捕获
KeyboardInterrupt信号,主动终止绘图子进程,避免产生僵尸进程。
额外注意事项
- Qt的GUI控件只能由创建它的进程操作,所有绘图更新逻辑必须在子进程内完成(这已经在代码中实现)。
- 如果传感器数据量很大,可以给队列设置
maxsize参数(比如mp.Queue(maxsize=10)),避免内存占用过高。 - 确保PyQtGraph和PyQt/PySide版本兼容,建议使用较新的稳定版本。
内容的提问来源于stack exchange,提问作者arlen
相关产品推荐
相关产品推荐

