🌊

リアルタイム波形表示アプリケーションの作成vol.2

2024/06/20に公開

はじめに

本記事はリアルタイム波形表示アプリケーションの作成vol.1の続きです.
まだお読みではない方は下記のリンクから是非読んでいただくとありがたいです.
https://zenn.dev/miguel/articles/21570276b58c0f

実行環境

  • Windows(10 or 11)
  • Windows用ソースコードエディター(VS codeなど)
  • Python 3.12.1(またはPython 3.10.9)
  • PyQtGraph 0.13.7
  • Numpy 1.26.4
  • PyQt5 5.15.10(またはPySide2)

要件

前回作成した波形表示アプリケーションが見にくいので,以下の要件を追加実装します.

  • 複数の波形を色分けする.
  • ラベルを追加する.
  • 3つの波形を重ねたグラフを上部に,それぞれの波形を個別に表示したグラフを下部に2つ並べて表示する.

実装

以下に,変更したコードの主要な部分について詳しく説明します.

__init__メゾット

def __init__(self):
    super().__init__()
    self.setWindowTitle("リアルタイム波形表示")
    self.central_widget = QtWidgets.QWidget()
    self.setCentralWidget(self.central_widget)
    self.layout = QtWidgets.QVBoxLayout(self.central_widget)

    self.plot_widget = pg.GraphicsLayoutWidget()
    self.layout.addWidget(self.plot_widget)

    self.combined_plot = self.plot_widget.addPlot(row=0, col=0, colspan=2, title="Combined Waveforms")
    self.combined_plot.addLegend()

    self.individual_plots = []
    for i in range(3):
        plot = self.plot_widget.addPlot(row=1, col=i, title=f"Waveform {i+1}")
        self.individual_plots.append(plot)

    self.start_stop_button = QtWidgets.QPushButton("Start")
    self.start_stop_button.clicked.connect(self.start_stop)
    self.layout.addWidget(self.start_stop_button)

    self.sample_rate_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
    self.sample_rate_slider.setRange(1, 100)
    self.sample_rate_slider.setValue(50)
    self.sample_rate_slider.valueChanged.connect(self.update_sample_rate)
    self.layout.addWidget(self.sample_rate_slider)

    self.combined_curves = []
    self.individual_curves = [[] for _ in range(3)]
    self.data = []
    self.colors = ['r', 'g', 'b']
    self.labels = ['Wave 1', 'Wave 2', 'Wave 3']
    for i in range(3):
        data = np.zeros(100)
        self.data.append(data)
        curve = self.combined_plot.plot(data, pen=self.colors[i], name=self.labels[i])
        self.combined_curves.append(curve)
        individual_curve = self.individual_plots[i].plot(data, pen=self.colors[i])
        self.individual_curves[i].append(individual_curve)

    self.timer = QtCore.QTimer()
    self.timer.timeout.connect(self.update_plot)
  • GraphicsLayoutWidgetを作成し,レイアウトに追加しました.
  • 重ねたグラフ用のプロット(combined_plot)を作成し,ラベルを追加しました.
  • 個別の波形グラフ用のプロット(individual_plots)を3つ作成しました.

update_plotメゾット

def update_plot(self):
    for i in range(len(self.combined_curves)):
        data = self.data[i]
        data[:-1] = data[1:]
        data[-1] = np.random.normal()
        self.combined_curves[i].setData(data)
        self.individual_curves[i][0].setData(data)
  • 重ねたグラフ用の曲線(combined_curves)にデータを設定しました.

下記にコードの全体と実行結果を記します.

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

class RealTimeWaveformPlotter(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("リアルタイム波形表示")
        self.central_widget = QtWidgets.QWidget()
        self.setCentralWidget(self.central_widget)
        self.layout = QtWidgets.QVBoxLayout(self.central_widget)

        self.plot_widget = pg.GraphicsLayoutWidget()
        self.layout.addWidget(self.plot_widget)

        self.combined_plot = self.plot_widget.addPlot(row=0, col=0, colspan=2, title="Combined Waveforms")
        self.combined_plot.addLegend()

        self.individual_plots = []
        for i in range(3):
            plot = self.plot_widget.addPlot(row=1, col=i, title=f"Waveform {i+1}")
            self.individual_plots.append(plot)

        self.start_stop_button = QtWidgets.QPushButton("Start")
        self.start_stop_button.clicked.connect(self.start_stop)
        self.layout.addWidget(self.start_stop_button)

        self.sample_rate_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
        self.sample_rate_slider.setRange(1, 100)
        self.sample_rate_slider.setValue(50)
        self.sample_rate_slider.valueChanged.connect(self.update_sample_rate)
        self.layout.addWidget(self.sample_rate_slider)

        self.combined_curves = []
        self.individual_curves = [[] for _ in range(3)]
        self.data = []
        self.colors = ['r', 'g', 'b']
        self.labels = ['Wave 1', 'Wave 2', 'Wave 3']
        for i in range(3):
            data = np.zeros(100)
            self.data.append(data)
            curve = self.combined_plot.plot(data, pen=self.colors[i], name=self.labels[i])
            self.combined_curves.append(curve)
            individual_curve = self.individual_plots[i].plot(data, pen=self.colors[i])
            self.individual_curves[i].append(individual_curve)

        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.update_plot)

    def start_stop(self):
        if self.timer.isActive():
            self.timer.stop()
            self.start_stop_button.setText("Start")
        else:
            self.timer.start()
            self.start_stop_button.setText("Stop")

    def update_sample_rate(self, value):
        self.timer.setInterval(value)

    def update_plot(self):
        for i in range(len(self.combined_curves)):
            data = self.data[i]
            data[:-1] = data[1:]
            data[-1] = np.random.normal()
            self.combined_curves[i].setData(data)
            self.individual_curves[i][0].setData(data)

if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    plotter = RealTimeWaveformPlotter()
    plotter.show()
    app.exec_()

まとめ

今回はPyQtGraphとNumpyを使って,
リアルタイムに波形を表示するアプリケーションを作成しました.
このアプリケーションでは,3つの波形を重ねて表示するグラフと,
それぞれの波形を個別に表示するグラフを,2つのセクションに分けて表示しています.
PyQtGraphとNumpyを組み合わせることで,データの生成からリアルタイムの可視化まで,
一貫したワークフローを実現できました.
このチュートリアルを通じて,PyQtGraphとNumpyの基本的な使い方と,
それらを組み合わせてアプリケーションを作成する方法を学ぶことができたと思います.
このアプリケーションは,さらに多くの機能を追加することができると思います.
例えば,波形データをファイルに保存する機能や,保存したデータを再生する機能などです.
また,より複雑なデータ処理や解析を行うために,
SciPyやPandasなどの他のPythonライブラリを組み合わせることで更なる発展ができると思います.
ここまでお読みいただきありがとうございました!

Github

exeファイルに変換してますので誰でも簡単にインストールできます!
https://github.com/xM1guel/WaveformApp

Discussion