QtQuick の ListModel を継承して使いたい
はじめに
ListModel と ListElement はよく使うエレメントですが、実は Qt Quick の中では一風変わったエレメントだということに気づいたことはありませんか?
一般的な QML のエレメントは「あらかじめ定義されているプロパティを設定する記述をする」もしくは「自分でプロパティの定義を記述する」ような使い方をしますが、ListElement は「プロパティの定義をせずに、プロパティの記述に似た文法で role/value のセットを記述できる(何言っているかわからない)」素敵なエレメントです。
ListModel も謎の性質があり、一般的な QML のエレメントでは、エレメント直下の子エレメントは、親エレメントの「リスト型の デフォルトのプロパティ に対する値の設定」となるのですが、ListModel には ListElement を受け付けるようなプロパティはありません。
ListModel は特別なパーサーを利用している
ListModel 自体は、Qt で一般的にリスト形式のモデルを扱う際に使われる QAbstractListModel の サブクラス となっています。
そのクラスを QML 側で利用できるようにする際に、以下のように 専用のパーサーを利用するように設定している ため、前述したような、一般的な記述方法で特殊な処理をするしくみになっています。
ListModel の派生エレメントを作成すると怒られる
というわけで、ListModel は特殊なパーサーを採用しているため、QML 側で派生クラスを作成すると(その場合、専用のパーサーが効かなくなるので)エラーが発生してしまいます。
import QtQml.Models 2.15
ListModel {
ListElement { key: 'key1'; value: 'value1' }
}
import QtQml.Models 2.15
AbstractMyListModel {
ListElement { key: 'key2'; value: 'value2' }
}
file:///.../main.qml:10 Type MyListModel unavailable file:///.../MyListModel.qml:4 Cannot assign to non-existent default property
どうしてもこうしたい場合の対応案(1)
派生クラスの Component.onCompleted で JSON 形式のデータを append() することで、実現可能です。
が、かっこ悪いです。
どうしてもこうしたい場合の対応案(2)
すこし工夫をして、同じ ような 記述ができるようにしてみました。
import QtQml 2.15
import QtQml.Models 2.15
ListModel {
id: root
property list<MyElement> elements: [
MyElement { key: 'key1'; value: 'value1' }
]
default property alias elements2: root.elements
Component.onCompleted: {
for (var i = 0; i < elements.length; i++)
root.append(elements[i])
}
}
AbstractMyListModel {
MyElement { key: 'key2'; value: 'value2' }
}
ListElement 相当のエレメント MyElement を、QtObject の派生クラスとして定義します。
(ListElement の記述の柔軟性を犠牲にしています。また、各項目毎に QtObject を生成することによりメモリの消費量とパフォーマンスも犠牲になっています)
AbstractMyListModel 内で、MyElement を受け取れるよう、デフォルトのプロパティを作成し、そのエレメントおよび、派生エレメントで MyElement を普通の書き方で記述していきます。
AbstractMyListModel のコンストラクタで、定義済みの MyElement を ListModel のデータとして登録しています。
main.qml は前述のものと同じものを利用可能です。
おわりに
複雑なシステムを構築する際に UI の定義などを継承して扱いたい場合があり、せっかく QML で書いているのだから QML らしく書きたいなーということで、いくつかの制限や犠牲はありつつも実現できました。
ソースコードは https://github.com/task-jp/qtquick–listmodel-sub-element から入手できますので、是非お試しください。