QtCore.QAbstractItemModelを使用したカスタムモデルの作成¶
今回は、AbstractItemModelを使用してカスタムModelを作成してから
TreeViewを構成するやり方について。
とりあえず長いですが全コードから。
長いですか、いくつか要点をまとめ。
データ構造を作る¶
まず、Modelを作成する場合は、指定の構造になるようにItemのツリーを
クラスオブジェクトで構成します。
指定の構造は↑の図の通り。
今回はTreeModelで説明しますが、ListでもModelでも基本は同じです。
(むしろTreeが一番面倒くさい)
TreeViewの場合はこうなります。
今回のサンプルTreeItemですが、オブジェクトに childItems と parentItem
変数を持ちます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
そのツリー構造を作成しているのが「setupModelData」関数です。
Itemを作成するときに親ノードにあたるItemを指定し、
オブジェクトを作成したらappendChildで親ノードの子にObjectをセットします。
トップにはRootItemを作成し、Rootは親が「None」になるようにします。
構造ができたら、このItemツリーをパースする機能をクラスに実装していきます。
関数の実装¶
必須なVirtual関数¶
QtCore.QAbstractItemModelを使用してModelを作成する場合、最低限実装する必要がある
関数があります。
それが、
- columnCount
- rowCount
- parent
- index
この4つになります。
column/rowCountについて¶
まず、Countについて。
これは名前の通り現在の親Index(引数で受け取る)の子がいくつあるかをreturnするようにします。
TreeViewの場合は、TreeごとにこのCountが呼ばれ
そのCount分Itemを表示します。
parentについて¶
1 2 3 4 5 6 7 8 9 |
|
parent関数は、引数のindexの親にあたるModelIndexをreturnするようにします。
ここで重要になるのがModelIndexについて。
PySideのModelは実体を持たずに、あくまでもIndexで親子関係を持ちます。
実際に表示するItemは「ModelIndex.internalPointer()」に書いてある通り
実体までのポインタ を、ModelIndexが保持します。
このModelIndexを作成しているのが self.createIndex 関数で
createIndex(row,column,実体のオブジェクト) を渡すことでModelIndexを生成してくれます。
なので、このparent関数がなにをしているかというと
- 親を取得したいIndexから、Itemの実体を取得
- 実体のTreeItemのparentを使用して親の実体を取得
- 親がない=Rootの場合は空のIndexを返す
- 親がある場合はcreateIndexを使用して親のModelIndexを返す
このような挙動をしています。
return QtCore.QModelIndex()
こうしている部分は index.isValud() の判定で無効扱いになります。
indexについて¶
基本はparentと同様ですが、こちらは parent から指定のrow/columnのModelIndexを取得します。
1 2 3 4 5 6 7 8 9 10 11 |
|
parentItemの実体のTreeItemオブジェクトを取得し、
childを取得、そしてそのChildの実体とrow,columnから
createIndexを使用してModelIndexを作成して返します。
実際の表示をどうするかを指定する¶
indexとparentを実装することで、最初に作成したItemのツリーをパースする構造はできました。
パースはできましたが、じゃあ実際に「なにを表示するのか」は data 関数を実装することで
指定をします。
1 2 3 4 5 6 7 8 |
|
dataは、表示する場所のindexとroleとよばれる「ふるまい方」を受け取って処理をします。
このroleは、Viewに関係するいろいろな情報の受け取り口になっています。
例えば「背景の色」であったり、「文字の色」であったり
表示する文字であったり、あるいは自分で指定したり。
その受け取り口の「今回はなにがほしいのか」が入るのがroleになります。
今回のように文字を取得したい場合は、QtCore.Qt.DisplayRole がroleに入るので
それ以外はNoneで終了、DisplayRoleの場合はItemのdataで指定した文字列が返ります。
あとは必要に応じてflagsやHeaderを指定したりすればOKです。
(この2つは見ての通りなので今回は説明スキップ)
重要なのはModelIndexのふるまいと構造について。
Tree構造をオブジェクトで作成し、internalPointerで実体にアクセスしながら
親のModelIndex、子のModelIndexを作って
dataで最終的な出力形式を指定する...
これを押さえておけば、TreeViewでListViewでも、TableViewでも
同じ考え方でModelを作成することができます。