我建立了一个小的测试示例,以更好地理解Qt 5提供的QML / C ++绑定。
QML方面基本上是一个StackLayout
,其中几个Page
在控制台或Label
中显示信息。用户可以使用Button
浏览这些页面。
只要所有内容都在单个线程中运行,就可以了。
通过QObject
的信号和插槽进行的QML / C ++绑定按预期工作。
但是当我尝试将暴露于QML的QObject
移动到另一个线程中时,我收到了此消息,并且该应用程序被杀死了:
QQmlEngine: Illegal attempt to connect to BackendWorker(0xbe8a7c28) that is in a different thread than the QML engine QQmlApplicationEngine(0xbe8a7c44.
这是应用程序的主要内容:
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
BackendWorker backendWorker; // expose a signal and a slot only
QQmlApplicationEngine engine;
QQmlContext *ctx = engine.rootContext();
ctx->setContextProperty("backendWorker", &backendWorker);
QThread t1;
backendWorker.moveToThread(&t1); // here is the offending part
t1.start();
engine.load(QUrl(QStringLiteral("qrc:/UI/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
是否所有暴露于QML的QObject
(具有属性,信号或插槽或Q_INVOKABLE
)都必须与QGuiApplication
处于同一线程中?
编辑:
这是一个较小的示例,显示QStringListModel
与QML ListView
处于不同的线程中并且可以工作,那么这怎么可能?
// main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QStringListModel>
#include <QQmlContext>
#include <QTimer>
#include <QThread>
class Functor
{
public:
Functor(QStringListModel *model) : m_model(model) { }
void operator()() {
QStringList list;
list << "item 5" << "item 6" << "item 7" ;
m_model->setStringList(list);
}
private:
QStringListModel *m_model;
};
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QStringListModel listModel;
Functor functor(&listModel);
QQmlApplicationEngine engine;
QQmlContext *ctx = engine.rootContext();
ctx->setContextProperty("listModel", &listModel);
QThread t1;
QStringList list;
list << "item 1" << "item 2" << "item 3" << "item 4" ;
listModel.setStringList(list);
listModel.moveToThread(&t1);
t1.start();
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
QTimer::singleShot(5000, functor);
return app.exec();
}
QML方面:
// main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
ListView {
width: 200
height: 500
anchors.centerIn: parent
model: listModel
delegate: Rectangle {
height: 50
width: 200
Text {
text : display
}
}
}
}
谢谢。
答案 0 :(得分:1)
Qt在C ++级别支持跨线程边界的连接,但在QML级别不支持。 QML引擎很可能在不久的将来(如果有的话)将不支持它们。请参阅错误报告It's not possible to connect two QML objects living in different threads,该报告已作为超出范围关闭。
下面,您将看到一个简化的示例,用于实现一个工作程序并将其作为上下文属性注册到QML引擎:
class BackendWorker : public QObject {
Q_OBJECT
public slots:
void operate() {
int i = 0;
while (i < 1000) {
report(i++);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
signals:
void report(int x);
};
class BackendController : public QObject {
Q_OBJECT
public:
BackendController() {
w.moveToThread(&t);
connect(this, &BackendController::start, &w, &BackendWorker::operate);
connect(&w, &BackendWorker::report, this, &BackendController::report);
t.start();
}
signals:
void start();
void report(int x);
private:
BackendWorker w;
QThread t;
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
BackendController c;
engine.rootContext()->setContextProperty("BackendController", &c);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}