Search code examples
python-3.xpyqt5pyqtgraph

pyqtgraph cross widget to plot not working?


I am trying to add GraphicsLayoutWidget and LinearRegionItem, that two ratio is 4:1 or m:n, but I don't found any layout in pygtgraph.So I try to the code like below, but unfortunately it don't work and cast out traceback error info.

code


import pyqtgraph as pg
import numpy as np
from PyQt5.QtWidgets import *


class Chart(pg.GraphicsLayoutWidget):
    def __init__(self):
        super().__init__()
        self.data = np.linspace(0, 2*np.pi, 100)
        self.data = np.sin(self.data)
        self.p = self.addPlot(y=self.data)

class LR(pg.GraphicsLayoutWidget):
    def __init__(self, target):
        super().__init__()
        self.p = self.addPlot()  # type:pg.PlotItem
        self.target = target  # type: pg.PlotItem

        rg = (target.data[0], target.data[-1])
        self.p.setXRange(rg[0], rg[1], padding=0)
        self.lr = pg.LinearRegionItem([rg[0], rg[1]])
        self.lr.sigRegionChanged.connect(self.onLRChanged)
        self.p.addItem(self.lr)

    def onLRChanged(self):
        print(self.target, self.lr.getRegion())
        self.target.setXRange(*self.lr.getRegion())


class Win(QWidget):
    def __init__(self):
        super().__init__()
        lay = QVBoxLayout()

        self.chart = Chart()
        self.lr = LR(self.chart)

        lay.addWidget(self.chart)
        lay.addWidget(self.lr)

        lay.setStretch(0, 4)
        lay.setStretch(1, 1)
        self.setLayout(lay)


app = QApplication([])
win = Win()
win.show()
app.exec()

image image

error
The traceback info will show when user drag LinearRegionItem line in both side of the bottom graph(plot)
image

environment
Python 3.8.10 (tags/v3.8.10:3d8993a, May 3 2021, 11:48:03) [MSC v.1928 64 bit (AMD64)] on win32

Name: pyqtgraph Version: 0.12.2
Name: numpy Version: 1.20.3
Name: PyQt5 Version: 5.15.4


Solution

  • The problem has nothing to do with the stretch factor but setting the range to the GraphicsLayoutWidget that expects a QRect, instead of the PlotItem that expects a tuple causing that exception. On the other hand you can improve how to place the initial range based on the range of the data, also it is better to use signals.

    from PyQt5.QtCore import pyqtSignal
    from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget
    
    import pyqtgraph as pg
    
    import numpy as np
    
    
    class Chart(pg.GraphicsLayoutWidget):
        def __init__(self, xdata, ydata):
            super().__init__()
            self.p = self.addPlot(x=xdata, y=ydata)
    
    
    class LR(pg.GraphicsLayoutWidget):
        range_changed = pyqtSignal(float, float)
    
        def __init__(self, xmin, xmax):
            super().__init__()
            self.lr = pg.LinearRegionItem(values=(xmin, xmax))
            self.p = self.addPlot()
            self.p.addItem(self.lr)
            self.lr.sigRegionChanged.connect(self.handle_region_changed)
    
        def handle_region_changed(self, item):
            self.range_changed.emit(*item.getRegion())
    
    
    class Win(QWidget):
        def __init__(self):
            super().__init__()
    
            xdata = np.linspace(0, 2 * np.pi, 100)
            ydata = np.sin(xdata)
    
            self.chart = Chart(xdata, ydata)
            self.lr = LR(xdata.min(), xdata.max())
            self.lr.range_changed.connect(self.chart.p.setXRange)
    
            lay = QVBoxLayout(self)
            lay.addWidget(self.chart, stretch=4)
            lay.addWidget(self.lr, stretch=1)
    
    
    def main():
        app = QApplication([])
        win = Win()
        win.show()
        app.exec()
    
    
    if __name__ == "__main__":
        main()