Можно ли установить QObject в качестве свойства контекста QML в отдельном потоке? - PullRequest
0 голосов
/ 28 августа 2018

Я создал небольшой тестовый пример, чтобы лучше понять привязки QML / C ++, предлагаемые Qt 5.

Сторона QML в основном представляет собой StackLayout с несколькими Page, которые отображают информацию на консоли или в Label. Пользователь может перемещаться по этим страницам, используя Button.

Пока все работает в одном потоке, все в порядке. Привязки QML / C ++ к сигналам и слотам QObject работают должным образом.

Но когда я пытаюсь переместить QObject, доступный для QML, в другой поток, я получаю это сообщение, и приложение уничтожается:

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

Должны ли все QObject, открытые для QML (со свойствами, сигналами или слотами или 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 голосов
/ 28 августа 2018

Соединения через границы потоков поддерживаются Qt на уровне C ++, но не на уровне QML. Вероятно, движок QML не будет поддерживать их в ближайшем будущем (если вообще когда-либо). См. Отчет об ошибке Невозможно соединить два объекта QML, находящихся в разных потоках , который был закрыт как вне области действия .

Ниже приведен упрощенный пример реализации работника и его регистрации в движке 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();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...