LoginSignup
0
0

More than 1 year has passed since last update.

[pyqtgraph] 左側に複数y軸を(違和感がないように)作成する

Last updated at Posted at 2021-12-03

はじめに

pyqtgraphで複数のy軸を左側に表示したかった時にハマったので記事にしました。

単純に左側に表示するだけならすぐにできるのですが、その場合もともとあるy軸と追加した軸の大きさが異なってしまいます。
miss.png
↑ 左側2軸の方が長い

試行錯誤して、(完全ではないですが)違和感の無い程度に揃えることができました。

result.png

環境

Windows10
Python 3.9.5

numpy 1.20.3
PySide6 6.1.0
pyqtgraph 0.12.1

pip install numpy PySide6 pyqtgraph

原因・対策

下記URLのように、PlotItemの中にあるAxisItemAxisItem単体を並べてしまうと大きさに差異が発生してしまいます。

原因.png

python - How can I have multiple left axisItems with the same alignment/position using pyqtgraph? - Stack Overflow


追加するAxisItemを別ウィジットに入れ、下にスペーサー(空白の文字列等)で高さ調節して上手くいきました。
対策.png

全文

import sys
from typing import List, Sequence

import numpy as np
from PySide6 import QtWidgets
import pyqtgraph as pg


class LeftYAxisWidget(QtWidgets.QGraphicsWidget):
    """追加するY軸用のウィジット"""

    def __init__(self, ylabels: List[str], *args, **kwargs) -> None:
        """初期化処理

        Parameters
        -----
        ylabels: List[str]
            生成するY軸のラベル
        """

        super(LeftYAxisWidget, self).__init__(*args, **kwargs)

        # レイアウト
        layout = QtWidgets.QGraphicsGridLayout()
        layout.setContentsMargins(1, 1, 1, 1)
        layout.setSpacing(0)
        layout.setHorizontalSpacing(10)
        layout.setVerticalSpacing(0)
        layout.setRowStretchFactor(0, 10)
        layout.setRowStretchFactor(1, 0)
        self.setLayout(layout)

        self.yaxes: List[pg.AxisItem] = []

        for i, label in enumerate(ylabels):
            yaxis = pg.AxisItem(orientation='left')
            yaxis.setLabel(label)
            self.yaxes.append(yaxis)

            layout.addItem(yaxis, 0, i)
        layout.addItem(pg.LabelItem(' '), 1, 0)


class MultiAxisWidget(pg.GraphicsLayoutWidget):
    """複数y軸グラフ"""
    def __init__(self, ylabels: List[str], *args, **kwargs) -> None:
        """初期化処理

        Parameters
        -----
        ylabels: List[str]
            生成するY軸のラベル
        """

        super(MultiAxisWidget, self).__init__(*args, **kwargs)

        self.plot = pg.PlotItem()
        self.plot.hideAxis('left')  # 元々のy軸は隠す
        self.yaxes = LeftYAxisWidget(ylabels)

        self.addItem(self.yaxes, 0, 0)  # 追加Y軸ウィジットをレイアウトに追加(0行0列目)
        self.addItem(self.plot, 0, 1)  # plotItemをレイアウトに追加(0行1列目)

        self.view_boxes: List[pg.ViewBox] = []
        self.data: List[pg.PlotDataItem] = []

        for i, yaxis in enumerate(self.yaxes.yaxes):
            view_box = pg.ViewBox()
            view_box.setXLink(self.plot)  # x軸をリンク
            self.plot.scene().addItem(view_box)  # plotItem内にviewBoxを追加

            yaxis.linkToView(view_box)  # 追加するy軸とviewBoxをリンク

            data = pg.PlotDataItem()
            view_box.addItem(data)

            self.view_boxes.append(view_box)
            self.data.append(data)

        self.plot.autoBtn.clicked.connect(self.reset_range)  # plotItemのautoBtnを押したときにself.reset_rageも実行
        self.plot.vb.sigResized.connect(self.update_views)  # plotItemのサイズが変わった時にviewBox達をそれに合わせる

        self.update_views()
        self.reset_range()

    def set_multi_value(self, values: List[Sequence]) -> None:
        """複数データ入力

        Parameters
        ----------
        values: List[Sequence]
            入力するデータ
        """

        for data, value in zip(self.data, values):
            data.clear()
            data.setData(value)

    def reset_range(self) -> None:
        """表示範囲をデータに合わせる"""

        for vb in self.view_boxes:
            vb.enableAutoRange(axis=pg.ViewBox.XYAxes, enable=True)

    def update_views(self) -> None:
        """self.plot.scene()のサイズが変わったとき,view_boxもその大きさに合わせる"""

        plot_vb = self.plot.vb
        for vb in self.view_boxes:
            vb.setGeometry(plot_vb.sceneBoundingRect())


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = MultiAxisWidget([f"y{i}" for i in range(3, 0, -1)])

    data1 = np.random.randint(0, 10, 10)
    data2 = np.random.randint(0, 10, 10)
    data3 = np.random.randint(0, 10, 10)

    window.set_multi_value([data1, data2, data3])

    for i, color in enumerate(['b', 'g', 'r']):
        window.yaxes.yaxes[i].setPen(pg.mkPen(color))
        window.data[i].setPen(pg.mkPen(color))

    window.show()
    sys.exit(app.exec())

詳細

LeftYAxisWidgetは追加Y軸用ウィジットです。2行目に空の文字列を入れて高さ調節しています。

MultiAxisWidgetのfor文内で軸とグラフをリンクしています。


        for i, yaxis in enumerate(self.yaxes.yaxes):
            view_box = pg.ViewBox()
            view_box.setXLink(self.plot)  # x軸をリンク挿せる
            self.plot.scene().addItem(view_box)  # plotItem内にviewBoxを追加

            yaxis.linkToView(view_box)  # 追加するy軸とviewBoxをリンクさせる

            data = pg.PlotDataItem()
            view_box.addItem(data)

参考

python - How can I have multiple left axisItems with the same alignment/position using pyqtgraph? - Stack Overflow
python - pyqtgraph: Multiple y-axis on left side? - Stack Overflow
PyQtGraphでグラフを描写する その5 - Y軸を増やす - Qiita

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0