制作 QList<QObject*>QML 中的 C++ 模型动态

2022-01-19 00:00:00 qt qml c++

我有一个 QList<QObject*> C++ 模型,其中包含自定义对象并暴露给 QML.

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

我的自定义对象如下所示:

My custom object looks like this:

class CustomObject : public QObject
{
  Q_OBJECT

    Q_PROPERTY(QString name READ getName NOTIFY nameChanged)
    Q_PROPERTY(QQmlListProperty<CustomObject READ getChildren NOTIFY childrenChanged)

    [...]

}

我的模型像这样暴露于 QML:

My model is exposed to QML like this:

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

到目前为止一切顺利.我可以使用视图,显示我的所有元素并递归地显示它们的子元素.

So far so good. I can use a view, display all my elements and recursively also display their children.

问题在于 QList 无法通知 QML 模型已更改.正如关于 QObjectList-based model 的文档中所述:

The problem is that QList has no way to notify QML that the model changed. As noted in the documentation about QObjectList-based model:

注意:视图无法知道QList 已更改.如果 QList 发生变化,则需要重新设置再次调用 QQmlContext::setContextProperty() 进行建模.

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

所以每次我添加或删除一个项目时,我都会调用:

So everytime I add or remove an item, I call:

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

这非常慢.

如果我理解正确,我需要改用QAbstractItemModel.

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

那么,是否可以在不更改 QML 部分的情况下从 QList 迁移到 QAbstractItemModel ?特别是,我应该将所有 Q_PROPERTY 从 CustomObject 迁移到角色还是可以重用它们"?

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

推荐答案

可以,你只需要稍微改变QML

Yes this is possible, you only need to change QML slightly

C++ 模型类

#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;
};

它适用于从 QObject 继承的任何类型.您可以使用 insert()remove() 插入和删除项目,这两种方法都适用于 C++ 和 QML.实现很简单

It will work with any types that inherit from QObject. You can insert and remove items by using insert() and remove() which works both from 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;
}

注意 roleNames()"item" 角色的使用.我们将在 QML 中使用它.您不需要将 CustomObject 属性迁移到角色,只需使用单个角色并从 Model::data() 返回 QObject* 指针.

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

模型注册

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

终于

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

您在 QML 中唯一需要做的就是将 namegetChildren 替换为 model.item.namemodel.item.getChildren.听起来很简单?

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

相关文章