在学习了MouseArea、Text、Image这些基本组件后,我们这一章学习如何在QML中完成一些异步处理。
这一章我们通过下述两个例子来分别讲解一下Timer和WorkerScript的使用。
这一个示例的原始代码中同时使用了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 = {'time': new Date().toTimeString()}; listModel.append(data); listModel.sync(); // updates the changes to the list } } } }那么从上面的代码可以看到,ListView、ListModel都是我们之前已经了解的组件,那么我们就把焦点放到Timer组件上。
A Timer can be used to trigger an action either once, or repeatedly at a given interval.
其实Timer组件还是很好理解的,就是一个定时器,按照给定的interval定时触发,我们只需要在onTriggered事件响应函数中完成自己需要的操作即可。
Timer的使用也就是上面的几个属性和事件通知了,示例中没有使用到的还有几个函数:restart()、start()和stop(),其意义和使用方法也很简单。
在上一个示例中,我们只关注了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的讲解暂时先到这里,后面再详细展开。
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必须有的几个元素:
下面就是代码中的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)。
总结
本节学到的知识点:
从今天开始,我们开始了学习如何在qml中完成一些逻辑,也算是摆脱了只是学习UI组件的不爽。但是无论后台逻辑完成什么复杂的处理,绝不能造成UI线程的阻塞,如果不能及时响应用户的操作,甚至出现假死的现象,对于应用开发都是大忌。
原文地址:http://blog.csdn.net/nfer_zhuang/article/details/41115081