
在科学计算和数据可视化应用中,pyqtgraph因其出色的性能和丰富的交互功能而受到青睐。然而,当处理海量数据(例如数百万个散点)时,如何高效地更新图表上的局部元素(如一个矩形选择框)而不必重新绘制整个图表,是一个常见的性能挑战。传统的clear()方法会清除所有已绘制的项并强制全图重绘,这对于大型数据集而言是不可接受的,因为它会导致显著的延迟和用户体验下降。
本教程将深入探讨如何在PyQtGraph中高效地动态更新QGraphicsRectItem,避免不必要的全图刷新,从而保持应用的响应性。
考虑一个典型的场景:你有一个PyQtGraph的PlotWidget,上面绘制了大量散点数据,这个过程可能需要数秒甚至更长时间。在此基础上,你希望添加一个可由用户通过滑块(如QDoubleRangeSlider)控制位置和大小的矩形区域。当滑块值变化时,矩形需要实时更新其位置和尺寸。
如果每次滑块值变化时都简单地创建一个新的QGraphicsRectItem并将其添加到图中,而不移除旧的,那么图上会积累大量重叠的矩形,导致视觉混乱和内存泄漏。更糟糕的是,如果为了清除旧矩形而调用self.widgetGraph.clear(),那么整个散点图也会被清除并需要重新绘制,这会带来巨大的性能开销。
最初的尝试可能是在每次需要更新矩形时,直接创建一个新的QGraphicsRectItem并将其添加到PlotWidget中,如下所示:
import pyqtgraph as pg
from PySide6 import QtWidgets
from superqt import QDoubleRangeSlider
import numpy as np
class MyWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
# ... (其他初始化代码,如滑块和布局)
self.widgetGraph = pg.PlotWidget()
self.plot_data() # 绘制大量散点数据
self.draw_rect_inefficient() # 首次绘制矩形
# 连接滑块信号到更新函数
self.QSlider.valueChanged.connect(self.draw_rect_inefficient)
self.QSlider2.valueChanged.connect(self.draw_rect_inefficient)
def plot_data(self):
# 模拟大量数据绘图
CH3 = np.random.rand(500,500)
CH2 = np.random.rand(500,500)
scatter = pg.ScatterPlotItem(CH2.flatten(), CH3.flatten(), size=1, pen=pg.mkPen(None), brush=pg.mkBrush(255, 255, 255, 120))
self.widgetGraph.addItem(scatter)
def draw_rect_inefficient(self):
# 每次都创建新的矩形,不移除旧的
CH2Start = self.QSlider.value()[0]
CH3Start = self.QSlider2.value()[0]
CH2Length = self.QSlider.value()[1]-self.QSlider.value()[0]
CH3Length = self.QSlider2.value()[1]-self.QSlider2.value()[0]
cond1Rect = pg.QtWidgets.QGraphicsRectItem(CH2Start, CH3Start, CH2Length, CH3Length)
cond1Rect.setPen(pg.mkPen('r', width=2))
self.widgetGraph.addItem(cond1Rect) # 不断添加新的矩形
# ... (主函数运行代码)这种方法的问题在于,每次滑块变化时都会在图上叠加一个新的矩形,而旧的矩形并不会消失。这不仅会导致视觉上的混乱,还会持续消耗内存,最终可能导致应用程序崩溃。
为了解决上述问题,一种直接的改进方法是在每次绘制新矩形之前,先将之前绘制的矩形从图中移除。这需要我们持有对矩形对象的引用。
核心思想:
以下是实现此方案的完整代码:
# ------------------------------------------------------
# ---------------------- main.py -----------------------
# ------------------------------------------------------
from PySide6 import QtWidgets
from PySide6.QtWidgets import*
from superqt import QDoubleRangeSlider
import pyqtgraph as pg
import numpy as np
class MyWidget(QtWidgets.QWidget):
cond1Rect = None # 声明一个类成员变量来存储矩形对象
def __init__(self):
super().__init__()
self.setWindowTitle("pyqtgraph refresh")
self.QSlider = QDoubleRangeSlider()
self.QSlider.setRange(0, 1)
self.QSlider.setValue((0.2, 0.8))
self.QSlider2 = QDoubleRangeSlider()
self.QSlider2.setRange(0, 1)
self.QSlider2.setValue((0.2, 0.8))
# 连接滑块的valueChanged信号到draw_rect方法
self.QSlider.valueChanged.connect(self.draw_rect)
self.QSlider2.valueChanged.connect(self.draw_rect)
self.widgetGraph = pg.PlotWidget()
self.layout = QtWidgets.QVBoxLayout(self)
self.layout.addWidget(self.QSlider)
self.layout.addWidget(self.QSlider2)
self.layout.addWidget(self.widgetGraph)
self.plot_data() # 绘制一次大量散点数据
self.draw_rect() # 首次绘制矩形
def plot_data(self):
# 模拟大量数据绘图,只执行一次
CH3 = np.random.rand(500,500)
CH2 = np.random.rand(500,500)
CH2_1d = CH2.flatten()
CH3_1d = CH3.flatten()
scatter = pg.ScatterPlotItem(CH2_1d, CH3_1d, size=1, pen=pg.mkPen(None), brush=pg.mkBrush(255, 255, 255, 120))
self.widgetGraph.addItem(scatter)
def draw_rect(self):
# 获取滑块值来确定矩形位置和大小
CH2Start = self.QSlider.value()[0]
CH3Start = self.QSlider2.value()[0]
CH2Length = self.QSlider.value()[1]-self.QSlider.value()[0]
CH3Length = self.QSlider2.value()[1]-self.QSlider2.value()[0]
# 如果cond1Rect已经存在,先从图中移除
if self.cond1Rect is not None:
self.widgetGraph.removeItem(self.cond1Rect)
# 创建新的QGraphicsRectItem并更新其引用
self.cond1Rect = pg.QtWidgets.QGraphicsRectItem(CH2Start, CH3Start, CH2Length, CH3Length)
self.cond1Rect.setPen(pg.mkPen('r', width=2))
# 将新的矩形添加到图中
self.widgetGraph.addItem(self.cond1Rect)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
window = MyWidget()
window.show()
app.exec_()通过引入self.cond1Rect并利用removeItem(),我们确保了每次只有一个矩形在图上显示,并且避免了重新绘制整个散点图,从而显著提高了性能。
WeMuseum博物馆微门户小程序主要功能包括展厅展馆、当前展览、馆藏文物、服务指南、活动讲座预约、参观预约等。具有“即开即用,用完即走”特点的微信小程序,创新了博物馆行业的服务方式,是传播博物馆服务和文化的便捷高效途径之一。微信小程序在博物馆中的应用,将会为博物馆文化的传播开辟出新渠道、新路径。我们的思路就是:轻量化前端,深度开发后台。
0
虽然“移除旧项,添加新项”的方法已经解决了重复叠加的问题,但它每次更新时都会销毁旧对象并创建新对象。对于QGraphicsRectItem这种简单的图形项,更高效的做法是直接修改其现有的几何属性,而不是重新创建。
QGraphicsRectItem提供了一个setRect(x, y, width, height)方法,可以直接更新矩形的位置和大小,而无需移除和重新添加。
核心思想:
以下是实现此更优方案的代码:
# ------------------------------------------------------
# ---------------------- main.py (优化版) -----------------
# ------------------------------------------------------
from PySide6 import QtWidgets
from PySide6.QtWidgets import*
from superqt import QDoubleRangeSlider
import pyqtgraph as pg
import numpy as np
class MyWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("pyqtgraph refresh")
self.QSlider = QDoubleRangeSlider()
self.QSlider.setRange(0, 1)
self.QSlider.setValue((0.2, 0.8))
self.QSlider2 = QDoubleRangeSlider()
self.QSlider2.setRange(0, 1)
self.QSlider2.setValue((0.2, 0.8))
self.QSlider.valueChanged.connect(self.update_rect) # 连接到update_rect
self.QSlider2.valueChanged.connect(self.update_rect) # 连接到update_rect
self.widgetGraph = pg.PlotWidget()
self.layout = QtWidgets.QVBoxLayout(self)
self.layout.addWidget(self.QSlider)
self.layout.addWidget(self.QSlider2)
self.layout.addWidget(self.widgetGraph)
self.plot_data() # 绘制一次大量散点数据
self.init_rect() # 首次初始化并添加矩形
def plot_data(self):
CH3 = np.random.rand(500,500)
CH2 = np.random.rand(500,500)
CH2_1d = CH2.flatten()
CH3_1d = CH3.flatten()
scatter = pg.ScatterPlotItem(CH2_1d, CH3_1d, size=1, pen=pg.mkPen(None), brush=pg.mkBrush(255, 255, 255, 120))
self.widgetGraph.addItem(scatter)
def init_rect(self):
# 首次创建矩形并添加到图中
CH2Start = self.QSlider.value()[0]
CH3Start = self.QSlider2.value()[0]
CH2Length = self.QSlider.value()[1]-self.QSlider.value()[0]
CH3Length = self.QSlider2.value()[1]-self.QSlider2.value()[0]
self.cond1Rect = pg.QtWidgets.QGraphicsRectItem(CH2Start, CH3Start, CH2Length, CH3Length)
self.cond1Rect.setPen(pg.mkPen('r', width=2))
self.widgetGraph.addItem(self.cond1Rect)
def update_rect(self):
# 获取滑块值来确定矩形位置和大小
CH2Start = self.QSlider.value()[0]
CH3Start = self.QSlider2.value()[0]
CH2Length = self.QSlider.value()[1]-self.QSlider.value()[0]
CH3Length = self.QSlider2.value()[1]-self.QSlider2.value()[0]
# 直接更新现有矩形的几何属性
self.cond1Rect.setRect(CH2Start, CH3Start, CH2Length, CH3Length)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
window = MyWidget()
window.show()
app.exec_()这种setRect()的方法是更新QGraphicsRectItem几何属性的最优解,因为它避免了对象的创建和销毁开销,仅更新了图形项的内部状态,PyQtGraph会自动处理重绘受影响的区域。
在PyQtGraph中处理大量数据并需要动态更新局部图形项时,避免使用clear()方法进行全图刷新至关重要。通过维护对QGraphicsRectItem等图形项的引用,我们可以选择两种高效的更新策略:
选择正确的更新策略,将使你的PyQtGraph应用在处理大数据可视化时依然保持卓越的性能和响应速度。
以上就是PyQtGraph中QGraphicsRectItem的高效动态更新指南的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号