Qt Tutorial - Chapter 5

# Chapter 5: Building Blocks

This example shows how to create and connect together several widgets using signals and slots, and how to handle resize events.

    /****************************************************************
    **
    ** Qt tutorial 5
    **
    ****************************************************************/

    #include <qapplication.h>
    #include <qpushbutton.h>
    #include <qscrollbar.h>
    #include <qlcdnumber.h>
    #include <qfont.h>

    class MyWidget : public QWidget
    {
    public:
        MyWidget( QWidget *parent=0, const char *name=0 );
    protected:
        void resizeEvent( QResizeEvent * );
    private:
        QPushButton *quit;
        QScrollBar  *sBar;
        QLCDNumber  *lcd;
    };

    MyWidget::MyWidget( QWidget *parent, const char *name )
            : QWidget( parent, name )
    {
        setMinimumSize( 200, 200 );

        quit = new QPushButton( "Quit", this, "quit" );
        quit->setGeometry( 10, 10, 75, 30 );
        quit->setFont( QFont( "Times", 18, QFont::Bold ) );

        connect( quit, SIGNAL(clicked()), qApp, SLOT(quit()) );

        lcd  = new QLCDNumber( 2, this, "lcd" );
        lcd->move( 10, quit->y() + quit->height() + 10 );

        sBar = new QScrollBar( 0, 99,                       // range
                               1, 10,                       // line/page steps
                               0,                           // inital value
                               QScrollBar::Horizontal,      // orientation
                               this, "scrollbar" );

        connect( sBar, SIGNAL(valueChanged(int)), lcd, SLOT(display(int)) );
    }

    void MyWidget::resizeEvent( QResizeEvent * )
    {
        sBar->setGeometry( 10, height() - 10 - 16, width() - 20, 16 );
        lcd->resize( sBar->width(), sBar->y() - lcd->y() - 5 );
    }

    int main( int argc, char **argv )
    {
        QApplication a( argc, argv );

        MyWidget w;
        w.setGeometry( 100, 100, 200, 200 );
        a.setMainWidget( &w );
        w.show();
        return a.exec();
    }

## Line by Line Walk-Through

        #include <qapplication.h>
        #include <qpushbutton.h>
        #include <qscrollbar.h>
        #include <qlcdnumber.h>
        #include <qfont.h>

Two new include files here, qscrollbar.h and qlcdnumber.h, because we use two new classes; QScrollBar and QLCDNumber.

        class MyWidget : public QWidget
        {
        public:
            MyWidget( QWidget *parent=0, const char *name=0 );

Nothing new compared to the previous chapter.

        protected:
            void resizeEvent( QResizeEvent * );

MyWidget now gains the ability to handle resize event.

resizeEvent() is one of many virtual functions in QWidget which are used to pass events of various kinds to widgets. These event functions are one of the the central control flow mechanisms in Qt, the other is the signal/slot mechanism.

This one is called, unsurprisingly, whenever the user or a part of the program resizes the widget.

        private:
            QPushButton *quit;
            QScrollBar  *sBar;
            QLCDNumber  *lcd;
        };

In chapter three, MyWidget didn't remember its child widgets. Now it does (so it can resize them).

        MyWidget::MyWidget( QWidget *parent, const char *name )
                : QWidget( parent, name )
        {
            setMinimumSize( 200, 200 );

Since we want MyWidget to handle all sizes greater than 200 by 200 pixels, we only set a minimum size.

            lcd  = new QLCDNumber( 2, this, "lcd" );
            lcd->move( 10, quit->y() + quit->height() + 10 );

`lcd` is a QLCDNumber, a widget which displays numbers in an LCD-like fashion. This instance is set up to display two digits, be a child of this and is named "lcd".

It is located at x position 10 and a y position giving 10 pixels between the quit button and the LCD number. It is a good idea to take a look at The Coordinate System. Note that its size is not set here, resizeEvent() will do that when MyWidget's own size is set.

Hardcoding positions like this is tedious when you have many widgets, but Qt does not include a nice designer yet. Stay tuned.

            sBar = new QScrollBar( 0, 99,                       // range
                                   1, 10,                       // line/page steps
                                   0,                           // inital value
                                   QScrollBar::Horizontal,      // orientation
                                   this, "scrollbar" );

QScrollBar is a classical window system scroll bar. This is one of several possible constructors, there are other constructors that take fewer arguments but then we'd have to set up the same state using several function calls.

We don't set up the scroll bar's geometry, the resize event will handle it.

            connect( sBar, SIGNAL(valueChanged(int)), lcd, SLOT(display(int)) );

Here we use the signal/slot mechanism to connect the scroll bar's valueChanged() signal to the LCD number's display() slot.

Whenever the scroll bar's value changes, it broadcasts the new value by emitting the valueChanged() signal. Since that signal is connected to the LCD number's display() slot, the slot is called when the signal is broadcast. Neither of the objects know about the other. This is essential in component programming.

Slots are otherwise normal C++ member functions and follow the normal C++ access rules.

        void MyWidget::resizeEvent( QResizeEvent * )
        {
            sBar->setGeometry( 10, height() - 10 - 16, width() - 20, 16 );
            lcd->resize( sBar->width(), sBar->y() - lcd->y() - 5 );
        }

Here we want to fit the child widgets nicely inside our widget when it is resized. We want the LCD number to be above the scroll bar. First we position the scroll bar at the bottom of MyWidget.

The scroll bar's geometry is set so there is a 10-pixel border to the left, right and below it, and its height is fixed at 16 pixels, which is recommended by most style guides.

Remember that the order of arguments to setGeometry() is left, top, width, height.

The x position is easy; 10 pixels from the left edge. The y position is calculated from the total height of the widget. We subtract 10 pixels for the border and 16 pixels for the scroll bar's height. The width of the scroll bar should be the entire width of MyWidget less a 10 pixel borders on each side. As mentioned above, we give the scroll bar a height of 16 pixels.

Finally, the LCD number is resized. The width is easy. We give the same width as the scroll bar. The height is the distance from the top of the scroll bar to the top of the LCD number less a 5 pixel border. Remember from the constructor that we have positioned the LCD number below the quit button.

Auto-placement of widgets like we do here is tedious. Qt includes geometry management classes that make this job easier. The classes are QBoxLayout and QGridLayout. Later versions of this tutorial will include chapters on geometry management. Stay tuned!

## Behavior

The LCD number reflects everything you do to the scroll bar, and the widget handles resizing well.

## Excercises

Try changing the LCD number to add more digits or change mode. You can even add four push buttons to set mode.

You can also change the scroll bar's range.

Try to make the application quit when the LCD number overflows.

You may now go on to chapter six.

[Previous tutorial] [Next tutorial] [Main tutorial page]