Search code examples
c++multithreadingqtsignals-slotsqthread

Cannot send events to objects owned by a different thread while runing a member method on another QThread


I need to run a method in my MainWindow class, in a different thread as it is a length and time consuming process.

This is what I tried:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    initGui(ui);

    // Create background worker thread
    backgroundWorker = QThread::create([this] {
        backgroundMethod();
    });

    // Define when finished with work
    connect(backgroundWorker, &QThread::finished, [this] () {
        qDebug() << "Background method has finished";

        // Stop movie
        ui->lblLoading->movie()->stop();

        // go to next screen
        ui->tabbarMainWidget->tabBar()->setCurrentIndex(1);

        //show backup icon files
        if(filesToBackup.size() > 0) {
            qDebug() << "There are files to backup!";
            ui->lblInfoImage->show();
        }
    });

    // Start worker thread
    backgroundWorker->start();
}

backgroundMethod

void MainWindow::backgroundMethod() {
    for (int i = 0; i < 10; i++) {
        qDebug() << "Hello World";
    }
}

I am omitting alot of code as it is not necessary. The basic logic is as follows:

  1. Start new thread using QThread::create()

  2. Run the backgroundMethod() until completed while having the UI free for other work.

  3. When the backgroundMethod() has finished, the QThread should emit the finished() signal.

  4. I have a connection setup between the backgroundWorker thread's finished() and the lambda to run some more code.

The problem:

Background method has finished

QObject::killTimer: Timers cannot be stopped from another thread

ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 0x0x2801d950. Receiver 'lblInfoImage' (of type 'QLabel') was created in thread 0x0x2688c4b0", file kernel\qcoreapplication.cpp, line 578 04:11:28: The program has unexpectedly finished.

In short, I am accessing the lblInfoImage on the backgroundWorker thread. I understood that using the signal/slot mechanism should cater for this issue, where my use of it is correct.

I am not sure why this is happening, I need some help in understanding what I did to cause the problem and how I can fix it


Solution

  • The problem is simple: you execute UI code on a non-UI thread which is strictly forbidden in Qt (and in many other UI frameworks across different languages). That happens because you do the connection wrong:

    connect(backgroundWorker, &QThread::finished, [this] () {
        ...
    });
    

    That connection means: whenever QThread emits a finished signal run this function. The thing is, it will run the function in the context of the emitted signal which is another thread not the thread backgroundWorker lives in. So you have to provide the UI thread context for receiving this signal:

    connect(backgroundWorker, &QThread::finished, this, [this] () {
            ...
        });
    

    Now the function provided will be executed in the context of the UI thread (this).