mroavi
mroavi

Reputation: 129

Communicating with a GUI thread that is not the main thread

I managed to implement a GUI Qt-based application in a worker C++ std::thread like described here. Now I need both the main and worker threads to communicate.

My question is: how can I communicate messages (an array of floats) from my main thread to the worker thread so that I can update the GUI?

I have an application that performs real-time signal processing. My goal is to create a Qt GUI that can be plugged in into my application to visualize various signals without influencing the real-time aspect. I investigated different methods of how to accomplish this and concluded that this post describes quite closely what I need and provides a solution for it. However, there is no information on how the main and worker threads can communicate with each other.

I tried using the Futures/Promises approach described here to accomplish the inter-thread communication. Although I was able to get this example running, I couldn't integrate it into my project. The reason is that this approach relies on having a busy loop inside the worker thread that is constantly checking whether a new message has been sent by the main thread. However, in a Qt application, the program blocks once it enters the main event loop in a.exec(). This prevents the busy loop check and hence causes the program to deadlock.

This is how I am spawning the GUI worker thread (based on this post).

#include <thread>

// Start the Qt realtime plot demo in a worker thread
int argc = 0;
char **argv = NULL;
std::thread t1
        (
                [&] {
                    QApplication application(argc, argv);
                    MainWindow mainWindow;
                    mainWindow.show();
                    return application.exec();
                }
        );

Upvotes: 3

Views: 1702

Answers (2)

midjji
midjji

Reputation: 417

For signal processing you generally do not want the overhead of qts virtual calls. But that aside,

A good solution for thread to gui communication is found in: [email protected]:midjji/convenient_multithreaded_qt_gui.git

then its just e.g.

enter code here
run_in_gui_thread(new RunEventImpl([](){
        QMainWindow* window=new QMainWindow();
        window->show();
    }));

callable from any thread, at any time, while taking care of setting things up for you in the bg.

Upvotes: 0

ypnos
ypnos

Reputation: 52397

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.

Upvotes: 1

Related Questions