QMLの基礎 - マルチスレッド
ナビゲーションに移動
検索に移動
概要
QMLとC++を連携させたマルチスレッドの実装手順、および、WorkerScript
を使用したマルチスレッドの実装手順を記載する。
WokerScriptの使用
WokerScriptでやり取りできるデータ型
bool
型、number
型、string
型- JavaScriptオブジェクトと配列
ListModel
(他のオブジェクトは不可)
また、WorkerScript
でListModel
にアイテムを更新した時は、sync
メソッドで反映できる。
処理の手順
- メインスレッドから
WokerScript
を起動する時は、sendMessage(jsobject)
を使用する。 WokerScript
側のメソッドは、WorkerScript.onMessage(jsobject)
を使用する。WokerScript
からメインスレッドへ通信する時は、WorkerScript.sendMessage(jsobject)
を使用する。- メインスレッド側で受ける
onMessage
シグナルの引数は、messageObject
を使用する。
サンプルコード
以下の例では、[Click]を押下する時、1秒間隔でリストへアイテムを追加している。
アイテムを追加している最中でも、リストをドラッグして動かすこともできる。
// main.qml
import QtQuick 2.15
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);
}
}
// リスト
ListView {
anchors.top: _btn.bottom
anchors.left: _root.left
anchors.right: _root.right
anchors.bottom: _root.bottom
clip: true
model: _model
delegate: _delegate
}
// リストの初期アイテム
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
}
}
}
}
// 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);
}
QMLとC++を連携させたマルチスレッド
QObjectクラスを継承したクラスを作成およびそのクラス内にワーカースレッドを作成して、メインスレッドをブロックせずにQML側と非同期に通信することができる。
以下の例では、ワーカースレッドを5秒停止かつシグナルを発信しながら、メインスレッドで画面を更新し続けている。
ワーカースレッドの実行中において、一時停止、中断、制御は一切できないため、それらが必要な場合はノンブロッキングワーカースレッドを実装する必要がある。
// CController.h
#include <QThread>
#include <QString>
#include <thread>
#include <memory>
class CWorkerThread : public QObject
{
Q_OBJECT
public slots:
void doWork()
{
int i = 0;
while (i < 10)
{
emit result(i++, tr("hoge"));
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
signals:
void result(int r, QString s);
};
class CController : public QObject
{
Q_OBJECT
public:
Controller()
{
m_pWorker = std::make_unique<CWorkerThread>();
m_pThread = std::make_unique<QThread>();
m_pWorker->moveToThread(m_pThread.get());
connect(this, SIGNAL(start()), m_pWorker.get(), SLOT(doWork()));
connect(m_pWorker.get(), SIGNAL(result(int,QString)), this, SIGNAL(result(int,QString)));
m_pThread->start();
}
private:
std::unique_ptr<CWorkerThread> m_pWorker;
std::unique_ptr<QThread> m_pThread;
signals:
void start();
void result(int r, QString s);
};
// main.cpp
// ...略
qmlRegisterType<CController>("Controller", 1, 0, "CController");
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
// ...略
// main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import Controller 1.0
ApplicationWindow {
id: mainWindow
// ...略
CController {
id: mainController;
}
Column {
Button {
id: start
onClicked: {
mainController.start()
}
}
Text {
id: res
}
}
// Result of Slot
Connections {
target: mainController
function onResult(r, s) {
res.text = r + qsTr(":") s;
}
}
// ...略
}