1

What I'd like to have is a main thread that instantiates a class that extends QQuickView and moves it to a second thread.

Ideally, I would like to do something like this:

main.cpp

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    MyClass myClassObj;

    myClassObj.init();

    return 0;
}

MyClass.cpp

void init()
{
    MyQtQuickClass view;

    QThread* GUIthread = new QThread(this);

    view.moveToThread(GUIthread);
    QObject::connect(GUIthread, SIGNAL(started()), &view, SLOT(init()));

    GUIthread.start();
}

MyQtQuickCLass.cpp

void init()
{
    QQmlContext* rootContext = this->rootContext();

    // Setup view code here

    this->show();

    QGuiApplication::instance()->exec();
}

With something like this I get this error: QQmlEngine: Illegal attempt to connect to QQmlContext(0x120fc60) that is in a different thread than the QML engine QQmlEngine(0xdf6e70).

Is there a workaround? Or a way to create the QML engine directly in the second thread?

Beriol
  • 646
  • 1
  • 10
  • 25

2 Answers2

3

If you want a QQuickView to live outside the main() thread, then you must:

  1. Create a new std::thread (NOT a QThread, because it is a QObject so it mustn't be created before your QGuiApplication)
  2. Run an init() function in that thread. Let it instantiate your QGuiApplication and start the event loop.
  3. Create your QQmlEngine/QQuickView/QQuickWidget in that thread too.
  4. Ensure that all of your GUI objects are accessed from that thread only.
  5. Ensure that your main() thread doesn't create any QObjects until after your QGuiApplication has been created in your other thread.

See How to avoid Qt app.exec() blocking main thread for more details.

JKSH
  • 2,658
  • 15
  • 33
  • Understood, I quickly tried it and it works. The problem is, how can I handle the communication between the 2 threads now? Before I wanted to use signals and slots, but now the class the instantiates the pthread can't be a QObject, so it won't work. Is there a workaround to this? – Beriol Jun 05 '16 at 11:41
  • 1
    One possible solution is this: After you spawn the secondary thread, block your main thread until you verify that the `QGuiApplication` has been fully created. After you unblock, you can create a QObject in the main thread and set up signal-slot connections. The class that instantiates the `pthread` can't be a `QObject`, but it can create other `QObject`s. – JKSH Jun 05 '16 at 12:40
  • Thanks for the valid suggestion, it solves the problems! – Beriol Jun 05 '16 at 13:47
2

QObjects have thread affinity to the parent QObject's thread, or the current thread if they don't have a parent. Most likely -- in your example -- some QObjects created during instantiation of MyQtQuickClass have affinity to the thread in which they were created. Creating the MyQtQuickClass in the GUIthread would solve this.

However, all Qt windows must run in the same thread as QGuiApplication, and your application would most likely crash if you tried.

arhzu
  • 550
  • 3
  • 13
  • The problem is exactly that, I'm forced to create the QGuiApplication object as the very first thing in the main, so it's stuck to the main thread. Is there a way to postpone the creation of the QGuiApplication object, so that I can create it directly inside the MyQtQuickClass object? – Beriol Jun 04 '16 at 20:09
  • You might be able to start a native thread (pthreads etc) and consider that one as your "main" Qt thread. – arhzu Jun 04 '16 at 20:14