You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt/QML中Repeater模型更新卡顿问题求助

Qt/QML网格按钮更新卡顿问题优化方案

问题描述

我们的项目基于Qt/QML+C++后端开发网格按钮功能,在搭载IMX537处理器、Qt版本为5.15.2的目标设备上遇到严重性能问题:

  • 更新Repeater的dataModel时UI卡顿明显,按钮数量越多延迟越高,例如64个按钮加载需6秒;
  • 复制对象数组到dataModel的耗时与对象数量成正比,30个对象需2秒。

控制台调试显示,执行console.log("GRID update data model")后,dataModel = buttons;这一行耗时最长。尝试使用ListModel未解决问题,现寻求减少该延迟的方案。

相关代码

function handleModelChanged() {
    delegateModel.model = areaViewModel;
    const newUiType = areaViewModel.uiType
    if (newUiType !== uiType || !modelDataIsEqual(delegateModel, dataModel) ) {
        var buttons = [] 

        for (var row = 0; row < delegateModel.model.rowCount(); row++) {
            var item = delegateModel.items.get(row).model;
            var button = dataModelItemToButton(item);
            
            buttons.push(button);
        }
        console.log("GRID clear data model")
        dataModel = []
        console.log("GRID change uiType " + uiType + " -> " + newUiType)
        uiType = newUiType
        console.log("GRID update data model")
        dataModel = buttons;
        console.log("GRID buttons changed uiType=" + uiType + " cls=" + areaViewModel.callClass);
        
    }  
}
Repeater 
{
    id: areaRepeater
    model: dataModel

    delegate: {
            gridButton;
        }
    onItemAdded: {              
        if (index == areaRepeater.count - 1) {
            console.log("GRID repeater added " + areaRepeater.count + " buttons")
            updateItems()
            }
        }
    }
}

function modelDataIsEqual(modelData, data) {
    if (!modelData || !data || modelData.model.rowCount() !== data.length)
        return false;
    for (var i = 0; i < modelData.model.rowCount(); ++i) {
        const item = modelData.items.get(i).model;
        const button = data[i];
        if (button.dataAreaShortName !== item.dataAreaShortName ||
                button.dataAreaName !== item.dataAreaName ||
                button.dataIsExitArea !== item.dataIsExitArea ||
                button.dataAreaGridCols !== item.dataAreaGridCols ||
                button.dataIsGroupedButton !== item.dataIsGroupedButton ||
                button.dataAreaLocked !== item.dataAreaLocked ||
                button.dataAreaIcon !== item.dataAreaIcon ||
                button.dataIsDopArea !== item.dataIsDopArea ||
                button.dataIsSideGroup !== item.dataIsSideGroup ||
                dlaButtonStyle !== areaModel.combineBtnStyle ||
                oddEvenBtnAppearance !== areaModel.getLockedButtonAppearance() ||
                button.dataButtonAppearance !== item.dataButtonAppearance ||
                button.dataCrowdedArea !== item.dataCrowdedArea ||
                button.dataOverCrowdedArea !== item.dataOverCrowdedArea ||
                button.dataPeopleIconArea  !== item.dataPeopleIconArea ||
                button.dataSideStateIconArea !== item.dataSideStateIconArea)
        {
            return false;
        }
    }
    return true;
}
function dataModelItemToButton(item) {
    if (!item)
        return null;
    return {
        dataAreaShortName: item.dataAreaShortName,
        dataAreaName: item.dataAreaName,
        dataIsExitArea: item.dataIsExitArea,
        dataAreaGridCols: item.dataAreaGridCols,
        dataIsGroupedButton: item.dataIsGroupedButton,
        dataAreaLocked: item.dataAreaLocked,
        dataAreaIcon: item.dataAreaIcon,
        dataIsDopArea: item.dataIsDopArea,
        dataIsSideGroup: item.dataIsSideGroup,
        dataButtonAppearance : item.dataButtonAppearance,
        dataCrowdedArea: item.dataCrowdedArea,
        dataOverCrowdedArea: item.dataOverCrowdedArea,
        dataPeopleIconArea : item.dataPeopleIconArea,
        dataSideStateIconArea : item.dataSideStateIconArea
    }
}

优化方案

1. 放弃全量替换模型,改用增量更新

直接赋值dataModel = buttons会触发Repeater销毁所有旧组件并重建新组件,这是卡顿的核心原因。改为仅更新变化的内容:

  • 遍历现有dataModel,移除在新buttons中不存在的项;
  • 遍历新buttons,添加dataModel中没有的项,对已存在的项仅更新变化的属性;
  • 这样能大幅减少组件销毁和重建的开销,尤其适合大部分按钮无变化的场景。

2. 迁移数据模型到C++端

在嵌入式设备上,C++模型的性能远优于QML数组。实现步骤:

  • 继承QAbstractListModel创建自定义模型类,将数据存储和更新逻辑放在C++中;
  • 仅在数据发生变化时发送dataChanged、rowsInserted、rowsRemoved等信号;
  • QML端直接绑定该C++模型,避免在QML中做数据转换和复制操作。

3. 优化数据对比逻辑

当前modelDataIsEqual函数存在性能浪费:

  • 将全局变量dlaButtonStyle、oddEvenBtnAppearance的对比移到循环外,避免每次循环重复判断;
  • 给每个数据项添加版本号或哈希值,对比时先校验版本号,不一致再对比详细属性,减少属性对比的次数。

4. 组件懒加载或虚拟化(按需使用)

如果网格按钮数量过大:

  • 使用GridView并开启虚拟化(Qt5.15支持基础虚拟化),仅渲染可见区域的按钮;
  • 在Repeater的delegate中用Loader懒加载gridButton组件,等所有模型项初始化完成后再统一加载组件,避免一次性创建大量组件导致的卡顿。

5. 优化按钮组件的创建开销

检查gridButton组件:

  • 移除不必要的嵌套布局、冗余绑定或初始化逻辑;
  • 提取静态不变的UI元素复用,减少重复创建;
  • 关闭不必要的渲染选项(如antialiasing),降低GPU负载。

内容的提问来源于stack exchange,提问作者Mohan S

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 04:34:54