Reputation: 835
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
Reputation: 7954
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