79284791

Date: 2024-12-16 13:12:58
Score: 1.5
Natty:
Report link

While the solution posted by @mr-rc may look OK at the first glance, it does not actually solve the problem. If you draw the line through all 0-s, it will not intersect with 0 point on the curves.

After spending some time researching this issue, I don't think there's a way to fix axis alignment and scale if it is outside the QGraphicsGridLayout that's created inside the PlotItem (which is internally created in the PlotWidget). Therefore, the proper solution to this issue would be implementation of an alternative to PlotItem that can host as many Y axes as you want.

However, if you don't want to re-implement the entire PlotItem class, here's the hack that I used in my application. The idea is simple, after PlotItem is created, I remove all the items from its internal layout, and insert them back, but to column indexes shifted by the amount of extra Y axes I want to add. And all the extra axes are inserted into the PlotItem internal layout. As result, all the axes are perfectly aligned with the curves in the view boxes. Here is the example how it looks like

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

pg.mkQApp()


x = [1, 2, 3, 4, 5, 6]
y = [
    ('axis 1','#FFFFFF',[0, 4, 6, 8, 10, 4]),
    ('axis 2','#2E2EFE',[0, 5, 7, 9, 11, 3]),
    ('axis 3','#2EFEF7',[0, 1, 2, 3, 4, 12]),
    ('axis 4','#2EFE2E',[0, 8, 0.3, 0.4, 2, 5]),
    ('axis 5','#FFFF00',[0, 1, 6, 4, 2, 1]),
    ('axis 6','#FE2E64',[0, 0.2, 0.3, 0.4, 0.5, 0.6]),
]

# main view
pw = pg.GraphicsView()
pw.setWindowTitle('pyqtgraph example: multiple y-axis')
pw.show()

# layout
layout = pg.GraphicsLayout()
pw.setCentralWidget(layout)

# utility variables
secondary_viewboxes = []
plot_item = None
main_viewbox = None
previous_viewbox = None
main_layout = None

for i, (name, color, y_data) in enumerate(y):
    pen = pg.mkPen(width=1,  color=color)
    column = len(y) - i

    curve = pg.PlotDataItem(x, y_data, pen=pen, name=name, autoDownsample=True)

    if i == 0: # first, main plot
        plot_item = pg.PlotItem()

        main_y_axis = plot_item.getAxis("left") # get Y axis
        main_y_axis.setTextPen(pen)
        main_y_axis.setLabel(name)
        main_x_axis = plot_item.getAxis("bottom") # get x axis
        main_viewbox = plot_item.vb # get main viewbox
        main_viewbox.setMouseMode(pg.ViewBox.RectMode)

        # trick
        main_layout = plot_item.layout # get reference to QGraphicsGridLayout from plot_item
        main_layout.removeItem(main_y_axis) # remove items created in PlotItem from its layout
        main_layout.removeItem(main_x_axis)
        main_layout.removeItem(main_viewbox)

        main_layout.addItem(main_y_axis, 2, column)  # shift them to the right, making space for secondary axes
        main_layout.addItem(main_viewbox, 2, column + 1)
        main_layout.addItem(main_x_axis,  3, column + 1)

        main_layout.setColumnStretchFactor(column + 1, 100) # fix scaling factor, in original layout col 1 contains
        main_layout.setColumnStretchFactor(1, 0)            # the view_box and it's stretched
        #  /trick
        layout.addItem(plot_item, row=0, col=column + 1)
        viewbox = previous_viewbox = main_viewbox
    else: # Secondary "sub" plots
        axis = pg.AxisItem("left")  # create axis
        axis.setTextPen(pen)
        axis.setLabel(name)
        main_layout.addItem(axis, 2, column)  # trick, add axis it into original plot_item layout
        viewbox = pg.ViewBox()  # create ViewBox
        viewbox.setXLink(previous_viewbox)  # link to previous
        previous_viewbox = viewbox
        axis.linkToView(viewbox)  # link axis with viewbox
        layout.scene().addItem(viewbox)  # add viewbox to layout
        viewbox.enableAutoRange(axis=pg.ViewBox.XYAxes, enable=True)  # autorange once to fit views at start

        secondary_viewboxes.append(viewbox)

    viewbox.addItem(curve)

# slot: update view when resized
def updateViews():
    for vb in secondary_viewboxes:
        vb.setGeometry(main_viewbox.sceneBoundingRect())


main_viewbox.sigResized.connect(updateViews)
updateViews()

if __name__ == '__main__':
    import sys

    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QGuiApplication.instance().exec_()
Reasons:
  • RegEx Blacklisted phrase (1): I want
  • Long answer (-1):
  • Has code block (-0.5):
  • User mentioned (1): @mr-rc
  • Low reputation (1):
Posted by: Igor Petrov