How to synchronize two x axes with different ranges in one PlotWidget?

1,051 views
Skip to first unread message

Cho

unread,
Jul 14, 2016, 11:41:15 AM7/14/16
to pyqtgraph
Hi everyone!
I have a question regarding plotting two x axes in one PlotWidget. I've seen a few examples regarding multiple axes in one plot but to no avail.
What I try to accomplish is the following:

I have a single line that I want plot in a PlotWidget and visualize that line using two differently scaled x axes. E.g. Let's say the bottom x axis to be in units of "Frames/Pictures taken" and the upper x axis to be in units of "Seconds". Hence, the mapping from one axis to the other should be strict and maintained when I zoom or pan the curve. In the "MultiplePlotAxes.py" example this is not the case since different datasets are plotted; zooming in only changes on y axis in that example. However, I need the two axes to zoom synchronized while maintaining the relationship between both x scales. Here is the code I pieced together not knowing what I'm really doing...:

# -*- coding: utf-8 -*-


import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui


pg.mkQApp()

MainPlot = pg.PlotWidget()
MainPlot.show()
MainPlot.setWindowTitle('Dual x axis example, synchronized zoom on different axes')
AuxPlot = pg.ViewBox()


#AuxPlot.setXLink(MainPlot) #this line will cause the two x axes to display the same range which is not wanted in this example

AuxPlot.setYLink(MainPlot)  #this will synchronize zooming along the y axis

MainPlot.showAxis('top')
MainPlot.scene().addItem(AuxPlot)
AuxPlot.setGeometry(MainPlot.getPlotItem().vb.sceneBoundingRect())
MainPlot.getAxis('top').linkToView(AuxPlot)

##plot data
MainPlot.plot([0,1,2,3],[0,1,2,3], pen="w")
AuxPlot.addItem(pg.PlotCurveItem([-1,0,1,2],[0,1,2,3], pen="b")) #this data is scaled differently along the x axis while y is the same as in MainPlot

## This commented section was taken from another user's example but
##only one x axis zooms while the other is unaffected

def updateViews():
    ## view has resized; update auxiliary views to match
    global MainPlot, AuxPlot
    AuxPlot.setGeometry(MainPlot.getPlotItem().vb.sceneBoundingRect())
    ## need to re-update linked axes since this was called
    ## incorrectly while views had different shapes.
    ## (probably this should be handled in ViewBox.resizeEvent)
    AuxPlot.linkedViewChanged(MainPlot.getPlotItem().vb, AuxPlot.YAxis)

updateViews()
MainPlot.getPlotItem().sigRangeChanged.connect(updateViews)

##end of other user's example

## 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_()

I included a snippet from another user (See post here: https://groups.google.com/forum/#!topic/pyqtgraph/7wQeFe2iVUw) but I don't understand what it is really doing (especially the line

AuxPlot.linkedViewChanged(MainPlot.getPlotItem().vb, AuxPlot.YAxis)

I then tried to manually set the viewed range by calculating the upper x axis range from the currently displayed lower x axis. This gives me synchronized zooming/panning but now there is a small offset between the two x axes (I think because there are in principle two datasets being displayed). Here is that code using manual setting the range of the upper x axis:

# -*- coding: utf-8 -*-


import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui


pg.mkQApp()

MainPlot = pg.PlotWidget()
MainPlot.show()
MainPlot.setWindowTitle('Dual x axis example, synchronized zoom on different axes')
AuxPlot = pg.ViewBox()
Shift = 1


#AuxPlot.setXLink(MainPlot) #this line will cause the two x axes to display the same range which is not wanted in this example

AuxPlot.setYLink(MainPlot)  #this will synchronize zooming along the y axis

MainPlot.showAxis('top')
MainPlot.scene().addItem(AuxPlot)
AuxPlot.setGeometry(MainPlot.getPlotItem().vb.sceneBoundingRect())
MainPlot.getAxis('top').linkToView(AuxPlot)

##plot data
MainPlot.plot([0,1,2,3],[0,1,2,3], pen="w")
AuxPlot.addItem(pg.PlotCurveItem([-1,0,1,2],[0,1,2,3], pen="b")) #this data is scaled differently along the x axis while y is the same as in MainPlot

## This commented section was taken from another user's example but
##only one x axis zooms while the other is unaffected; modified to
##manually set XRange; doesnt work as both x axes are not aligned correctly

def updateViews():
    ## view has resized; update auxiliary views to match
    global MainPlot, AuxPlot
    AuxPlot.setGeometry(MainPlot.getPlotItem().vb.sceneBoundingRect())
    
    ## need to re-update linked axes since this was called
    ## incorrectly while views had different shapes.
    ## (probably this should be handled in ViewBox.resizeEvent)
    AuxPlot.linkedViewChanged(MainPlot.getPlotItem().vb, AuxPlot.YAxis)
    MainPlotXMin, MainPlotXMax = MainPlot.viewRange()[0]
    
    AuxPlotXMin = (MainPlotXMin)-Shift
    AuxPlotXMax = (MainPlotXMax)-Shift
    AuxPlot.setRange(xRange=[AuxPlotXMin,AuxPlotXMax])
    
    print("MainPlotXMin: ",MainPlotXMin)
    print("(MainPlotXMin)-Shift: ",(MainPlotXMin)-Shift)

updateViews()
MainPlot.getPlotItem().sigRangeChanged.connect(updateViews)

##end of other user's example (modified)

## 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_()

What is the correct way of displaying two x axes which represent the same single curve in two different "units" in one plot? I am new to this and can't get my head around all the different objects (PlotItem, ViewBox etc...). Is there a simple way like telling pyqtgraph to plot one dataset and then give it scaling factors describing the mapping to the second x axis?

Thanks for your help and time, Chris

Cho

unread,
Jul 14, 2016, 11:42:21 AM7/14/16
to pyqtgraph
Sorry, had to remove the code highlighting for the first section otherwise it wouldn't let me post...

Cho

unread,
Jul 25, 2016, 6:08:48 AM7/25/16
to pyqtgraph
Hi,

the problem seems to be that AuxPlot.setRange doesn't work as I expect it to. Only when manually panning the example curve to the center of the view the second upper scale matches the lower scale. There seems to be some offset building up when panning off center which causes the two scales to move asynchronously. anyone know why?

Best, Chris

Reply all
Reply to author
Forward
0 new messages