【问题标题】:Making QList<QObject*> C++ model dynamic in QML在 QML 中使 QList<QObject*> C++ 模型动态化
【发布时间】:2016-09-08 08:59:39
【问题描述】:

我有一个 QList&lt;QObject*&gt; C++ 模型,其中包含自定义对象并暴露给 QML。

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

class CustomObject : public QObject
{
  Q_OBJECT

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

    [...]

}

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

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

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

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

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

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

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

这非常慢。

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

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

【问题讨论】:

  • 我没有使用过 QML。但是将 Qlist 迁移到模型中很容易。还有你应该使用的QAbstractListModel。您必须更改一些内容,但可能是因为该列表应该对 QML 隐藏并且只能由模型访问。但我猜 Qt 有一些简单的系统来设置 QML 列表视图的模型。似乎是一个标准用例。另外:您使用列表而不是向量的任何具体原因?
  • @Hayt:如果你想将 QList 用作 QML 中的模型,则必须使用它。
  • 事情是这样的。您真的想使用“模型”类作为模型并将容器隐藏在模型中(然后您可以使用任何您想要的东西)。您可能已经看过它,但这里有一个模型类的示例:doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html 在文档下方。

标签: c++ qt qml


【解决方案1】:

是的,这是可能的,您只需稍微更改 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;
};

它适用于从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;
}

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

模型注册

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

终于

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

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

【讨论】:

  • 我什至没有想过这么简单的事情。太好了,谢谢!
  • 你知道为什么这样的模型不直接包含在 Qt 中吗?似乎很有用。它有问题吗?关于内存管理? stackoverflow.com/q/43809751
  • thx,但是如何更改我的模型的具体值,例如list model
猜你喜欢
  • 2014-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-18
相关资源
最近更新 更多