QMLモデルの異種階層

はじめに


多くの場合、モデルを次のように構成する必要があります。1つの構造を持つモデルの同じレベルで、別のレベルでモデルの構造が変更されます。 たとえば、デバイスのリストを表示し、各デバイスに設定グループがあり、各設定グループにさまざまなタイプの設定のリストがあるタスクを考えます。 簡単にするために、デバイスには名前とグループのリストのみがあると仮定します。 グループには、名前と設定のリストのみがあります。 設定には、名前とタイプ(チェックボックス、テキストボックス、またはスライダー)のみがあります。



このパターンは、 記事に基づいて体系化されました。 以下は、GoFに似たパターンの説明です。

予定


QMLを使用したC ++での複雑なモデルの使用を構造化するパターン。 ネストされたモデルリストの使用を促進して、階層構造を形成します。 同時に、QMLで使用する場合、複雑さは増加しません。

適用性


次の場合にパターンを使用します。


構造




会員



関係


リストオブジェクトは、コンストラクターでプロトタイプとして指定されたリストアイテムのクラスにロールデータを委任します。 サブモデルを持つ要素から、または単純なリストアイテムから継承する必要があります。 QMLでは、子モデルにアクセスするには、subModelFromIdメソッドを呼び出す必要があります。ここで、パラメーターは現在の要素のidロールです。

C ++コード例


デバイスモデルを追加します。

class DeviceModelItem : public Models::SubListedListItem { Q_OBJECT public: enum GroupModelItemRoles { deviceId = Qt::UserRole + 1, deviceNameRole }; DeviceModelItem(QObject* parent = 0); int id() const; QVariant data(int role) const; QHash<int, QByteArray> roleNames() const; Models::ListModel* submodel() const; private: int _id; static int g_id; QString deviceName; Models::ListModel* groupListModel; }; 

識別子を追跡するには、グローバルカウンターg_idを使用します。現在のデバイスの識別子は_idです。 コンストラクターで、サブモデルを追加し、名前を初期化します。

 DeviceModelItem::DeviceModelItem(QObject *parent):SubListedListItem(parent), _id(g_id++) { deviceName = QString("Device %1").arg(_id); groupListModel = new Models::SubListedListModel(new GroupModelItem()); //     groupListModel->appendRow(new GroupModelItem()); groupListModel->appendRow(new GroupModelItem()); } 

ロール処理:

 QVariant DeviceModelItem::data(int role) const { switch (role) { case deviceId: return this->id(); case deviceNameRole: return this->deviceName; default: return QVariant(); } } QHash<int, QByteArray> DeviceModelItem::roleNames() const { QHash<int, QByteArray> roles; roles[deviceId] = "deviceId"; roles[deviceNameRole] = "deviceName"; return roles; } 

グループのモデルは似ていますが、コンストラクターでサブモデルのないリストを作成する点が異なります。

 GroupModelItem::GroupModelItem(QObject *parent):SubListedListItem(parent), _id(g_id++) { groupName = QString("Group %1").arg(_id); settingsListModel = new Models::ListModel(new SettingsModelItem()); ... } 

モデルでは、設定はすでにListItemから継承されています。

 class SettingsModelItem : public Models::ListItem { Q_OBJECT public: enum SettingsModelItemRoles { settingsId = Qt::UserRole + 1, settingsNameRole, settingsTypeRole } ... } 

設定にサブモデルは必要ありません。 次に、ルートデバイスモデルをコンテキストに追加します。

 int main(int argc, char *argv[]) { //     touch QGuiApplication app(argc, argv); QQmlApplicationEngine engine; Models::ListModel* devicesModel = new Models::SubListedListModel(new DeviceModelItem()); //DEBUG devicesModel->appendRow(new DeviceModelItem()); devicesModel->appendRow(new DeviceModelItem()); devicesModel->appendRow(new DeviceModelItem()); devicesModel->appendRow(new DeviceModelItem()); devicesModel->appendRow(new DeviceModelItem()); engine.rootContext()->setContextProperty("deviceModel",devicesModel); ... } 

QMLサンプルコード


ナビゲーションモデルとして、StackView(main.qml)を使用します。

 StackView { id: stackView anchors.fill: parent initialItem: Item { width: parent.width height: parent.height ListView { model: deviceModel anchors.fill: parent delegate: AndroidDelegate { text: deviceName onClicked: stackView.push({item:Qt.resolvedUrl("pages/GroupPage.qml"), properties:{subModel:deviceModel.subModelFromId(model.deviceId)}}) } } } } 

グループページの場合、subModelFromIdを介してサブモデルがインストールされました。 グループモデルでは、同じ方法で処理します。

 ScrollView { ... property variant subModel: null ListView { ... model: subModel delegate: AndroidDelegate { text: groupName onClicked: stackView.push({item:Qt.resolvedUrl("SettingsPage.qml"), properties:{subModel:subModel.subModelFromId(model.groupId)}}) } } ... } 

設定ページのみのリストの場合:

 ListView { id: settingsView ... model: subModel delegate: Item { CheckBox{ visible: settingsType == 0 ... } Column{ ... visible: settingsType == 1 Text{text: settingsName} TextField {text: "Text input"} } Column{ ... visible: settingsType == 2 Text{text: settingsName} Slider {value: 1.0} } } 

結果のスクリーンショット




ソースへのリンク: GitHub
ソース記事へのリンク: 記事

Source: https://habr.com/ru/post/J268437/


All Articles