Featured image of post Qt UI and Threading

Qt UI and Threading

How to Make Your UI Feel Smooth and Responsive

Mastering Threading in Qt: How to Make Your UI Feel Smooth and Responsive

So, you’ve built a Qt app, and everything is great—until you introduce some long-running tasks. Suddenly, your app freezes, the UI becomes unresponsive, and users start rage-clicking.

What Are Threads?

A thread is like a mini-program running inside your application. The main thread (where the UI runs) is responsible for handling user interactions, drawing widgets, and responding to events. If you perform heavy computations or network operations on the main thread, the UI freezes, and users get frustrated.

To avoid that, Qt provides multiple ways to use threads:

  1. QThread – Create and manage threads manually.
  2. QtConcurrent – Run tasks asynchronously without dealing with thread management.
  3. QThreadPool – A thread pool for managing multiple worker threads efficiently.

We’ll focus on QThread, as it gives the most control and is widely used in Qt applications.

Creating a Background Thread with QThread

Let’s say we have a long-running task (like downloading a large file, parsing a massive JSON, or training a tiny AI model). Instead of freezing the UI, we’ll offload the work to a separate thread.

Example: A Background Thread That Updates the UI

Here’s how to create a worker thread that does some background work while updating a progress bar in the UI.

1. Create a Worker Class

A worker class must inherit from QObject and move to a separate QThread.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <QObject>
#include <QThread>
#include <QDebug>
#include <QTimer>

class Worker : public QObject {
    Q_OBJECT

public:
    explicit Worker(QObject *parent = nullptr) {}

signals:
    void progress(int value);  // Signal to update UI
    void finished();           // Signal when work is done

public slots:
    void doWork() {
        for (int i = 0; i <= 100; ++i) {
            QThread::sleep(1);  // Simulate a heavy task
            emit progress(i);    // Send progress to UI
        }
        emit finished();
    }
};

2. Use the Worker in a QThread

Now, we’ll set up our UI and connect the worker’s signals to update it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <QApplication>
#include <QMainWindow>
#include <QPushButton>
#include <QProgressBar>
#include <QVBoxLayout>
#include <QThread>
#include "worker.h"

class MainWindow : public QMainWindow {
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
        auto *centralWidget = new QWidget(this);
        auto *layout = new QVBoxLayout(centralWidget);
        setCentralWidget(centralWidget);

        progressBar = new QProgressBar();
        progressBar->setRange(0, 100);
        QPushButton *startButton = new QPushButton("Start Task");

        layout->addWidget(progressBar);
        layout->addWidget(startButton);

        // Create worker and thread
        QThread *workerThread = new QThread(this);
        Worker *worker = new Worker();

        worker->moveToThread(workerThread);

        // Connect signals and slots
        connect(startButton, &QPushButton::clicked, worker, &Worker::doWork);
        connect(worker, &Worker::progress, progressBar, &QProgressBar::setValue);
        connect(worker, &Worker::finished, workerThread, &QThread::quit);
        connect(worker, &Worker::finished, worker, &Worker::deleteLater);
        connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);

        workerThread->start();
    }

private:
    QProgressBar *progressBar;
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    MainWindow mainWindow;
    mainWindow.show();
    return app.exec();
}

#include "main.moc"

Key Ideas

ConceptDescription
QThreadManages a separate thread
Signals & SlotsEnables safe communication between threads
moveToThread()Moves an object to another thread
Queued ConnectionEnsures thread-safe UI updates
Stopping ThreadsUse a QAtomicBool to stop safely

References