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

时间:2016-01-28 15:33:34

标签: c++ qt qml qtquick2

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

  1. 将新对象添加到列表时,只会刷新此项
  2. 当任何传递的对象发生更改时,必须自动刷新Qml内容
  3. 列表必须是通用的,没有硬编码的属性名称
  4. 解决方案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准备Model对象,并且在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。虽然这不是什么大问题,但直接对象访问会很棒。

    问题:

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

    有没有什么干净的方法如何创建QObject列表,将其传递给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;
    }
    

1 个答案:

答案 0 :(得分:0)

  

此解决方案符合所有要求。不幸的是,它是必要的   使用model.object.property访问此QObject而不是更多   直截了当的model.property。虽然这不是什么大不了的事,但是   很高兴有直接对象访问。

如果问题出在model.object.property且您的解决方案较短model.property,那么一个同样出色的解决方案是object.property,它完全正常。您可以直接在委托对象中使用roleName

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