哪些是将 QObject 列表传递给 QML 上下文的可能方法

Which are the possibile ways to pass a list of QObject to QML context?

本文关键字:上下文 QML 方法 QObject 列表      更新时间:2023-10-16

我正在寻找一种方法,如何将大量QObjects作为模型传递给QML,而不是使用Repeater {}来绘制它们。此解决方案必须满足以下条件:

  1. 新对象添加到列表时,将仅刷新此项
  2. 当任何传递的对象被更改时,Qml内容必须自动刷新
  3. 列表必须是通用的,没有硬编码的属性名称

解决方案 1.

我知道我可以使用QQmlListProperty<QObject>. 不幸的是,如果添加/删除该对象,则所有其他对象都将在 Qml 中刷新。在更复杂/大量对象的情况下,这是非常笨拙的。

此解决方案满足 2) 和 3)。当对象通过setter更新并调用notifyChange()时,qml会自动更新内容,并且可以在任何QObject上使用它

解决方案 2.

QAbstractList,具有文档 (http://doc.qt.io/qt-5/qtquick-modelviewsdata-cppmodels.html#qabstractitemmodel) 中所述的已实现角色。

此解决方案满足 1) 但不满足 2) 和 3)。添加新对象并调用 beginInsertRows/endInsertRows 时,只会正确刷新一个项目。但是有必要为每个 QObject 准备模型对象,并且当 QObject 更改时必须手动更新模型

解决方案 3.

我试图实现QAbstractListModel,它在内部保存QObjects指针列表:QList<boost::shared_ptr<AnimalObject>> m_animals;

roleNames()方法未实现时,Qml根本不会通过data()方法查询数据。因此,似乎无法使用默认Qt::DisplayRole角色从QAbstractList返回QObject

当使用单个角色实现 roleNames() 方法时,例如 "object",并且data()返回内部 QObject 作为 QVariant,可以从 QML 访问它:

QVariant AnimalModel2::data(const QModelIndex & index, int role) const {
    if ( index.row() < 0 || index.row() >= m_animals.count() )
        return QVariant();
    auto ptrAnimal = m_animals[index.row()];
    if ( role == ObjectRole )
    return qVariantFromValue(ptrAnimal.get());
}
QHash<int, QByteArray> AnimalModel2::roleNames() const {
    QHash<int, QByteArray> roles;
    roles[ObjectRole] = "object";
    return roles;
}

这就是从QML访问QObject的方法

Repeater {
  model: myModel
  Text {
    text: "[" + model.object.name + "]";
  }
}

此解决方案满足所有要求。不幸的是,有必要使用model.object.property而不是更直接的model.property来访问此QObject。虽然这没什么大不了的,但直接访问对象会很棒。

问题:

我的问题是。这三种方法是解决此问题的唯一可能方法,还是我完全错过了任何其他方法?

有没有干净的方法可以创建QObjects列表,将其传递给QML,完全支持从C++中添加/删除/更新对象并在QML中直接更新它们?

PS:我在这里描述了所有这些方法,因为我相信这对许多其他方法都很有用。我花了几个小时才弄清楚如何做到这一切。

编辑

建议的解决方案 1.

正如@Velkan所建议的,可以使用QMetaObject::propertyQMetaObject::propertyCount来动态扩展对象的属性的QAbstractListModel。不幸的是,这意味着通过QDynamicPropertyChangeEvent信号为每个对象/属性实现单独的刷新。

不幸的是,对于大量的对象和属性,此解决方案可能效率非常低。对于对此解决方案感兴趣的任何人,这里有一个带有QMetaObject::property测试的代码片段:

QVariant AnimalModel4::data(const QModelIndex & index, int role) const {
    if ( index.row() < 0 || index.row() >= m_animals.count() )
        return QVariant();
    auto ptrAnimal = m_animals[index.row()];
    const QMetaObject * pMeta = ptrAnimal->metaObject();
    int propertyNo= role - (Qt::UserRole + 1);
    QMetaProperty propMeta = pMeta->property(propertyNo);
    QVariant value = propMeta.read(ptrAnimal.get());
    return value;
}
QHash<int, QByteArray> AnimalModel4::roleNames() const {
    QHash<int, QByteArray> roles;
    if ( m_animals.size() == 0 )
        return roles;
    int role= Qt::UserRole + 1;
    const QMetaObject * pMeta = m_animals.front()->metaObject();
    for ( int propertyNo= 0; propertyNo< pMeta->propertyCount(); propertyNo++ )
    {
        QMetaProperty propMeta = pMeta->property(propertyNo);
        roles[role++] = propMeta.name();
    }
    return roles;
}

此解决方案满足所有要求。不幸的是,有必要 使用 model.object.property 而不是更多内容访问此 QObject 直接的模型属性。虽然没什么大不了的,但会 很高兴可以直接访问对象。

如果你的问题与model.object.property有关,而你的解决方案是一个较短的model.property,那么一个同样好的解决方案将是object.property它是完全有效的。可以直接在委托对象中使用roleName

至于通用列表/模型类,我已经在另一个问题中解决了这个问题。