Search code examples
pythonpyqtgraphpyside6qgraphicsrectitem

Update QGraphicsRectItem in pyqtgraph without clear()


I have a pyqtgraph widget in my pyside6 app where I plot a huge amount of data. Afterwards, I add a rectangle, QGraphicsRectItem to the plot using .addItem(..). This rectangle I would like to move when a QSlider is changed.

Since the original plot of data takes around 10 sec, I do not want to update the whole pyqtgraph by using .clear() and plot everything again.

Is there an option to only update the position of the rectangle without touching the rest of the plot?

The fundamental code snippets are:

CH3 = np.random.rand(5000,5000)
CH2 = np.random.rand(5000,5000)
CH2_1d = CH2.flatten()
CH3_1d = CH3.flatten()

# here comes the data plot
scatter = pg.ScatterPlotItem(CH2_1d, CH3_1d, size=1, pen=pg.mkPen(None), brush=pg.mkBrush(255, 255, 255, 120))
self.ui.widgetPlot.addItem(scatter)   # the widgetPlot is a widget which is promoted to pyqtgraph in the Qt designer

# here the rectangle is generated
CH2Start = self.ui.sliderCond1CH2.value()[0]
CH3Start = self.ui.sliderCond1CH3.value()[0]
CH2Length = self.ui.sliderCond1CH2.value()[1]-self.ui.sliderCond1CH2.value()[0]
CH3Length = self.ui.sliderCond1CH3.value()[1]-self.ui.sliderCond1CH3.value()[0]

cond1Rect = pg.QtWidgets.QGraphicsRectItem(CH2Start, CH3Start, CH2Length, CH3Length)
cond1Rect.setPen(pg.mkPen('r', width=2))
self.ui.widgetPlot.addItem(cond1Rect)

Any other suggestions to plot the date and draw semi-interactive rectangles is also welcomed. I tried to use Matplotlib as well. But I found no better implementation and pyqtgraph has the nicer zooming options.

Edit:

I made minimal example that should run. You can see the issue when changing one of the QDoubleRangeslider. The code just adds a new rectangle. But i want to either change or replace the old one without replotting the scatterplot.

# ------------------------------------------------------
# ---------------------- 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.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)   # the widgetPlot is a widget which is promoted to pyqtgraph in the Qt designer
    
    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 = pg.QtWidgets.QGraphicsRectItem(CH2Start, CH3Start, CH2Length, CH3Length)
        cond1Rect.setPen(pg.mkPen('r', width=2))
        self.widgetGraph.addItem(cond1Rect)


if __name__ == "__main__":
    app = QtWidgets.QApplication([])

    window = MyWidget()
    window.show()
    app.exec_()

Solution

  • I found an solution by storing the QRect in a global variable and remove it for every sliderChange:

    # ------------------------------------------------------
    # ---------------------- 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))
    
            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)   # the widgetPlot is a widget which is promoted to pyqtgraph in the Qt designer
        
        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]
            
            if self.cond1Rect != None:
                self.widgetGraph.removeItem(self.cond1Rect)
            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_()