Qt logo


Kapitola 7: Jedna vec vedie k druhej


Screenshot of tutoriálu č. seven

Tento príklad ukazuje ako vytvoriť vlastné widgety so signálmi a slotmi a ako ich prepojiť komplexnejším spôsobom. Prvýkrát je zdrojový text programu umiestnený vo viacerých súboroch.

Prechádzka riadok po riadku

lcdrange.h

Tento súbor je zväčša prevzatý z main.cpp z kapitoly 6 a sú v ňom len malé zmeny.

    #ifndef LCDRANGE_H
    #define LCDRANGE_H

Toto je klasická C-čková konštrukcia na zabránenie chybám v prípade, že sa hlavičkový súbor vkladá viac než raz. Ak ste to ešte nepoužili, zapamätajte si to - je to veľmi dobrá cesta ako tvoriť hlavičkové súbory. Direktíva #ifndef by mala uzatvárať obsah celého hlavičkového súboru.

    #include <qwidget.h>
    class QScrollBar;
    class QLCDNumber;

Je vložený qwidget.h. LCDRange je potomkom QWidget, a hlavičkový súbor triedy predka musí byť vložený. Doteraz bol qwidget.h vždy vložený nepriamo cez iné hlavičkové súbory ako napríklad qpushbutton.h.

Pretože deklarácia tried používa iba smerníky na QScrollBar a QLCDNumber a nepotrebujeme ich definície, deklarujeme iba ich mená na začiatku hlavičkového súboru. Trošku to uľahčí prácu kompilátoru.

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

Všimnite si Q_OBJECT. Toto makro musia obsahovať všetky triedy, ktoré obsahujú signály a sloty. Pre zaujímavosť - toto makro definuje funkcie implementované v súbore meta objektov.

        int value() const;
    public slots:
        void setValue( int );
    signals:
        void valueChanged( int );

Títo traja členovia vytvárajú interface medzi týmto widgetom a ostatnými komponentmi programu. Doteraz LCDRange nemal prakticky žiadny interface.

Funkcia value() je verejná funkcia na prístup k hodnote LCDRange. Funkcia setValue() je náš prvý vlastný slot a funkcia valueChanged() je náš prvý vlastný signál.

Sloty musia byť implementované normálnou cestou (pamätajte, že slot je aj C++ členskou funkciou). Signály su automaticky implementované v súbore meta objektov. Signály sa riadia prístupovými právami chránených (protected) C++ funkcií, t.j. môžu byť vysielané iba triedami, v ktorých sú definované, alebo ich potomkami.

lcdrange.cpp

Tento súbor je zväčša prevzatý z t6/main.cpp a sú v ňom len malé zmeny.

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

Tento kód je z konštruktoru LCDRange.

Prvý connect() je ten istý, ktorý sme videli v predchádzajúcej kapitole. Druhý je nový: spája signál rolovacej lišty valueChanged() so signálom valueChanged() tohoto objektu. Funkcia connect() s tromi argumentami vždy pripája signál na signál alebo slot objektu this.

Áno, je to tak správne. Signály môžu byť napojené na signály. Keď je vyslaný prvý, je vyslaný aj druhý.

Pozrime sa, čo sa stane, keď užívateľ posunie rolovaciu lištu: lišta zistí, že sa zmenila jej hodnota a vyšle signál valueChanged(). Ten je pripojený aj na slot display() patriaci QLCDNumber aj na signál valueChanged() patriaci LCDRange.

Takže keď je signál vyslaný, LCDRange vyšle svoj vlastný signál valueChanged() a naviac je zavolaný slot QLCDNumber::display(), ktorý zobrazí nové číslo.

Pozor na to, že neexistuje žiadne zaručené poradie vykonania týchto krokov.

    int LCDRange::value() const
    {
        return sBar->value();
    }

Implementácia funkcie value() je veľmi priama, jednoducho vráti hodnotu rolovacej lišty.

    void LCDRange::setValue( int value )
    {
        sBar->setValue( value );
    }

Implementácia setValue() je podobne priama. Všimnite si, že vďaka tomu, že rolovacia lišta a LCD číslo sú spojené, nastavenie hodnoty lišty automaticky vedie k zobrazeniu správnej hodnoty na LCD paneli. Navyše rolovacia lišta automaticky prispôsobí hodnotu, ak je táto mimo platného rozsahu lišty.

main.cpp

        for( int i = 0 ; i < 16 ; i++ ) {
            value[i] = new LCDRange( this );
            if ( i > 0 )
                connect( value[i], SIGNAL(valueChanged(int)), 
                         value[i - 1], SLOT(setValue(int)) );
        }

Odhliadnúc od presunutia LCDRange do dvoch osobitných súborov, tieto riadky sú jediné zmeny v súbore main.cpp. Ako v predchádzajúcej kapitole vytvoríme 16 objektov triedy LCDRange. Navyše ich teraz spojíme dohromady pomocou mechanizmu signálov a slotov. Každý jeden má vlastný signál valueChanged() spojený so slotom setValue() predchádzajúceho. Keďže LCDRange vysiela signál valueChanged() keď sa zmení jeho hodnota (ake prekvapenie!), automaticky sme vytvorili "reťaz" signálov a slotov.

Správanie

Na začiatku je vzhľad programu rovnaký ako v predchádzajúcej kapitole. Skúste posunúť lištu vpravo dole...

Cvičenia

Pomocou lišty vpravo dole nastavte všetky LCD displeje na 30. Potom nastavte hornú polovicu na 29 pomocou krajnej pravej lišty v druhom riadku. Teraz použite lištu vľavo od nej na nastavenie prvých siedmych displejov späť na 30. Stlačte ľavú šípku pravej dolnej rolovacej lišty. Čo sa stalo? Prečo je to korektné správanie?

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

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


Copyright © 1998 Troll TechTrademarks
Qt version 1.42