如何使用 pyqtgraph 使多个左 axisItems 具有相同的对齐/位置?

问题描述 投票:0回答:3

我想知道是否有人可以帮助我。我正在尝试转换 pyqtgraph 中的 MultiplePlotAxes.py 示例,以便所有轴都位于右侧并对齐。这是我的代码,我添加了额外的情节:

"""
Demonstrates a way to put multiple axes around a single plot.
(This will eventually become a built-in feature of PlotItem)
"""
#import initExample  ## Add path to library (just for examples; you do not need this)

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

pg.mkQApp()

pw = pg.PlotWidget()
pw.show()
pw.setWindowTitle('pyqtgraph example: MultiplePlotAxes')
p1 = pw.plotItem
p1.setLabels(left='axis 1')

## create a new ViewBox, link the right axis to its coordinate system
p2 = pg.ViewBox()
p1.showAxis('right')
p1.scene().addItem(p2)
p1.getAxis('right').linkToView(p2)
p2.setXLink(p1)
p1.getAxis('right').setLabel('axis2', color='#0000ff')

## create third ViewBox.
## this time we need to create a new axis as well.
p3 = pg.ViewBox()
ax3 = pg.AxisItem('right')
p1.layout.addItem(ax3, 2, 3)
p1.scene().addItem(p3)
ax3.linkToView(p3)
p3.setXLink(p1)
ax3.setZValue(-10000)
ax3.setLabel('axis 3', color='#ff0000')

## create forth ViewBox.
## this time we need to create a new axis as well.
p4 = pg.ViewBox()
ax4 = pg.AxisItem('right')
p1.layout.addItem(ax4, 2, 4)
p1.scene().addItem(p4)
ax4.linkToView(p4)
p4.setXLink(p1)
ax4.setZValue(-10000)
ax4.setLabel('axis 4', color='#2EFEF7')

## Handle view resizing
def updateViews():
    ## view has resized; update auxiliary views to match
    global p1, p2, p3, p4
    p2.setGeometry(p1.vb.sceneBoundingRect())
    p3.setGeometry(p1.vb.sceneBoundingRect())
    p4.setGeometry(p1.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)
    p2.linkedViewChanged(p1.vb, p2.XAxis)
    p3.linkedViewChanged(p1.vb, p3.XAxis)
    p4.linkedViewChanged(p1.vb, p4.XAxis)

updateViews()
p1.vb.sigResized.connect(updateViews)

p1.plot([1, 2, 4, 8, 16, 32])
p2.addItem(pg.PlotCurveItem([1, 2, 4, 9, 16, 32], pen='b'))
p3.addItem(pg.PlotCurveItem([1, 2, 4, 7, 16, 32], pen='r'))
p4.addItem(pg.PlotCurveItem([1, 3, 5, 7, 17, 32], pen='c'))

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

这会产生以下图片,我添加了一个红色箭头来显示我想要做什么。 enter image description here

根据我的了解,我可以使用 pg.GraphicsLayout() 来做到这一点。但是,与plotItem直接关联的axisItem和创建的axisItem不匹配,因为附加到plotitem的axisItem较小。

这是我认为正在发生的事情的图片:

enter image description here

这是我的问题,因为我希望它们全部对齐。在附加的代码中,您可以将 addItem(axes,2,1) 添加到右侧,但它在左侧不起作用。有人知道我该怎么做吗?

预先感谢您提供的任何帮助。

python plot pyqt5 pyqtgraph
3个回答
2
投票

我也遇到了同样的问题,看到了这篇文章,对缺乏答案感到失望,但我最终确实解决了这个问题,所以我想我会在这里发布。

如果您可以发布右侧轴的代码,我可以对其进行修补,但基本上我所做的是将底部轴附加到其自己的一行。

self.main_pI = pg.PlotItem()
self.axis_bottom = self.main_pI.getAxis('bottom')
self.axis_bottom.setLabel('Time (seconds)', color='#ffffff')
self.layout.addItem(self.main_pI, row=1, col=2, rowspan=2)
self.layout.addItem(self.axis_bottom, row=3, col=2, rowspan=2)

我是 pyqtgraph 的菜鸟,所以我确信我会抛弃这个术语,但我分离了“底部”轴,并将其重新连接到布局,它在自己的一行上。

希望有帮助。


2
投票

我发现了一些并不完美但非常接近的东西。使用上面的建议并在底部适当的空间中添加一个额外的轴项目并将其空白,我想出了这个。所有线都与 y 轴对齐,但并不完美,但足够接近,可以摆脱它。这就是区别

原创 enter image description here 新的 enter image description here 代码如下:

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

pg.mkQApp()

# Axis
a2 = pg.AxisItem("left")
a3 = pg.AxisItem("left")
a4 = pg.AxisItem("left")  
a5 = pg.AxisItem("left")
a6 = pg.AxisItem("left")

# ViewBoxes
v2 = pg.ViewBox()
v3 = pg.ViewBox()
v4 = pg.ViewBox()
v5 = pg.ViewBox()
v6 = pg.ViewBox()

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

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

# add axis to layout
## watch the col parameter here for the position
l.addItem(a2, row=1, col=5, rowspan=1, colspan=1)
l.addItem(a3, row=1, col=4, rowspan=1, colspan=1)
l.addItem(a4, row=1, col=3, rowspan=1, colspan=1)
l.addItem(a5, row=1, col=2, rowspan=1, colspan=1)
l.addItem(a6, row=1, col=1, rowspan=1, colspan=1)

# Blank axis used for aligning things
ax = pg.AxisItem(orientation='bottom')
ax.setPen('#000000')
pos = (2,2)
l.addItem(ax, *pos)

# plotitem and viewbox
## at least one plotitem is used whioch holds its own viewbox and left axis
pI = pg.PlotItem()
v1 = pI.vb  # reference to viewbox of the plotitem
l.addItem(pI, row=1, col=8, rowspan=2, colspan=1)  # add plotitem to layout

# split off 1st axis and put to side
pI.axis_left = pI.getAxis('left')
pos = (1,7)
l.addItem(pI.axis_left, *pos)

# add viewboxes to layout
l.scene().addItem(v2)
l.scene().addItem(v3)
l.scene().addItem(v4)
l.scene().addItem(v5)
l.scene().addItem(v6)

# link axis with viewboxes
a2.linkToView(v2)
a3.linkToView(v3)
a4.linkToView(v4)
a5.linkToView(v5)
a6.linkToView(v6)

# link viewboxes
v2.setXLink(v1)
v3.setXLink(v2)
v4.setXLink(v3)
v5.setXLink(v4)
v6.setXLink(v5)

# axes labels
pI.getAxis("left").setLabel('axis 1 in ViewBox of PlotItem', color='#FFFFFF')
a2.setLabel('axis 2 in Viewbox 2', color='#2E2EFE')
a3.setLabel('axis 3 in Viewbox 3', color='#2EFEF7')
a4.setLabel('axis 4 in Viewbox 4', color='#2EFE2E')
a5.setLabel('axis 5 in Viewbox 5', color='#FFFF00')
a6.setLabel('axis 6 in Viewbox 6', color='#FE2E64')


# slot: update view when resized
def updateViews():
    v2.setGeometry(v1.sceneBoundingRect())
    v3.setGeometry(v1.sceneBoundingRect())
    v4.setGeometry(v1.sceneBoundingRect())
    v5.setGeometry(v1.sceneBoundingRect())
    v6.setGeometry(v1.sceneBoundingRect())

# data
x = [1, 2, 3, 4, 5, 6]
y1 = [0, 4, 6, 8, 10, 4]
y2 = [0, 5, 7, 9, 11, 3]
y3 = [0, 1, 2, 3, 4, 12]
y4 = [0, 8, 0.3, 0.4, 2, 5]
y5 = [0, 1, 6, 4, 2, 1]
y6 = [0, 0.2, 0.3, 0.4, 0.5, 0.6]

# plot
v1.addItem(pg.PlotCurveItem(x, y1, pen='#FFFFFF'))
v2.addItem(pg.PlotCurveItem(x, y2, pen='#2E2EFE'))
v3.addItem(pg.PlotCurveItem(x, y3, pen='#2EFEF7'))
v4.addItem(pg.PlotCurveItem(x, y4, pen='#2EFE2E'))
v5.addItem(pg.PlotCurveItem(x, y5, pen='#FFFF00'))
v6.addItem(pg.PlotCurveItem(x, y6, pen='#FE2E64'))

# updates when resized
v1.sigResized.connect(updateViews)

# autorange once to fit views at start
v2.enableAutoRange(axis=pg.ViewBox.XYAxes, enable=True)
v3.enableAutoRange(axis=pg.ViewBox.XYAxes, enable=True)
v4.enableAutoRange(axis=pg.ViewBox.XYAxes, enable=True)
v5.enableAutoRange(axis=pg.ViewBox.XYAxes, enable=True)
v6.enableAutoRange(axis=pg.ViewBox.XYAxes, enable=True)

updateViews()

if __name__ == '__main__':
    import sys

    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()

希望对以后遇到同样问题的人有帮助


0
投票

虽然@mr-rc发布的解决方案乍一看可能看起来不错,但它实际上并没有解决问题。如果你画一条经过所有 0 的线,它不会与曲线上的 0 点相交。

花了一些时间研究这个问题后,我认为如果它位于

QGraphicsGridLayout
内部创建的
PlotItem
之外(在
PlotWidget
内部创建),则没有办法修复轴对齐和缩放。因此,解决此问题的正确方法是实施
PlotItem
的替代方案,它可以托管任意数量的 Y 轴。

但是,如果您不想重新实现整个

PlotItem
类,这里是我在应用程序中使用的 hack。这个想法很简单,创建
PlotItem
后,我从其内部布局中删除所有项目,然后将它们插入回去,但列索引移动了我想要添加的额外 Y 轴的数量。并且额外的轴被插入到
PlotItem
内部布局中。结果,所有轴都与视图框中的曲线完美对齐。 这是示例

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_()
© www.soinside.com 2019 - 2024. All rights reserved.