[pyqtgraph] Plot Options -> Transformsの表示をリンクさせる

# やりたいこと
pyqtgraph内で右クリックしてPlot Options -> Transformsと進むとグラフの表示形式を変更してくれます。
<img width=500 alt='describe.gif' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/779817/0c814c79-374d-d71b-4afb-9a658599410d.gif'>

表示変更の種類
<img width=300 alt='transforms.png' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/779817/dfd556ed-afea-434f-f240-53ed8a0650ae.png'>

この変更を複数のグラフでリンクさせるのが目的です。
<img width=500 alt='link.gif' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/779817/c40d592e-d169-007c-a1d7-5e737dbb7027.gif'>

# 環境
Mac OS
Python 3.8.5

PyQt5             5.15.2
PyQt5-sip         12.8.1
pyqtgraph         0.11.1

`pip install PyQt5 PyQt5-sip pyqtgraph`

# コード
プロット用にnumpyを使用しています。 `pip install numpy`

```python
"""pyqtgraphのPlot Options -> Transformsをリンクさせる"""

import dataclasses
import itertools
from typing import Optional
import sys

import numpy as np
from PyQt5 import QtWidgets  # ほぼ使わない
import pyqtgraph as pg

SAMPLE_DATA1 = np.linspace(-100, 100) ** 2
SAMPLE_DATA2 = np.linspace(-100, 100) ** 3


@dataclasses.dataclass
class GraphLinkWidget(pg.GraphicsLayoutWidget):
    """メイン画面
    Attributes #
    ----------
    parent: Optional[QtWidgets.QWidget] default=None
        親画面
    """
    parent: Optional[QtWidgets.QWidget] = None

    def __post_init__(self) -> None:
        """スーパークラス読み込みとプロット追加"""
        super(GraphLinkWidget, self).__init__(parent=self.parent)

        self.plotter1 = self.addPlot(row=0, col=0)
        self.plotter1.showGrid(x=True, y=True, alpha=0.8)
        self.plotter1_curve = self.plotter1.plot(pen=pg.mkPen('#f00', width=5))  # 線色:赤, 幅:5
        self.plotter1_curve.setData(SAMPLE_DATA1)
        self.plotter2 = self.addPlot(row=0, col=1)
        self.plotter2.showGrid(x=True, y=True, alpha=0.8)
        self.plotter2_curve = self.plotter2.plot(pen=pg.mkPen('#00f', width=5))
        self.plotter2_curve.setData(SAMPLE_DATA2)

        self.connect_slot()

    def connect_slot(self) -> None:
        """スロット接続
        itertools.permutationsは順列を作成してくれる関数
        ex) list(itertools.permutations([1, 2, 3]))
        >> [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
        """
        for plot1, plot2 in itertools.permutations([self.plotter1, self.plotter2]):
            plot1.ctrl.fftCheck.toggled.connect(plot2.ctrl.fftCheck.setChecked)
            plot1.ctrl.logXCheck.toggled.connect(plot2.ctrl.logXCheck.setChecked)
            plot1.ctrl.logYCheck.toggled.connect(plot2.ctrl.logYCheck.setChecked)
            plot1.ctrl.derivativeCheck.toggled.connect(plot2.ctrl.derivativeCheck.setChecked)
            plot1.ctrl.phasemapCheck.toggled.connect(plot2.ctrl.phasemapCheck.setChecked)


def main():
    app = QtWidgets.QApplication(sys.argv)
    window = GraphLinkWidget(parent=None)
    window.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
```

# 詳細
以下でリンクさせています

```python
    def connect_slot(self) -> None:
        """スロット接続
        itertools.permutationsは順列を作成してくれる関数
        ex) list(itertools.permutations([1, 2, 3]))
        >> [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
        """
        for plot1, plot2 in itertools.permutations([self.plotter1, self.plotter2]):
            plot1.ctrl.fftCheck.toggled.connect(plot2.ctrl.fftCheck.setChecked)
            plot1.ctrl.logXCheck.toggled.connect(plot2.ctrl.logXCheck.setChecked)
            plot1.ctrl.logYCheck.toggled.connect(plot2.ctrl.logYCheck.setChecked)
            plot1.ctrl.derivativeCheck.toggled.connect(plot2.ctrl.derivativeCheck.setChecked)
            plot1.ctrl.phasemapCheck.toggled.connect(plot2.ctrl.phasemapCheck.setChecked)
```

`plot1.ctrl.logXCheck.toggled.connect(plot2.ctrl.logXCheck.setChecked)`
例えば上記の場合、片方のグラフ(plot1)のlogXチェクボックスが押されたらもう片方(plot2)のlogXチェックボックスにもチェックをいれてリンクさせています。

self,plotter1, self.plotter2はPlotItemという型です。pg.PlotWidgetでグラフを作成した場合は`getPlotItem()`で取得できます。

> class 'pyqtgraph.graphicsItems.PlotItem.PlotItem.PlotItem'
>
> PlotWidgetの場合
> plotter = pg.PlotWidget()
> plotitem = plotter1.getPlotItem()



# 応用

GUIのボタンからグラフ表示を変更する
<img width=500 alt0'gui.gif' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/779817/c9ec59cc-fd01-a073-61a5-70697e6741dc.gif'>

```python
"""GUIのボタンからグラフ表示を変更する"""

import dataclasses
from typing import Optional
import sys

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

SAMPLE_DATA1 = np.linspace(-10, 10) ** 2
SAMPLE_DATA2 = 2 * np.linspace(-10, 10) ** 2


@dataclasses.dataclass
class PlotLinkWindow(QtWidgets.QWidget):
    """メイン画面
    Attributes #
    ----------
    parent: Optional[QtWidgets.QWidget] default=None
        親画面
    """
    parent: Optional[QtWidgets.QWidget] = None

    def __post_init__(self) -> None:
        """スーパークラス読み込みとウィジット作成"""
        super(PlotLinkWindow, self).__init__(parent=self.parent)
        self.setGeometry(100, 100, 800, 500)
        self.setWindowTitle('PlotLinkWindow')

        self.create_widgets()
        self.create_layouts()
        self.set_layouts()
        self.connect_slot()

    def create_widgets(self) -> None:
        """ウィジット作成"""
        # toggle buttons
        self.fft_btn = QtWidgets.QPushButton('FFT')
        self.fft_btn.setCheckable(True)
        self.logX_btn = QtWidgets.QPushButton('logX')
        self.logX_btn.setCheckable(True)
        self.logY_btn = QtWidgets.QPushButton('logY')
        self.logY_btn.setCheckable(True)
        self.derivative_btn = QtWidgets.QPushButton('dy/dx')
        self.derivative_btn.setCheckable(True)
        self.phasemap_btn = QtWidgets.QPushButton("Y' vs Y")
        self.phasemap_btn.setCheckable(True)

        # graph
        self.plotter1 = pg.PlotWidget()
        self.plotter1.showGrid(x=True, y=True, alpha=0.8)
        self.plotter1_curve1 = self.plotter1.plot(pen=pg.mkPen('#f00', width=5))
        self.plotter1_curve2 = self.plotter1.plot(pen=pg.mkPen('#0f0', width=5))
        self.plotter1_curve1.setData(SAMPLE_DATA1)
        self.plotter1_curve2.setData(SAMPLE_DATA2)

        self.plotitem = self.plotter1.getPlotItem()

    def create_layouts(self) -> None:
        """レイアウト作成"""
        self.main_layout = QtWidgets.QHBoxLayout()
        self.button_layout = QtWidgets.QVBoxLayout()

    def set_layouts(self) -> None:
        """ウィジットをレイアウトにセット"""
        self.main_layout.addLayout(self.button_layout)
        self.main_layout.addWidget(self.plotter1)

        self.button_layout.addWidget(self.fft_btn)
        self.button_layout.addWidget(self.logX_btn)
        self.button_layout.addWidget(self.logY_btn)
        self.button_layout.addWidget(self.derivative_btn)
        self.button_layout.addWidget(self.phasemap_btn)

        self.setLayout(self.main_layout)

    def connect_slot(self) -> None:
        """スロット接続"""
        self.fft_btn.clicked.connect(lambda: self.plotitem.ctrl.fftCheck.setChecked(self.fft_btn.isChecked()))
        self.logX_btn.clicked.connect(lambda: self.plotitem.ctrl.logXCheck.setChecked(self.logX_btn.isChecked()))
        self.logY_btn.clicked.connect(lambda: self.plotitem.ctrl.logYCheck.setChecked(self.logY_btn.isChecked()))
        self.derivative_btn.clicked.connect(
            lambda: self.plotitem.ctrl.derivativeCheck.setChecked(self.derivative_btn.isChecked()))
        self.phasemap_btn.clicked.connect(
            lambda: self.plotitem.ctrl.phasemapCheck.setChecked(self.phasemap_btn.isChecked()))


def main():
    app = QtWidgets.QApplication(sys.argv)
    window = PlotLinkWindow()
    window.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
```

# 参考
[pyqtgraph、リンク軸間のトラックログ/リニア軸変換の違い](https://stackoverrun.com/ja/q/8496244)
[pyqtgraph.graphicsItems.PlotItem.PlotItem — pyqtgraph 0.11.1.dev0 documentation](https://pyqtgraph.readthedocs.io/en/latest/_modules/pyqtgraph/graphicsItems/PlotItem/PlotItem.html) 
この辺り
> self.ctrl = c = Ui_Form()
 ...
  c.fftCheck.toggled.connect(self.updateSpectrumMode)
  c.logXCheck.toggled.connect(self.updateLogMode)
  c.logYCheck.toggled.connect(self.updateLogMode)
  c.derivativeCheck.toggled.connect(self.updateDerivativeMode)
  c.phasemapCheck.toggled.connect(self.updatePhasemapMode)

itertools
[すごいぞitertoolsくん - Qiita](https://qiita.com/anmint/items/37ca0ded5e1d360b51f3)