WorkerScript处理qml多线程处理异步数据

问题引入

如下代码在qml主线程进行listmodel大量数据更新操作导致qml主线程界面UI卡顿

bash 复制代码
import QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick.Controls 2.14

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    Item{
        id:itemRoot
        anchors.left: parent.left
        anchors.right: parent.right
        height: 40
        clip: true
        Row{
            Repeater{
                model: ["property1","property2","property3", "property4", "property5"]
                Rectangle{
                    width: itemRoot.width / 5
                    height: itemRoot.height
                    color: "cyan"
                    Text {
                        anchors.centerIn: parent
                        text: modelData
                    }
                }
            }
        }
    }

    Timer{
        interval: 1
        running: true
        repeat: true
        onTriggered: {
            for(let i = 0; i < listmode.count; i++)
            {
                let data = listmode.get(i)
                listmode.set(i, {val1:data.val1 + 0.001,val2:data.val2 + 0.002,val3:data.val3 + 0.003,val4:data.val4 + 0.004,val5:data.val5 + 0.005})
            }
        }
    }

    ListModel{
        id:listmode
        Component.onCompleted: {
            for(let i = 0; i < 10000; i++)
            {
                listmode.append({val1:0.001,val2:0.002,val3:0.003,val4:0.004,val5:0.005})
            }
        }
    }
    Item{
        anchors.fill: parent
        anchors.topMargin: 40
        clip: true
        ListView{
            id:listview
            model:listmode
            anchors.fill: parent
            delegate: Rectangle{
                width: itemRoot.width
                height: 40
                color: "white"
                border.width: 1
                border.color: "yellow"
                Row{
                    Item{
                        width: listview.width / 5
                        height: 40
                        Text {
                            text: val1
                            anchors.centerIn: parent
                        }
                    }
                    Item{
                        width: listview.width / 5
                        height: 40
                        Text {
                            text: val2
                            anchors.centerIn: parent
                        }
                    }
                    Item{
                        width: listview.width / 5
                        height: 40
                        Text {
                            text: val3
                            anchors.centerIn: parent
                        }
                    }
                    Item{
                        width: listview.width / 5
                        height: 40
                        Text {
                            text: val4
                            anchors.centerIn: parent
                        }
                    }
                    Item{
                        width: listview.width / 5
                        height: 40
                        Text {
                            text: val5
                            anchors.centerIn: parent
                        }
                    }
                }
            }

        }
    }
}

问题分析

QML 的界面渲染、动画播放和用户交互都在同一个主线程中运行。如果你在主线程中执行复杂的 JavaScript 计算(如大量数据处理、复杂算法、文件读写),界面就会"冻结",直到计算完成。

复杂的 ListModel 操作

场景:当 ListView 需要加载成千上万条数据时,直接在主线程循环 append 会非常慢。

WorkerScript 方案:利用 Worker 处理数据模型,甚至可以使用 ListModel.sync() 方法(特定于 Qt 的扩展功能)来高效地批量更新列表数据,减少 UI 的重绘次数

解决方案

WorkerScript 通过引入多线程机制,将这些繁重的任务剥离到后台线程执行,从而保证主界面的流畅度

  • 核心机制:消息传递 (Message Passing)

    使用 WorkerScript 时,你必须接受一个限制:后台线程无法直接访问 QML 界面元素(如不能直接修改 textItem.text)。

    它们之间的通信完全依赖消息传递:

  • 主线程 -> 子线程:使用 worker.sendMessage({ data: ... }) 发送任务参数。

  • 子线程处理:在 worker.js 中监听 WorkerScript.onMessage,执行耗时计算。

  • 子线程 -> 主线程:计算完成后,使用 WorkerScript.sendMessage({ result: ... }) 发回结果。

  • 主线程更新:在 QML 中监听 onMessage 信号,拿到结果后更新 UI

具体代码实现

调用的worker.js文件

bash 复制代码
WorkerScript.onMessage = function func(msg) {
    let listmodel = msg
    for(let i = 0; i < listmodel.count; i++)
    {
        let data = listmodel.get(i)
        listmodel.set(i, {val1:data.val1 + 0.001,val2:data.val2 + 0.002,val3:data.val3 + 0.003,val4:data.val4 + 0.004,val5:data.val5 + 0.005})
    }
    listmodel.sync()   //将子线程处理后的结果返回给qml主线程,qml主线程通过onMessage接收消息
}

拓展:

如果想要

worker.sendMessage({model:listmodel, value:"123"})

那么在work.js中

c 复制代码
WorkerScript.onMessage = function func(msg) {
//    let listmodel = msg
    let listmodel = msg.model 
    for(let i = 0; i < listmodel.count; i++)
    {
        let data = listmodel.get(i)
        listmodel.set(i, {val1:data.val1 + 0.001,val2:data.val2 + 0.002,val3:data.val3 + 0.003,val4:data.val4 + 0.004,val5:data.val5 + 0.005})
    }
    listmodel.sync()   //将子线程处理后的结果返回给qml主线程,qml主线程通过onMessage接收消息
}

总结

  • 解决"界面假死"问题 (UI Freezing)

    这是最直接的应用场景。

    问题:当你点击一个按钮,需要处理一个包含 10,000 条数据的数组,或者进行复杂的数学运算(如斐波那契数列计算、图像处理算法)。如果直接在 QML 的 onClicked 中写循环,界面会卡住不动,用户无法点击其他按钮,动画也会停止。

    WorkerScript 方案:将计算逻辑放入 worker.js 文件中。主线程发送数据给 Worker,Worker 在后台算好后,通过消息机制把结果发回主线程更新 UI。在此过程中,用户依然可以流畅地拖动窗口、点击菜单。

  • 处理大数据与复杂逻辑

    JSON 解析/序列化:解析巨大的 JSON 字符串(例如从服务器返回的大型报表数据)非常消耗 CPU。

    数据清洗与排序:对 ListModel 中的大量条目进行复杂的筛选、排序或格式化。

    加密/解密:涉及复杂的位运算和数学计算的加密算法。

  • 文件 I/O 操作 (配合 LocalStorage 或 C++)

    问题:虽然 QML 本身不直接支持在 Worker 中读写文件(受限于沙箱安全机制),但通过 Worker 可以避免同步 I/O 造成的阻塞。

    场景:读取一个很大的日志文件,或者将大量数据写入数据库。通常的做法是在 Worker 中调用 C++ 暴露的接口,或者使用 QtQuick.LocalStorage 进行异步数据库操作,避免磁盘 I/O 等待阻塞界面渲染。

  • 网络请求数据处理

    虽然 QML 的 XmlHttpRequest 本身支持异步,但如果你需要在请求回来后立刻进行大量的数据转换(例如将原始数据转换为图表所需的格式),这个转换过程依然可能卡顿。

    WorkerScript 方案:网络请求回来后,把原始数据丢给 Worker 进行解析和重组,主线程只负责最后展示。

  • 复杂的 ListModel 操作

    场景:当 ListView 需要加载成千上万条数据时,直接在主线程循环 append 会非常慢。

    WorkerScript 方案:利用 Worker 处理数据模型,甚至可以使用 ListModel.sync() 方法(特定于 Qt 的扩展功能)来高效地批量更新列表数据,减少 UI 的重绘次数。

相关推荐
小灰灰搞电子2 小时前
Qt 中的队列解析
qt
原来是猿8 小时前
QT初识【创建项目+对象树】
开发语言·qt
-凌凌漆-9 小时前
【Qt】 QSerialPort::flush()介绍
开发语言·qt
咸鱼翻身小阿橙10 小时前
QT P4
数据库·qt·nginx
Wild_Pointer.13 小时前
项目实战:编写CMakeLists管理Qt+OpenCV项目
开发语言·c++·qt
星越华夏13 小时前
Qt5状态栏刷新显示内容
python·qt
sycmancia13 小时前
Qt——Qt中的文件操作、文本流和数据流
开发语言·qt
雾岛听蓝1 天前
Qt操作指南:窗口组成与菜单栏
开发语言·经验分享·笔记·qt
(Charon)1 天前
【C++/Qt】C++/Qt 实现 TCP Server:支持启动监听、消息收发、日志保存
c++·qt·tcp/ip