我相信我已经使用模型解决了这个问题,因此 QML 可以安全地与另一个线程通信。
模型在 UI 线程中实例化。该模型使用来自另一个线程(这是线程安全的)的信号和槽接收数据。该模型具有 QML 可以与之交互的 QProperties。当模型从另一个线程接收到新数据时,QProperties 被更新并且 QML 接收到新数据。
该项目是here,但也包含在下面
TestQPropertyThread.pro
QT += quick
CONFIG += c++11
DEFINES += QT_DEPRECATED_WARNINGS
QMAKE_CXXFLAGS += -g -Wall -Werror
SOURCES += \
controller.cpp \
controllerchild.cpp \
main.cpp \
model.cpp
RESOURCES += qml.qrc
HEADERS += \
controller.h \
controllerchild.h \
model.h
main.cpp
#include "controller.h"
#include "model.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
Controller test;
Model model(test);
engine.rootContext()->setContextProperty(QStringLiteral("model"), &model);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
控制器.cpp
#include "controller.h"
Controller::Controller(QObject *parent) : QObject(parent), testChild(this), timer(this) // testChild to move to m_workerThread
{
uiThread = QThread::currentThreadId();
// move this object to a separate thread so does not interrupt UI thread
moveToThread(&m_workerThread); // commenting this line will make test::timerExpired() run on UI thread and slow down UI
timer.setInterval(0); // with interval 0, more likely to seg fault due to data race
connect(&timer, SIGNAL(timeout()), this, SLOT(timerExpired()));
connect(&m_workerThread, SIGNAL(started()), &timer, SLOT(start()));
m_workerThread.start();
}
void Controller::timerExpired()
{
if (uiThread == QThread::currentThreadId())
qCritical() << "Controller::timerExpired() Controller thread is same as UI thread";
// prevent UI thread from accessing objects below while they update with a QMutexLocker
QMutexLocker locker(&mutex);
QString time;
time = QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
qDebug() << "Controller::timerExpired()" << time; // this can impact preformance when usleep is commented out
QThread::usleep(100000); // long operation
testChild.setTime(time);
}
控制器.h
#ifndef TEST_H
#define TEST_H
#include <QObject>
#include <QTimer>
#include <QThread>
#include <QMutex>
#include <QDebug>
#include <QDateTime>
#include "controllerchild.h"
class Controller : public QObject
{
Q_OBJECT
public:
explicit Controller(QObject *parent = nullptr);
ControllerChild testChild;
public slots:
void timerExpired();
private:
QTimer timer;
QThread m_workerThread;
Qt::HANDLE uiThread;
QMutex mutex;
};
#endif // TEST_H
controllerchild.cpp
#include "controllerchild.h"
ControllerChild::ControllerChild(QObject *parent) : QObject(parent)
{
}
void ControllerChild::setTime(const QString &value)
{
time = value;
emit timeChanged(time);
}
void ControllerChild::onButtonPress(const QString &value)
{
qDebug() << "ControllerChild::onButtonPress()" << value << QThread::currentThreadId() << QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
emit onBoolChanged(value); // send value back to UI label
}
controllerchild.h
#ifndef TESTCHILD_H
#define TESTCHILD_H
#include <QObject>
#include <QDebug>
#include <QThread>
#include <QDateTime>
class ControllerChild : public QObject
{
Q_OBJECT
public:
explicit ControllerChild(QObject *parent = nullptr);
void setTime(const QString &value);
public slots:
void onButtonPress(const QString &value);
signals:
void timeChanged(const QString &value);
void onBoolChanged(QString value);
private:
QString time;
};
#endif // TESTCHILD_H
模型.cpp
#include "model.h"
Model::Model(Controller &test, QObject *parent) : QObject(parent), test(test)
{
connect(&test.testChild, &ControllerChild::timeChanged, this, [=](const QString &time){ this->setTime(time); });
connect(this, &Model::onButtonPressChanged, &test.testChild, &ControllerChild::onButtonPress);
connect(&test.testChild, &ControllerChild::onBoolChanged, this, [=](const QString &value){ this->setBoolValue(value); });
}
QString Model::getTime() const
{
// return QString("test Child %1").arg(time);
return time;
}
void Model::setTime(const QString &value)
{
time = value;
emit timeChanged();
}
void Model::onButtonPress(const QString &value)
{
qDebug() << "Model::onButtonPress()" << value << QThread::currentThreadId() << QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
emit onButtonPressChanged(value);
}
void Model::onBool(const QString &value)
{
qDebug() << "Model::onBool()" << value << QThread::currentThreadId() << QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
emit onBoolChanged(value);
}
void Model::setBoolValue(const QString &value)
{
boolValue = value;
qDebug() << "Model::setBoolValue()" << value << QThread::currentThreadId() << QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
emit onBoolChanged(value);
}
模型.h
#ifndef MODEL_H
#define MODEL_H
#include <QObject>
#include "controller.h"
class Model : public QObject
{
Q_OBJECT
Q_PROPERTY(QString time READ getTime WRITE setTime NOTIFY timeChanged)
Q_PROPERTY(QString boolValue MEMBER boolValue NOTIFY onBoolChanged)
public:
explicit Model(Controller &test, QObject *parent = nullptr);
QString getTime() const;
void setTime(const QString &value);
void setBoolValue(const QString &value);
public slots:
void onButtonPress(const QString &value);
void onBool(const QString &value);
signals:
void timeChanged();
void onButtonPressChanged(const QString &value);
void onBoolChanged(const QString &value);
private:
QString time;
Controller &test;
QString boolValue;
};
#endif // MODEL_H
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Label {
id: time1
text: model.time
}
BusyIndicator {
id: busyIndicator
x: 0
y: 23
}
PathView {
id: pathView
x: 0
y: 135
width: 640
height: 130
delegate: Column {
spacing: 5
Rectangle {
width: 40
height: 40
color: colorCode
anchors.horizontalCenter: parent.horizontalCenter
}
Text {
x: 5
text: name
anchors.horizontalCenter: parent.horizontalCenter
font.bold: true
}
}
path: Path {
startY: 100
startX: 120
PathQuad {
x: 120
y: 25
controlY: 75
controlX: 260
}
PathQuad {
x: 120
y: 100
controlY: 75
controlX: -20
}
}
model: ListModel {
ListElement {
name: "Grey"
colorCode: "grey"
}
ListElement {
name: "Red"
colorCode: "red"
}
ListElement {
name: "Blue"
colorCode: "blue"
}
ListElement {
name: "Green"
colorCode: "green"
}
}
}
Button {
id: button
x: 0
y: 271
text: qsTr("Press me!!")
checkable: true
onToggled: model.onButtonPress(button.checked)
}
Label {
id: label
x: 106
y: 294
text: model.boolValue
}
}