是否有可能在单独的线程中将QObject设置为QML上下文属性?

时间:2018-08-28 12:04:29

标签: c++ qt qml

我建立了一个小的测试示例,以更好地理解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
            }
        }
    }
}

谢谢。

1 个答案:

答案 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();
}