如何向C++定义的QQmlListProperty添加QML侧创建的新对象
QQmlListProperty 实现QML侧操作同步到C++的实现方法
你之前关于回调触发逻辑的认知完全正确:只要按定义实现四个对应回调,QML侧对该列表的常规操作会自动触发C++侧逻辑,完成双向同步。
前置准备
首先必须把CustomData类正确注册到QML类型系统,否则QML侧根本无法生成该类型的对象实例,自然也没法完成数据传递:
// 在程序入口的初始化代码中添加 qmlRegisterType<CustomData>("MyCustomType", 1, 0, "CustomData");
同时要确保四个回调函数的实现是直接操作类内部存储的数据源(一般是QList<CustomData*>类的成员变量),不能是空实现,否则即使触发回调也不会真正保存数据。对应你现有代码的四个回调参考实现如下:
// 头文件私有区声明静态回调函数 static void appendCustomDatas(QQmlListProperty<CustomData>* prop, CustomData* data); static qsizetype customDataCount(QQmlListProperty<CustomData>* prop); static CustomData* customData(QQmlListProperty<CustomData>* prop, qsizetype index); static void clearCustomDatas(QQmlListProperty<CustomData>* prop); // 回调函数具体实现 void Dataset::appendCustomDatas(QQmlListProperty<CustomData>* prop, CustomData* data) { // 取出构造QQmlListProperty时传入的Dataset实例指针 Dataset* self = static_cast<Dataset*>(prop->data); // 设置对象父级,避免QML创建的对象被JS垃圾回收 data->setParent(self); // 写入内部存储列表 self->m_customDatas.append(data); // 触发自定义更新信号,通知绑定了列表数据的UI刷新 emit self->customDataListChanged(); } qsizetype Dataset::customDataCount(QQmlListProperty<CustomData>* prop) { Dataset* self = static_cast<Dataset*>(prop->data); return self->m_customDatas.count(); } CustomData* Dataset::customData(QQmlListProperty<CustomData>* prop, qsizetype index) { Dataset* self = static_cast<Dataset*>(prop->data); return self->m_customDatas.value(index); } void Dataset::clearCustomDatas(QQmlListProperty<CustomData>* prop) { Dataset* self = static_cast<Dataset*>(prop->data); self->m_customDatas.clear(); emit self->customDataListChanged(); }
QML侧实际操作方式
当C侧的回调实现完成后,你在QML侧不需要写任何额外的同步代码,直接按照操作普通JS数组的方式调用列表方法即可,对应的操作都会自动触发C回调:
- 追加数据:调用列表的
push()方法,传入QML侧创建的CustomData实例,自动触发appendCustomDatas回调 - 读取列表长度:直接读取列表的
length属性,会触发customDataCount回调 - 按索引读取数据:直接用
列表名[索引]的方式取值,会触发customData回调 - 清空列表:给列表赋值空数组、或者直接把列表的
length设为0,会触发clearCustomDatas回调
参考QML侧的使用示例:
// 假设Dataset实例已经以id为dataset的对象暴露到QML环境 Item { // 定义CustomData的创建组件 Component { id: dataCreator CustomData {} } Button { text: "新增自定义数据项" onClicked: { // 在QML侧创建CustomData实例 const item = dataCreator.createObject() item.name = "测试数据" item.value = Math.round(Math.random() * 100) // 推送到列表,自动触发C++的appendCustomDatas回调 dataset.customDatas.push(item) } } Button { text: "清空所有数据" onClicked: { // 给列表赋值空数组,自动触发C++的clearCustomDatas回调 dataset.customDatas = [] } } }
常见踩坑点
- 不能用普通JS对象字面量作为列表元素,比如
dataset.customDatas.push({name: "test"})这种写法会直接报类型不匹配错误,列表要求每个元素必须是注册过的CustomData类型实例 - 要注意 对象所有权:如果QML侧创建
CustomData实例时没有设置父对象,在C++的append回调里必须给该对象设置父级为当前Dataset实例,否则该对象会被JS引擎的垃圾回收机制回收,导致列表出现野指针 - 基础的四个回调只覆盖了追加、按索引读、取长度、清空这几个操作,如果你需要支持
splice、排序等其他数组操作,需要额外实现对应的高版本QQmlListProperty接口回调,普通基础用法下不需要关心
内容的提问来源于stack exchange,提问作者user8453362
相关产品推荐
相关产品推荐

