|
Komunikaty systemowe i funkcje wirtualne:
Tą cześć kursu zaczniemy od uporządkowania i podzielenia naszego kodu źródłowego. Definicje klas umieścimy w specjalnie stworzonych plikach nagłówkowych, a całą operację tworzenia okna wrzucimy do konstruktora naszej klasy aplikacji.
Definicje klasy naszego okna wrzucimy do pliku PierwszeOkno.h , definicje klasy aplikacji do PierwszaApp.h . Wszystkie definicje funkcji okna będziemy zapisywać w pliku PierwszeOkno.cpp . Funkcja main() i funkcje aplikacji będą znajdować się w pliku PierwszaApp.cpp .
Przed rozpoczęciem modularyzacji kodu warto, wtrącić małe przypomnienie :
W plikach nagłówkowych korzystamy z makro definicji preprocesora aby zapobiec wielokrotnemu włączeniu tego samego kodu.
Algorytm sprawdzający czy dany kod został już włączony jest bardzo prosty. Najpierw sprawdzamy czy istnieje jakieś makro za pomocą dyrektywy #ifndef jeżeli nie to definiujemy je za pomocą dyrektywy #define i dodajemy nasze definicje, deklaracje. Jeżeli preprocesor napotka nasz plik nagłówkowy drugi raz dyrektywa #ifndef nie pozwoli dołączyć wszystkiego co znajduje się między nią a #endif czyli naszych deklaracji. Więcej na temat modularyzacji kodu napisałem tutaj [Link].
A więc zacznijmy od pliku nagłówkowego naszego okna :
Plik nagłówkowy PierwszeOkno.h |
//Plik nagłówkowy definiujący klasę okna. PierwszeOkno.h
#ifndef _PIERWSZE_OKNO_H_
#define _PIERWSZE_OKNO_H_
#include<Window.h>
extern BRect obszarokna;
class PierwszeOkno : public BWindow
{
public :
PierwszeOkno():BWindow(obszarokna,"Pierwsze okno",B_TITLED_WINDOW,0,B_CURRENT_WORKSPACE)
{
}
};
#endif
|
Plik źródłowy naszego okna będzie narazie bardzo krótki i będzie jedynie definiował obiekt obszarokna :
Plik źródłowy PierwszeOkno.cpp |
//Plik źródłowy PierwszeOkno.cpp
#include"PierwszeOkno.h"
BRect obszarokna(20,20,300,200);
|
Klasę okna mamy już zdefiniowaną, czas na klasę aplikacji, tutaj będzie trochę więcej zmian :
Plik nagłówkowy PierwszaApp.h |
//Plik nagłówkowy definiujący klasę aplikacji PierwszaApp.h
#ifndef _PIERWSZA_APP_H_
#define _PIERWSZA_APP_H_
#include <Application.h>
#include"PierwszeOkno.h"
class PierwszaApp : public BApplication
{
public :
PierwszaApp();
PierwszeOkno * POkno; //wskaźnik do naszego okna będzie należał do
//aplikacji
};
|
Ostatnim plikiem jest PierwszaApp.cpp :
Plik źródłowy PierwszaApp.cpp |
#include"PierwszaApp.h"
int main()
{
new PierwszaApp();
be_app->Run();
delete be_app;
}
PierwszaApp::PierwszaApp():BApplication ("application/pierwszaapp_z_oknem")
{
POkno = new PierwszeOkno;
POkno->Show();
}
|
Teraz nasz projekt Pierwsze okno, konstrukcją kodu bardziej już przypomina aplikację BeOSa, oraz będzie o wiele łatwiejszy do rozbudowy.
Oczywiście zmiany w podziale kodu nie wpłynęły na działanie naszego programu i nadal zamknięcie okna nie powoduje zakończenia działania aplikacji.
Aby zmienić zachowanie naszej aplikacji zależnie od zdarzeń generowanych przez użytkownika (takich jak np. zamknięcie okna) musimy poznać mechanizm komunikatów systemowych BeOSa.
Jakiekolwiek działania użytkownika na naszą aplikację powodują wysyłanie komunikatów do naszej aplikacji. Komunikaty te są obsługiwane przez specjalne funkcje tzw hook functions.
Funkcje te dzielimy na trzy rodzaje:
- fukcje w pełni zaimpelnetowane przez system, najczęściej przytaczanym przykładem jest kliknięcie w przycisk maksymalizuj (zoom) na pasku tytułowym.
- funkcje zainplementowane, ale których obsługa w szczególnych wypadkach może nie być wystarczająca,wtedy w nadpisywanej funkcji wywołuje się także standardową.
- funkcje wogóle nie zaimplementowane, które muszą być w pełni implementowane przez programistę.
Hook functions są zadeklarowane jako wirtualne, możemy je więc nadpisywać we własnych klasach utworzonych z wykorzystaniem dziedziczenia.
Zanim zaczniemy przyglądać się poszczególnym funkcjom obsługi zdarzeń, musimy jeszcze dowiedzieć się jak zaimplementowano obsługę zdarzeń w BeOSie.
Otóż każde okno i obiekt aplikacji posiadają własną pętlę obsługi zdarzeń.
Na obsługę zdarzeń składają się 2 klasy BHandler i BLooper będące w relacji :
Sama klasa BHandler implementuje jedynie sposób obsługi komunikatu, nie posiada pętli komunikatów. Dopiero klasa BLooper posiada tą pętlę i kolejkę komunikatów, a jako że dziedziczy z BHandler potrafi też obsługiwać komunikaty.
Innymi słowy klasa BHandler potrafi jedynie odpowiadać na komunikaty, nie potrafi ich jednak wyłapywać. Dopiero klasa BLooper potrafi wyłapywać komunikaty i dzieki temu że jest oparta na BHandler potrafi je także obsłużyć. Idąc dalej widzimy że klasy BWindow i BApplication dziedziczą z BLooper więc posiadają własne pętle obsługi zdarzeń i kolejki komunikatów.
Wróćmy teraz do naszego programu. Chcieliśmy aby nasza aplikacja kończyła swoje działanie wraz z zamknięciem okna. W momencie zamknięcia okna wysyłany jest komunikat systemowy B_QUIT_REQUESTED , który obsługiwany jest przez funkcję QuitRequested() . Czyli w momencie kliknięcia w przycisk zamykający uruchamiana jest funkcja obsługi komunikatów QuitRequested() . Jeżeli funkcja ta zwróci wartość true okno jest zamykane, w przeciwnym wypadku kontynuuje działanie.
Standardowo funkcja ta zwraca wartość true i okno jest zamykane, możemy ją jednak przeciążyć aby wykonywała jeszcze inne operacje np. zamykała aplikację lub pytała czy napewno zamknąć okno.
Funkcja QuitRequested() dziedziczona jest z klasy BLooper , więc posiada ją każda klasa wywodząca się z BLooper (np. BApplication ,BWindow ). Aby zamknąć całą aplikacje wystarczy wysłać komunikat B_QUIT_REQUESTED do obiektu aplikacji.
Funkcją służącą do wysyłania komunikatów do własnej pętli obsługi zdarzeń jest PostMessage() której jedynym argumentem jest stała komunikatu (np. B_QUIT_REQUESTED ).
Podsumowując aby nasza aplikacja kończyła działanie po zamknięciu okna, musimy przeciążyć funkcję QuitReqested() naszego okna w ten sposób aby wysłała ona do obiektu naszej aplikacji komunikat B_QUIT_REQUESTED i zwracała wartość true .
Na początku do klasy naszego okna (plik PierwszeOkno.h ) dodajemy prototyp funkcji QuitRequested() :
virtual bool QuitRequested();
Następnie definiujemy naszą funkcję QuitReqested() w pliku PierwszeOkno.cpp :
bool PierwszeOkno::QuitRequested()
{
be_app->PostMessage(B_QUIT_REQUESTED);
return(true);
}
Po wprowadzeniu tych zmian nasza aplikacja kończy działanie zaraz po zamknięciu okna.
Zmienione pliki w całości wyglądają teraz tak :
PierwszeOkno.h : |
//Plik nagłówkowy definiujący klasę okna PierwszeOkno.h
#ifndef _PIERWSZE_OKNO_H_
#define _PIERWSZE_OKNO_H_
#include<Window.h>
extern BRect obszarokna;
class PierwszeOkno : public BWindow
{
public :
PierwszeOkno():BWindow(obszarokna,"Pierwsze okno",B_TITLED_WINDOW,0,B_CURRENT_WORKSPACE)
{
}
virtual bool QuitRequested();
};
#endif
|
PierwszeOkno.cpp : |
#include"PierwszeOkno.h"
#include"PierwszaApp.h"
BRect obszarokna(20,20,300,200);
bool PierwszeOkno::QuitRequested()
{
be_app->PostMessage(B_QUIT_REQUESTED);
return(true);
}
|
Klasy BApplication i BWindow posiadają po kilkanaście funkcji obsługi komunikatów (hook functions), najważniejsze z nich opisałem pokrótce poniżej. Pełny opis wraz z argumentami i zwracanymi wartościami dostępny jest w podręczniku programisty "BeBook", który można ściągnąć np. z bebits.com.
Najważniejsze funkcje obsługi zdarzeń klasy BApplication : |
ArgvReceived() | funkcja obsługuje komunikat systemowy B_ARGV_RECEIVED ; jest wywoływana jedynie gdy zostały podane jakieś parametry linii poleceń, przyjmuje dwa argumenty argc - mówiący o ilości argumentów i argv - wskaźnik do tablicy zawierającej te parametry |
ReadyToRun() | funkcja jest wywoływana tuż przed uruchomieniem pętli obsługi zdarzeń |
AppActivated() | funkcja jest wywoływana gdy aplikacja jest aktywowana lub przestaje być aktywna |
Pulse() | funkcja jest wywoływana gdy aplikacja otrzymuje komunikat systemowy B_PULSE . Jest to komunikat wysyłany do aplikacji co pewien określony czas. Czas ten określamy za pomocą funkcji SetPulseRate() |
AboutRequested() | jest uruchomiana gdy aplikacja otrzyma komunikat B_ABOUT_REQUESTED , w tej funkcji powinny być zawarte operacje wyświetlające okienko z informacją o programie |
Najważniejsze funkcje obsługi zdarzeń klasy BWindow : |
FrameMoved() | funkcja wywoływana gdy zostanie zmienione położenie okna |
FrameResized() | funkcja wywoływana gdy zostanie zmieniona wielkość okna |
ScreenChanged() | funkcja wywoływana gdy zmieni się rozdzielczość lub głębia kolorów pulpitu na którym znajduje się okno |
WindowActivated() | funkcja wywoływana gdy okno zostaje uaktywnione lub przestaje być aktywne |
WorkspaceActivated() | funkcja wywoływana gdy zmieni się aktualnie wyświetlany na ekranie pulpit |
Program przykładowy można pobrać stąd [Link].
Powrót
|