Qt Qt Quarterly
Qt Development Frameworks | Documentation | Qt Quarterly
« Writing ODF Files with Qt | The Panel Stack Pattern »

Keeping the GUI Responsive

by Witold Wysota

At QtCentre people come to us with the recurring problem of their GUIs freezing during long operations. The issue is not difficult to overcome but you can do it in many different ways, so I'd like to present a range of possible options that can be used depending on the situation you are dealing with.

Performing Long Operations

The first thing to do is to state the domain of the problem and outline paths that can be taken to solve it. The aforementioned problem can take one of two forms. The first variation is when a program has to execute a calculation-intensive task that is described as a series of operations to be performed in sequence in order to obtain the final result. An example of such a task is calculating a Fast Fourier Transform.

The second variation is when a program has to trigger some activity (for instance a network download) and wait for it to be completed before continuing to the next step of the algorithm. This variation of the problem is, in itself, easy to avoid when using Qt because most of the asynchronous tasks performed by the framework emit a signal when they have finished doing their job, and you can connect it to a slot that will continue the algorithm.

During the calculations (regardless of any usage of signals and slots) all event processing gets halted. As a result, the GUI is not refreshed, user input is not processed, network activity stops and timers don't fire—the application looks like it's frozen and, in fact, the part of it not related to the time-intensive task is frozen. How long are "long operations"? Everything that will distract the end user from interacting with the application is long. One second is long, everything longer than two seconds is definitely too long.

Our aim in this article is to keep the functionality while preventing the end user from getting irritated by a frozen GUI (and the network and timers). To do that let's take a look at possible classes of solutions and domains of the problem.

We can reach our final goal of performing calculations in one of two ways—either by doing the computation in the main thread (the single-threaded approach) or in separate threads (the multithreaded approach). The latter is widely known and used in the Java world, but it is sometimes abused where one thread would do the job just fine. Contrary to popular opinion, threads can often slow down your application instead of speeding it up, so unless you are sure your program can benefit from being multithreaded (either in terms of speed or simplicity), try to avoid spawning new threads simply because you can.

The domain of the problem can be treated as one of two cases. Either we can or cannot divide the problem into smaller parts like steps, iterations or sub-problems (usually it shouldn't be monolithic). If the task can be split into chunks, each of them can either depend on others or not. If they are independent, we can process them at any time and in arbitrary order. Otherwise, we have to synchronize our work. In the worst case, we can only do one chunk at once and we can't start the next one until the previous one is finished. Taking all these factors into consideration, we can choose from different solutions.

Manual event processing

The most basic solution is to explicitly ask Qt to process pending events at some point in the computation. To do this, you have to call QCoreApplication::processEvents() periodically. The following example shows how to do this:

    for (int i = 3; i <= sqrt(x) && isPrime; i += 2) {
        label->setText(tr("Checking %1...").arg(i));
        if (x % i == 0)
            isPrime = false;
        QCoreApplication::processEvents();
        if (!pushButton->isChecked()) {
            label->setText(tr("Aborted"));
            return;
        }
    }

This approach has significant drawbacks. For example, imagine you wanted to perform two such loops in parallel—calling one of them would effectively halt the other until the first one is finished (so you can't distribute computing power among different tasks). It also makes the application react with delays to events. Furthermore the code is difficult to read and analyze, therefore this solution is only suited for short and simple problems that are to be processed in a single thread, such as splash screens and the monitoring of short operations.

 Actually you should call QCoreApplication::sendPostedEvents(). See the documentation for processEvents().

Using a Worker Thread

A different solution is to avoid blocking the main event loop by performing long operations in a separate thread. This is especially useful if the task is performed by a third party library in a blocking fashion. In such a situation it might not be possible to interrupt it to let the GUI process pending events.

One way to perform an operation in a separate thread that gives you most control over the process is to use QThread. You can either subclass it and reimplement its run() method, or call QThread::exec() to start the thread's event loop, or both: subclass and, somewhere in the run() method, call exec(). You can then use signals and slots to communicate with the main thread—just remember that you have to be sure that QueuedConnection will be used or else threads may lose stability and cause your application to crash.

There are many examples of using threads in both the Qt reference documentation and online materials, so we won't make our own implementation, but instead concentrate on other interesting aspects.

Waiting in a Local Event Loop

The next solution I would like to describe deals with waiting until an asynchronous task is completed. Here, I will show you how to block the flow until a network operation is completed without blocking event processing. What we could do is essentially something like this:

    task.start();
    while (!task.isFinished())
        QCoreApplication::processEvents();

This is called busy waiting or spinning—constantly checking a condition until it is met. In most cases this is a bad idea, it tends to eat all your CPU power and has all the disadvantages of manual event processing.

Fortunately, Qt has a class to help us with the task: QEventLoop is the same class that the application and modal dialogs use inside their exec() calls. Each instance of this class is connected to the main event dispatching mechanism and, when its exec() method is invoked, it starts processing events until you tell it to stop using quit().

We can use the mechanism to turn asynchronous operations into synchronous ones using signals and slots—we can start a local event loop and tell it to exit when it sees a particular signal from a particular object:

    QNetworkAccessManager manager;
    QEventLoop q;
    QTimer tT;
    
    tT.setSingleShot(true);
    connect(&tT, SIGNAL(timeout()), &q, SLOT(quit()));
    connect(&manager, SIGNAL(finished(QNetworkReply*)),
            &q, SLOT(quit()));
    QNetworkReply *reply = manager.get(QNetworkRequest(
                   QUrl("http://www.qtcentre.org")));
    
    tT.start(5000); // 5s timeout
    q.exec();
    
    if(tT.isActive()){
        // download complete
        tT.stop();
    } else {
        // timeout
    }

We use one of the new additions to Qt—a network access manager—to fetch a remote URL. As it works in an asynchronous fashion, we create a local event loop to wait for a finished() signal from the downloader. Furthermore, we instantiate a timer that will terminate the event loop after five seconds in case something goes wrong. After connecting appropriate signals, submitting the request and starting the timer we enter the newly created event loop. The call to exec() will return either when the download is complete or five seconds have elapsed (whichever comes first). We find out which is the case by checking if the timer is still running. Then we can process the results or tell the user that the download has failed.

We should note two more things here. Firstly, that a similar approach is implemented in a QxtSignalWaiter class that is part of the libqxt project (http://www.libqxt.org). Another thing is that, for some operations, Qt provides a family of "wait for" methods (for example QIODevice::waitForBytesWritten()) that will do more or less the same as the snippet above but without running an event loop. However, the "wait for" solutions will freeze the GUI because they do not run their own event loops.

Solving a Problem Step by Step

If you can divide your problem into subproblems then there is a nice path you can take to perform the computation without blocking the GUI. You can perform the task in short steps that will not obstruct event processing for longer periods of time. Start processing and, when you notice you have spent some defined time on the task, save its state and return to the event loop. There needs to be a way to ask Qt to continue your task after it is done with events.

Fortunately there is such a way, or even two. One of them is to use a timer with an interval set to zero. This special value will cause Qt to emit the timeout signal on behalf of the timer once its event loop becomes idle. If you connect to this signal with a slot, you will get a mechanism of calling functions when the application is not busy doing other things (similar to how screen savers work). Here is an example of finding prime numbers in the background:

    class FindPrimes : public QObject
    {
        Q_OBJECT
    public:
        FindPrimes(QObject *parent = 0) : QObject(){}
    public slots:
        void start(qlonglong _max);
    private slots:
        void calculate();
    signals:
        void prime(qlonglong);
        void finished();
    private:
        qlonglong cand, max, curr;
        double sqrt;
        void next(){ cand+=2; curr = 3; sqrt = ::sqrt(cand);}
    };

    void FindPrimes::start(qlonglong _max)
    {
        emit prime(1); emit prime(2); emit prime(3);
        max = _max; cand = 3; curr = 3;
        next();
        QTimer::singleShot(0, this, SLOT(calculate())); 
    }
    
    void FindPrimes::calculate()
    {
        QTime t;
        t.start();
        while (t.elapsed() < 150) {
            if (cand > max) {
                emit finished();        // end
                return;
            }
            if (curr > sqrt) {
                emit prime(cand);       // prime
                next();
            } else if (cand % curr == 0)
                next();                 // not prime
            else
                curr += 2;              // check next divisor
        }
        QTimer::singleShot(0, this, SLOT(calculate()));
    }

The FindPrimes class makes use of two features—it holds its current calculation state (cand and curr variables) so that it can continue calculations where it left off, and it monitors (by the use of QTime::elapsed()) how long it has been performing the current step of the task. If the time exceeds a predefined amount, it returns to the event loop but, before doing so, it starts a single-shot timer that will call the method again (you might call this approach "deferred recurrency").

I mentioned there were two possibilities of doing a task in steps. The second one is to use QMetaObject::invokeMethod() instead of timers. This method allows you to call any slot from any object. One thing that needs to be said is that, for this to work in our case, we need to make sure the call is made using the Qt::QueuedConnection connection type, so that the slot is called in an asynchronous way (by default, slot calls within a single thread are synchronous). Therefore we might substitute the timer call with the following:

    QMetaObject::invokeMethod(this, "calculate",
                              Qt::QueuedConnection);

The advantage of this over using timers is that you can pass arguments to the slot (for example, passing it the current state of a calculation). Apart from that the two methods are equivalent.

Parallel Programming

Finally, there is the situation where you have to perform a similar operation on a set of data—for instance, creating thumbnails of pictures from a directory. A trivial implementation would look like this:

    QList<QImage> images = loadImages(directory);
    QList<QImage> thumbnails;
    foreach (const QImage &image, images) {
        thumbnails << image.scaled(QSize(300,300), 
            Qt::KeepAspectRatio, Qt::SmoothTransformation);
        QCoreApplication::sendPostedEvents();
    }

A disadvantage of such an approach is that creating a thumbnail of a single image might take quite long, and for that time the GUI would still be frozen. A better approach would be to perform the operation in a separate thread:

    QList<QImage> images = loadImages(directory);
    ThumbThread *thread = new ThumbThread;
    connect(thread, SIGNAL(finished(QList<QImage>)),
            this, SLOT(showThumbnails(QList<QImage>)));
    thread->start(images);

This solution is perfectly fine, but it doesn't take into consideration that computer systems are evolving in a different direction than five or ten years ago—instead of having faster and faster processing units they are being equipped with multiple slower units (multicore or multiprocessor systems) that together offer more computing cycles with lower power consumption and heat emission. Unfortunately, the above algorithm uses only one thread and is thus executed on a single processing unit, which results in slower execution on multicore systems than on single core ones (because a single core in multicore systems is slower than in single core ones).

To overcome this weakness, we must enter the world of parallel programming—we divide the work to be done into as many threads as we have processing units available. Starting with Qt 4.4, there are extensions available to let us do parallel programming: these are provided by QThreadPool and Qt Concurrent.

The first possible course of action is to use so called runnables—simple classes whose instances can be executed by a thread. Qt implements runnables through its QRunnable class. You can implement your own runnable based on the interface offered by QRunnable and execute it using another entity offered by Qt. I mean a thread pool—an object that can spawn a number of threads that execute arbitrary jobs. If the number of jobs exceeds the number of available threads, jobs will be queued and executed when a thread becomes available.

Let's go back to our example and implement a runnable that would create an image thumbnail using a thread pool.

    class ThumbRunnable : public QRunnable {
    public:
        ThumbRunnable(...)  : QRunnable(), ... {}
        void run(){ m_result = m_image.scaled(...); }
        const QImage &result() const{ return m_result; }
    };
    
    QList<ThumbRunnable *> runnables;
    foreach(const QImage &image, images){
        ThumbRunnable *r = new ThumbRunnable(image, ...);
        r->setAutoDelete(false);
        QThreadPool::globalInstance()->start(r);
        runnables << r;
    }

Basically, everything that needs to be done is to implement the run() method from QRunnable. It is done the same way as subclassing QThread, the only difference is that the job is not tied to a thread it creates and thus can be invoked by any existing thread. After creating an instance of ThumbRunnable we make sure it won't be deleted by the thread pool after job execution is completed. We need to to that because we want to fetch the result from the object. Finally, we ask the thread pool to queue the job using the global thread pool available for each application and add the runnable to a list for future reference.

We then have to periodically check each runnable to see if its result is available, which is boring and troublesome, but fortunately there is a better approach when you need to fetch a result. Qt Concurrent introduces a number of paradigms that can be invoked to perform SIMD (Single Instruction Multiple Data) operations. Here we will take a look only at one of them, the simplest one that lets us process each element of a container and have the results ready in another container.

    typedef QFutureWatcher<QImage> ImageWatcher;
    QImage makeThumb(const QString &img)
    {
        return QImage(img).scaled(QSize(300,300), ...);
    }
    
    QStringList images = imageEntries(directory);
    ImageWatcher *watcher = new ImageWatcher(this);
    connect(watcher, SIGNAL(progressValueChanged(int)),
            progressBar, SLOT(setValue(int)));
    QFuture<QImage> result 
        = QtConcurrent::mapped(images, makeThumb);
    watcher->setFuture(result);

Easy, isn't it? Just a few lines of code and the watcher will inform us of the state of the SIMD program held in the QFuture object. It will even let us cancel, pause and resume the program. Here, we used the simplest possible variant of the call—using a standalone function. In a real situation you would use something more sophisticated than a simple function—Qt Concurrent lets you use functions, class methods and function objects. Third party solutions allow you to further extend the possibilities by using bound function arguments.

Conclusion

I have shown you a whole spread of solutions based on type and complexity of problem related to performing time consuming operations in Qt-based programs. Remember that these are only the basics—you can build upon them, for example by creating your own "modal" objects using local event loops, swift data processors using parallel programming, or generic job runners using thread pools. For simple cases, there are ways to manually request the application to process pending events, and for more complex ones dividing the task into smaller subtasks could be the right direction.

You can download complete examples that use techniques described in this article from the Qt Quarterly Web site. If you would like to discuss your solutions, work together with others to solve a problem that's bothering you, or simply spend some time with other Qt (cute?) developers, you can always reach me and others willing to share their knowledge at http://www.qtcentre.org.

Never again let your GUI get frozen!

Site Map Accessibility Contact