PySide Model/View【第6弾】カスタムモデルでリアルタイム更新!QAbstractTableModelの実装

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

前回までは QTableView + QStandardItemModelProxyModel を使って「既成のモデル」を利用する方法を解説しました。
今回はさらに一歩進んで,自分でモデルを定義する「カスタムモデル」を作ります。

QAbstractTableModel を継承すると、リアルタイム更新が可能なデータテーブルを作成でき、外部データソース(センサー値やDBのデータ)をGUIに即時反映できます。

実務で「外部から値が流れてくるダッシュボード」や「常に更新されるリスト表示」を作るときに必須のテクニックです。

目次

カスタムモデルとは?

PySide ではデータを柔軟に扱うために カスタムモデル を作成できます。
標準の QStandardItemModelQStringListModel では対応しきれないような、
リアルタイム更新や複雑なデータ構造を管理したい場合にQAbstractTableModel が活躍します。

QAbstractTableModel の基本

QAbstractTableModel は「抽象クラス」であり、表形式データを表示するための枠組みを提供します。
自分でデータの格納方法や更新処理を定義することで、柔軟かつ効率的にデータを扱えます。

例えば「株価のリアルタイム更新」「センサー値の監視」「外部データベースとの同期」など、
動的なデータ処理に最適です。

基本コード

このコードでは、QAbstractTableModelを継承したクラスを作り、
1秒ごとにランダムなセルを更新してリアルタイムに表示が変わる仕組みを実装しています。

from PySide6.QtCore import Qt, QAbstractTableModel, QTimer
from PySide6.QtWidgets import QApplication, QTableView
import sys, random

class CustomTableModel(QAbstractTableModel):
    def __init__(self, data=None):
        super().__init__()
        self._data = data or [[0, 0, 0] for _ in range(5)]

    def rowCount(self, parent=None):
        return len(self._data)

    def columnCount(self, parent=None):
        return len(self._data[0])

    def data(self, index, role=Qt.DisplayRole):
        if role == Qt.DisplayRole:
            return self._data[index.row()][index.column()]

    def setData(self, index, value, role=Qt.EditRole):
        if role == Qt.EditRole:
            self._data[index.row()][index.column()] = value
            self.dataChanged.emit(index, index)
            return True
        return False

    def flags(self, index):
        return Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable

# 実行用
app = QApplication(sys.argv)
view = QTableView()
model = CustomTableModel()
view.setModel(model)

# 定期的に値を更新
def update_data():
    row, col = random.randint(0, 4), random.randint(0, 2)
    model.setData(model.index(row, col), random.randint(1, 100))

timer = QTimer()
timer.timeout.connect(update_data)
timer.start(1000)

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

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

Noメソッド / プロパティ説明
1rowCount()行数を返す。
2columnCount()列数を返す。
3data(index, role)データを返す。表示や編集の切り替えに必須。
4setData(index, value, role)データを更新し、dataChanged をemitする。
5flags(index)各セルの状態(編集可能かなど)を設定する。
6headerData(section, orientation, role)ヘッダのラベルを返す。
7insertRows(position, rows, parent)行を追加する。
8removeRows(position, rows, parent)行を削除する。
9beginInsertRows() / endInsertRows()行挿入の前後に呼び出す必要がある。
10beginRemoveRows() / endRemoveRows()行削除の前後に呼び出す必要がある。

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

【1】行数を返す

概要:表の行数を返す。
メソッド:

def rowCount(self, parent=None):
    return len(self._data)

使い方:

print("行数:", model.rowCount())
# 行数: 5

【2】列数を返す

概要:表の列数を返す。
メソッド:

def columnCount(self, parent=None):
    return len(self._data[0])

使い方:

print("列数:", model.columnCount())
# 列数: 3

【3】データを返す

概要:セルに表示する値を返す。
メソッドdata(self, index, role)

def data(self, index, role=Qt.DisplayRole):
    if role == Qt.DisplayRole:
        return self._data[index.row()][index.column()]

使い方

def update_data()
    print("データ:", model.data(model.index(row, col)))

# データ: 75
# データ: 55
# データ: 17

【4】データを更新する

概要:セルの値を編集し、変更をビューに通知する。
メソッド:setData(self, index, value, role=Qt.EditRole)

def setData(self, index, value, role=Qt.EditRole):
    if role == Qt.EditRole:
        self._data[index.row()][index.column()] = value
        self.dataChanged.emit(index, index)
        return True
    return False

使い方:

# 定期的に値を更新
def update_data():
    row, col = random.randint(0, 4), random.randint(0, 2)
    model.setData(model.index(row, col), random.randint(1, 100))

【5】セルの状態を返す

概要:編集可能かどうかを制御する。
メソッド:flags(self, index)
使い方:

def flags(self, index):
    return Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsEditable

よく使うもの

  • 選択可ItemIsSelectable
  • 有効化ItemIsEnabled
  • 編集可ItemIsEditable
  • チェックボックス可ItemIsUserCheckable
フラグ(定数)説明
Qt.ItemIsSelectable項目を選択できる(マウスクリックやキーボードで選択可能)
Qt.ItemIsEditable項目を編集できる(セルをダブルクリックなどで編集可能)
Qt.ItemIsDragEnabled項目をドラッグのソースにできる
Qt.ItemIsDropEnabled項目をドロップのターゲットにできる
Qt.ItemIsUserCheckable項目にチェックボックスを表示して、ユーザーが操作できる
Qt.ItemIsEnabled項目が有効(無効ならグレーアウトして操作できない)
Qt.ItemIsAutoTristateチェックボックスが3状態(オン・オフ・不確定)を持てる
Qt.ItemNeverHasChildren子アイテムを持たないことを示す(ツリー構造で便利)
Qt.ItemIsUserTristateユーザーがチェック状態を3状態で切り替えられる(AutoTristateと似ているが挙動が異なる)

【6】ヘッダを返す

概要:列や行のヘッダに表示するテキストを返す。
メソッド:headerData(self, section, orientation, role)
使い方:

def headerData(self, section, orientation, role):
    if role == Qt.DisplayRole:
        if orientation == Qt.Horizontal:
            return f"列 {section}"
        if orientation == Qt.Vertical:
            return f"行 {section}"

【7】行を追加する

概要:新しい行を挿入する。Model/View では、ビュー(QTableViewなど)に反映させるために、必ず begin~ / end~ 系の関数 を呼ぶ必要があります。

メソッド:insertRows(position, rows, parent)

from PySide6.QtCore import QModelIndex

def insertRows(self, position, rows=1, parent=None):
    # 1. 追加開始をビューに通知
    self.beginInsertRows(parent or QModelIndex(), position, position + rows - 1)
    # 2. 内部データに行を挿入
    for _ in range(rows):
        self._data.insert(position, [0] * self.columnCount())
    # 3. 追加終了をビューに通知
    self.endInsertRows()
    return True

使い方:

def update_data():
    # 行を追加
    row_position = model.rowCount()
    model.insertRows(row_position, 1)

【8】行を削除する

概要:行を削除します。ビューとモデルの同期を保つために、削除処理の前後で beginRemoveRows / endRemoveRows を必ず呼びます。

メソッド:removeRows(position, rows, parent)

def removeRows(self, position, rows=1, parent=None):
    self.beginRemoveRows(parent or QModelIndex(), position, position + rows - 1)
    for _ in range(rows):
        del self._data[position]
    self.endRemoveRows()
    return True

使い方:

# 2行目を削除
model.removeRows(1)

# 1行目から3行分削除
model.removeRows(0, 3)

【9】行追加の前後通知

  • 概要:
    insertRows() の前後で必ず呼ぶ通知メソッド です。ビュー(例:QTableView)に「これから行を追加する」「追加が終わった」と伝えることで、モデルとビューの状態を同期させます。
  • メソッド:beginInsertRows() / endInsertRows()
  • 使い方
  1. beginInsertRows(parent, first, last)
    • これから行を追加することをビューに通知。
    • 追加位置(first から last)を指定。
  2. 実際のデータ追加処理
    • モデル内部のリストやデータ構造に新しい行を挿入。
  3. endInsertRows()
    • 行の追加が完了したことをビューに通知。
    • ビューは再描画され、ユーザーに変更が反映される。

【10】行削除の前後通知

  • 概要:removeRows() の前後で必ず呼ぶ通知メソッドです。ビュー(例:QTableView)に「これから行を削除する」「削除が完了した」と伝えることで、モデルとビューの状態を正しく同期させます。
  • メソッド:beginRemoveRows() / endRemoveRows()
  • 使い方:
  1. beginRemoveRows(parent, first, last)
    • これから削除する行の範囲(first から last)をビューに通知。
    • ビューは内部的に該当する行を「消える予定」として準備する。
  2. 実際のデータ削除処理
    • モデル内部のリストやデータ構造から対象行を削除。
  3. endRemoveRows()
    • 行の削除が完了したことをビューに通知。
    • ビューは再描画され、ユーザーに削除が反映される。

よくある質問(FAQ)

QStandardItemModelとの違いは?

QStandardItemModelは「便利な既成品」、QAbstractTableModelは「フルカスタム可能な低レベルAPI」。リアルタイム更新や外部データ連携では後者が強いです。

setDataで更新しても表示が変わらないのはなぜ?

dataChanged.emit() を呼んでいない可能性があります。必ず更新後に通知してください。

行追加・削除でエラーが出るのは?

beginInsertRows() / endInsertRows() を呼び忘れている可能性があります。

編集不可にしたいときは?

flags() から Qt.ItemIsEditable を外せば編集できなくなります。

リアルタイム更新でUIが止まるのは?

重い処理は別スレッド(QThread)で行い、結果だけをモデルに反映するようにしてください。

まとめ

カスタムモデル(QAbstractTableModel)は、PySideのModel/Viewを実務で使えるレベルに引き上げる必須の仕組みです。

  • 行数・列数・データを定義するのが最初のステップ
  • setData() + dataChanged で「リアルタイム更新」できる
  • 行追加・削除には beginInsertRows / endInsertRows が必須
  • 編集可否は flags() でコントロールできる

QStandardItemModelでは限界のある「外部データ連携」や「リアルタイムUI」を作りたいときに、このカスタムモデルの実装は必ず役に立ちます。

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

コメント

コメントする

目次