
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
|