You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 19:32:02