5

I want to synchronize the X-Axis of several pyqtgraph plots. When the user rescales the X-Axis with mouse interactions (e.g. scroll-wheel while mouse on x-Axis) I want to assign the same changes to all the other plots. So how do I do this?

I derived a minimized code from a basic example below.

Do I have to overwrite the viewRangeChanged() functions of w1 and w2?

import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph.console
import numpy as np

from pyqtgraph.dockarea import *


win = QtGui.QMainWindow()
area = DockArea()
win.setCentralWidget(area)
win.resize(1000,500)
win.setWindowTitle('pyqtgraph example: dockarea')


d1 = Dock("Dock1")
d2 = Dock("Dock2")
area.addDock(d1, 'bottom')
area.addDock(d2, 'bottom', d1)

w1 = pg.PlotWidget(title="Dock 1 plot")
w1.plot(np.random.normal(size=100))
d1.addWidget(w1)

w2 = pg.PlotWidget(title="Dock 2 plot")
w2.plot(np.random.normal(size=100))
d2.addWidget(w2)

win.show()

## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
    import sys
    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()

This question has a follow up here with another answer to this question.

user_na
  • 2,154
  • 1
  • 16
  • 36

3 Answers3

6

We need to use the sigRangeChanged signal and connect it to a slot, the problem is that the change of the range another item would generate the signal sigRangeChanged and so on generating an infinite loop, to solve this you must disconnect those signals before making the modifications and reconnect them to the final.

w1.sigRangeChanged.connect(onSigRangeChanged)
w2.sigRangeChanged.connect(onSigRangeChanged)

def onSigRangeChanged(r):
    w1.sigRangeChanged.disconnect(onSigRangeChanged)
    w2.sigRangeChanged.disconnect(onSigRangeChanged)
    if w1 == r:
        w2.setRange(xRange=r.getAxis('bottom').range)
    elif w2 == r:
        w1.setRange(xRange=r.getAxis('bottom').range)

    w1.sigRangeChanged.connect(onSigRangeChanged)
    w2.sigRangeChanged.connect(onSigRangeChanged)

Example:

import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
from pyqtgraph.dockarea import *
import sys


def onSigRangeChanged(r):
    w1.sigRangeChanged.disconnect(onSigRangeChanged)
    w2.sigRangeChanged.disconnect(onSigRangeChanged)
    if w1==r:
        w2.setRange(xRange=r.getAxis('bottom').range)
    elif w2 == r:
        w1.setRange(xRange=r.getAxis('bottom').range)

    w1.sigRangeChanged.connect(onSigRangeChanged)
    w2.sigRangeChanged.connect(onSigRangeChanged)

app = QtGui.QApplication(sys.argv)

win = QtGui.QMainWindow()
area = DockArea()
win.setCentralWidget(area)
win.resize(1000,500)
win.setWindowTitle('pyqtgraph example: dockarea')


d1 = Dock("Dock1")
d2 = Dock("Dock2")
area.addDock(d1, 'bottom')
area.addDock(d2, 'bottom', d1)

w1 = pg.PlotWidget(title="Dock 1 plot")
it=w1.plot(np.random.normal(size=100))

d1.addWidget(w1)

w2 = pg.PlotWidget(title="Dock 2 plot")
w2.plot(np.random.normal(size=100))
d2.addWidget(w2)

w1.sigRangeChanged.connect(onSigRangeChanged)
w2.sigRangeChanged.connect(onSigRangeChanged)

win.show()

sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
5

Better yet,

Instead of disconnecting then reconnecting signals, it is possible to use blockSignals.

here is a generic way to synchronize any number of plots :

syncedPlots = [w1, w2, w3] # put as many plots as you wish

def onSigRangeChanged(r):
    for g in syncedPlots:
        if g !=r :
            g.blockSignals(True)
            g.setRange(xRange=r.getAxis('bottom').range)
            g.blockSignals(False)

for g in syncedPlots:
     g.sigRangeChanged.connect(onSigRangeChanged)
Dr ALOUI
  • 331
  • 4
  • 8
3

There is a better answer in this question:

Instead of connecting to the sigRangeChanged event we can directly link the axes scales by w2.setXLink(w1).

Compholio
  • 733
  • 1
  • 9
  • 17
  • Yes, this is the same answer as in the linked follow up question https://stackoverflow.com/questions/44612914 – user_na Feb 19 '21 at 08:58