How to avoid Qt app.exec() blocking main thread
I'm new to Qt, but need to solve a difficult problem.
I've created a VERY simple GUI that I need to add to an existing C++ application. The problem is, I'm writing just one module that plugs into a larger architecture which restricts my access to the main thread.
My code must reside inside the four following functions: an Init() function, which runs in the main thread. and WorkerStart(), WorkerStep(), and WorkerStop() functions that run in a worker thread.
I coded my QApplication and GUI objects in the Init() function. But of course, calling app.exec() at the end of that function blocks the entire rest of the code. Not workable.
Everything I'm reading says that Qt gui objects can only run in the main thread.
So my question is, how can I set up my gui in the init() function (main thread) and allow it to run by only using the worker thread from then on?
I found this: QApplication In Non-Main Thread
and those solutions gave me some different behavior. In the right direction, but not stable or fully functional. But I dont understand why those are solutions at all if qt gui's can only run in main thread, and these solutions put them in other threads. So thats sending mixed messages on what can and can not run in other threads, and it becomes very confusing.
It seems that adding a gui to an existing C++ program without locking it up in the exec() func should be a fairly common situation so I feel like I'm missing something obvious. Can someone help with how I can solve this?
Much thanks in advance. Phil
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:
- Create a new
std::thread
(NOT aQThread
) - Run an
init
function in that thread. Let it instantiate yourQApplication
/QGuiApplication
and start the event loop - 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.