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 = {'time': new Date().toTimeString()};
                listModel.append(data);
                listModel.sync();   // updates the changes to the list
            }
        }
    }
}
那麼從上面的代碼可以看到,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線程的阻塞,如果不能及時響應用戶的操作,甚至出現假死的現象,對於應用開發都是大忌。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章