Friday, May 23, 2008

Qt: running a time-consuming operation in a separate thread to the GUI

In this example we assume that a function my_func takes a long time to run, and would cause the GUI to freeze if it were run in the main GUI thread. In order to avoid the GUI freezing, we run my_func in a separate thread (sometimes referred to as a worker thread). The GUI will be therefore be fully functional while my_func is running.

When this application launched, a small GUI will appear containing a single button. When the button is clicked, the function my_func is run in a separate thread. When the thread finishes running, a message appears on the screen, thus also illustrating a very useful feature of Qt: signals can be sent from one thread to another.

The example code consists of: main.C, user_def.h, window.C, window.h, thread.C and thread.h.

main.C

Here we define the time-consuming operation. In this example, it is just a summation. Of course, in a real application it can be something much more complex.

#include <QApplication>

#include "window.h"
#include "user_def.h"

//----------------------------------------------------------------------------
//
// the user's time-consuming function
//
//----------------------------------------------------------------------------

void my_func(void)
{
// time-consuming code
double sum;
for (int i=0; i<100000; i++)
{
for (int j=0; j<100000; j++)
{
sum = sum * i / j;
}
}
}

//----------------------------------------------------------------------------
//
// main program
//
//----------------------------------------------------------------------------

int main (int argc, char **argv)
{
QApplication app(argc, argv);
Window window;
window.show();
return app.exec();
}


user_def.h
#ifndef USER_DEF_H_
#define USER_DEF_H_

void my_func(void);

#endif /*USER_DEF_H_*/



window.C

Here we setup a very simple GUI just containing a single button. When the button is clicked, the time-consuming operation is run in another thread when QThread::start() is called. The other thread sends a signal to the GUI thread notifying that it has finished. When the GUI thread receives this signal, it displays a message on the screen using a QMessageBox.

#include "window.h"

//----------------------------------------------------------------------------
//
// the main GUI
//
//----------------------------------------------------------------------------

//------------
// constructor
//------------

Window::Window(QWidget *parent) : QWidget(parent)
{
setWindowTitle("Qt example 01");
runButton = new QPushButton("Run the function...");
connect(runButton, SIGNAL(clicked()),
this, SLOT(run_thread()));

QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(runButton);
setLayout(layout);

// the other thread

otherThread = new timeConsumingThread();

// handle finished signal from the other thread

connect(otherThread, SIGNAL(finished()),
this, SLOT(displayFinishedBox()));

}

//-----
// slot
//-----

void Window::run_thread()
{
if (!otherThread->isRunning())
{
otherThread->start();
}
}

//----------------
// display message
//----------------

void Window::displayFinishedBox()
{
QMessageBox::information(this, tr("Qt Example 01"),
tr("The function my_func has finished."));
}


window.h
#ifndef WINDOW_H_
#define WINDOW_H_

#include <QPushButton>
#include <QThread>
#include <QHBoxLayout>
#include <QMessageBox>

#include "thread.h"

class Window : public QWidget
{
Q_OBJECT

public:
Window(QWidget *parent = 0);

private:
QPushButton *runButton;
timeConsumingThread *otherThread;

public slots:
void run_thread();
void displayFinishedBox();
};

#endif /*WINDOW_H_*/


thread.C

Here we setup the other thread. In the run() reimplementation we define what is to be run in the other thread; in this example we just run the function my_func.

#include "thread.h"
#include "user_def.h"

//----------------------------------------------------------------------------
//
// thread which runs the time-consuming function
//
//----------------------------------------------------------------------------

//------------
// constructor
//------------

timeConsumingThread::timeConsumingThread(QObject *parent)
: QThread(parent)
{

}

//----
// run
//----

void timeConsumingThread::run()
{
my_func();
}


thread.h
#ifndef THREAD_H_
#define THREAD_H_

#include <QThread>

class timeConsumingThread : public QThread
{
Q_OBJECT

public:
timeConsumingThread(QObject *parent = 0);

protected:
void run();

};

#endif /*THREAD_H_*/

This example can be easily compiled using

qmake -project
qmake
make

No comments: