Showing posts with label Qt. Show all posts
Showing posts with label Qt. Show all posts

Monday, June 2, 2008

Qt example : generating a single QImage in a separate thread to the GUI

Here we consider the situation when a GUI-driven application needs to generate a QImage, however, due to the time required to generate the QImage, the GUI would freeze unless the QImage is generated in a separate thread. This example is similar to the Mandelbrot example code in the Qt documentation, however the code presented here is much simpler and easier to follow.

It is important to remember that while a QImage can be made outside the GUI thread, this cannot be done for QPixmaps or QWidgets, for example (QPixmaps and QWidgets can only be used in the GUI thread).

The example consists of the following files: main.C, window.C, window.h, thread.C, thread.h, imagewindow.C and imagewindow.h.

main.C

#include <QApplication>
#include "window.h"

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

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


window.C

Here we make a simple GUI containing just a single button. When the button is clicked, an abstract image is generated in another thread when QThread::start() is called. Using Qt's signals and slots, when the image has been generated, it is sent to the GUI thread and then displayed on the screen.
#include "window.h"

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

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

Window::Window(QWidget *parent) : QWidget(parent)
{
setWindowTitle("Qt example 02");
runButton = new QPushButton("Generate image...");
connect(runButton, SIGNAL(clicked()),
this, SLOT(run_thread()));

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

// window to display the image

imageWindow = new ImageWindow();

// the QImage generation thread

qRegisterMetaType<QImage>("QImage");
otherThread = new timeConsumingThread();

// slot to respond to the QImage generation thread
// - display the image in a window

connect(otherThread, SIGNAL(theImage(const QImage &)),
this, SLOT(displayImage(const QImage &)));
}

//--------------------
// slot to run thread
//-------------------

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

//-------------------
// display the QImage
//-------------------

void Window::displayImage(const QImage &image)
{
imageWindow->plot = QPixmap::fromImage(image);
imageWindow->show();
imageWindow->update();
imageWindow->raise();
imageWindow->activateWindow();
}


window.h

#ifndef WINDOW_H_
#define WINDOW_H_

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

#include "thread.h"
#include "imagewindow.h"

class Window : public QWidget
{
Q_OBJECT

public:
Window(QWidget *parent = 0);

private:
QPushButton *runButton;
timeConsumingThread *otherThread;
ImageWindow *imageWindow;

public slots:
void run_thread();
void displayImage(const QImage &);
};

#endif /*WINDOW_H_*/


thread.C

Here we have the time-consuming image generation in the run() reimplementation. In this example, the rendering of the image doesn't actually take too long and is just a very simple abstract image. After the image has been generated, we emit a signal containing the image.
#include "thread.h"

//----------------------------------------------------------------------------
//
// thread which runs the time-consuming image generation
//
//----------------------------------------------------------------------------

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

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

}

//-----------------------
// run - paint the QImage
//-----------------------

void timeConsumingThread::run()
{
QImage myQImage(600, 600, QImage::Format_RGB32);
QPainter painter(&myQImage);
for (int i=0; i<600; i++)
{
for (int j=0; j<600; j++)
{
double hue = (double)(i + j + i*j)/361200.0;
QColor myColor;
myColor.setHsvF(hue, 1.0, 1.0, 1.0);
painter.setPen(myColor);
painter.drawPoint(i, j);
}
}
emit theImage(myQImage);
}


thread.h
#ifndef THREAD_H_
#define THREAD_H_

#include <QThread>
#include <QImage>
#include <QColor>
#include <QPainter>

class timeConsumingThread : public QThread
{
Q_OBJECT

public:
timeConsumingThread(QObject *parent = 0);

signals:
void theImage(const QImage &);

protected:
void run();

};

#endif /*THREAD_H_*/


imagewindow.C

Here we setup a simple window which is used for displaying the image. In paintEvent() we draw the QPixmap containing our image onto the window.
#include "imagewindow.h"

//----------------------------------------------------------------------------
//
// image window
//
//----------------------------------------------------------------------------

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

ImageWindow::ImageWindow(QWidget *parent) : QWidget(parent)
{
setFixedSize(600, 600);
setWindowTitle("Qt Example 02: Image");
}

//-----------------------------------------------------------------------------
//
// paint the image
//
//-----------------------------------------------------------------------------

void ImageWindow::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.drawPixmap(0, 0, plot);
}


imagewindow.h

#ifndef IMAGEWINDOW_H_
#define IMAGEWINDOW_H_

#include <QWidget>
#include <QPixmap>
#include <QPainter>

class ImageWindow : public QWidget
{
Q_OBJECT

public:
ImageWindow(QWidget *parent = 0);
QPixmap plot;

private:

protected:
void paintEvent(QPaintEvent *event);

};

#endif /*IMAGEWINDOW_H_*/
This example can be compiled by the following:

qmake -project
qmake
make

Sunday, June 1, 2008

Qt: simple example of use of QtConcurrent

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. 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.

This example makes uses QtConcurrent::run(), QFuture and QFutureWatcher, rather than just a QThread. Qt 4.4.0 or above is required.

This example consists of the following codes: main.C, user_def.h, window.C and window.h.

main.C

Here we define the time-consuming operation. In this example, it is just a summation.
#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();
window.setMaximumSize(window.minimumWidth(),
window.minimumHeight());
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 my_func is run in another thread when QtConcurrent::run() is called. We use a QFutureWatcher to monitor the state of the other thread. When the GUI thread receives a signal from the QFutureWatcher that the function has finished running, 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 03");
runButton = new QPushButton("Run the function...");
connect(runButton, SIGNAL(clicked()),
this, SLOT(run_thread()));

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

// create a QFuture and a QFutureWatcher

future = new QFuture<void>;
watcher = new QFutureWatcher<void>;

// display a message box when the calculation has finished

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

}

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

void Window::run_thread()
{
*future = QtConcurrent::run(my_func);
watcher->setFuture(*future);
}

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

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


window.h
#ifndef WINDOW_H_
#define WINDOW_H_

#include <QPushButton>
#include <QThread>
#include <QHBoxLayout>
#include <QMessageBox>
#include <QtConcurrentRun>
#include <QFuture>
#include <QFutureWatcher>

#include "user_def.h"

class Window : public QWidget
{
Q_OBJECT

public:
Window(QWidget *parent = 0);

private:
QPushButton *runButton;
QFuture<void> *future;
QFutureWatcher<void> *watcher;

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

#endif /*WINDOW_H_*/


This example can be compiled easily using:

qmake -project
qmake
make

Sunday, May 25, 2008

How to include rich text in a QImage

One method is to use a QTextDocument. In the example code fragment below we assume that text is a QString containing some rich text.

QTextDocument doc;
doc.setHtml(text);
double offsetV = doc.size().height()*0.5;
double offsetH = 0.;
painter->translate(QPointF(-offsetH, -offsetV));
doc.drawContents(painter);
painter->translate(QPointF(offsetH, offsetV));

This will give left-aligned text at the current position. For centre-aligned text use
   double offsetH = doc.size().width()*0.5;
or for right-aligned text
   double offsetH = doc.size().width();

C++ inheritance: a simple example

Suppose we want to write a scientific plotting application using Qt that can make simple 2D plots as well as 3D plots. One option would be to make two classes, Plot2D and Plot3D. For example:
class Plot2D
{
   public:
      Plot2D();
      ~Plot2D();
      void setAxisLimitsX(int, int);
      void setAxisLimitsY(int, int);
      void setNumDataPoints(int);
      void setData(float *x, float *y);
   private:
      QImage *plot;
};

Plot3D
{
   public:
      Plot3D();
      ~Plot3D();
      void setAxisLimitsX(int, int);
      void setAxisLimitsY(int, int);
      void setAxisLimitsZ(int, int);
      void setNumDataPoints(int);
      void setData(float *x, float *y, float *z);
   private:
      QImage *plot;
};
Clearly, both classes are quite similar. Both have methods for setting the x-axis limits, the y-axis limits, the number of data points, and a QImage containing the actual plot. The only differences are that Plot3D has an additional method for setting the z-axis limits, and the setData method has an additional array for the 3rd component of the data.

An alternative to the above is to use inheritance. We make a class called Plot:
class Plot
{
   public:
      Plot();
      ~Plot();
      void setAxisLimitsX(int, int);
      void setAxisLimitsY(int, int);
      void setNumDataPoints(int);
      void setData(float *x, float *y);
   private:
      QImage *plot;
};
This is very general and has methods and data that apply to both 2D and 3D plots. We can now define the Plot2D and Plot3D classes:
class Plot2D : public Plot
{
   public:
      Plot2D();
      ~Plot2D();
};

class Plot3D : public Plot
{
   public:
      Plot3D();
      ~Plot3D();
      void setAxisLimitsZ(int, int);
      void setData(float *x, float *y, float *z);
};
These two classes (known as derived classes) inherit from the Plot class, and contain all public and protected variables of the base class Plot (but not private variables). For example, both Plot2D and Plot3D here have access to setAxisLimitsX and setAxisLimitsY. However, note that the constructors and destructors of base classes are not inherited.

The use of inheritance enables code to be reused. After defining the base class, we don't need to define the same features in multiple derived classes. This sames time!

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