将 QObject 列表传递给 QML 上下文的可能方法有哪些?
Which are the possibile ways to pass a list of QObject to QML context?
我正在寻找一种方法,如何将更大量的 QObjects
作为模型传递给 QML,而不是使用 Repeater {}
来绘制它们。此解决方案必须满足以下条件:
- 当新对象添加到列表时,只有此项会被刷新
- 当任何传递的对象发生变化时,Qml 内容必须自动刷新
- 列表必须是通用的,没有硬编码的 属性 名称
解决方案 1.
我知道我可以使用 QQmlListProperty<QObject>
。不幸的是,如果该对象是 added/removed,所有其他对象都会在 Qml 中刷新。如果有更多 complex/larger 个对象,这将非常笨拙。
这个解决方案满足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改变时必须手动更新Model
解决方案 3.
我尝试实现 QAbstractListModel,它在内部保存 QObjects 指针列表:QList<boost::shared_ptr<AnimalObject>> m_animals;
。
当 roleNames()
方法未实现时,Qml 根本不会通过 data()
方法查询数据。所以似乎不可能使用默认 Qt::DisplayRole
角色从 QAbstractList
返回 QObject
当 roleNames() 方法以单个角色实现时,例如 "object" 和 data()
returns 内部 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++ 的 adding/removing/updating 对象,并直接在 QML 中更新它们?
PS:我在这里描述了所有这些方法,因为我相信这对很多人有用 others.It 我花了几个小时才弄清楚如何完成所有这些。
编辑
建议的解决方案 1.
正如@Velkan 所建议的,可以使用 QMetaObject::property
和 QMetaObject::propertyCount
动态扩展对象属性的 QAbstractListModel。不幸的是,这意味着要通过 QDynamicPropertyChangeEvent
信号为每个 object/property 实现单独刷新。
不幸的是,对于大量对象和属性,此解决方案可能效率很低。对于任何对此解决方案感兴趣的人,这里有一个带有 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;
}
This solution meets all requirements. Unfortunately it's necessary to
access this QObject with model.object.property instead of more
straightforward model.property. Although it's not a big deal, would be
great to have direct object access.
如果您的问题出在 model.object.property
上,而您的解决方案较短 model.property
,那么同样好的解决方案是 object.property
,它完全可用。您可以直接在委托对象中使用 roleName
。
至于通用 list/model class,我已经在 中解决了。
我正在寻找一种方法,如何将更大量的 QObjects
作为模型传递给 QML,而不是使用 Repeater {}
来绘制它们。此解决方案必须满足以下条件:
- 当新对象添加到列表时,只有此项会被刷新
- 当任何传递的对象发生变化时,Qml 内容必须自动刷新
- 列表必须是通用的,没有硬编码的 属性 名称
解决方案 1.
我知道我可以使用 QQmlListProperty<QObject>
。不幸的是,如果该对象是 added/removed,所有其他对象都会在 Qml 中刷新。如果有更多 complex/larger 个对象,这将非常笨拙。
这个解决方案满足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改变时必须手动更新Model
解决方案 3.
我尝试实现 QAbstractListModel,它在内部保存 QObjects 指针列表:QList<boost::shared_ptr<AnimalObject>> m_animals;
。
当 roleNames()
方法未实现时,Qml 根本不会通过 data()
方法查询数据。所以似乎不可能使用默认 Qt::DisplayRole
角色从 QAbstractList
当 roleNames() 方法以单个角色实现时,例如 "object" 和 data()
returns 内部 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++ 的 adding/removing/updating 对象,并直接在 QML 中更新它们?
PS:我在这里描述了所有这些方法,因为我相信这对很多人有用 others.It 我花了几个小时才弄清楚如何完成所有这些。
编辑
建议的解决方案 1.
正如@Velkan 所建议的,可以使用 QMetaObject::property
和 QMetaObject::propertyCount
动态扩展对象属性的 QAbstractListModel。不幸的是,这意味着要通过 QDynamicPropertyChangeEvent
信号为每个 object/property 实现单独刷新。
不幸的是,对于大量对象和属性,此解决方案可能效率很低。对于任何对此解决方案感兴趣的人,这里有一个带有 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;
}
This solution meets all requirements. Unfortunately it's necessary to access this QObject with model.object.property instead of more straightforward model.property. Although it's not a big deal, would be great to have direct object access.
如果您的问题出在 model.object.property
上,而您的解决方案较短 model.property
,那么同样好的解决方案是 object.property
,它完全可用。您可以直接在委托对象中使用 roleName
。
至于通用 list/model class,我已经在