哪些是将 QObject 列表传递给 QML 上下文的可能方法
Which are the possibile ways to pass a list of QObject to QML context?
我正在寻找一种方法,如何将大量QObjects
作为模型传递给QML,而不是使用Repeater {}
来绘制它们。此解决方案必须满足以下条件:
- 将
- 新对象添加到列表时,将仅刷新此项
- 当任何传递的对象被更改时,Qml内容必须自动刷新
- 列表必须是通用的,没有硬编码的属性名称
解决方案 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::property
和QMetaObject::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
。
至于通用列表/模型类,我已经在另一个问题中解决了这个问题。
- 从C++实例化QML
- 使用CMake创建QML插件
- #为""定义宏;静态";针对不同的上下文
- 与互斥锁相比,旋转锁可以保证上下文切换
- 线程,如果else语句,都是错误的上下文切换后,会发生什么
- QT通过C++添加映射QML项目
- 如何在没有信号的情况下从C++执行QML插槽
- 为什么我不能使用 EGL 创建无头 OpenGl 上下文?
- QML按钮点击功能执行顺序
- 是否可以在单独的线程中将 QObject 设置为 QML 上下文属性?
- 可以从C++在QML上编写上下文属性吗?
- 当删除上下文属性中的QLIST对象时,QT QML应用程序崩溃
- Qt-Qml连接到上下文属性的QObject属性的信号
- 如何将 C++ 插件属性公开给 QML 上下文
- 如何为第三方渲染提供空的QML项目的OpenGL上下文
- 如何在 QML 文件中查找属性名称为字符串的 QML 上下文属性
- 哪些是将 QObject 列表传递给 QML 上下文的可能方法
- 从QQuickWidget上下文调用QML方法
- c++ /QML:如何为动态创建的组件定义和处理多个上下文
- QML 报告引用错误:XYZ 未在添加到上下文C++对象上定义