Creating a QList <QObject *> C ++ Model Dynamics in QML

I have a QList<QObject*> C ++ model containing custom objects and subject to QML.

My custom object is as follows:

 class CustomObject : public QObject { Q_OBJECT Q_PROPERTY(QString name READ getName NOTIFY nameChanged) Q_PROPERTY(QQmlListProperty<CustomObject READ getChildren NOTIFY childrenChanged) [...] } 

My model is subject to QML as follows:

 qmlEngine->rootContext()->setContextProperty("internalModel", QVariant::fromValue(m_internalModel)); 

So far so good. I can use the view, display all my elements and recursively display their children as well.

The problem is that QList is not able to notify QML that the model has changed. As stated in the documentation for the QObjectList-based model :

Note. There is no way for the view to know that the contents of the QList has changed. If the QList changes, you need to reset by calling QQmlContext :: setContextProperty () again.

Therefore, every time I add or remove an item, I call:

 qmlEngine->rootContext()->setContextProperty("internalModel", QVariant::fromValue(m_internalModel)); 

And it is very slow.

If I understood correctly, I need to use QAbstractItemModel instead.

So, is it possible to migrate from QList<QObject*> to QAbstractItemModel without changing part of QML? In particular, should I transfer all Q_PROPERTY from CustomObject to roles or "reuse them"?

+4
source share
1 answer

Yes it is possible, you need to modify QML a bit

C ++ Model Class

 #pragma once #include <QAbstractListModel> #include <QVector> class Model : public QAbstractListModel { Q_OBJECT public: int rowCount(const QModelIndex&) const override; QVariant data(const QModelIndex& index, int role) const override; public slots: void insert(QObject* item); void remove(QObject* item); protected: QHash<int, QByteArray> roleNames() const override; private: QVector<QObject*> mItems; }; 

It will work with any types that inherit from QObject . You can insert and remove elements with insert() and remove() , which works with both C ++ and QML. The implementation is pretty simple

 #include "model.h" int Model::rowCount(const QModelIndex&) const { return mItems.size(); } QVariant Model::data(const QModelIndex& index, int /*role*/) const { QObject* item = mItems.at(index.row()); return QVariant::fromValue(item); } void Model::insert(QObject* item) { beginInsertRows(QModelIndex(), 0, 0); mItems.push_front(item); endInsertRows(); } void Model::remove(QObject* item) { for (int i = 0; i < mItems.size(); ++i) { if (mItems.at(i) == item) { beginRemoveRows(QModelIndex(), i, i); mItems.remove(i); endRemoveRows(); break; } } } QHash<int, QByteArray> Model::roleNames() const { QHash<int, QByteArray> roles; roles[Qt::UserRole + 1] = "item"; return roles; } 

Note the use of the role roleNames() and "item" . We will use it in QML. You do not need to transfer your CustomObject properties to roles, just use a single role and return the QObject* pointer from Model::data() .

Model Registration

 Model internalModel; qmlEngine->rootContext()->setContextProperty("internalModel", &internalModel); 

Finally

 ListView { model: internalModel delegate: Text { text: model.item.name } } 

The only thing you need to do in QML is to replace name and getChildren with model.item.name and model.item.getChildren . Does that sound simple?

+5
source

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


All Articles