简体   繁体   中英

Updating Qt GUI from a different std::thread

In a separate component of my application that I want to keep free of Qt dependency I am using std::thread for some operations. I want to make changes in my main application during the processing. For that I have attempted to pass a function (ie: function<void(UpdateNode*)> nodeUpdatedCallback ) to my component from my application.

This function updates the UI, but since I am calling that function from another thread, Qt says that I cannot access the UI from a non main thread.

I have seen many articles to solve this issue by using QThread and signals, creating a worker and moving it to that thread.

Since I want to use std::thread , is it possible to update a UI based on Qt using std::thread ?

When I used Qt in a multithreaded environment, and didn't want to/couldn't use idiomatic signals/slots, I added the following method to my objects:

    typedef std::function<void()> function_t;
    void executeInObjectsThread(function_t const&);

private slots:
    void executeInObjectsThreadSlot(function_t);

and the definition:

void MyObj::executeInObjectsThread(function_t const& f)
{
    QMetaObject::invokeMethod(
        this,
        "executeInObjectsThreadSlot",
        Qt::QueuedConnection,
        Q_ARG(function_t,f)
    );
}

then in your other thread you'd just call

foo->executeInObjectsThread([=]{
    foo->addWidget(new QWidget(foo));
    // ...
    foo->editBox->setText(QString::number(currentResult));
    foo->progressBar->setValue(n);
});

Basically you're not, ever, supposed to modify any GUI related things from another thread. There's a very good reason for this: GUIs depend on event loops that are not thread-safe. You start playing with that, and you'll get undefined behavior due to race conditions. Thanks to Qt for protecting you from messing it up!

However, the solution to your problem is very simple. What I do in your case is that I define a common variable that the thread-modifies. So I use a QTimer to check whether the thread has finished its job. And to know whether a thread has finished, you can use an std::atomic<bool> flag that will be set when the computation is over (or use std::promise/future , which I prefer). Then, the GUI will just read the (also thread-safe) results from the main thread and display them. This is 100% safe.

Recently I did this with Neblio. Check out my code there for an auto-updater that runs in another thread and displays the results on the GUI.

Qt indeed uses a signal/slot mechanism. You only mention the signal part, but here the slot is very important too. A slot lives in an object, and if that object is a Qt object with thread affinity then the slot will execute in that thread. This holds even if the signal comes from another thread.

This is particularly useful when the slot owner is a QWindow or something similar with UI thread affinity. It makes sure the UI code runs in the right thread. And almost certainly, the UI thread is the main() thread of your application.

So you can still have a signal from your std::thread . It won't cause the slot to run in the same std::thread .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM