Qt GUI Toolkit

This GUI toolkit makes porting graphics to multiple platforms a snap.
Double-buffering

Flickering is a common problem in graphics programming. Some GUI programs do updating by clearing the area of a widget and then draw the different graphics elements. This process normally takes enough time for the eye to notice the clearing and drawing process. The widget flickers, the program looks unprofessional and fatigues the eyes of the users.

A technique called double-buffering can be used to solve this problem. A pixmap (i.e., pixel map—an off-screen memory segment used as if it were a part of the screen raster buffer) is used, and all drawing is done off-screen on this pixmap. The pixmap is then transferred to the screen in one lightning-fast operation. This pixmap transfer is normally so fast that on most systems it appears instantaneous to the human eye.

Sometimes a pixmap the size of the widget to update is used, in other cases, only certain parts of the widget are double-buffered. Which method will be most effective must be considered in each case.

Pixmaps often contain large amounts of data and are often slow to create and handle (in CPU-time). A good technique is to store the buffer pixmap as a part of the widget. When the widget needs to update itself (in Qt, whenever it receives a paint event), it simply copies the required part of the buffer pixmap to the part of the widget that must be repainted.

Often, it is useful to include a dirty flag as a part of the widget. All state changes (i.e., changes to member variables) that affect the visual appearance of the widget can then simply set this flag to TRUE telling the widget to repaint itself. The paint event function then checks the dirty flag, and updates the buffer pixmap before it updates the screen. This ensures that all widget painting code is in one place, making the widget easier to maintain and debug.

I've found this technique to be very useful and powerful and have used it on a variety of GUI-systems, as well as Qt.

Making Your Own Software Components

OK, now that we've looked at different parts of Qt, let's use it to build a custom-made software component that can display an image and rotate it by an angle. This widget should contain slots with instructions to let the user choose a file on disk, and it should have the ability to print the rotated image on a printer.

As a start we decide to give it the following signals and slots:

public slots:
    void  setAngle( int degrees );
    void  load();
    void  print();
signals:
    void  angleChanged( int );
    void  filePathChanged( const char * );

We can now set the rotation angle setAngle, let the user choose a new image file load, and print the image print. We choose to implement the functionality we need for the first version first. Later this component can be expanded to include slots like setPixmap(QPixmap) or setFilePath(QString).

The two signals tell the world about a change in the rotation angle (angleChanged) or the image file being displayed (filePathChanged).

Next, we include two member functions to fetch the angle and file path:

public:
    int   angle()          const { return ang;  }
    const char *filePath() const { return name; }

And we include the following member variables:

private:
    int         ang;
    QString     name;
    QPixmap     pix;
    QPrinter    printer;
    QFileDialog fileDlg;
    QPixmap     bufferPix;
    bool        dirty;

By setting these variables, we store an angle, file name, pixmap, printer and file selection dialog in the component. We want the widget to update itself smoothly and have decided to use the double-buffering technique, so we store a buffer pixmap. In addition, we have a dirty flag which is set when the widget needs to update the buffer pixmap. The combination of a buffer pixmap and a dirty flag is a very useful and powerful GUI technique that can be used in a large range of widgets.

Also, the widget has a paint event function and a resize event function, see code Listing 3 for the full class declaration.

Since we want to be able to paint to both the screen and a printer, we put the drawing code in a private member function that operates on a QPainter:

void PixmapRotator::paintRotatedPixmap(QPainter *p)
{
// need device width and height
    QPaintDeviceMetrics m( p->device() );
// center point
    p-$gt;translate( (m.width())/2,
                (m.height()) / 2 );
    p->rotate( ang );
    p->drawPixmap( - (pix.width())/2,
                - (pix.height())/2, pix );
}

First we fetch the metrics of the device the painter is operating on. We use the width and height of the device to put the center (0,0) of our coordinate system in the middle of the device. Next we rotate the coordinate system by the wanted angle and draw the pixmap with its center point at (0,0). In other words, the center point of the pixmap is put at the center point of the device.

The paint event function looks like this:

void PixmapRotator::paintEvent( QPaintEvent *e )
{
    if ( dirty ) {      // buffer needs update?
// same size as widget
        bufferPix.resize( size() );
// clear pixmap
        bufferPix.fill( backgroundColor() );
        QPainter p;
// paint on buffer pixmap
        p.begin( &bufferPix );
        paintRotatedPixmap( &p );
        p.end();
        dirty = FALSE;  // buffer now new and clean
    }                   // update exposed region:
    bitBlt( this, e->rect().topLeft(), &bufferPix,
                 e->rect() );
}

If the widget is “dirty”, we need to update the buffer pixmap. We set its size to the size of the widget, clear it and call our local painting function. Don't forget to reset the dirty flag when a buffer pixmap has been updated.

Finally, we use the QPaintEvent pointer to find out which part of the widget must be updated and call bitBlt. bitBlt is a global function that can transfer data from one paint device to another as fast as possible. bitBlt is common GUI shorthand for “bit block transfer”.

With double-buffering and a dirty flag, the resize event function becomes trivial:

void PixmapRotator::resizeEvent( QResizeEvent *e )
{
    dirty = TRUE;               // need to redraw
}

Again, it is never necessary to repaint a widget in a resize event, since Qt automatically sends a paint event after the resize event.

With a common drawing function doing printing is also easy:

void PixmapRotator::print()
{
// opens printer dialog
    if ( printer.setup(this) ) {
        QPainter p;
        p.begin( &printer );    // paint on printer
        paintRotatedPixmap( &p );
        p.end();                // send job to printer
    }
}

First we let the user setup the printer, then we open a painter on that printer, and finally, call the drawing function.

Loading a new image takes a bit more code:

void PixmapRotator::load()
{
    QString newFile;
    QPixmap tmpPix;
    while ( TRUE ) {
// open file dialog
        if ( fileDlg.exec() != QDialog::Accepted )
            return;     // the user clicked cancel
// get the file path
        newFile = fileDlg.selectedFile();
// is it an image?
        if ( tmpPix.load( newFile ) )
            break;      // yes, break the loop
        QString s;      // build a message string
        s.sprintf("Could not load \"%s\"",
                newFile.data() );
// sorry!
        QMessageBox::message( "Error", s );
    }
    pix   = tmpPix;     // keep the pixmap
    name  = newFile;    // new file name
    emit filePathChanged( name );  // tell world
    dirty = TRUE;       // need to redraw
    repaint( FALSE );   // paint the whole widget
}

We set up a loop that opens the file dialog box. If the user has selected a file that cannot be loaded, we tell her and let her try again. If a valid file has been selected, we copy the new pixmap and the file path. Finally, we emit a signal to tell the world, mark the widget as dirty and repaint all of it (the FALSE argument means that Qt should not clear the widget before sending the paint event).

We have now made a new software component which can be connected to others through the signal/slot mechanism. See www.troll.no/qt for the full code of the PixmapRotator widget.

______________________

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

GUI deveopment using QT

Anonymous's picture

i wanted to know as to how a GUI can be developed using QT with C++ program give an example .

linux

sowmya's picture

sir,
i am troubling about qt.i am begineer of linux.In my project i am doing in linux.for gui designing we use qt design.i not very well about qt:gui design and where we write code in qt,and how to complie in c++ coding.i am using fedora.pls give me steps for designing and coding... i am waiting..

Webinar
One Click, Universal Protection: Implementing Centralized Security Policies on Linux Systems

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Learn More

Sponsored by Bit9

Webinar
Linux Backup and Recovery Webinar

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.

Learn More

Sponsored by Storix