はじめに
pyqtgraphで複数のy軸を左側に表示したかった時にハマったので記事にしました。
単純に左側に表示するだけならすぐにできるのですが、その場合もともとあるy軸と追加した軸の大きさが異なってしまいます。
↑ 左側2軸の方が長い
試行錯誤して、(完全ではないですが)違和感の無い程度に揃えることができました。
環境
Windows10
Python 3.9.5
numpy 1.20.3
PySide6 6.1.0
pyqtgraph 0.12.1
pip install numpy PySide6 pyqtgraph
原因・対策
下記URLのように、PlotItem
の中にあるAxisItem
とAxisItem
単体を並べてしまうと大きさに差異が発生してしまいます。
python - How can I have multiple left axisItems with the same alignment/position using pyqtgraph? - Stack Overflow
追加するAxisItem
を別ウィジットに入れ、下にスペーサー(空白の文字列等)で高さ調節して上手くいきました。
全文
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