QML(Qt)でマルチスレッド処理

QMLでもマルチスレッド処理できます。

WorkerScript」エレメントを使用して実装します。

/// 今回のポイント ///
・スレッド間でやり取りできるデータは以下の3種類。
 ・boolean, number, string型
 ・JavaScript objectと配列
 ・ListModel object (他のQObjectはNG)
 3つ目のListModelが渡せるのが最重要。
・WorkerスレッドでListModelにアイテムを更新したときは「sync」メソッドで更新。


/// 手順的な箇条書き ///
・メインスレッドからスレッドの起動は「sendMessage(jsobject)」
・Workerスレッドで受けるメソッドは「WorkerScript.onMessage(jsobject)」
・Workerスレッドからメインスレッドへの通信は「WorkerScript.sendMessage(jsobject)」
・メインスレッドで受けるonMessageシグナルの引数は「messageObject」


どう考えても裏で情報いっぱい取得とかして順次ListViewへ表示するためって感じですね。
なので、わかりやすいサンプルを作って見ました。
「Click」ってところをクリックすると1秒間隔でリストへアイテムを追加する動作をします。
アイテムを追加していく過程でもリストをドラッグしてうにょーんて動かせます。
とまっててわかりにくいスクリーンショット
 
メインスレッドからもWorkerスレッドからも通信メソッドが「sendMessage」なのがなんとなく不思議な感じがしなくもないです。


import QtQuick 1.0

Rectangle {
    id: _root
    width: 200
    height: 360
    // ボタン
    Rectangle{
        id: _btn
        width: _root.width
        height: _lavel.paintedHeight * 2
        color: "#ddddff"
        border.color: "#dddddd"
        border.width: 1
        Text{
            id: _lavel
            anchors.centerIn: parent
            text: "Click!"
        }
        // スレッド開始処理
        MouseArea{
            anchors.fill: parent
            onClicked: {
                console.debug("click");
                // スレッド開始
                _thread.sendMessage({"model":_model
                                        , "number": 20
                                        , "string": "Start!"
                                    });
            }
        }
    }
    // スレッド用エレメント
    WorkerScript {
        id: _thread
        source: "scripts.js"     // スレッド処理をするスクリプトファイルの指定
        onMessage: {
            // Workerスレッドからの通信イベント
            console.debug("finish thread:" + messageObject.result
                          + ", split=" + messageObject.split);
        }
    }
    // リストの初期アイテム
    ListModel{
        id: _model
        ListElement{
            _message: "Item"
        }
    }
    // リスト用レイアウト
    Component{
        id: _delegate
        Rectangle {
            width: _root.width
            height: _text.paintedHeight * 2
            border.color: "#dddddd"
            border.width: 1
            Text{
                id: _text
                anchors.centerIn: parent
                text: _message
            }
        }

    }
    // リスト
    ListView{
        anchors.top: _btn.bottom
        anchors.left: _root.left
        anchors.right: _root.right
        anchors.bottom: _root.bottom
        clip: true
        model: _model
        delegate: _delegate
    }
}
// scripts.js
WorkerScript.onMessage = function(message) {
    console.debug("start worker script:" + message.number
                  + "," + message.string);

    var now = 0;
    var split = new Date();

    for(var i=0; i<message.number; i++){
        delay(1000);

        now = new Date();
        message.model.append({"_message": i + " : " + Qt.formatDateTime(now, "yyyy/MM/dd hh:mm:ss")});
        message.model.sync();
    }
    split = (new Date()) - split;

    // メインスレッドへの応答
    WorkerScript.sendMessage({ "result": "tRue", "split": split })
}

// ウエイトを入れる
function delay(msec){
    var start = 0;
    start = new Date();
    do{
        now = new Date();
    }while((now - start) < msec);
}