PySide Model/View【第7弾】GUIグラフ連携!Matplotlib × QTableViewで動的チャートを作る

\ 迷ったらまずTechAcademyの無料カウンセリング! /

前回までは テーブルやツリー、リストの表示や操作 を中心に学んできました。
今回は一歩進んで、QTableView に入力したデータを Matplotlib のグラフにリアルタイム反映させる方法を解説します。

GUIアプリでは「データを表形式で入力 → グラフで可視化」という流れはよくあるパターンです。
例えば、

  • 売上データを表に入力し、グラフで動的に表示
  • センサー値をテーブルで管理し、その変化を折れ線グラフに反映
  • 実験データを入力してすぐに可視化

といったケースに応用できます。

ここでは QTableView + QStandardItemModel をベースにし、Matplotlib と連携する仕組みを紹介します。

目次

Matplotlib連携と完成コード

PySideのModel/View機構とMatplotlibを組み合わせると、次のような動的アプリが作れます:

  1. QTableViewに数値を入力
  2. モデル(QStandardItemModel)が更新される
  3. MatplotlibのFigureに再描画を要求
  4. チャートが最新データを反映して表示される

つまり「モデルがデータ管理」「ビューが表表示」「Matplotlibが可視化」という役割分担になります。
Matplotlibに関する使い方は下記記事を参考にしてください

完成アプリ:折れ線グラフと棒グラフの切り替え

折れ線グラフ棒グラフ

完成コード:折れ線グラフと棒グラフの切り替え

import sys
from PySide6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QTableView
)
from PySide6.QtGui import QStandardItemModel, QStandardItem, QFont
from PySide6.QtCore import Qt

# Matplotlib(Qt連携用)
from matplotlib.backends.backend_qt5agg import (
    FigureCanvasQTAgg as FigureCanvas,
    NavigationToolbar2QT as NavigationToolbar
)
from matplotlib.figure import Figure


class TableGraphApp(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QTableView × Matplotlib Demo")
        self.resize(800, 600)

        # レイアウト
        layout = QVBoxLayout(self)

        # モデルとビュー
        self.model = QStandardItemModel(5, 2)  # 5行2列
        self.model.setHorizontalHeaderLabels(["X", "Y"])
        self.view = QTableView()
        self.view.setModel(self.model)

        # Matplotlibグラフ
        self.figure = Figure()
        self.canvas = FigureCanvas(self.figure)
        self.ax = self.figure.add_subplot(111)

        # ツールバー
        self.toolbar = NavigationToolbar(self.canvas, self)

        # レイアウトに追加
        layout.addWidget(self.view)
        layout.addWidget(self.toolbar)
        layout.addWidget(self.canvas)

        # データ変更時にグラフ更新
        self.model.dataChanged.connect(self.update_plot)

        # 初期データをセット
        self.set_initial_data()
        self.update_plot()

    def set_initial_data(self):
        """初期データをモデルにセット"""
        data = [(1, 10), (2, 15), (3, 8), (4, 12), (5, 18)]
        for row, (x, y) in enumerate(data):
            self.model.setItem(row, 0, QStandardItem(str(x)))
            self.model.setItem(row, 1, QStandardItem(str(y)))

    def update_plot(self):
        """テーブルのデータを読み取り、グラフを更新"""
        x, y = [], []
        for row in range(self.model.rowCount()):
            item_x = self.model.item(row, 0)
            item_y = self.model.item(row, 1)
            if item_x and item_y:  # Noneチェック
                try:
                    x_val = float(item_x.text())
                    y_val = float(item_y.text())
                    x.append(x_val)
                    y.append(y_val)
                except ValueError:
                    continue  # 数値に変換できない場合はスキップ

        # グラフ更新
        self.ax.clear()
        if x and y:
            self.ax.plot(x, y, marker="o", linestyle="-", color="blue", label="Data")
            self.ax.set_xlabel("X")
            self.ax.set_ylabel("Y")
            self.ax.set_title("Dynamic Plot from QTableView", fontname='Meiryo')
            self.ax.legend()

        self.canvas.draw_idle()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = TableGraphApp()
    window.show()
    sys.exit(app.exec())

主なプロパティとメソッド一覧(全体解説)

Noプロパティ / メソッド説明
1QApplication(...) / app.exec()Qt アプリケーションの作成とイベントループ開始。app.exec() で GUI のメインループを回します。
2QWidget.show()ウィンドウ(またはウィジェット)を画面に表示します。
3setWindowTitle(title)ウィジェット(ウィンドウ)のタイトルバーに表示する文字列を設定します。
4resize(w, h)ウィジェットの初期サイズを指定します。
5QVBoxLayout() / addWidget(widget)垂直レイアウトを作り、子ウィジェットを追加します。
6QTableView.setModel(model)テーブルビューにデータモデルをセットします。
7QStandardItemModel(rows, cols) / setHorizontalHeaderLabels(labels)標準アイテムモデルの生成(行数・列数指定)とヘッダラベルの設定。
8QStandardItemModel.setItem(row, col, item)指定セルに QStandardItem をセットします。
9QStandardItemModel.rowCount()モデルの行数を取得します(ループなどで利用)。
10QStandardItemModel.item(row, col)指定セルの QStandardItem を取得します。
11dataChanged(シグナル)モデル内のデータが編集されたときに発火するシグナル。
12QStandardItem(text) / QStandardItem.text()セルに入れる標準アイテムの生成と、そのテキスト取得。
13QComboBox.addItems(list)コンボボックスに複数の選択肢を追加します。
14QComboBox.currentIndexChanged / currentText()選択変更のシグナルと、現在選択中のテキスト取得。
15Figure() / Figure.add_subplot()Matplotlib の Figure(図)の生成と描画領域(Axes)の生成。
16FigureCanvasQTAgg(Figure)Matplotlib 図を Qt ウィジェットに埋め込むラッパー(キャンバス)。
17NavigationToolbar2QT(canvas, parent)Matplotlib の表示ツールバー(ズームや保存など)を追加するウィジェット。
18Axes.clear() / Axes.plot() / Axes.bar() / Axes.set_xlabel() / set_ylabel() / set_title() / legend()描画(折れ線 / 棒グラフ)の実行とラベル/タイトルの設定、凡例表示。
19FigureCanvas.draw_idle()描画を要求(非同期的で効率的)。更新を画面に反映します。
20signal.connect(slot)(一般)Qt のシグナルとスロット(関数)を接続する基本パターン。

各プロパティ・メソッド 詳細解説

【1】 Qtアプリの開始

概要:Qt アプリケーションのエントリポイントです。QApplication を生成してから app.exec() を呼ぶことでイベントループが開始され、GUI が動作します。
メソッド:QApplication(...) / app.exec()
使い方:

import sys
from PySide6.QtWidgets import QApplication

app = QApplication(sys.argv)
# ウィンドウを作って show() する...
sys.exit(app.exec())   # イベントループ開始、戻り値で終了コードを返す

同一プロセスに QApplication は一つだけ。exec() を二重に呼ばないこと。

【2】 ウィンドウ画面の表示

概要:ウィジェットを画面に表示します。show() することでウィンドウがマップされます。
メソッド:QWidget.show()
使い方:

window = TableGraphApp()
window.show()

show() 後にウィジェットはユーザー操作を受け付けます。非表示にするには hide()

【3】 ウィンドウのタイトル

概要:ウィンドウ(トップレベルウィジェット)のタイトルを設定します。日本語でも問題ありません
メソッド:setWindowTitle(title)
使い方:

self.setWindowTitle("QTableView × Matplotlib Demo")

【4】 ウィンドウの初期サイズ

概要:ウィンドウの初期サイズ(幅,高さ)をピクセル単位で指定します。
メソッド:resize(w, h)
使い方:

self.resize(800, 600)

setFixedSize() で固定サイズにでき、setMinimumSize, setMaximumSize で範囲を制限できます。

【5】 垂直レイアウト

概要:垂直方向にウィジェットを配置するレイアウト。addWidget() で子要素を追加します。
メソッド:QVBoxLayout() / addWidget(widget)
使い方:

# レイアウト
layout = QVBoxLayout(self)
    
# レイアウトに追加
layout.addWidget(self.view)        # QTableView
layout.addWidget(self.toolbar)     # Matplotlib ツールバー
layout.addWidget(self.graph_selector_label)
layout.addWidget(self.graph_selector)
layout.addWidget(self.canvas)      # FigureCanvas

同じウィジェットを複数のレイアウトに追加しない。レイアウトは親ウィジェットにセットして使います(あるいはコンストラクタで親を渡す)。

【6】 テーブルビューにデータセット

概要:QTableView にデータソース(モデル)をセットします。ビューはモデルのデータを描画します。
メソッド:QTableView.setModel(model)
使い方:

self.view = QTableView()
self.view.setModel(self.model)

Model/View アーキテクチャにより表示ロジックとデータロジックが分離されます。

【7】標準アイテムモデルの生成

概要:QStandardItemModel は簡単に使える汎用モデルです。コンストラクタで行列サイズを与え、ヘッダラベルを設定できます。
メソッド:QStandardItemModel(rows, cols) / setHorizontalHeaderLabels(labels)
使い方:

self.model = QStandardItemModel(5, 2)    # 5行2列
self.model.setHorizontalHeaderLabels(["X", "Y"])

動的に行を増やす場合は appendRowinsertRow を使う/setItem で値を埋める。

【8】 QStandardItem のセット

概要:指定位置に QStandardItem を置きます。セルを編集可能にしたい場合は QStandardItem を使います。
メソッド:QStandardItemModel.setItem(row, col, item)
使い方:

from PySide6.QtGui import QStandardItem

data = [(1, 10), (2, 15), (3, 8), (4, 12), (5, 18)]
for row, (x, y) in enumerate(data):
    self.model.setItem(row, 0, QStandardItem(str(x)))
    self.model.setItem(row, 1, QStandardItem(str(y)))

setItem で入れたアイテムは編集可能/表示されます。セル変更は dataChanged を発生させます。

【9】 モデルの現在の行数

概要:モデルの現在の行数を返します。ループで全行を処理するときによく使います。
メソッド:QStandardItemModel.rowCount()
使い方:

for r in range(self.model.rowCount()):
    # item を取得して処理

【10】 指定セルの取得

概要:指定セルの QStandardItem を取得します(存在しない場合は None)。取得後 .text() で中身の文字列を得られます。
メソッド:QStandardItemModel.item(row, col)
使い方:

item_x = self.model.item(row, 0)
if item_x:
    text = item_x.text()


# 1
# 1
# 1
# 2
# 1
# 2
# 1
# 2
.......

セルが空(None)か、テキストが数値でないときの例外処理を忘れないこと。

【11】 データ編集時のシグナル

概要:モデル内のデータが編集されたときに Qt が発するシグナルです。ビューが編集されるとこのシグナルが飛びます。
メソッド:dataChanged(シグナル)
使い方:

self.model.dataChanged.connect(self.update_plot)

dataChanged は通常 QModelIndex 範囲を引数に取りますが、connect だけすれば問題なく呼べます。大量更新時は発火回数に注意。

【12】 セルに格納するアイテムの作成

概要:セルに格納するアイテムを作ります。テキストの取得は .text()
メソッド:QStandardItem(text) / QStandardItem.text()
使い方:

item = QStandardItem("123")
s = item.text()  

# "123"

数値扱いにする場合は文字列を数値に変換する(float(item.text()))必要があります。

【13】コンボボックスへの追加

概要:複数の項目をまとめてコンボボックスへ追加します。
メソッド:QComboBox.addItems(list)
使い方:

self.graph_selector.addItems(["折れ線グラフ", "棒グラフ"])

【14】 選択変更時のシグナル

概要:選択が変わったときに発火するシグナル(currentIndexChanged)と、現在選択されているテキストを取得する currentText()
メソッド:QComboBox.currentIndexChanged / currentText()
使い方:

self.graph_selector.currentIndexChanged.connect(self.update_plot)
typ = self.graph_selector.currentText()

# 折れ線グラフ

currentIndexChanged は引数に index を渡す場合とテキストを渡す場合があるので接続先のシグネチャに注意。

【15】 Matplotlib の図オブジェクト

概要:Matplotlib の図オブジェクト(Figure)を作り、add_subplot() で描画領域(Axes)を得ます。
メソッド:Figure() / Figure.add_subplot()(Matplotlib)
使い方:

from matplotlib.figure import Figure
self.figure = Figure()
self.ax = self.figure.add_subplot(111)

add_subplot(111) は 1×1 の一つ目の領域という意味。複数サブプロットも可能。
詳細は下記記事を参考にしてください

【16】 グラフをウィジェットに埋め込み

概要:Matplotlib の Figure を Qt のウィジェットとして表示するためのラッパー。これをレイアウトに入れることで Matplotlib グラフを埋め込みます。
メソッド:FigureCanvasQTAgg(Figure)
使い方:

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
self.canvas = FigureCanvas(self.figure)
layout.addWidget(self.canvas)

Matplotlib の GUI バックエンドによってクラス名が違う場合があるが、この方法が一般的(PySide6 でも backend_qt5agg を使う)。

【17】 Matplotlib の標準ツールバー

概要:Matplotlib の標準ツールバー(ズーム、パン、保存など)を表示するウィジェット。canvas と親ウィジェットを渡して生成します。
メソッド:NavigationToolbar2QT(canvas, parent)
使い方:

from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
self.toolbar = NavigationToolbar(self.canvas, self)
layout.addWidget(self.toolbar)

【18】 描画のための主要メソッド


概要:描画のための主要メソッド群です。前の描画を消す clear()、折れ線 plot()、棒グラフ bar()、ラベルやタイトル、凡例の設定。
メソッド:Axes.clear() / Axes.plot() / Axes.bar() / set_xlabel() / set_ylabel() / set_title() / legend()
使い方(抜粋):

self.ax.clear()
self.ax.plot(x, y, marker="o", linestyle="-")
self.ax.set_xlabel("X")
self.ax.set_ylabel("Y")
self.ax.set_title("タイトル")
self.ax.legend()

常に clear() → 描画 → draw_idle() の順で更新するのが安定。plot()bar() の引数で見た目を調整できます。

【19】 キャンバス上の描画更新

概要:キャンバス上の描画更新を要求します。draw() と違い、イベントループのアイドル時に効率よく再描画されるため UI の応答性が良くなります。
メソッド:FigureCanvas.draw_idle()
使い方:

self.canvas.draw_idle()

頻繁に更新する場合(リアルタイム等)は draw_idle() を優先的に使うのが推奨されます。

【20】 Qt のシグナルとの接続

概要:Qt のシグナル(イベント)と任意の関数(スロット)を結びつける基本操作。上のdataChanged.connect(self.update_plot)currentIndexChanged.connect(...) がこれに該当します。
メソッド:signal.connect(slot)(一般)
使い方:

self.model.dataChanged.connect(self.update_plot)
self.graph_selector.currentIndexChanged.connect(self.update_plot)

接続したスロットはシグナルが発火した時に呼ばれます。接続したいスロットのシグネチャ(引数の数)に合わせること。

よくある質問Q&A

グラフ更新が遅いのですが?

canvas.draw_idle() を使うと効率よく描画できます。

テーブル以外からもデータを追加したい

model.setItem()insertRow() を使えばプログラム的に追加可能です。

Matplotlibのグラフを別ウィンドウに表示できますか?

はい。figure.show() で独立したウィンドウ表示も可能です。

グラフの凡例やタイトルは?

Matplotlib標準API (ax.set_title, ax.legend) をそのまま使えます。

Pandasのデータフレームと連携できますか?

可能です。DataFrameをモデルに変換して QTableView に渡せば、そのままグラフに反映できます。

まとめ

  • QTableView + QStandardItemModel をデータ入力のUIに利用
  • dataChangedシグナル → update_plot() でグラフを自動更新
  • MatplotlibをQtに組み込むことで、GUI上で 入力と可視化のループ が作れる
  • 実用例:データ分析ツール、センサー監視、売上ダッシュボード

Model/Viewの基礎を学んだら、こうした 動的なデータ可視化 に進むと応用力が一気に高まります。

シェアしてくださると嬉しいです!
  • URLをコピーしました!

コメント

コメントする

目次