在QML中使QList <QObject*> C++模型动态化

Kor*_*idu 5 c++ qt qml

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

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

class CustomObject : public QObject
{
  Q_OBJECT

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

    [...]

}
Run Code Online (Sandbox Code Playgroud)

我的模型接触到QML,如下所示:

qmlEngine->rootContext()->setContextProperty("internalModel", QVariant::fromValue(m_internalModel));
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.我可以使用视图,显示我的所有元素,并递归地显示他们的孩子.

问题是QList无法通知QML模型已更改.如关于基于QObjectList的模型的文档中所述:

注意:视图无法知道QList的内容已更改.如果QList发生变化,则需要通过再次调用QQmlContext :: setContextProperty()来重置模型.

所以每当我添加或删除一个项目时,我都会打电话:

qmlEngine->rootContext()->setContextProperty("internalModel", QVariant::fromValue(m_internalModel));
Run Code Online (Sandbox Code Playgroud)

这非常慢.

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

那么,是不是可以从迁移QList<QObject*>QAbstractItemModel不改变QML一部分?特别是,我应该将所有Q_PROPERTY从CustomObject 迁移到角色还是可以"重用它们"?

psy*_*hed 6

是的,这是可能的,你只需要稍微改变QML

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;
};
Run Code Online (Sandbox Code Playgroud)

它适用于任何继承的类型QObject.您可以插入并通过删除项目insert()remove()其无论是从C++和QML工作.实现非常简单

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

型号注册

Model internalModel;
qmlEngine->rootContext()->setContextProperty("internalModel", &internalModel);
Run Code Online (Sandbox Code Playgroud)

最后

ListView {
  model: internalModel
  delegate: Text {
    text: model.item.name
  }
}
Run Code Online (Sandbox Code Playgroud)

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