Qt logo


Kapitola 6: Mnoho stavebných blokov


Screenshot of tutoriálu č. six

Tento príklad ukazuje ako zapuzdriť dva widgety do nového komponentu a ako sa dá ľahko použiť veľa widgetov. Po prvý krát použijeme vlastný widget ako dcérsky widget.

/****************************************************************
**
** Qt tutorial 6
**
****************************************************************/

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

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

LCDRange::LCDRange( QWidget *parent, const char *name )
        : QWidget( parent, name )
{
    lcd  = new QLCDNumber( 2, this, "lcd"  );
    lcd->move( 0, 0 );
    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 LCDRange::resizeEvent( QResizeEvent * )
{
    sBar->setGeometry( 0, height() - 16, width(), 16 );
    lcd->resize( width(), sBar->y() - 5 );
}

class MyWidget : public QWidget
{
public:
    MyWidget( QWidget *parent=0, const char *name=0 );
protected:
    void resizeEvent( QResizeEvent * );
private:
    QPushButton *quit;
    LCDRange *value[16];
};

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

    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()) );

    for( int i = 0 ; i < 16 ; i++ )
        value[i] = new LCDRange( this );
}

void MyWidget::resizeEvent( QResizeEvent * )
{
    int startx      = 10;
    int starty      = quit->y() + quit->height() + 10;
    int valueWidth  = (width() - startx - 10 - 3*5)/4;
    int valueHeight = (height() - starty - 10 - 3*5)/4;
    for( int i = 0 ; i < 16 ; i++ )
        value[i]->setGeometry( startx + (i%4)*(5+valueWidth),
                               starty + (i/4)*(5+valueHeight),
                               valueWidth, valueHeight );
}

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

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

Prechádzka riadok po riadku

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

Widget LCDRange je zapuzdrením QScrollBar a QLCDNumber, spojených dohromady.

    LCDRange::LCDRange( QWidget *parent, const char *name )
            : QWidget( parent, name )
    {
        lcd  = new QLCDNumber( 2, this, "lcd"  );
        lcd->move( 0, 0 );
        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)) );
    }

Toto je v zásade prevzaté priamo z konštruktoru MyWidget-u v kapitole päť. Jediný rozdiel je, že lcd je teraz umiestnený na pozícii 0,0 (ľavý horný roh widgetu LCDRange), keďže vytvárame nový widget a nepotrebujeme okraj. Ak užívateľ widgetu chce okraj, môže ho vytvoriť okolo LCDRange.

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

Vďaka tomu, že LCDRange nemá okraj ani quit tlačidlo, je zmena veľkosti ešte jednoduchšia než tá v MyWidget.

    private:
        QPushButton *quit;
        LCDRange *value[16];
    };

MyWidget teraz obsahuje staré známe tlačidlo quit a pole šestnástich ukazateľov na LCDRange.

        for( int i = 0 ; i < 16 ; i++ )
            value[i] = new LCDRange( this );

V konštruktore MyWidget-u vytvoríme 16 inštancií LCDRange, všetky s this ako rodičovským objektom. Pamätajte, že všetky budú zrušené knižnicou Qt keď bude rušený MyWidget.

    void MyWidget::resizeEvent( QResizeEvent * )
    {
        int startx      = 10;
        int starty      = quit->y() + quit->height() + 10;
        int valueWidth  = (width() - startx - 10 - 3*5)/4;
        int valueHeight = (height() - starty - 10 - 3*5)/4;
        for( int i = 0 ; i < 16 ; i++ )
            value[i]->setGeometry( startx + (i%4)*(5+valueWidth),
                                   starty + (i/4)*(5+valueHeight),
                                   valueWidth, valueHeight );
    }

Pri zmene veľkosti musíme prepočítať geometriu všetkých šestnástich objektov LCDRange v jednej slučke.

Najprv vypočítame ľavý horný roh ľavého horného objektu LCDRange. Necháme mu 10 pixelový okraj zľava aj zhora pod tlačidlom quit (viď systém súradníc).

Potom vypočítame veľkosť každého objektu LCDRange. Chceme mriežku 4x4 widgety s 10 pixelovým okrajom vpravo a piatimi pixelmi medzi jednotlivými objektmi LCDRange (t.j. 3 medzery po 5 pixelov v každom smere). Všimnite si, že pretože delíme celé číslo štvorkou, dostaneme malú chybu zo zaokrúhlenia.

Nakoniec nastavíme individuálnu geometriu v jednoduchej slučke. Počítame pozície použijúc celočíslené delenie a zvyšky, pamätajúc na pridanie 5 pixelovej medzery medzi každé dva objekty.

Správanie

Tento program ukazuje aké ľahké je používať veľa widgetov naraz. Každý jeden sa správa ako lišta a LCD číslo v predchádzajúcej kapitole. Rozdiel je opäť v implementácii.

Cvičenia

Zmeňte MyWidget tak, aby bolo isté, že každý LCDRange bude štvorec.

Inicializujte každú rolovaciu lištu inou/náhodnou hodnotou.

Teraz môžete ísť na kapitolu sedem.

[Predchádzajúci tutoriál] [Ďalší tutoriál] [Hlavná stránka tutoriálu]


Copyright © 1998 Troll TechTrademarks
Qt version 1.42