首页 > 代码库 > QT Demo 之 threading

QT Demo 之 threading

在学习了MouseArea、Text、Image这些基本组件后,我们这一章学习如何在QML中完成一些异步处理。

这一章我们通过下述两个例子来分别讲解一下Timer和WorkerScript的使用。

threadedlistmodel/timedisplay.qml

这一个示例的原始代码中同时使用了Timer和WorkerScript来完成一个比较简单的工作,为了简化处理,我针对代码做了一些小改动,去掉了WorkerScript部分(同时包括dataloader.js部分),只使用Timer来完成是示例一样的效果。修改后的代码如下(原始代码请参考Qt的示例代码):

Rectangle {
    color: "white"
    width: 200
    height: 300

    ListView {
        anchors.fill: parent
        model: listModel
        delegate: Component {
            Text { text: time }
        }

        ListModel { id: listModel }

        Timer {
            id: timer
            interval: 2000; repeat: true
            running: true
            triggeredOnStart: true

            onTriggered: {
                var data = http://www.mamicode.com/{'time': new Date().toTimeString()};>那么从上面的代码可以看到,ListView、ListModel都是我们之前已经了解的组件,那么我们就把焦点放到Timer组件上。

Timer简述

A Timer can be used to trigger an action either once, or repeatedly at a given interval.

其实Timer组件还是很好理解的,就是一个定时器,按照给定的interval定时触发,我们只需要在onTriggered事件响应函数中完成自己需要的操作即可。

  • interval指定了触发的事件间隔是2s
  • repeat和running都是true,表示该Timer循环进行触发,而不是只触发一次
  • triggeredOnStart表示上来就触发一次,而不是等interval之后才触发第一次
  • onTriggered事件相应函数中,我们通过listModel的数据来刷新ListView中显示

Timer的使用也就是上面的几个属性和事件通知了,示例中没有使用到的还有几个函数:restart()、start()和stop(),其意义和使用方法也很简单。

workerscript/workerscript.qml

在上一个示例中,我们只关注了Timer,去掉了WorkerScript相关的代码。在这一节中,我们就需要完全使用WorkerScript来完成我们的需求了。

先看代码整体结构:

Rectangle {
    width: 320; height: 480

//! [1]
    WorkerScript {...}
//! [1]
    Row {...}

    Text {...}

    Text {...}
}
其中两个Text分别是显示用户提示("Pascal‘s Triangle Calculator")和计算结果(resultText),而Row部分则是水平排列的两个Spinner。

这里用的Spinner是一个自定义的Component,具体实现是在:/threading/workerscript/Spinner.qml文件中,这里暂时先不详细分析这个文件,我们先看一看如何使用Spinner:

        Spinner {
            id: rowSpinner
            label: "Row"
            onValueChanged: {
                resultText.text = "Loading...";
                myWorker.sendMessage( { row: rowSpinner.value, column: columnSpinner.value } );
            }
        }
注:另外一个Spinner除了id和label不同之外,onValueChanged事件响应函数是一模一样的,此处不再重复贴上代码。

从代码上看,一个Spinner的属性包括label(即显示在Spinner上面的文字),一个value(即当前Spinner显示的数据),如下图所示:

当Spinner中的数据发送变化时,则会触发onValueChanged事件相应函数。关于自定义组件Spinner的讲解暂时先到这里,后面再详细展开。

WorkerScript简述

Use WorkerScript to run operations in a new thread. This is useful for running operations in the background so that the main GUI thread is not blocked.

从上面官方说明上,我们可以了解WorkerScript就是设计成可以在后台执行操作,而不会阻塞UI线程的一种组件。通过上面的说明,我们也可以了解WorkerScript必须有的几个元素:

  • source:既然是后台执行操作,这里使用了source指定一个js文件运行后台的操作
  • onMessage:既然是异步处理,那么肯定涉及到的message,这里的message是双向的,从js文件到qml文件使用onMessage事件相应函数
  • sendMessage(jsobject message):从qml到js文件,使用sendMessage函数发送给js文件需要执行的操作

下面就是代码中的WorkerScript部分:

    WorkerScript {
        id: myWorker
        source: "workerscript.js"

        onMessage: {
            if (messageObject.row == rowSpinner.value && messageObject.column == columnSpinner.value){ //Not an old result
                if (messageObject.result == -1)
                    resultText.text = "Column must be <= Row";
                else
                    resultText.text = messageObject.result;
            }
        }
    }
而workerscript.js中的onMessage函数如下:

WorkerScript.onMessage = function(message) {
    //Calculate result (may take a while, using a naive algorithm)
    var calculatedResult = triangle(message.row, message.column);
    //Send result back to main thread
    WorkerScript.sendMessage( { row: message.row,
                                column: message.column,
                                result: calculatedResult} );
}

从上面代码可以很明显的看到:
从qml到js使用的是myWorker.sendMessage( { row: rowSpinner.value, column: columnSpinner.value } );,数据包含row和column,在workerscript.js中的onMessage函数中也是使用参数message的两个属性值;
从js到qml使用的是WorkerScript.sendMessage( { row: message.row,column: message.column,result: calculatedResult} );,数据包含row、colume和result,在qml的onMessage函数中也是使用了这三个参数,不过需要注意的是这里的messageObject应该是内置变量(吐槽+1)。

总结

本节学到的知识点:

  1. Timer组件的使用方法
  2. WorkerScript组件的使用方法
  3. 学习如何通过Timer组件或WorkerScript组件来完成后台线程执行操作,而在UI线程中刷新的方法
  4. 稍微了解如何在qml中如何使用js文件

从今天开始,我们开始了学习如何在qml中完成一些逻辑,也算是摆脱了只是学习UI组件的不爽。但是无论后台逻辑完成什么复杂的处理,绝不能造成UI线程的阻塞,如果不能及时响应用户的操作,甚至出现假死的现象,对于应用开发都是大忌。

QT Demo 之 threading