Qapplication in Non-Main Thread

QApplication In Non-Main Thread

If you are using QThread then you already have normal Qt event loop and can just run exec() inside QThread::run() function. While you can't work with GUI objects outside of the main thread you still can interact with them through queued signal/slot connections. Maybe you can try to store pointer to the main thread QThread object and call QObject::moveToThread() to move your GUI objects to the main thread instead of moving QApplication into another thread.

I think it's not really good idea to try to go against toolkit with different kind of hacks and kluges.

QApplication in a non-main thread of second process with pyQt4: is this code legal, and if not, why does it work?

The concept of main thread is not clearly defined in Qt documentation. Actually, the main thread of a process (process that executes the Process.run function) can be different from the main Qt thread (thread that instantiates the first Qt object like a QApplication), although both "main" threads are often the same one.

Example of valid code structure:

function below will run in the process' non-main thread 'thread-1', that will become immediately Qt's main thread.

def startThread1():      
app = QApplication(sys.argv)
app.exec_() # enter event loop

code below run in process' main thread, not to be confused with the main Qt and unique GUI thread of the process.

thread1 = Thread(target=self.startThread1)
thread1.start()
input('I am busy until you press enter')

creating QApplication in a different thread

I would answer my own question after understanding how to overcome this problem

first the problem was to integrate Qt GUI as a plugin into another Application, so the main issue was the Event loop collision between Qt Events and any other Application Events

my first thoughts was to separate both, so QApplication will stay at a different thread, but this was a totally wrong approach and here is what I have noticed:

1- Qt GUI Must stay in the main() thread so there is no other place for QApplication
2- to avoid the blocking QApplication::exec() , embed QApplication::processEvents() into the other Application Event loop

here is a working code:

//main.cpp

#include <QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
QApplication mApp(argc, argv);
MainWindow w;
w.show();

//just for testing and holding the program so it doesn't end
for(int i = 0; i < 100000000; ++i)
{
mApp.processEvents();
}
return 0;
}

edit:thanks to pavel-strakhov for his great suggestion.

How to avoid Qt app.exec() blocking main thread

Most of the time, "main thread" == "GUI thread", so people use those terms interchangeably -- even the official documentation does that. I agree that it's confusing though, because they don't have to be the same.^ The actual rule is this:

GUI classes must only be accessed from the thread which instantiates QApplication/QGuiApplication

With a plugin like yours, here is what you need to do:

  1. Create a new std::thread (NOT a QThread)
  2. Run an init function in that thread. Let it instantiate your QApplication/QGuiApplication and start the event loop
  3. Ensure that all your GUI objects are accessed from that thread only.

Voila, you now have a GUI thread that is not your main thread.


^Note: It is a different story on Mac OS X. Due to restrictions in the Cocoa framework, the main thread MUST be the GUI thread. The steps I outlined above will work on Windows/Linux but not on Mac. For Mac, you need to inject your code into the main thread -- see Kuba Ober's comments below.

Is it legal to create QCoreApplication object not in main()'s thread?

As from the documentation, it is legal, but not recommended:

In general, we recommend that you create a QCoreApplication, QGuiApplication or a QApplication object in your main() function as early as possible

Directing QT Gui from non-main thread

Consider your ProgressDialogWrapper::event implementation...

bool event (QEvent *e) override
{
switch(e->type()) {
case QEvent::User:
create();
mWidget->show();
return true;
case SET_VALUE:
mWidget->setValue(mWidget->value() + 1);
return true;
default:
return false;
}
}

You only ever process the CREATE and SET_VALUE cases: all other events are effectively discarded by this ProgressDialogWrapper instance including the QEvent::MetaCall events used by queued connections.

Replacing the current implementation with...

bool event (QEvent *e) override
{
switch(e->type()) {
case QEvent::User:
create();
mWidget->show();
return true;
case SET_VALUE:
mWidget->setValue(mWidget->value() + 1);
return true;
default:

/*
* Defer to the base class implementation.
*/
return return QObject::event(e);
}
}

should fix the problem.

Communicating with a GUI thread that is not the main thread

You can use Qt's own methods to communicate between the threads. Each Qt object has a thread affinity. If you create your MainWindow object in the separate GUI thread, it will be attached to that thread. If you use Qt signals and slots, and connect them with Qt::QueuedConnection, the slot will be called in the object's thread, through that thread's main loop, regardless of where the signal came from.

Note that you can define signals in the MainWindow class but invoke them from outside, as they are public. This means you do not need a separate sending object. In MainWindow constructor (for example) you can connect its own signal to a slot or lambda method.

To be able to emit the MainWindow signal, you can have a MainWindow pointer set to nullptr outside the GUI thread, and set that pointer (through lambda capture) to the new object you create inside the GUI thread. Outside your thread, whenever you want to emit a signal, you can do sth. like if (mainWindow) mainWindow->signal(…). I assume you need a check anyways as your GUI is optional.

Two notes:

  1. You could also forego the signal with QMetaObject::invokeMethod, it removes boilerplate, however signals can be connected through referencing the class method which is checked at compile time; The QMetaObject method uses string matching at runtime. Also with public signals, but protected slots, you can ensure that outside code doesn't accidentally call methods directly.
  2. When you connect the signal to a lambda, ensure that you specify the receiving object. Whereas connect(this, &MainWindow::signal, this, [this] {…}); will execute the lambda in the receiving thread, connect(this, &MainWindow::signal, [this] {…}); will not.


Related Topics



Leave a reply



Submit