Ten przykład pokazuje ja tworzyć własne widgety z sygnałami i slotami, oraz jak połączyć je razem w bardzo skomplikowane sposoby. Po raz pierwszy podzielimy źródło na kilka plików.
#ifndef LCDRANGE_H #define LCDRANGE_HJest to klasyczna dla C konstrukcja dzięki której unika się błędów gdy plik nagłówkowy zostanie zawarty więcej niż raz. Jeśli nie używasz jej jeszcze: czas zacząć, to dobry zwyczaj. Dyrektywa #ifndef powinna zawierać wszystkie pliki nagłówkowe.
#include <qvbox.h>qvbox.h zostaje dołączone. LCDRange dziedziczy QVBox, a plik nagłówkowy klasy rodzica musi być zawsze zawarty. Troszeczkę oszukiwaliśmy w poprzednich rozdziałach, i pozwaliśmy aby qwidget.h był włączany pośrednio poprzez inne pliki nagłówkowe jak qpushbutton.h.
class QSlider;Jest to jeszcze jeden klasyczny trik, lecz jest znacznie rzadziej używany. Ponieważ nie potrzebujemy QSlider w interfejsie klasy, a tylko w implementacji, używamy poprzedającej deklaracji klasy w pliku nagłówkowym, oraz #include pliku nagłówkowego QSlider w pliku .cpp.
Czyni to kompilację dużych projektów znacznie szybszą, ponieważ kiedy zmienia się plik nagłówkowy mniej plików musi zostac przekompilowanych. Może to nawet dwukrotnie zwiększyć szybkość kompilacji.
class LCDRange : public QVBox { Q_OBJECT public: LCDRange( QWidget *parent=0, const char *name=0 );Zwróć uwagę na Q_OBJECT. To makro musi być zawarte we wszystkich klasach, które zawierają sygnały i/lub sloty. Jeśli jesteś ciekawy definjuje ono impementację funkci z pliku meta objektów.
int value() const; public slots: void setValue( int ); signals: void valueChanged( int );Tych trzech członków tworzą interfejs pomiędzy tym widgetem a innymi komponentami programu. Do tej pory LCDRange tak naprawdę nie posiadał interfejsu.
value() jest funckją publiczną umożliwiającą dostęp do wartości LCDRange. setValue() jest naszym pierwszym własnym slotem, a valueChanged() jest naszym pierwszym własnym sygnałem.
Slots muszą być zaimplementowane w normalny sposób (pamiętaj, slot to także członek funkcji C++). Sygnały są automatycznie implementowane w plikumeta objektów. Signały spełniają prawa dostępu chronioncyh funkcji C++ , tzn. mogą być emitowane jedynie przez klasę w której są zdefiniowane lub przez klasę dziedziczącą z nich.
Sygnał valueChanged() zostaje użyty gdy wartość LCDRange ulegnie zmianie - tak ja to odgadłeś z jego nazwy. I to nie jest ostatni sygnał kóry nazywa się w stylu cośChanged().
connect( slider, SIGNAL(valueChanged(int)), lcd, SLOT(display(int)) ); connect( slider, SIGNAL(valueChanged(int)), SIGNAL(valueChanged(int)) );To jest kod konstruktora LCDRange.
Pierwsze połączenie jest takie samo jak w poprzednim rozdziale. Drugie jest nowe: Łączy sygnał suwaka valueChanged() do sygnału tego objektu valueChanged. connect() z trzema argumentami zawsze łączy do sygnałów lub slotów w tym objekcie.
Tak to prawda. Sygnały mogą być podłączane do innych sygnałów. Kiedy wymeitowany zostanie pierwszy, to i drugi sygnał zostanie wyemitowany.
Spójrzmy co się stanie kiedy użytkownik przsunie suwak: Suwak rozpoznaje, że jego wartość uległa zmianie, więc emituje sygnał valueChanged(). Ten sygnał jest podłączony zarówno do slotu display() z QLCDNumber jak i do slotu valueChanged() z LCDRange.
Tak więc, gdy emitowany jest sygnał, LCDRange emituje swój własny sygnał valueChanged(). Dodatkowo, wywoływany jest QLCDNumber::display() i pokauje nową cyfrę.
Zauważ, iż nie masz gwarancji jakiejkolwiek kolejności wykonania - LCDRange::valueChanged() może zostać wyemitowany przed lub po QLCDNumber::display(), całkowicie arbitralnie.
int LCDRange::value() const { return slider->value(); }Implementacja value() jest bardzo prosta, po prostu zwraca wartość suwaka.
void LCDRange::setValue( int value ) { slider->setValue( value ); }Implementacja setValue() jest równie prosta. Zauważ, iż skoro suwak i wyświetlacz LCD są połaczone, ustawienie wartości suwaka automatycznie uaktualni wskazanie wyświetlaczaLCD number. Dodatkowo, suwak automatycznie ustawi swoją wartość gdyby znalazła się poza przewidzianym zakresem.
LCDRange *previous = 0; for( int r = 0 ; r < 4 ; r++ ) { for( int c = 0 ; c < 4 ; c++ ) { LCDRange* lr = new LCDRange( grid ); if ( previous ) connect( lr, SIGNAL(valueChanged(int)), previous, SLOT(setValue(int)) ); previous = lr; } }Całość main.cpp została skopiowana z poprzedniego rozdziału, za wyjątkiem konstruktora dla MyWidget, gdzie tworzyliśmy 16 objektów LCDRange teraz łaczymy je razem przy użyciu mechanizmowi sygnałów/slotów. Każdy ma swój sygnał valueChanged() podłączony do slotu setValue() w poprzednim. Ponieważ LCDRange emituje sygnał valueChanged() kiedy jego wartość zmienia się (niespodzianka!), tworzymu tu łańcuch "chain" sygnałów i slotów.
Kliknij w lewą część suwaka na dolnym prawym. Co się dzieje ? Dlaczego jest to poprawne zachowanie ?
Możesz teraz przejść do rozdziału ósmego.
[Poprzedni tutorial] [Następny tutorial] [Głowna strona tutoriala]
Copyright (c) 2000 Troll Tech | Znaki towarowe |
Wersja Qt 2.1.0
|