読者です 読者をやめる 読者になる 読者になる

bravo's blog

マルチプラットフォームGUIツールキット+軽量言語であるPyQtのプログラミングとか

PyQtでのMVC その1 (データ表示)

大量のデータをまとめるために

多くのデータを扱うアプリケーションでは、 使い手がどのようなデータが存在しているかを理解しやすくする仕組みを提供しています。 例に挙げる以下のような公共のサービスであっても、元のデータに対するタイトルや内容、 説明といったデータが含まれていることがわかります。

  • 検索エンジンの検索結果(タイトル、URL、本文など)
  • オークションの商品(商品名、写真、現在価格、商品説明など)
  • 動画サイトの動画(タイトル、サムネイル、再生数など)

このように大量のデータを扱うための代表的な手段としてMVCがあります。 MVCとはプログラムをModel、View、Controlerと3つの要素に分割する方式です。 それぞれの役割は以下の通りです。

  • Model: データへのアクセス(いわゆるCRUD処理)
  • View: 画面への表示(ユーザインタフェース、ユーザエクスペリエンス)
  • Controler: ユーザーからの入出力制御

PyQtでのMVC

PyQtMVCではModelを扱うクラスとViewを扱うクラスをそれぞれ用意します。 Controlerに該当する明確なクラスは無く、Viewを扱うクラスでユーザーからのイベントを受け、 Modelを扱うクラスを制御します。Modelを扱うクラスは、Viewを扱うクラスに対し、データの変更を伝えることで、Viewを扱うクラスが表示の制御をします。またModelを扱うクラスはViewを扱うクラスを介さずに直接アクセスすることもあります。

以上から、Viewはフロントエンドで、Modelはバックエンドという理解で良さそうです。

Viewを扱うクラス

Viewを扱う基底クラスはQAbstractItemViewクラスで、 一般的にはこれを継承したQListView、QTableView、QTreeViewといった具象クラスを使います(継承も可能です)。

Modelを扱うクラス

Modelを扱う基底クラスであるQAbstractItemModelを継承するか、 あらかじめ用意されたQStandardItemModelや"Q*Model"のような名前のクラスを使います(継承も可能です)。

QAbastractItemModelからクラスを設計する場合、次の5つの最低限必要なメソッドを実装する必要があります。

  • index(): 何行何列目(必要であれば親ノードから何行目)といった情報をQModelIndexクラスのインスタンスとして返す(親クラスから継承したcreateIndex()をそのまま使うことが多い)
  • parent(): 親ノードを表現したQModelIndexクラスのインスタンス
  • rowCount(): 全体で何行あるか(現在の階層に限る)
  • columnCount(): 全体で何列あるか
  • data(): データの内容(修飾情報も含まれる)

サンプル

QTableViewとQAbstractItemModelを使い、Pythonの2次元リストの内容をそのまま表示します。

f:id:bravo:20160105024657p:plain

import sys

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

class Model(QAbstractItemModel):
    def __init__(self, parent=None):
        super(Model, self).__init__(parent)
        self.items = [
            ['たぬき','そば','温'],
            ['きつね','うどん','温'],
            ['月見','うどん','冷'],
            ['天ぷら','そば','温'],
            ]

    def index(self, row, column, parent=QModelIndex()):
        return self.createIndex(row, column, None)

    def parent(self, child):
        return QModelIndex()

    def rowCount(self, parent=QModelIndex()):
        return len(self.items)

    def columnCount(self, parent=QModelIndex()):
        if self.items:
            return max([len(item) for item in self.items])
        return 0

    def data(self, index, role=Qt.DisplayRole):
        if role == Qt.DisplayRole:
            try:
                return self.items[index.row()][index.column()]
            except:
                return None
        return

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

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

def main():
    app = QApplication(sys.argv)
    w = MainWindow()
    w.show()
    w.raise_()
    app.exec_()

if __name__ == '__main__':
    main()

広告