「QMLの基礎 - マルチスレッド」の版間の差分
ナビゲーションに移動
検索に移動
(ページの作成:「== 概要 == QMLとC++を連携させたマルチスレッドの実装手順、および、<code>WorkerScript</code>を使用したマルチスレッドの実装手順を…」) |
編集の要約なし |
||
144行目: | 144行目: | ||
now = new Date(); | now = new Date(); | ||
}while((now - start) < msec); | }while((now - start) < msec); | ||
} | |||
</syntaxhighlight> | |||
<br><br> | |||
== QMLとC++を連携させたマルチスレッド == | |||
QObjectクラスを継承したクラスを作成およびそのクラス内にワーカースレッドを作成して、メインスレッドをブロックせずにQML側と非同期に通信することができる。<br> | |||
<br> | |||
以下の例では、ワーカースレッドを5秒停止かつシグナルを発信しながら、メインスレッドで画面を更新し続けている。<br> | |||
ワーカースレッドの実行中において、一時停止、中断、制御は一切できないため、それらが必要な場合はノンブロッキングワーカースレッドを実装する必要がある。 | |||
<syntaxhighlight lang="c++"> | |||
// 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); | |||
}; | |||
</syntaxhighlight> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// main.cpp | |||
// ...略 | |||
qmlRegisterType<CController>("Controller", 1, 0, "CController"); | |||
QQmlApplicationEngine engine; | |||
const QUrl url(QStringLiteral("qrc:/main.qml")); | |||
// ...略 | |||
</syntaxhighlight> | |||
<br> | |||
<syntaxhighlight lang="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; | |||
} | |||
} | |||
// ...略 | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> |
2021年11月1日 (月) 00:36時点における最新版
概要
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;
}
}
// ...略
}