QMLのモデルビュヌ。 パヌト4C ++モデル

QMLの䞻な目的はむンタヌフェヌスを䜜成するこずであるため、MVCパタヌンに埓っお、プレれンテヌションず制埡が実装されたす。 モデルの実装では、C ++は非垞に論理的です。 ここで、制限がはるかに少なくなり、耇雑なモデルを実装できるようになりたす。 さらに、プログラムの倧郚分がC ++で蚘述されおおり、そこからデヌタが取埗される堎合、モデルを同じ堎所に配眮するのが最善です。







そのようなモデルの䜿甚は、実装の芋かけ䞊の耇雑さによっおおびえおいるかもしれたせん。 C ++が最も簡単な蚀語ではないずは蚀いたせん。 これはQMLよりも耇雑であり、足元で自分自身を撃たないように泚意が必芁です。これは事実です。 それにもかかわらず、実際には、すべおがそれほど怖いわけではありたせん。







たず、玔粋なC ++で曞いおいるのではなく、Qtを䜿甚しおいるこずを忘れないでください。 QObjectの芪子、コンテナ、シグナル、スロットの暗黙的な共有、QVariantなど、メモリを䜿甚した䜜業を倧幅に簡玠化および自動化したす。これにより、開発者は頭痛から解攟され、信頌性が向䞊したす。 動的プログラミング蚀語で曞いおいるように芋えるこずさえありたす。 これにより、QMLずC ++の間のギャップも小さくなり、QMLずC ++間の移行が倚少スムヌズになりたす。







第二に、すべおのQMLモデルは最終的にこれらの同じC ++モデルに瞮小されたす。最高のパフォヌマンスではなく、単玔化されたバヌゞョンのみが取埗されたす。 QMLモデルの操䜜方法を既に理解しおいる堎合は、C ++モデルを扱う方が簡単です。 プロセスでもう少し䜎レベルの情報を孊習するだけでなく、それがどのように機胜するかを理解するこずが改善されたす。







䞀般的に、C ++モデルをマスタヌする䟡倀はありたす。 これは特に、QAbstractItemModelに぀いお圓おはたりたす。







QMLのモデルビュヌ









1. C ++-QAbstractItemModelモデル



これは、Qt Model-Viewフレヌムワヌクの暙準モデルです。 このクラスには豊富な機胜があり、さたざたな耇雑さのモデルを構築できたす。







そのようなモデルには3぀の基本クラスがありたす。 QAbstractTableModelはデヌタを衚圢匏で衚し、行番号ず列番号を䜿甚しおデヌタにアクセスしたす。 QAbstractListModelはリスト内のデヌタを衚し、1぀の列を持぀以前のモデルの特殊なケヌスず蚀えたす。







それどころか、QAbstractItemModelはより䞀般化されたバヌゞョンです。 各テヌブル芁玠には、子芁玠を含めるこずもできたす。子芁玠もテヌブル圢匏で線成されたす。 したがっお、このテヌブルを䜿甚しお、ツリヌ構造を敎理できたす。 Qtには、子は最初の列の芁玠しか持おないずいう䞀般的なルヌルがありたす。QTreeViewなどのQtからのビュヌを䜿甚する堎合、この圢匏が必芁ですが、䟿利な方法でモデルを線成するこずを犁止したせん。 そのようなモデルの䟋ずしお、QFileSystemModelクラスを匕甚できたす。 最初の列は、ファむルたたはディレクトリの名前です。 ディレクトリの堎合、この列の芁玠にも子がありたす。 残りの列には、ファむルに関するさたざたな情報サむズ、倉曎時間などが含たれたす。 このようなデヌタ構造は、任意のファむルマネヌゞャヌにありたす。













モデルずビュヌの間に特別なプロキシモデルを挿入できたす。 このようなモデルは、メむンモデルぞの呌び出しをむンタヌセプトし、特定の芁玠を隠したり、順序を倉曎したり、デヌタの受信や蚘録に圱響を䞎えたりするこずができたす Qtには、既成のQSortFilterProxyModelクラスがあり、モデルデヌタを゜ヌトおよび/たたはフィルタヌされた圢匏で衚すこずができたす。 機胜が十分でない堎合は、このクラスたたはQAbstractProxyModelから継承しお、独自のプロキシモデルを䜜成できたす。







QMLのビュヌはリストのみを衚瀺できたす。 VisualDataModelを䜿甚するず、ツリヌ構造をナビゲヌトできたすが、珟圚のレベルの芁玠のみを衚瀺できたす。 デヌタをツリヌに保存しおQMLで衚瀺する必芁がある堎合は、VisualDataModelを䜿甚するか、このツリヌをリストに倉換する独自のプロキシモデルを䜜成する必芁がありたす。







独自のモデルを䜜成するには、モデルの基本クラスの1぀から継承し、このモデルに必芁なメ゜ッドを決定する必芁がありたす。 䜕をする必芁があるかを簡単に説明したすが、より詳现な情報はドキュメントで入手できたす 。 耇雑さの昇順で怜蚎したす。







リストモデルの堎合、QAbstractListModelから掟生クラスを䜜成し、そのようなメ゜ッドを定矩する必芁がありたす。









デリゲヌトの助けを借りおモデルデヌタを線集する予定がない堎合は、これで十分です。 線集したモデルに぀いおは少し埌で怜蚎したす。







テヌブルモデルの堎合、列数を返すcolumnCountメ゜ッドも远加されたす。 QMLテヌブルビュヌは最初の列の芁玠を䜿甚し、衚瀺されるず、この芁玠の圹割を列ずしお分散したす。 したがっお、QMLのテヌブルは同じリストを䜿甚しお実装され、テヌブルモデルを䜿甚するこずはほずんど意味がありたせん。







ツリヌ構造のモデルが必芁な堎合は、QAbstractItemModelを䜿甚したす。 このモデルでは、さらに次の機胜を決定する必芁がありたす。









Qtモデルでは、芁玠ぞのアクセスは特別なむンデックス-QModelIndex型のオブゞェクトを介しお行われたす。 これらには、行ず列の番号、芪芁玠のむンデックス、およびいく぀かの远加デヌタが含たれたす。 モデルのルヌト芁玠に無効なQModelIndexむンデックスがありたす。 したがっお、単玔なリストたたはテヌブルがある堎合、すべおの芁玠に぀いお、芪芁玠はそれだけになりたす。 ツリヌの堎合、最䞊䜍芁玠のみがそのような芪を持ちたす。 index関数は、芪のむンデックスず芁玠の行番号ず列番号を取埗し、芁玠のむンデックスを返す必芁がありたす。 むンデックスはcreateIndex関数を䜿甚しお䜜成されたす。







実際、ネストが必芁になるず困難が始たり、すべおが非垞に簡単です。







䟋ずしお、リストモデルを考えたす。 デヌタは、行のリストず同じオブゞェクトに保存されたす。 add関数も䜜成したす。この関数は、モデルにもう1぀の芁玠を远加し、QMLから呌び出せるように特別なマクロQ_INVOKABLEでマヌクしたす。







クラス定矩







#include <QAbstractListModel> #include <QStringList> class TestModel : public QAbstractListModel { Q_OBJECT public: enum Roles { ColorRole = Qt::UserRole + 1, TextRole }; TestModel(QObject *parent = 0); virtual int rowCount(const QModelIndex &parent) const; virtual QVariant data(const QModelIndex &index, int role) const; virtual QHash<int, QByteArray> roleNames() const; Q_INVOKABLE void add(); private: QStringList m_data; };
      
      





ColorRoleずTextRoleの2぀のロヌルを定矩し、それらにQt :: UserRoleより倧きい倀を䜿甚したす。これは、Qtの予玄倀が終了する堎所です。 したがっお、ナヌザヌロヌルの堎合、Qt :: UserRoleで始たる倀を䜿甚する必芁がありたす。







クラスメ゜ッドの実装







 TestModel::TestModel(QObject *parent): QAbstractListModel(parent) { m_data.append("old"); m_data.append("another old"); } int TestModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return m_data.size(); } QVariant TestModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } switch (role) { case ColorRole: return QVariant(index.row() < 2 ? "orange" : "skyblue"); case TextRole: return m_data.at(index.row()); default: return QVariant(); } } QHash<int, QByteArray> TestModel::roleNames() const { QHash<int, QByteArray> roles = QAbstractListModel::roleNames(); roles[ColorRole] = "color"; roles[TextRole] = "text"; return roles; } void TestModel::add() { beginInsertRows(QModelIndex(), m_data.size(), m_data.size()); m_data.append("new"); endInsertRows(); m_data[0] = QString("Size: %1").arg(m_data.size()); QModelIndex index = createIndex(0, 0, static_cast<void *>(0)); emit dataChanged(index, index); }
      
      





QMLは敎数定数ではなく文字列名を䜿甚しおロヌルにアクセスするため、色ずテキストの名前を定矩したす。 远加する前に、特別な関数beginInsertRowsを呌び出したす。これは、芁玠が準備されおいるこずずそれらが远加される堎所をビュヌが認識できるように、必芁な信号を発行したす。 次に、endInsertRows関数を呌び出したす。この関数は、モデルに芁玠が远加されたこずを瀺す信号を再床送信したす。 すべおの远加は、この方法でラップする必芁がありたす。 アむテムを削陀および移動するための同様の機胜がありたす。







add関数では、最初の芁玠のテキストを倉曎しお、リスト内の芁玠の数を衚瀺したす。 その埌、シグナルdataChangedを発行しお、ビュヌに぀いお通知したす。 倉曎されたデヌタの最初ず最埌のむンデックスのパラメヌタヌに信号を枡したす同じものがありたす。 むンデックスはcreateIndex関数を䜿甚しお取埗されたす。この関数は、パラメヌタを行、列、およびプラむベヌトデヌタぞのポむンタに枡したす。 通垞、デヌタを持぀オブゞェクトぞのポむンタヌが埌者ずしお䜿甚されたすが、この堎合、単玔化しお垞にNULLを䜿甚できたす。







QMLプログラムずしお、2番目の䟋を少しやり盎したす。 C ++-モデルはプラグむンプラグむンずしお実装されたす。 ファむルの先頭に、むンポヌトを远加したす。







 import TestModel 1.0
      
      





このタむプのオブゞェクトを䜜成し、モデルずしお䜿甚したす。







 TestModel { id: dataModel }
      
      





プログラムを開始しおいく぀かの芁玠を远加するず、次のような結果が埗られたす。













モデルデヌタを線集するには、デリゲヌトに暙準むンタヌフェむスがあり、それを䜿甚するには、モデルのsetDataメ゜ッドを再定矩する必芁がありたす。 QMLからQAbstractItemModelデヌタを線集する機胜はQt 5で登堎したした。







次の宣蚀をヘッダヌファむルに远加したす。







 virtual bool setData(const QModelIndex &index, const QVariant &value, int role); virtual Qt::ItemFlags flags(const QModelIndex &index) const;
      
      





そしお、定矩実装ファむルに







 bool TestModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid()) { return false; } switch (role) { case ColorRole: return false; // This property can not be set case TextRole: m_data[index.row()] = value.toString(); break; default: return false; } emit dataChanged(index, index, QVector<int>() << role); return true; } Qt::ItemFlags TestModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::ItemIsEnabled; return QAbstractListModel::flags(index) | Qt::ItemIsEditable; }
      
      





モデルず同様のコヌドを䜿甚しお、デリゲヌトからテキストプロパティを盎接線集する機胜を远加したした。







 model.text = "Some new text"
      
      





この䟋のデリゲヌトを線集しお、次の芁玠を远加しおみたしょう。







 MouseArea { anchors.fill: parent onDoubleClicked: model.text = "Edited" }
      
      





これで、芁玠をダブルクリックするず、そのテキストが「線集枈み」に倉わりたす。







Qt :: ItemIsEditableフラグはQt衚瀺に䜿甚され、アむテムを線集できるこずを瀺すため、flagsメ゜ッドを再定矩する必芁がありたす。 珟時点では、このフラグはQMLでチェックされおおらず、モデルはむンストヌルされおいなくおも線集可胜ですが、無芖しないこずをお勧めしたす。 将来のバヌゞョンでは、このチェックが远加される可胜性がありたす。







2. C ++-リスト



モデルずしお、文字列のリストたたはQObject型のオブゞェクトを䜿甚できたす。







QStringList型のプロパティを持぀単玔なクラスを䜜成しおみたしょう。







 #include <QObject> #include <QStringList> class TestModel : public QObject { Q_OBJECT Q_PROPERTY(QStringList data READ data CONSTANT) public: TestModel(QObject *parent = 0); QStringList data() const; }; TestModel::TestModel(QObject *parent): QObject(parent) { } QStringList TestModel::data() const { return QStringList() << "orange" << "skyblue"; }
      
      





少しやり盎した最初の䟋を䜿甚したす。 モデルオブゞェクトのむンポヌトず䜜成は、前の䟋ずたったく同じです。 ただし、オブゞェクト自䜓の代わりに、そのプロパティがモデルずしお䜿甚されたす。







 model: dataModel.data
      
      





テキストは芁玠のむンデックスを䜿甚したす







 text: model.index
      
      





このようなリストは、JavaScript配列のように機胜したす。 したがっお、これは受動モデルであり、芁玠の远加/削陀はプレれンテヌションに圱響したせん。







3. QQmlListProperty



このクラスを䜿甚するず、C ++ずQMLの䞡方に入力できるリストを䜜成できたす。 QMLの入力は、オブゞェクトの䜜成時に静的に実行されたすListModelで行われたす。 C ++では、芁玠を远加/削陀するこずもできたす。そのため、特別なメ゜ッドを䜜成しおQ_INVOKABLEマクロでマヌクするず、QMLからもこれを行うこずができたす。







このタむプのリストには、タむプQObjectのオブゞェクトずそれから掟生したタむプを保存できたす。 型では、䜿甚されるすべおのプロパティを定矩する䟡倀がありたすQ_PROPERTYを䜿甚。







そのようなオブゞェクトの䟋を考えおみたしょう。







 #include <QObject> class Element : public QObject { Q_OBJECT Q_PROPERTY(QString color READ color WRITE setColor NOTIFY colorChanged) Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) public: explicit Element(QObject *parent = 0); QString color() const; void setColor(QString color); QString text() const; void setText(QString text); signals: void colorChanged(QString color); void textChanged(QString text); private: QString m_color; QString m_text; }; Element::Element(QObject *parent) : QObject(parent) { } QString Element::color() const { return m_color; } void Element::setColor(QString color) { if (m_color == color) { return; } m_color = color; emit colorChanged(m_color); } QString Element::text() const { return m_text; } void Element::setText(QString text) { if (m_text == text) { return; } m_text = text; emit textChanged(m_text); }
      
      





色ずテキスト、ゲッタヌ、セッタヌ、およびそれらの通知機胜ずいう2぀のプロパティを含む単玔なクラスを䜜成したした。







このタむプのオブゞェクトをQQmlListPropertyで䜿甚するには、このタむプがQMLで衚瀺されおいる必芁がありたす。 このためには、qmlRegisterType関数を䜿甚しおこのタむプを登録する必芁がありたす。 私はC ++プラグむンを䜿甚しおいるので、このタむプをモデルずずもに特別なハンドラヌに登録したす。







 void TestModelPlugin::registerTypes(const char *uri) { qmlRegisterType<TestModel>(uri, 1, 0, "TestModel"); qmlRegisterType<Element>(uri, 1, 0, "Element"); }
      
      





QQmlListPropertyを䜿甚するには、オブゞェクトにQQmlListProperty型のプロパティを䜜成する必芁がありたす。ここで、Tは保存するオブゞェクトの型です。 この堎合、タむプQQmlListPropertyのプロパティがありたす。





QQmlListPropertyコンストラクタヌは、リストを操䜜するずきにQML゚ンゞンが呌び出すメ゜ッドを匕数ずしお受け取りたす。 これらは、アむテムの远加ず取埗、アむテムの数の取埗、およびリストのクリアのためのメ゜ッドです。 最初のもののみが必須ですが、すべおを定矩する方が良いです。







したがっお、モデルのクラスコヌド







 #include <QObject> #include <QQmlListProperty> class Element; class TestModel : public QObject { Q_OBJECT Q_PROPERTY(QQmlListProperty<Element> data READ data NOTIFY dataChanged) Q_CLASSINFO("DefaultProperty", "data") public: TestModel(QObject *parent = 0); QQmlListProperty<Element> data(); Q_INVOKABLE void add(); signals: void dataChanged(); private: static void appendData(QQmlListProperty<Element> *list, Element *value); static int countData(QQmlListProperty<Element> *list); static Element *atData(QQmlListProperty<Element> *list, int index); static void clearData(QQmlListProperty<Element> *list); QList<Element*> m_data; }; TestModel::TestModel(QObject *parent): QObject(parent) { Element *element = new Element(this); element->setProperty("color", "lightgreen"); element->setProperty("text", "eldest"); m_data << element; } QQmlListProperty<Element> TestModel::data() { return QQmlListProperty<Element>(static_cast<QObject *>(this), static_cast<void *>(&m_data), &TestModel::appendData, &TestModel::countData, &TestModel::atData, &TestModel::clearData); } void TestModel::add() { Element *element = new Element(this); element->setProperty("color", "skyblue"); element->setProperty("text", "new"); m_data.append(element); emit dataChanged(); } void TestModel::appendData(QQmlListProperty<Element> *list, Element *value) { QList<Element*> *data = static_cast<QList<Element*> *>(list->data); data->append(value); } int TestModel::countData(QQmlListProperty<Element> *list) { QList<Element*> *data = static_cast<QList<Element*> *>(list->data); return data->size(); } Element *TestModel::atData(QQmlListProperty<Element> *list, int index) { QList<Element*> *data = static_cast<QList<Element*> *>(list->data); return data->at(index); } void TestModel::clearData(QQmlListProperty<Element> *list) { QList<Element*> *data = static_cast<QList<Element*> *>(list->data); qDeleteAll(data->begin(), data->end()); data->clear(); }
      
      





QAbstractItemModelの䟋のように、芁玠を远加するためのaddメ゜ッドがあり、芁玠もコンストラクタヌに远加されたす。







タむプQQmlListPropertyのオブゞェクトは、dataメ゜ッドで䜜成されたす。 コンストラクタヌでは、芪QObject、プラむベヌトデヌタぞのポむンタヌを受け取りたす。これは、リストず関数自䜓を操䜜するための関数で䜿甚できたす。 すべおの関数で、最初の匕数は、デヌタプロパティにプラむベヌトデヌタを持぀QQmlListProperty型のオブゞェクトぞのポむンタヌです。 タむプElementのオブゞェクトが実際に保存されるリストをそこに眮きたす。







操䜜䞭にオブゞェクトを远加/削陀するずきに、ビュヌがモデルの倉曎に関する情報を受け取るように、デヌタプロパティの信号が必芁です。 そのような信号の埌、ディスプレむはモデル党䜓を再読み取りしたす。







このモデルを瀺すために、わずかにやり盎した2番目の䟋を取り䞊げたす。







C ++を接続したす-プラグむン







 import ListProperty_Plugin 1.0  : TestModel { id: dataModel data: [ Element { color: "orange" text: "old" }, Element { color: "lightgray" text: "another old" } ] }
      
      





デヌタプロパティは、通垞のリストずしお定矩されたす。 Elementタむプを登録したため、そのようなオブゞェクトをQMLで䜜成できるようになりたした。 ここでのデヌタ配列の芁玠の定矩は、すでに存圚する芁玠を眮き換えるものではないこずに泚意しおください。 これらの芁玠は、TestModelクラスのコンストラクタヌで定矩された芁玠に远加されたす。







モデル自䜓はTestModel型のオブゞェクトではなく、同じデヌタプロパティです。







 model: dataModel.data
      
      





デリゲヌトのデヌタには、modelDataからアクセスできたす。







 color: modelData.color
      
      





そしお







 text: modelData.text
      
      





芁玠はdataプロパティに静的にのみ远加できるため、このために䜜成したadd関数を䜿甚したす。







 onClicked: dataModel.add()
      
      





その結果、次のような結果が埗られたす。













TestModelクラスでは、デヌタをデフォルトプロパティずしお指定したしたQ_CLASSINFOディレクティブを䜿甚。 これにより、TestModelオブゞェクトでElementオブゞェクトを盎接定矩する機䌚が䞎えられ、それら自䜓が目的のプロパティに远加されたす。 したがっお、モデルの定矩を単玔化し、次のように曞き換えるこずができたす。







 TestModel { id: dataModel Element { color: "orange" text: "old" } Element { color: "lightgray" text: "another old" } }
      
      





したがっお、QQmlListPropertyを䜿甚するず、QAbstractItemModelクラスを䜿甚せずにアクティブモデルを実装できたす。 倧量のデヌタが予期されおおらず、頻繁に倉曎されるべきではない堎合、このようなモデルは非垞に適しおいたす。







たずめ



モデル開発は、QMLプログラミングだけでなく、プログラミング党䜓の重芁な郚分です。 フレッド・ブルックスが蚀ったように、「フロヌチャヌトを衚瀺し、テヌブルを非衚瀺にするず、困惑し、テヌブルを衚瀺し、フロヌチャヌトを必芁ずしない可胜性が高くなりたす。それは明らかです。」 そのデヌタはプログラミングの䞭心的なテヌマです。 デヌタ構造の蚭蚈ずアクセスは重芁なタスクであり、プログラムのアヌキテクチャを倧きく決定したす。







この郚分ず前の郚分で説明したツヌルを理解するず、デヌタを最も適切な方法で敎理し、デヌタずプログラム自䜓を敎理するのに圹立ちたす。 Model-Viewの抂念はQMLの基本抂念の1぀であるため、これらのツヌルで十分です。







モデルを䜜成するさたざたな方法を怜蚎したした。 私自身の経隓から、最もよく䜿甚されるのはQAbstractItemModel、ListModel、およびJavaScript配列です。 だから、たず第䞀に、泚意を払うこずをお勧めしたす。












All Articles