news 2026/10/22 4:26:17

PyQtGraph实时数据可视化实战:从基础绘图到高性能性能优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PyQtGraph实时数据可视化实战:从基础绘图到高性能性能优化

如果想在桌面应用里画实时数据曲线,又不想被 matplotlib 那种几十毫秒才刷新一帧的龟速拖垮,PyQtGraph 基本是绕不开的选择。这个库在科研仪器控制、传感器数据采集、上位机开发圈子里用得非常频繁,我最早接触它是在做一个多通道生理信号采集的模拟项目,当时拿 matplotlib 刷新 4 条波形,CPU 直接飙到 60% 以上,换 PyQtGraph 之后降到 5% 左右,帧率还翻了好几倍,从那以后就再也没回头。

这篇东西不是官方文档的翻译,而是我在实际项目里把 PyQtGraph 从入门到进阶整个摸了一遍之后,把核心原理、操作细节、踩过的坑全部整理出来。无论你是刚接触 PyQt 的新手,还是已经在用 matplotlib 做可视化、想换更快的方案,都可以照着这篇文章一步步搭出属于自己的实时数据监控界面。

1. PyQtGraph 到底是什么,它解决了什么问题

PyQtGraph 是一个基于 PyQt/PySide 的纯 Python 图形库,核心目标就是高性能的实时数据可视化。它底层直接依赖 Qt 的 Graphics View 框架,同时把大量热点计算丢给 NumPy 数组运算,绘制路径在合适条件下还会走 OpenGL 管线,所以才能做到实时刷新不卡顿。

1.1 和 matplotlib 相比,它强在哪里

matplotlib 对科学出版级的静态图很友好,但它的架构决定了它不适合做高频交互界面。它每次重绘都要重新创建大量图形对象,坐标轴、刻度、标签全走一遍,这个开销在实时数据流面前就是灾难。PyQtGraph 的思路是:能复用的对象绝不重建,能批量更新的数据绝不逐个赋值。你把 PlotDataItem 创建一次,之后只是替换它的 setData 里的数组,底层会尽量只重绘发生变化的区域,这就是它性能优势的根本来源。

我个人的实测结果,同一台机器上绘制 1 万个点的滚动波形,matplotlib 刷新一次大概 80~120 毫秒,PyQtGraph 能跑到 10 毫秒以内。如果开启 OpenGL 加速,甚至能达到 3~5 毫秒。这不是说 matplotlib 烂,而是两者的设计目标本来就不同。

1.2 典型应用场景和适合人群

PyQtGraph 最常用的地方是这几类:

  • 仪器仪表上位机:示波器、光谱仪、温控设备的数据实时显示
  • 传感器采集系统:温度、压力、振动、电流电压等多通道波形
  • 生物医学信号处理:心电、脑电、肌电信号的可视化回放
  • 机器人调试:关节角度、力矩、轨迹数据的实时监控
  • 量化交易:行情走势、策略回测结果的快速绘图

适合用它的人也很明确:你已经在用 PyQt/PySide 做桌面应用,又需要高性能绘图能力,不希望再引入一个重型依赖。它本身是 MIT 协议,商用也没有顾虑。

1.3 和 PyQtGraph 配合的生态选型

PyQtGraph 不是孤立的,它和 PyQt5/PyQt6/PySide2/PySide6 都兼容。这里要提一个选型上的关键点:PyQtGraph 官方维护支持 PySide6 的力度在不断加大,新版本对 pyqtgraph 的 Qt 绑定已经做到了自动识别。你在写代码时用import pyqtgraph as pg,它内部会自动帮你决定用当前环境里的哪个绑定库。

从我自己踩坑经验来看,如果新项目从零开始,优先推荐 PySide6。原因有二:LGPL 协议更友好(PyQt 的 GPL 对商用有一些额外要求),官方文档和示例也逐渐向 PySide6 倾斜。当然你如果已经有一套 PyQt5 的老项目,PyQtGraph 依然可以无缝接入,不需要改绑定库。

2. 环境准备与第一个 PyQtGraph 窗口

用 PyQtGraph 之前,先把环境理清楚。很多人一开始就卡在安装阶段,所以这里单独聊一聊依赖关系、安装方式、还有最基础的窗口生成。

2.1 依赖清单与推荐版本组合

PyQtGraph 的核心依赖只有三个:Python(3.8 以上都行)、NumPy、以及任意一套 Qt 绑定库。如果要开启 OpenGL 加速,还需要安装PyOpenGL。实际项目里我推荐把以下组合作为基准:

组件推荐版本说明
Python3.9~3.12太老的版本对新 NumPy 支持差
NumPy1.24 以上PyQtGraph 内部大量依赖 NumPy 数组操作
PySide66.5 以上最推荐,协议友好且维护活跃
PyQtGraph0.13.7 及以上新版对 PySide6 支持非常完善
PyOpenGL3.1.7 以上仅 OpenGL 加速需要

安装方式非常直接。如果你用 PySide6,一条命令就能搞定:

pip install pyqtgraph PySide6 PyOpenGL

如果只是学习,不想装 PySide 相关的完整环境,也可以直接用pip install pyqtgraph pyqt5,两个都行。我自己习惯建独立的虚拟环境来装,避免全局环境里 qt 依赖冲突。你如果管理多个 Python 项目,这一步千万别省。

2.2 5 分钟写出第一个实时窗口

先来一个最基础的静态图窗口,把流程跑通:

import pyqtgraph as pg from pyqtgraph.Qt import QtCore, QtWidgets app = QtWidgets.QApplication([]) win = pg.GraphicsLayoutWidget(show=True, title="Basic PyQtGraph") win.resize(800, 500) plot = win.addPlot(title="简单曲线") plot.plot([1, 2, 3, 4, 5], [3, 1, 4, 2, 6], pen='r') QtWidgets.QApplication.instance().exec()

这段代码干了四件事:创建 QApplication、创建 GraphicsLayoutWidget 窗口、在里面加一个 Plot 对象、直接绘制一条红色折线。注意pg.plot和win.addPlot是两条完全不同的路径——前者是便捷函数,后者的控件可以在复杂的窗口布局里自由排列。

从 Qt 事件循环的角度讲,exec()是必须的。没有它窗口会一闪而过。如果你是在 Jupyter Notebook 里跑,需要加上%gui qt或者在创建 QApplication 前设置环境变量。

2.3 理解 GraphicsLayoutWidget 和 PlotItem 的关系

用 PyQtGraph 容易犯的第一个错误就是分不清 Widget 和 Item。Qt 原生概念里,Widget 是有实际窗口句柄的控件,Item 是绘制在场景里的图形对象。PyQtGraph 的GraphicsLayoutWidget是一个 Widget,它的作用相当于一个画板;而PlotItem是 Item,承载坐标轴、数据曲线、图例等所有绘图元素。你在GraphicsLayoutWidget上调用addPlot(),本质上是往画布里添加了一个绘图区域,之后可以继续用addLayout()添加其它区域,做多面板布局。

win = pg.GraphicsLayoutWidget() p1 = win.addPlot(row=0, col=0) # 左上 p2 = win.addPlot(row=1, col=0) # 左下 p3 = win.addPlot(row=0, col=1, rowspan=2) # 右侧跨两行

这样就能很轻松地搭建一个 2x2 的自定义布局,这在多通道数据展示里特别常用。

3. 核心绘图功能拆解:从曲线到图像

PyQtGraph 支持的图形类型非常多,但实际项目里高频用到的就几类:折线图、散点图、条形图、图像热力图、等值线图。下面逐一说明它们的核心用法和不为人注意的细节。

3.1 折线图和 PlotDataItem 的正确用法

折线图是最基础也是最常用的功能。官方推荐的方式是把数据包装成PlotDataItem,因为这样后续更新数据可以只修改内部数据源,不需要从场景里删除重建:

curve = plot.plot(pen=pg.mkPen(color=(255, 100, 100), width=2)) curve.setData(y_data, x_data)

一个小细节:setData的第一个参数是 y,第二个是 x。很多从 matplotlib 转过来的人会习惯写x, y,结果图像完全错乱。PyQtGraph 的函数签名是setData(y, x),plot()同样如此,这一点非常坑,但文档里其实写得很清楚。

另外如果数据量很大(超过几万个点),建议开启clipToView选项,让 Qt 只绘制视口范围内的点:

curve.setClipToView(True)

这样缩放时性能提升非常明显。

3.2 散点图和分布显示

散点图在 PyQtGraph 里对应ScatterPlotItem。它和折线图最大的区别是每个点都可以独立设置位置、大小、颜色和形状。

scatter = pg.ScatterPlotItem( x=x_data, y=y_data, size=10, pen=pg.mkPen(None), brush=pg.mkBrush(0, 0, 255, 120) ) plot.addItem(scatter)

这里有个重要技巧:把pen设为None可以取消散点描边,让整体视觉更干净。brush的第四个参数是透明度,适合大量点重叠时体现密度分布。如果你要做一个聚类结果的二维散点图,不同类别的点可以用不同颜色,PyQtGraph 性能足以轻松应对几万个散点。

3.3 条形图和柱状数据

条形图在 PyQtGraph 里的实现方式是BarGraphItem。注意它不接受x和y数组,而是需要x0/width/height或者x/y这种形式。赋值有点绕,但搞明白一次就够了:

bg = pg.BarGraphItem( x=[0, 1, 2, 3, 4], height=[1.2, 3.4, 2.1, 4.5, 0.9], width=0.6, brush='g' ) plot.addItem(bg)

这里 x 表示柱的中心位置,width 决定柱子宽度。如果你需要绘制实时更新的柱状频谱,每帧调用setOpts去更新 height 即可,效率也很高。

3.4 图像与热力图显示

医疗影像和热力图的实时显示是 PyQtGraph 的强项,它通过ImageView控件来承载图像数据。

img_view = pg.ImageView() img_view.setImage(data_2d) # data_2d 是一个 numpy 2D 数组

ImageView提供了内置的直方图控件,可以直接调整亮度和对比度。它还支持setImage的第 3 个参数autoLevels=False,这在处理连续帧时非常重要——如果不关闭自动色阶,每一帧的亮度都会重新统计,动态影像会闪烁。实际项目中我通常这样做:

img_view.setImage(new_frame, autoLevels=False)

如果你不理解autoLevels的含义,可以类比手机相机的 HDR:它每张照片都会重新调整曝光。但视频场景下每帧都自动调整,画面就会狂闪。所以连续帧显示时一定要统一色阶范围。

3.5 等值线图和三维表面显示

对科研用户来说,等值线图也很常用。pg.isocurve()可以提供等值线数据,但更常见的是借助gaussianFilter平滑后再画。PyQtGraph 自带SurfacePlot之类的高级功能,但实际绘制三维曲面更多依赖GLSurfacePlotItem:

from pyqtgraph.opengl import GLSurfacePlotItem

这块需要比较完整的 OpenGL 环境,新手可以暂时跳过,知道有这个能力就行。

4. 实时数据刷新机制:性能的核心

实时数据可视化是最多人关心的地方。这一部分我详细展开数据更新的原理、几种更新策略、以及如何减少 UI 卡顿。

4.1 为什么 setData 比 addItem 高效得多

PyQtGraph 里最影响性能的,就是你的数据更新方式。假如你用一个 Timer 每 30 毫秒向场景里addItem一个新的曲线,那 Qt 场景会不断膨胀,最终卡成幻灯片。正确的做法是创建一个PlotDataItem,然后在 timer 里反复调用它的setData方法。因为setData底层不会重建整套绘图对象,只是把内部数组的引用换掉,并且把重绘范围限制在有变化的区域。

这里有确定性结论:在绝大多数实时场景下,我们应该只做一次plot()创建,之后的每个更新周期只做setData。这是从 matplotlib 转型过来的人最容易犯的第一个错误,必须改掉。

4.2 定时器还是 QThread,选哪个

实时数据的来源不一样,更新方式也该不一样。我根据自己的经验整理成下表,你可以直接对着抄:

数据来源推荐更新方式优点
模拟数据、定时生成QTimer实现简单,开销小
串口/网络流QThread + Signal不阻塞 UI,数据到达即刷新
高频采集(>100Hz)数据缓冲+批量更新避免每帧都触发重绘

用 QTimer 时注意一点:QTimer最小间隔受操作系统调度影响,实际精度远达不到标称值。如果你看到曲线抖动,不要先用「算法问题」去排查,先确认是不是定时器抖动导致的。

timer = QtCore.QTimer() timer.timeout.connect(update_plot) timer.start(33) # 约 30 fps

4.3 应用缓冲批量更新提升帧率

如果你的数据显示频率非常高,比如采集卡输出每秒 5000 个点,完全没必要每个点都刷新一次 UI。常见做法是缓冲区攒一批数据,达到一定数量或一定时间间隔后再一次性更新。我一般使用 numpy 数组作为环形缓冲区,保持固定大小,每次只替换头部,这样标记为新数据然后整体 setData。

BUFFER_SIZE = 2000 data_buf = np.zeros(BUFFER_SIZE) idx = 0 def on_new_value(v): global idx data_buf[idx % BUFFER_SIZE] = v idx += 1 if idx % 50 == 0: # 累计 50 个点刷新一次 display_curve.setData(data_buf)

这种批量更新的方式原理解释起来也很直观:UI 重绘本身有固定开销,你把这笔开销从「每个数据点付一次」变成「每批数据点付一次」,自然省下了大量 CPU。做数据可视化性能优化,核心就是尽量降低重绘次数,而不是让一次重绘更快。

4.4 开启 OpenGL 加速的注意事项

PyQtGraph 里开 OpenGL 很简单,在创建窗口后设置PlotItem的渲染模式即可:

plot.setUseOpenGL(True)

不是所有绘图元素都支持 OpenGL。折线图支持得最好,散点图支持一般,绘图项和一些高档功能可能退化。开启 OpenGL 之后不要急着高兴,先对比实际帧率,确定是否真的更快——在某些老旧显卡或者驱动不完善的机器上,OpenGL 反而不如 CPU 绘制稳定。我记得在某个老旧嵌入式平台调试时,一开 OpenGL 花纹直接错乱,关掉反而一切正常。

5. 交互操作与样式定制:让界面像个正经产品

一个能用和好用的差距,很大程度体现在交互设计上。PyQtGraph 提供了一系列交互机制,合理利用它们可以让你的工具类软件直接拥有「商业软件」的质感。

5.1 缩放、平移和坐标十字线

PyQtGraph 的 PlotItem 默认支持中键拖拽平移、滚轮缩放、右键弹出菜单。如果你想禁用某一种交互,可以通过setMouseEnabled(x=False, y=False)单独控制。比如时间序列只允许横向缩放,把 y 轴缩放关掉,可以防止用户误操作把幅值轴拉爆。

坐标十字线(crosshair)是调试数据时非常好用的功能。官方推荐的做法是用InfiniteLine叠加。我常用的是SignalProxy加Scene.sigMouseMoved信号:

vline = pg.InfiniteLine(angle=90, movable=False) hline = pg.InfiniteLine(angle=0, movable=False) plot.addItem(vline, ignoreBounds=True) plot.addItem(hline, ignoreBounds=True) def mouse_moved(evt): pos = plot.vb.mapSceneToView(evt) vline.setPos(pos.x()) hline.setPos(pos.y())

这个组合在数据排查时简直救命。你可以直接看到光标所在位置的 X/Y 数值,不需要脑补坐标。

5.2 坐标轴、图例和标题设置

标题要穿插一点实际经验:很多人设置标题直接调plot.setTitle("xxx"),这个标题默认颜色和背景对比度很低,在浅色背景下几乎看不见。所以我通常习惯用setLabel配合单独的颜色设置。

图例用法也比较简单:

plot.addLegend() curve1 = plot.plot(..., name="信号A", pen='r') curve2 = plot.plot(..., name="信号B", pen='b')

如果你做了多面板布局,每个面板都可以有独立的坐标轴和标题,不互相干扰。左下角的 axis 方向需要特别留意,PyQtGraph 默认把第 0 轴放在下边,第 1 轴放在左边,和 matplotlib 习惯一致。

5.3 控件的组合:Checkbox、SpinBox 和绘图区联动

在真实桌面工具里,绘图区通常不是孤立的。你会有启动/停止按钮、阈值输入框、通道选择下拉框。PyQtGraph 本身是绘图库,但它搭档 Qt Widgets 毫无压力。把GraphicsLayoutWidget放进QMainWindow的 central widget 里,旁边加几个控件,组成一套完整的控制台。

举个例子:用 QSpinBox 控制波形幅值,用 QCheckBox 控制是否显示网格:

grid_checkbox = QtWidgets.QCheckBox("显示网格") grid_checkbox.toggled.connect(plot.showGrid) amp_spin = QtWidgets.QSpinBox() amp_spin.setRange(1, 100) amp_spin.valueChanged.connect(update_amp)

这样把 Qt 原生控件的信号和 PyQtGraph 的接口串起来,一个简单的采集监控界面雏形就出来了。PyQtGraph 不会限制你用其它 Qt 能力,你可以自由发挥。

6. 性能优化策略与常见坑

这一部分绝大多数来自我在实际项目里的真实记录。PyQtGraph 看似简单,真正把它用对、用顺,还是有很多细节。

6.1 如何解决大点数曲线渲染卡顿

如果你要显示几十万甚至上百万个数据点,直接 setData 依然会卡。针对这类场景,PyQtGraph 提供了一个利器:setDownsampling(ds=auto)和setClipToView(True)。它的原理是:当数据点数远大于屏幕像素数时,先将数据做降采样,只保留每个像素区间的最大最小值,从而避免绘制过多不可见点。这个降采样是实时的、自动的,对用户完全透明。

curve.setDownsampling(ds=1, auto=True) curve.setClipToView(True)

设置之后,哪怕你往里面丢 50 万个点,界面也能顺滑缩放平移。不过要注意,降采样用的是局部极值法,对信号峰值形态保留效果很好,但在统计意义上会损失一些随机细节。如果你在调试传感器原始数据,建议降采样只用于「浏览模式」,细看数据时临时关闭。

6.2 多曲线绘图时的性能陷阱

多曲线场景下,最容易踩的坑是每条曲线用独立的高消耗图元,比如给每条曲线加渐变填充、阴影、或者开启不必要的高分辨率抗锯齿。PyQtGraph 默认的抗锯齿已经足够好,你在QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)之后再额外调用pg.setConfigOptions(antialias=True),会让所有绘图元素默认抗锯齿。但抗锯齿是性能杀手,如果曲线数量很多且实时刷新,不建议全局开抗锯齿,只对少数重点曲线单独配制更好。

另一个细节是:把多条曲线放在同一个 PlotItem 中,比开多个 PlotItem 更高效。因为场景的重绘粒度是 Item 级别的,多条曲线在一个 PlotItem 里共享一次坐标变换计算。

6.3 OpenGL 的兼容性和稳定性问题

之前已经说过 OpenGL 的加速效果和硬件驱动强相关。这里再补充一个非常实际问题:在有些机器上开启 OpenGL 后,截屏或者用录屏软件抓取窗口内容会变成黑屏,这是 OpenGL 混成和桌面合成器之间的兼容问题。如果你做了工业级交付,这台机器用不上 GPU 加速,那就默认关闭 OpenGL,改用 CPU 绘制,稳定性和兼容性都更好。

6.4 抗锯齿、DPI 缩放和清晰度

在 Windows 高 DPI 环境下,PySide6 应用容易出现字体和绘图模糊。最有效的解决办法是在程序最开始设置 Qt::AA_EnableHighDpiScaling 属性,并调用pg.setConfigOptions(antialias=True)。这里的顺序很重要:先设置 QApplication 的属性,再创建 QApplication 实例,最后导入 PyQtGraph 并画图。如果顺序反了,DPI 缩放不生效,字全是糊的。

import sys from pyqtgraph.Qt import QtCore, QtWidgets QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True) app = QtWidgets.QApplication(sys.argv) import pyqtgraph as pg pg.setConfigOptions(antialias=True)

6.5 数据对象生命周期管理与内存泄漏

PyQtGraph 里最常见的隐性内存泄漏,是把PlotDataItem以局部变量的形式创建之后,没有保留引用,但仍存在于场景中。当你想清空旧数据、重新绘制新曲线时,旧曲线没有被正确移除,场景里的 Item 数量持续增长,内存越用越多。

正确做法是把所有动态添加的绘图项存到列表里,清空时逐个调用plot.removeItem(item),或者调用plot.clear()。如果是长期运行的采集程序,需要特别注意这一点,它不会立刻报警,但会让你的程序运行几小时后越来越卡。

7. 常见问题与排查技巧实录

这里整理一份我项目过程中遇到的经典问题速查表,很多问题在网上问来问去都找不到确切答案,实际上都是几个基础细节没搞对。

现象根本原因解决方案
曲线显示顺序反了setData(y, x) 参数记反统一使用 y, x 顺序
刷新实时图时 UI 卡死在 UI 线程里做了大量耗时计算耗时计算放入 QThread,只把结果发回主线程
波形显示闪屏未关闭 autoLevelssetImage(..., autoLevels=False)
大量点绘制卡顿未开降采样setDownsampling + setClipToView
文字模糊没有启用高 DPI 缩放启动最前设置 AA_EnableHighDpiScaling
图例不显示创建曲线时忘了给 name 参数plot(..., name="曲线名") 即可
窗口关闭后进程仍不退有 QTimer 还在跑关闭窗口时 stop timer

先说一个我印象很深刻的问题:某次做一个实时频谱分析工具,曲线刷新频率 30 fps,逻辑上每一步都正确,但 UI 就是偶发卡顿。排查到最后发现罪魁祸首是在 UI 线程里做了一个 FFT 计算,这个计算耗时 50 多毫秒,直接把事件循环的刷新节奏打乱了。解决方案非常简单:把 FFT 丢到 QThread 里算,算完再通过信号把频谱数据传回主线程更新曲线。所以,任何耗时超过几毫秒的操作,都别放在 UI 线程里。

另一个高频坑是GraphicsLayoutWidget.show()之后忘了保留引用,导致控件被 Python 垃圾回收。现象就是你看到一个窗口闪了一下就消失,程序也不报错。如果你在类里直接把win = pg.GraphicsLayoutWidget()作为局部变量,后面没有持有引用,Qt 的 C++ 对象虽然还在,Python 包装对象被回收后行为就会变得诡异。务必把窗口对象挂到 QMainWindow 或某个长期存活的对象上。

还有一点:PyQtGraph 的SignalProxy是一个非常实用的工具。当你想监听鼠标移动事件刷新数据信息时,Scene.sigMouseMoved的信号频率可能非常高,直接触发槽函数会拖慢 UI。用SignalProxy做去抖,每次移动只触发有限的回调。我在做鼠标十字线追踪时,这个技巧帮我省掉了大量无效计算。

8. 从简单曲线到复杂仪表盘的完整示例

把前面所有东西串起来,我写一个相对完整的上位机数据监控示例。这个例子模拟一个多通道传感器数据源,包含实时曲线、散点统计和图像显示,你看完直接可以照着改成自己的采集程序。

8.1 主窗口与布局设计

import sys import numpy as np import pyqtgraph as pg from pyqtgraph.Qt import QtCore, QtWidgets QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True) app = QtWidgets.QApplication(sys.argv) pg.setConfigOptions(antialias=True) win = QtWidgets.QMainWindow() win.setWindowTitle("多通道实时监控模拟") central = pg.GraphicsLayoutWidget() win.setCentralWidget(central) # 两个波形通道、一个图像区 p_wave1 = central.addPlot(row=0, col=0, title="通道A") p_wave2 = central.addPlot(row=1, col=0, title="通道B") p_spect = central.addPlot(row=0, col=1, title="散点分布") img_view = pg.ImageView()

8.2 模拟数据更新逻辑

N = 1000 t = np.linspace(0, 10, N) wave1 = np.sin(t) wave2 = np.cos(t) curve1 = p_wave1.plot(pen='y') curve2 = p_wave2.plot(pen='c') scatter = pg.ScatterPlotItem() p_spect.addItem(scatter) frame = np.random.rand(64, 64) def update(): global wave1, wave2, frame wave1 = np.roll(wave1, 1) wave1[0] = np.sin(QtCore.QDateTime.currentMSecsSinceEpoch() / 1000.0) + np.random.normal() curve1.setData(wave1) wave2 = np.roll(wave2, 1) wave2[0] = np.cos(QtCore.QDateTime.currentMSecsSinceEpoch() / 1000.0) + np.random.normal() curve2.setData(wave2) scatter.setData( x=np.random.randn(200), y=np.random.randn(200), size=5, brush=pg.mkBrush(100, 200, 255, 80) ) frame = np.roll(frame, 1, axis=0) frame[0] = np.random.rand(64) img_view.setImage(frame, autoLevels=False) timer = QtCore.QTimer() timer.timeout.connect(update) timer.start(50) win.show() app.exec()

这个小例子里有几个细节值得注意:wave1 和 wave2 时刻保留各自的引用,update 里只变更局部数组内容再 setData,不产生新的 ProcessDataItem;散点图每次刷新是重新 setData,而不是 addItem;图像帧用 np.roll 做滚动更新,模拟数据流式到达。整个 50 毫秒刷新一次,CPU 占用极低。

你在做真实项目时,只需要把 update 里的随机数据替换成真实的串口/网络/采集卡数据源,把timer.start(50)改成适合你数据频率的周期即可。

最后聊聊我个人在实际项目里的两个心得

第一个心得是:能用 PyQtGraph 原生 API 解决的,不要自己去拼 OpenGL 或者 SVG。PyQtGraph 的封装已经帮了你一半,自己再往底层钻,工程复杂度会成倍上升,而且未必更快。很多卡顿问题的根源不在渲染管线,而在业务逻辑里是不是把不必要的计算和 IPC 放在 UI 线程里。

第二个心得是:实时可视化调试时,不要把性能优化作为最先考虑的事情。先把功能跑通,数据链路正确,再回头用 profile 工具确认瓶颈在绘图还是算法。我见过很多人在开发前期纠结于是不是要用 OpenGL、是不是要开抗锯齿,结果最后性能瓶颈却是在一个time.sleep(0.1)上。把精力放在真正影响结果的地方,效率才是最高的。

如果这篇文章对你有帮助,建议拿着最后那个完整示例跑一遍,再尝试把波形换成真实数据源,亲手体验一下从传感器到屏幕整个数据链路的构建过程。以后做任何桌面端实时可视化需求,PyQtGraph 大概率都能稳稳接住。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/22 4:26:08

QT开发环境搭建全攻略:从版本选型到VS2019集成避坑指南

1. 为什么还要聊QT开发环境搭建这件事刚入行那会儿,我对QT的印象就是“一个做界面的库”。直到接手一个工业上位机项目,客户要求界面流畅、跨平台、还得跟底层硬件通信,我才真正意识到QT这套东西的体量有多大。它不是简单的UI框架&#xff0c…

作者头像 李华
网站建设 2026/10/22 4:25:15

GPT-6智能界面开放,AI速递11条深度解读

10月8日这天的AI圈,节奏快得有点不讲道理。上午还在讨论OpenAI开放GPT-6智能界面带来的交互变化,下午就被新模型、视频生成、智能体工具链的更新刷了屏。衍辉AI速递这一期把11条值得留意的资讯整理完了,有头条级别的平台变动,也有…

作者头像 李华
网站建设 2026/10/22 4:25:08

基于知识图谱与RAG的GEO系统:让品牌被AI搜索引用

品牌方现在最焦虑的事情之一,是用户在AI搜索里根本搜不到自己。这不是危言耸听。身边越来越多的用户,已经习惯打开生成式引擎直接问“XX面膜敏感肌能用吗”“XX和YY哪个更适合油皮”,而回答这些问题的不是品牌官网,也不是电商详情…

作者头像 李华
网站建设 2026/10/22 4:25:07

AI日报:多模态大模型、开源代码与算力调度新动向

今天(2026年10月8日)照例把各路AI信息源过了一遍,整理出这份AI日报。说实话,每天打着“重磅”“首发”“超越”旗号的消息太多了,真正值得花时间细看的其实没几条。所以我没打算做纯新闻搬运,而是挑出几条我…

作者头像 李华
网站建设 2026/10/22 4:23:32

Agent记忆系统设计:前端工程师的转型断层带

1. 这不是“加个缓存”就能解决的记忆问题“前端转型 Agent 开发”这个系列标题,我看到过太多人把它当成一句轻飘飘的口号——仿佛只要把 React 换成 LangChain、把 useState 换成 run(),就能摇身一变成为 AI 工程师。但真正动手做过几个完整 Agent 的人…

作者头像 李华
网站建设 2026/10/22 4:23:13

JSP学生信息管理系统源码:含角色权限与MySQL部署全指南

简介:这是一份面向Java Web初学者与课程设计实践者的JSP学生信息管理系统源码,基于JSPServletMySQL三层架构实现,覆盖学生、教师、管理员三类角色的完整业务流程,适用于高校Web开发实训、毕业设计参考及Java EE入门项目演练。压缩…

作者头像 李华