使用pyqtgraph实时绘制DataFrame数据的过渡优化问题
问题描述
我有一个包含100条数据的DataFrame,导入Python模块后使用pyqtgraph进行实时绘制。现有代码可正常运行,但两次数据数组切换时的过渡效果很差。我认为需要使用缓冲区,比如用第二个列表存储每100条样本数据,再从中取100条数据用于绘制,绘制完成后再取下一批100条样本。尝试过多种列表实现方式,但目前均未解决问题。现有代码如下:
def update_plot_data(self): from safe_to_excel import df spo2_list = df['Red'].tail(SAMPE_ARRAY).astype(float) # 获取最后100条数据 spo2_list.reset_index(drop=True, inplace=True) # 重置索引 index = 0 # 数据读取起始索引 while index < SAMPE_ARRAY: data_point = spo2_list[index] # 当前索引对应的数据点 print(f"数据点 {index + 1}: {data_point}") # 打印数据点 self.data = np.roll(self.data, -1) self.data[-1] = data_point self.plot_item.setData(y=self.data) # 更新绘图数据 pg.QtCore.QCoreApplication.processEvents() # 更新绘图组件 time.sleep(0.017) # 17毫秒暂停 index += 1 # 处理完100条数据后 # 更新DataFrame `df`并加载新数据 from safe_to_excel import df spo2_list = df['Red'].tail(SAMPE_ARRAY).astype(float) # 获取最后100条数据 spo2_list.reset_index(drop=True, inplace=True) # 重置索引 index = 0 # 重置索引为下一轮循环做准备
解决方案:双缓冲区平滑过渡
原代码的问题在于新旧数据切换时是一次性替换所有绘制点,导致曲线跳变。通过加长环形缓冲区衔接新旧数据,逐点更新绘制内容,就能实现平滑过渡。
修改后的代码实现
import numpy as np import pyqtgraph as pg import time from safe_to_excel import df # 全局导入,避免重复加载开销 def update_plot_data(self): SAMPE_ARRAY = 100 # 初始化2倍长度的缓冲区,用于衔接新旧数据 if not hasattr(self, 'buffer'): initial_data = df['Red'].tail(SAMPE_ARRAY).astype(float).values self.buffer = np.zeros(2 * SAMPE_ARRAY) self.buffer[SAMPE_ARRAY:] = initial_data # 初始填充后100位 while True: # 加载最新的100条数据 new_data_batch = df['Red'].tail(SAMPE_ARRAY).astype(float).values # 逐点将新数据加入缓冲区,同时绘制最后100条实现平滑过渡 for data_point in new_data_batch: # 滚动缓冲区,替换最后一位为新数据点 self.buffer = np.roll(self.buffer, -1) self.buffer[-1] = data_point # 始终绘制缓冲区的最后100条数据 self.plot_item.setData(y=self.buffer[-SAMPE_ARRAY:]) pg.QtCore.QCoreApplication.processEvents() time.sleep(0.017)
关键改动说明
- 全局导入
df,避免循环内重复加载,减少性能开销 - 初始化长度为200的缓冲区,预留空间存储新旧数据的衔接部分
- 新数据到来时,逐点替换缓冲区内容,同时始终绘制缓冲区的最后100条数据,让曲线从旧数据逐步过渡到新数据,消除跳变
- 移除原代码中重复加载数据的冗余逻辑,改为循环内持续拉取最新数据
进阶优化:用Qt定时器替代sleep
time.sleep()可能阻塞Qt事件循环,改用QTimer触发更新更稳定流畅:
def __init__(self): # 初始化组件时设置定时器 self.SAMPE_ARRAY = 100 self.timer = pg.QtCore.QTimer() self.timer.timeout.connect(self.update_single_point) self.timer.start(17) # 约60帧/秒 # 初始化缓冲区和数据索引 initial_data = df['Red'].tail(self.SAMPE_ARRAY).astype(float).values self.buffer = np.zeros(2 * self.SAMPE_ARRAY) self.buffer[self.SAMPE_ARRAY:] = initial_data self.current_batch = initial_data self.batch_index = 0 def load_new_batch(self): # 加载最新的100条数据批次 self.current_batch = df['Red'].tail(self.SAMPE_ARRAY).astype(float).values self.batch_index = 0 def update_single_point(self): if self.batch_index >= self.SAMPE_ARRAY: # 当前批次数据耗尽,加载新批次 self.load_new_batch() # 更新缓冲区 self.buffer = np.roll(self.buffer, -1) self.buffer[-1] = self.current_batch[self.batch_index] # 绘制缓冲区最后100条数据 self.plot_item.setData(y=self.buffer[-self.SAMPE_ARRAY:]) self.batch_index += 1
内容的提问来源于stack exchange,提问作者tenebris in kux
相关产品推荐
相关产品推荐

