Gaberocksall
Gaberocksall

Reputation: 835

Qt GUI hanging with worker in a QThread

I have a worker which needs to complete an arbitrary blocking task

class Worker : public QObject
{
    Q_OBJECT;

public:
    using QObject::QObject;

public slots:
    void start()
    {
        for (int i = 0; i < 10; i++)
        {
            qDebug("I'm doing work...");
            Sleep(1000);
        }
    }
};

In my MainWindow constructor, I start the task like so:

class MainWindow : public QWidget
{
    Q_OBJECT;

public:
    explicit MainWindow(QWidget* parent = nullptr) : QWidget(parent)
    {
        QThread* t = new QThread(this);
        Worker* w = new Worker(this);
        w->moveToThread(t);

        this->connect(
            t, &QThread::started,
            w, &Worker::start
        );
        this->connect(
            t, &QThread::finished,
            t, &QThread::deleteLater
        );

        t->start();
    }
};

And my entry point:

int main(int argc, char** argv)
{
    QApplication app(argc, argv);

    MainWindow mainWindow{};
    mainWindow.show();

    return app.exec();
}

When I run the program, I just get a spinning mouse circle, and the window contents don't load until 10 seconds later (when the worker is complete). Why is this so? What do I need to do so that Worker::start is run in the background without affecting the GUI?

P.S. I am using Qt 6.2.2 and Windows 11, but I highly doubt that has anything to do with this issue.

Upvotes: 0

Views: 191

Answers (1)

The problem is that the worker is actually not moved to thread (Isn't a warning written to console? I bet it is.), because its parent, i.e. MainWindow instance is still in the GUI thread.

When moving to thread, you can only move the whole hierarchy of objects by moving the very top parent. You cannot have parent in different thread than its children.

You should create the worker without parent.

Worker* w = new Worker();

Of course you should also add some finished() signal to your Worker class.

class Worker : public QObject
{
    Q_OBJECT;

public:
    using QObject::QObject;

    void start()
    {
        for (int i = 0; i < 10; i++)
        {
            qDebug("I'm doing work...");
            Sleep(1000);
        }
        emit finished();
    }

signals:
    void finished();
};

and add these connections:

this->connect(
            w, &Worker::finished,
            t, &QThread::quit
        ); 
this->connect(
            t, &QThread::finished,
            w, &Worker::deleteLater
        );

otherwise your thread's even loop will not end and the worker will not be deleted.

Upvotes: 2

Related Questions