88
1 Projektowanie Graficznych Interfejsów Użytkownika Robert Szmurło

Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

  • Upload
    others

  • View
    0

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

1

Projektowanie Graficznych Interfejsów Użytkownika

Robert Szmurło

Page 2: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

2

Komunikaty...

(...)2068 typedef struct tagMSG2069 {2070 HWND hwnd;2071 UINT message;2072 WPARAM wParam;2073 LPARAM lParam;2074 DWORD time;2075 POINT pt;2076 } MSG, *PMSG, *LPMSG;(...)

Page 3: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

3

Zabawa...

Page 4: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

4

Na początek: programowanie zdarzeniowe● Przetwarzanie wsadowe (sekwencyjne) –

popularne w dla aplikacji na konsolę tekstową, zwłaszcza w środowisku Unix,– komendy, przetwarzanie potokowe,

bogate opcje, słaba użyteczność dla użytkowników początkujących,

● Przetwarzanie zdarzeniowe – wykonywanie zadań na żądanie, udostępnienie punktów startowych zadań,

● A 'vi', a Norton Commander? Programowanie zdarzeniowe jest ogólną koncepcją i nie jest związane wyłącznie z graficznymi interfejsami użytkownika. Niemniej programowanie zdarzeniowe stanowi trzon interfejsów graficznych.

szmurlor@nor:~> cdrecord --helpThis is wodim, not cdrecord. Don't expect it to behave like cdrecord in anyway, don't refer to it as "cdrecord". Send problem reports [email protected], don't bother Joerg Schilling with anyproblems caused by this application.Copyright (C) 2006 cdrkit maintainers, (C) 1994-2006 Joerg Schilling

Usage: wodim [options] track1...tracknOptions: -version print version information and exit dev=target SCSI target to use as CD/DVD-Recorder gracetime=# set the grace time before starting to write to #. timeout=# set the default SCSI command timeout to #. debug=#,-d Set to # or increment misc debug level kdebug=#,kd=# do Kernel debugging -verbose,-v increment general verbose level by one -Verbose,-V increment SCSI command transport verbose level by one -silent,-s do not print status of failed SCSI commands driver=name user supplied driver name, use with extreme care driveropts=opt a comma separated list of driver specific options -setdropts set driver specific options and exit -checkdrive check if a driver for the drive is present -prcap print drive capabilities for MMC compliant drives -inq do an inquiry for the drive and exit -scanbus scan the SCSI and IDE buses and exit -reset reset the SCSI bus with the cdrecorder (if possible) -abort send an abort sequence to the drive (may help if hung) -overburn allow to write more than the official size of a medium -ignsize ignore the known size of a medium (may cause problems) -useinfo use *.inf files to overwrite audio options. speed=# set speed of drive blank=type blank a CD-RW disc (see blank=help) -format format a CD-RW/DVD-RW/DVD+RW disc formattype=# select the format method for DVD+RW disc ts=# set maximum transfer size for a single SCSI command -load load the disk and exit (works only with tray loader) -lock load and lock the disk and exit (works only with tray loader) -eject eject the disk after doing the work -dummy do everything with laser turned off -msinfo retrieve multi-session info for mkisofs >= 1.10 -toc retrieve and print TOC/PMA data -atip retrieve and print ATIP data -multi generate a TOC that allows multi session In this case default track type is CD-ROM XA mode 2 form 1 - 2048 bytes -fix fixate a corrupt or unfixated disk (generate a TOC) -nofix do not fixate disk after writing tracks -waiti wait until input is available before opening SCSI -immed Try to use the SCSI IMMED flag with certain long lasting commands -force force to continue on some errors to allow blanking bad disks -tao Write disk in TAO mode. This option will be replaced in the future. -dao Write disk in SAO mode. This option will be replaced in the future. -sao Write disk in SAO mode. This option will be replaced in the future. -raw Write disk in RAW mode. This option will be replaced in the future. -raw96r Write disk in RAW/RAW96R mode. This option will be replaced in the future. -raw96p Write disk in RAW/RAW96P mode. This option will be replaced in the future. -raw16 Write disk in RAW/RAW16 mode. This option will be replaced in the future. -clone Write disk in clone write mode. tsize=# Length of valid data in next track padsize=# Amount of padding for next track pregap=# Amount of pre-gap sectors before next track defpregap=# Amount of pre-gap sectors for all but track #1 mcn=text Set the media catalog number for this CD to 'text' isrc=text Set the ISRC number for the next track to 'text' index=list Set the index list for the next track to 'list' -text Write CD-Text from information from *.inf or *.cue files textfile=name Set the file with CD-Text data to 'name' cuefile=name Set the file with CDRWIN CUE data to 'name' -audio Subsequent tracks are CD-DA audio tracks -data Subsequent tracks are CD-ROM data mode 1 - 2048 bytes (default) -mode2 Subsequent tracks are CD-ROM data mode 2 - 2336 bytes -xa Subsequent tracks are CD-ROM XA mode 2 form 1 - 2048 bytes -xa1 Subsequent tracks are CD-ROM XA mode 2 form 1 - 2056 bytes -xa2 Subsequent tracks are CD-ROM XA mode 2 form 2 - 2324 bytes -xamix Subsequent tracks are CD-ROM XA mode 2 form 1/2 - 2332 bytes -cdi Subsequent tracks are CDI tracks -isosize Use iso9660 file system size for next data track -preemp Audio tracks are mastered with 50/15 s preemphasis -nopreemp Audio tracks are mastered with no preemphasis (default) -copy Audio tracks have unlimited copy permission -nocopy Audio tracks may only be copied once for personal use (default) -scms Audio tracks will not have any copy permission at all -pad Pad data tracks with 15 zeroed sectors Pad audio tracks to a multiple of 2352 bytes -nopad Do not pad data tracks (default) -shorttrack Subsequent tracks may be non Red Book < 4 seconds if in SAO or RAW mode -noshorttrack Subsequent tracks must be >= 4 seconds -swab Audio data source is byte-swapped (little-endian/Intel)The type of the first track is used for the toc type.Currently only form 1 tracks are supported.szmurlor@nor:~>

Page 5: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

5

Sekwencyjne kontra ZdarzenioweOprogramowanie sekwencyjne:

● Program wykonuje pewną czynność a następnie użytkownik odpowiada,● Program kontroluje użytkownika.

Oprogramowanie sterowane zdarzeniowo● Powszechne dla systemów okienkowych (graficznych i tekstowych)● Użytkownik wykonuje pewną czynność (np. wprowadza dane), a następnie program

odpowiada,● Użytkownik może wydawać komendy w dowolnym momencie,● Użytkownik kontroluje program,● Rzeczywista kontrola systemu operacyjnego (zarządza komunikatami przesyłanymi między

aplikacjami)● Model zdecydowanie zalecany w przypadku aplikacji

wymagającej znacznej interakcji z użytkownikiem,

Page 6: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

6

KomunikacjaKomunikaty stanowią trzon działania graficznych interfejsów użytkownika.

– Zaleta: Asynchroniczna obsługa, filtracja, możliwość rozgłaszania.– Komunikaty są w swojej zasadzie działania podobne do działania pakietów sieci

komputerowej. (Dlatego np. System graficzny X Window jest oparty na sieci komputerowej.)

– Komunikaty służą do komunikacji między kontrolkami na ekranie, aplikacjami,– Między kontrolkami i formularzem lub pewnym obiektem kontrolującym, czyli są

wykorzystywane przez aplikację, (ale tylko w Microsoft Windows)● Uruchomienie: exec, click, start,● Uzyskanie kontroli (focus, onEnter itp)● Interakcja z klawiaturą i myszką● Wystąpienie błędu● Zmiana wartości (przez użytkownika)● Komunikaty o tworzeniu kontrolki, usuwaniu, pokazywaniu, chowaniu● Zmiana rozmiaru, uaktualnienie widoku● Stoper (Timer)● Pojawienie się nowego urządzenia, itp...

Problem z czegoskłada się komunikat...

Page 7: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

7

Obsługa komunikatów w Microsoft Windows

Kolejka Komunikatów

(Message Queue)

Pętla obsługi Komunikatów(Message Loop)

Procedura obsługiOkna

(Window Procedure)

GetMessage()

DispatchMessage()DefWindowProc()

Zewnętrzne komunikaty

Windows

Wątek aplikacji

Klawiatura, mysz, porty, inne aplikacje

Page 8: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

8

Pętla obsługi komunikatów

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg; while (GetMessage(&msg, NULL, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }

Kolejka Komunikatów

(Message Queue)

Pętla obsługi Komunikatów

(Message Loop)

Procedura obsługiOkna

(Window Procedure)

GetMessage()DispatchMessage()

DefWindowProc()

Zewnętrzne komunikaty

Windows

Wątek aplikacji

Klawiatura, mysz, porty, inne aplikacje

Page 9: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

9

Procedura obsługi oknaLRESULT WindowProc(HWND hwnd, UINT msg,

WPARAM wp, LPARAM lp) { if (msg == WM_COMMAND) { if (OnCommand(wp, lp)) return 1L; // command handled else return DefWindowProc(msg, wp, lp); } // special case for notifies if (msg = = WM_NOTIFY) { LRESULT lResult = 0; NMHDR* pNMHDR = (NMHDR*)lp; if (pNMHDR->hwndFrom != NULL && OnNotify(wp, lp, &lResult)) return lResult; // command handled else return DefWindowProc(msg, wp, lp); } • • }

Page 10: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

10

Typowa obsługa komunikatów...

switch (iMsg){case WM_CREATE : [process WM_CREATE message] return 0 ; case WM_PAINT : [process WM_PAINT message] return 0 ; case WM_DESTROY : [process WM_DESTROY message] return 0 ;}return DefWindowProc (hwnd, iMsg, wParam, lParam) ;

Słynne „wielkie switch/case”

Page 11: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

11

KomunikatyKomunikaty składają się z:

– Identyfikatora odbiorcy (handle)– Identyfikatora komunikatu (zazwyczaj liczba całkowita) (UINT message)– Czasu kiedy komunikat został wysłany (time)– Dodatkowych danych (wParam, lParam)– Współrzędnych wskaźnika myszki, których komunikat dotyczy (Point)

W pliku winuser.h w systemie Microsoft Windows mamy zdefiniowaną strukturę dla komunikatu następująco (ta struktura jest w kolejce):

(...)2068 typedef struct tagMSG2069 {2070 HWND hwnd;2071 UINT message;2072 WPARAM wParam;2073 LPARAM lParam;2074 DWORD time;2075 POINT pt;2076 } MSG, *PMSG, *LPMSG;(...)

typedef struct tagPOINT {LONG x;LONG y;

} POINT,POINTL,....;

A w windef.h :

Page 12: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

12

Typy komunikatów● Komunikaty kolejkowane (queued) - PostMessage()

– Takie, których obsługa może być odłożona w czasie (czyli do kolejki komunikatów)

– Zazwyczaj wszystkie związane z interakcją użytkownika (WM_KEYUP, WM_LBUTTONDOWN, itp.)

● Komunikaty niekolejkowane (nonqueued) - SendMessage()– Takie, których obsługa powinna być natychmiastowa (bezpośrednio procedura obsługi okna)

– Zmiana rozmiaru okna, utworzenie procesu, żądanie przerysowania okna (WM_PAINT)

● Komunikaty zdefiniowane w systemie– Komunikaty zdefiniowane w pliku winuser.h

– Wykorzystują przedrostki, np.: BM_ (Button control), CB_ (Combo box control), EM_ (Edit control), WM_ (General Window) itp...

– Zdefiniowane w zakresie: 0x0000 do 0x03FF

● Komunikaty zdefiniowane przez aplikację– Wykorzystywane prywatnie przez aplikację

– W zakresie 0x0400 (wartość WM_USER) do 0x7FFF (lub 0xBFFF)

Page 13: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

13

Typy komunikatów● Komunikaty rozgłoszeniowe (Broadcasting Messages)

– Wysyłane do wszystkich odbiorców w systemie.– Wykorzystanie procedury BroadcastSystemMessage()

● Komunikaty rozgłoszeniowe do głównych okien aplikacji (top-level windows)

– W polu hwnd dla procedur z rodzin SendMessage i PostMessage należy użyć stałej HWND_BROADCAST

● Komunikaty zapytań (Query Message)– Pochodzą z rodziny komunikatów rozgłoszeniowych– Wykorzystuje się je do synchronizacji procesów (podobne do semaforów z

Unixowego POSIXa)– Wykorzystuje się metodę BroadcastSystemMessage(), ale trzeba ustawić flagę

BSF_QUERY w polu dwFlags– Jeżeli którykolwiek z odbiorców komunikatu zwróci

BROADCAST_QUERY_DENY, wówczas rozgłoszenie zostaje zakończone, a funkcja BroadcastSystemMessage() zwraca 0.

Page 14: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

14

Śledzenie komunikatów Spy++● Narzędzie monitorowania komunikatów.

Page 15: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

15

Spy++● Filtrowanie komunikatów...

Page 16: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

16

Przykład OpenOffice Impress● Wykorzystanie komunikatów użytkownika przez biblioteki do obsługi

komunikacji między własnymi komponentami (OpenOffice):

Page 17: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

17

Podsumowanie obsługi komunikatów● Samą komunikacją zajmuje się system graficzny. On jest odpowiedzialny

za dotarcie komunikatu do odbiorcy.● Programy zajmują się jedynie obsługą komunikatów na dwóch

poziomach: całego wątku (wstępna filtracja) i poszczególnych okien (procedury obsługi).

● Zazwyczaj większość komunikatów jest obsługiwana przez biblioteki i przekierowywana do odpowiednich kontrolek graficznych. (GUI Toolkits)

– Zadaniem programisty jest implementacja odpowiednich zmapowanych przez bibliotekę GUI procedur obsługi komunikatów. (Message Handler)

– Program nie musi obsługiwać wszystkich komunikatów.– Wszystkie biblioteki GUI umożliwiają stworzenie własnych sposobów obsługi

komunikatów.– W .NET przekazywaniem komunikatów zajmuje się zazwyczaj jedna pętla

komunikatów.

● Komunikaty mogą służyć do implementacji komunikacji międzyprocesowej.

Page 18: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

18

Struktura programu w WinAPI 32

(jako wprowadzenie do problemu obsługi komunikatów, która wymaga od programisty dużych nakładów)

Page 19: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

19

Program „Hello World” w Win32 API● Struktura programu wykorzystującego Win32 API w języku C.

– Charles Petzold, autor popularnych książek o Windows API powiedział: "The original hello-world program in the Windows 1.0 SDK was a bit of a scandal. HELLO.C was about 150 lines long, and the HELLO.RC resource script had another 20 or so more lines. (...) Veteran C programmers often curled up in horror or laughter when encountering the Windows hello-world program."

dalej...

Page 20: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

20

Kod „Hello World” - część 11. Deklaracja procedury, która będzie obsługiwała nadsyłane

komunikaty.

2. Punkt startowy programu, który w przypadku Win32

zawsze nazywał się WinMain

3. Długa i skomplikowana specyfikacja parametrów

tworzonego okna.

4. Rejestracja nowego okna z wprowadzonymi

parametrami.

5. Wywołujemy funkcję tworzącą instancję

zarejestrowanego okna.Tworzymy nowy wątek.

Page 21: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

21

Kod „Hello World” - część 2Pokazujemy stworzone okno na

ekranie.1. Pokazujemy stworzone okno

na ekranie.

2. Klasyczna pętla komunikatów.

3. Wcześniej zadeklarowana pętla, która obsługuje komunikaty

przesyłane do okienka.

Page 22: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

22

WindowProcedure - szczegóły

2. Komunikat przesyłany gdy okno wymaga przerysowania.

3. Podwójnie wciśnięty lewy przycisk myszki.

4. Komunikat wysyłany tuż przed zamknięciem okna.

(usunięciem z pamięci)

5. Jeżeli nie jest to żaden przez nas obsługiwany komunikat, wywołaj obsługę domyślną.

1. Dodatkowe argumenty, przekazywane razem z komunikatem.

Page 23: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

23

Realizacja - Pętla obsługi komunikatów● Podejście tradycyjne (Win32 API, GLUT):

● Podejście obiektowe (Qt, MFC, .NET, Java):

GUIMessage * msg;

while (( msg = waitForNextMessage() ) != null) {DispatchMessage( ApplicationHandle, msg );

if (msg == GUIQuitMessage) {PostQuitMessage( ApplicationHandle, msg );break;

}}

Application app = new Application(argc, argv);Window w = new MyWindow();w.show();

return app.Exec();

Page 24: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

24

Biblioteka Microsoft MFC● Microsoft Foundation Classes – było

odpowiedzią Microsoftu na biblioteki takie jak VCL opracowane przez Borlanda oraz na własny produkt Visual Basic.

● Jej zadaniem jest „obudowanie” standardowego i niewygodnego Win32 API za pomocą obiektowych komponentów, ułatwiających tworzenie aplikacji.

● Należy zdawać sobie sprawę, że Win32 API jest napisane w języku C, a MFC w obiektowym języku C++.

● .NET jest w również nakładką na Win32 API. Można powiedzieć następcą MFC (i jeszcze kilku innych technologii).

● MFC zawiera około 200 logicznie zorganizowanych klas zamiast ponad 2000 funkcji Win32 API.

Aplikacja C++ Windows

Biblioteka MFC

Interfejs: Win 32 API

Sprzęt komputerowy

Page 25: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

25

Biblioteka Microsoft MFC● Redukcja kodu programu implementując wielokrotnie wykorzystywany

kod inicjalizujący zasoby.● Mniejszy rozmiar plików wykonywalnych. (Znaczna część funkcjonalności

znajduje się w bibliotece dzielonej np. mfc422.dll)● Szybsza implementacja i rozbudowa aplikacji. (Prostszy kod)● Było problematyczne dla pierwszych programistów, którzy byli

nieprzyzwyczajeni do programowania obiektowego.● Programy MFC muszą być napisane w C++ i wymagają wykorzystania

klas. A w szczególności technologii:– tworzenia i zarządzania klasami, dziedziczenia, przesłaniania metod, enkapsulacji

danych, polimorfizmu.

Page 26: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

26

Obiektowość? A funkcje globalne Afx...?● Pomimo obiektowego projektu, projektantom MFC nie udało się obejść

bez funkcji globalnych. (W rzeczywistości .NET też posiada specjalny typ funkcji globalnych w postaci metod statycznych.)

● Przykłady funkcji globalnych:● AfxAbort() - bezwarunkowe zakończenie aplikacji,● AfxBeginThread() - utworzenie i uruchomienie nowego wątku,● AfxGetApp() - zwraca wskaźnik do obiektu aplikacji,● AfxGetMainWnd() - zwraca wskaźnik do głównego okna aplikacji,● AfxGetInstanceHandle() - zwraca uchwyt aktualnej aplikacji,● AfxRegisterWndClass() - rejestruje własną, dodatkową klasę okna dla naszej aplikacji MFC

● Przypis: Wnioskiem może być stwierdzenie, że obiektowość nie jest „panaceum” i stanowi rozwiązanie tylko części problemów projektowania oprogramowania.

Page 27: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

27

MFC – Hello World#include <afxwin.h> // Base MFC header file

class CHelloApp : public CWinApp{public: virtual BOOL InitInstance();};class CHelloWnd : public CFrameWnd{protected: afx_msg void OnPaint(); DECLARE_MESSAGE_MAP();};void CHelloWnd::OnPaint(){ CPaintDC dc(this); dc.TextOut(72, 72, "Hello MFC!"); }BEGIN_MESSAGE_MAP(CHelloWnd, CWnd) ON_WM_PAINT()END_MESSAGE_MAP()CHelloApp helloApp; // The single global CHelloApp objectBOOL CHelloApp::InitInstance(){ CHelloWnd* pMainWnd = new CHelloWnd; if (!pMainWnd->Create("AfxFrameOrView", "Hello MFC App")) return FALSE; m_pMainWnd = pMainWnd; m_pMainWnd->ShowWindow(m_nCmdShow); m_pMainWnd->UpdateWindow(); return TRUE;}

● Minimalna aplikacja MFC musi implementować co najmniej dwie klasy dziedziczące z:– CWinApp

● definiuje obiekt aplikacji,● zawiera w sobie pętlę obsługi

komunikatów– CFrameWnd (zazwyczaj)

● definiuje główne okno aplikacji.– Aby w MFC powyższe klasy były

widoczne dla aplikacji musimy dołączyć plik <Afxwin.h>

Page 28: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

28

MFC – Hello World#include <afxwin.h> // Base MFC header file

class CHelloApp : public CWinApp{public: virtual BOOL InitInstance();};class CHelloWnd : public CFrameWnd{protected: afx_msg void OnPaint(); DECLARE_MESSAGE_MAP();};void CHelloWnd::OnPaint(){ CPaintDC dc(this); dc.TextOut(72, 72, "Hello MFC!"); }BEGIN_MESSAGE_MAP(CHelloWnd, CWnd) ON_WM_PAINT()END_MESSAGE_MAP()CHelloApp helloApp; // The single global CHelloApp

BOOL CHelloApp::InitInstance(){ CHelloWnd* pMainWnd = new CHelloWnd; if (!pMainWnd->Create("AfxFrameOrView", "Hello MFC App")) return FALSE; m_pMainWnd = pMainWnd; m_pMainWnd->ShowWindow(m_nCmdShow); m_pMainWnd->UpdateWindow(); return TRUE;}

● Mapa komunikatów, która jest wykorzystywana przez pętlę obsługi komunikatów zaimplementowanę w MFC w klasie CwinApp.

● Specjalne makro implementujące szereg zmiennych i funkcji potrzebnych do obsługi komunikatów.

● Inicjujemy naszą aplikację. Metoda InitInstance jest automatycznie wywoływana przez MFC po uruchomieniu aplikacji.

– Tworzymy tutaj nowe okno.– Zapamiętujemy wskaźnik do

niego jako do okna głównego.– Pokazujemy i uaktualniamy.

Page 29: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

29

MFC - Obsługa komunikatów● W Win32 API komunikaty są obsługiwane za pomocą „wielkiej” sekcji switch/case.

● W MFC „mapę komunikatów” zrealizowano w formie tabeli przechowującej:

– numer komunikatu (np WM_PAINT, WM_RESIZE, itp.)– wskaźnik do odziedziczonej funkcji obsługi komunikatu

● MFC udostępnia szereg predefiniowanych metod obsługi komunikatów (ang. message handlers) dla wszystkich możliwych komunikatów. Naszym zadaniem zazwyczaj jest przesłonięcie oryginalnej metody. Do zrobienia tego musimy:

– Poinformować MFC, że zamierzamy implementować własną metodę obsługi (za pomocą makra w mapie komunikatów o odpowiedniej nazwie)

– Musimy napisać naszą metodę stosując narzuconą konwencję nazewnictwa oraz odpowiednio zadeklarować argumenty. (Bardzo często wymaga to przeglądania dokumentacji MSDN)

● Dla przykładu, metoda OnPaint nie pobiera żadnych argumentów i reaguje na komunikat z żądaniem przerysowania ekranu. Jej prototyp wygląda:

afx_msg void OnPaint();afx_msg informuje kompilator, że metoda obsługuje komunikat. (Podobno nie miało to znaczenia, ale było zarezerwowane dla przyszłych wersji MFC.)

Page 30: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

30

Nazewnictwo makr obsługi● Jeżeli mapa zawiera:

– ON_WM_CHAR( )– ON_WM_LBUTTONDOWN( )– ON_WM_RBUTTONDOWN( )

● Wówczas odpowiadające makrom komunikaty to:– WM_CHAR– WM_LBUTTONDOWN– WM_RBUTTONDOWN

● A w naszym programie musimy przesłonić domyślne funkcje naszymi własnymi:

– CWnd::OnChar(UINT ch, UINT count, UINT flags);– CWnd::OnLButtonDown(UINT flags, CPoint loc);– CWnd::OnRButtonDown(UINT flags,CPoint loc);

Page 31: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

31

GLUT – aplikacje OpenGL● Aplikacja OpenGL jest wyświetlana w środowisku graficznym. Jej struktura nie

odbiega zasadniczo od pozostałych przykładów.

#include <iostream>#include <cstdlib>#include <GL/glut.h>using namespace std;

// function prototypesvoid disp(void);void keyb(unsigned char key, int x, int y);

// window identifierstatic int win;

int main(int argc, char **argv){ // initialize glut glutInit(&argc, argv); // specify the display mode to be RGB and single buffering // we use single buffering since this will be non animated glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE); // define the size glutInitWindowSize(500,500); // the position where the window will appear glutInitWindowPosition(100,100); // create the window, set the title and keep the // window identifier. win = glutCreateWindow("Yet another teapot"); glutDisplayFunc(disp); glutKeyboardFunc(keyb); // define the color we use to clearscreen glClearColor(0.0,0.0,0.0,0.0); // enter the main loop glutMainLoop(); return 0;}

void disp(void){ glClear(GL_COLOR_BUFFER_BIT); glutWireTeapot(0.5); // glutSolidTeapot(0.5); // glutWireSphere(0.5,100,100);}void keyb(unsigned char key, int x, int y){ cout << "Pressed key " << key << "on coordinates (" << x << "," << y << ")"; cout << endl; if(key == 'q'){ cout << "Got q,so quitting " << endl; glutDestroyWindow(win); exit(0); }}

1. Jak widzimy struktura programu jest bardzo podobna. Zawiera on inicjalizację

oraz wywołanie metody obsługującej mapowanie komunikatów na procedury

obsługi. W naszym przypadku są to disp i keyb.

Page 32: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

32

Delphi● Struktura programu opiera się na VCL (Visual Component Library)

● „VCL (ang. Visual Component Library) - biblioteka stworzona w języku Object Pascal (obiektowej wersji języka Pascal) przez firmę Borland na potrzeby środowiska Delphi, potem zaadaptowana też do środowiska C++ Builder.

● Biblioteka VCL należy do jednych z bardziej przejrzystych i dobrze zaprojektowanych bibliotek wspomagających programowanie w środowisku Windows, zwłaszcza tworzenie interfejsu użytkownika. Przez długie lata dystansowała w tej dziedzinie konkurencyjną bibliotekę firmy Microsoft - MFC, dołączaną do środowiska Visual Studio.

● Obecnie biblioteka VCL integruje w sobie też możliwość korzystania z technologii .NET firmy Microsoft.

● Podstawą biblioteki VCL jest klasa bazowa TObject. Z niej dziedziczą wszystkie pozostałe klasy biblioteki.”

Źródło: http://pl.wikipedia.org/wiki/Visual_Component_Library

Page 33: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

33

Delphi – Struktura Programu

program Project1;

uses Forms, Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run;end.

2. Punkt startowy programu, który w przypadku VCL jest procedurą startową modułu Object Pascala.

(W tym przypadku programu.)

1. Dołączenie zasobów takich jaki ikony, obrazy, itp.

3. Standardowa inicjalizacja parametrów aplikacji. (Czyli

pierwsza część z poprzedniego przykładu.)

4. Inicjalizacja okna zdefiniowanego za pomocą klasy TForm1. Wyświetlenie

okna w zależności od atrybutu Visible.

5. Uruchomienie pętli obsługi komunikatów.

Project1.pas

Page 34: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

34

Definicja przykładowej klasy okna: TForm1 unit Unit1;

interface

uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs;

type TForm1 = class(TForm) private { Private declarations } public { Public declarations } end;

var Form1: TForm1;

implementation

{$R *.dfm}

end.

1. Nasza klasa okna dziedziczy funkcjonalność klasy TForm.

TForm = class(TCustomForm) public procedure ArrangeIcons; procedure Cascade; procedure Next; procedure Previous; procedure Tile;

(...)

2. Która z kolei dziedziczy funkcjonalność z TCustomForm.

TCustomForm = class(TScrollingWinControl) private FActiveControl: TWinControl; FFocusedControl: TWinControl;(...) procedure RefreshMDIMenu; procedure ClientWndProc(var Message: TMessage); procedure CloseModal;(...)

3. I tutaj mamy znaną, tylko pod trochę inną nazwą funkcję

która implementuje obsługę komunikatów.

4. Importujemy projekt wizualny czyli graficzny układ kontrolek na ekranie wraz ze zdefiniowanymi wartościami

atrybutów.

itd.

Page 35: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

35

Procedura obsługi komunikatów - Delphiprocedure TCustomForm.ClientWndProc(var Message: TMessage);(...)var DC: HDC; PS: TPaintStruct; R: TRect;begin with Message do case Msg of WM_PAINT: begin DC := TWMPaint(Message).DC; if DC = 0 then TWMPaint(Message).DC := BeginPaint(ClientHandle, PS); try if DC = 0 then begin GetWindowRect(FClientHandle, R); R.TopLeft := ScreenToClient(R.TopLeft); MoveWindowOrg(TWMPaint(Message).DC, -R.Left, -R.Top); end; PaintHandler(TWMPaint(Message)); finally if DC = 0 then EndPaint(ClientHandle, PS); end; end; else Default; end;end;

1. Zwróćmy uwagę, że komunikaty zostają nam przekazane za pomocą

rekordu, a nie osobnych parametrów.

2. Atrybut Msg jest bezpośrednio mapowany z WParam i stanowi kod

komunikatu.

3. Wywołanie odpowiedniej procedury rysującej (zazwyczaj zaimplementowanje w obsludze

komunikatu OnPaint), prpo zainicjowaniu odpowiedniego

kontekstu rysowania.

4. Jeżeli do tej pory nie obsłużyliśmy komunikatu,

przekażmy go do obsługi domyślnej.

Page 36: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

36

Procedura obsługi w TComponent procedure TControl.WndProc(var Message: TMessage);var Form: TCustomForm; KeyState: TKeyboardState; WheelMsg: TCMMouseWheel;begin if (csQDesigning in ComponentState) then begin Form := GetParentForm(Self); if (Form <> nil) and (Form.Designer <> nil) and Form.Designer.IsDesignMsg(Self, Message) then Exit end; if (Message.Msg >= WM_KEYFIRST) and (Message.Msg <= WM_KEYLAST) then begin Form := GetParentForm(Self); if (Form <> nil) and Form.WantChildKey(Self, Message) then Exit; end

(...)

else if Message.Msg = CM_VISIBLECHANGED then with Message do SendDockNotification(Msg, WParam, LParam); Dispatch(Message);end;

1. Funkcja przekazująca kontrolę do procedur obsługi komunikatów

oznaczonych specjalnym słowem kluczowym message.

Page 37: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

37

Słowo kluczowe message

TScrollingWinControl = class(TWinControl) private FHorzScrollBar: TControlScrollBar;(...) procedure UpdateScrollBars; procedure WMSize(var Message: TWMSize); message WM_SIZE; procedure WMHScroll(var Message: TWMHScroll); message WM_HSCROLL; procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL; procedure CMBiDiModeChanged(var Message: TMessage); message CM_BIDIMODECHANGED; protected procedure AdjustClientRect(var Rect: TRect); override;

● W Object Pascalu (języku programowania Delphi) wprowadzono specjalne słowo kluczowe message, które wykorzystywane jest przez procedury obsługi komunikatów zdefiniowane w komponentach VCL do mapowania komunikatów systemu operacyjnego (i nie tylko) na odpowiednie procedury obsługi (ang. handling procedures).

● Można definiować własne komunikaty. Do tego wykorzystuje się liczbę WM_USER, która definiuje minimalny kod komunikatu.

Page 38: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

38

Microsoft .NET – Struktura programu● Idea obudowania Win32 API pozostała ta sama co w MFC.● Klasy mają inne nazwy. ● Prawdziwa obiektowość za pomocą delegacji i zdarzeń.● Minimalny program pokazujący na ekranie puste okno.

using System;using System.Windows.Forms;namespace Project1{ class OknoByHand : Form { public OknoByHand() { Text = "okno by hand lub Hello World!"; } public static int Main() { Application.Run(new OknoByHand()); return 0; } }}

1. Dziedziczymy funkcjonalność z klasy Form, która za nas implementuje obsługę

mapowania komunikatów, posiada zdefiniowane szereg zdarzeń, itp. Idea

bardzo zbliżona do RAD z Delphi.

2. W konstruktorze definiujemy napis na pasku tytułu. Tutaj

również powinniśmy zainicjalizować

komponenty naszego okna.

3. Nie musimy deklarować własnego obiektu Application. >NET robi to za nas udostępniając obiekt aplikacji związany

z głównym wątkiem. przekazując do Run nasze okno wskazujemy, że aplikacja ma działać dopóki to okno nie

zostanie zamknięte.

Page 39: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

39

Microsoft .NET – struktura programu● Dodaliśmy kontrolkę do naszego projektu

w „stylu .NET Framework 1.1”.

● 1. Zadeklarowaliśmy zmienną lokalną, która przechowuje referencję do kontrolki TextBox.

● 2. „Manualnie” zdefiniowaliśmy parametry kontrolki.

● 3. Dodaliśmy kontrolkę do naszego okna.

using System;using System.Windows.Forms;

namespace Project1{ class OknoByHand : Form { public TextBox txtUserName;

public OknoByHand() { Text = "okno by hand";

txtUserName = new TextBox(); txtUserName.Left = 20; txtUserName.Top = 20; txtUserName.Width = 200; txtUserName.Text = "wprowadz nazwe uzytkownika";

Controls.Add(txtUserName); }

public static int Main() { Application.Run(new OknoByHand()); return 0; } }}

Page 40: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

40

Microsoft .NET 2.0● Microsoft w .NET 2.0 dodał do języka

nowe słowo kluczowe partial.● Słowo to pozwala dzielić definicję jednej

klasy na wiele plików. Dzięki temu możemy elegancko oddzielić kod odpowiadający za konfigurację interfejsu graficznego od kodu kontrolera.

Kontroler (logika aplikacji)

Widok (prezentacja)

Page 41: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

41

Microsoft .NET - Delegacje● Specjalna „struktura”, typ, w rzeczywistości w CIL klasa, która potrafi

przechowywać wskaźnik (referencję) do metody statycznej lub należącej obiektu, która ma zostać uruchomiona w momencie uruchomienia delegacji. W rzeczywistości delegacja w naturalny sposób potrafi przechowywać listę metod, które mają zostać uruchomione.

● Delegacja w ukryty sposób przechowuje informacje o:– nazwie wywoływanej metody– typie wywoływanego obiektu– argumentach wywoływanej metody– argumentach zwracanych przez wywoływaną metodę

● Delegacja przechowuje powyższe informacje w sposób ukryty, co oznacza, że nie jest to widoczne bezpośrednio dla programisty. Co więcej, programista zazwyczaj nie używa właściwości delegacji związanych z tym, że jest ona klasą. Jedną z ważniejszych cech delegacji jest to, że aby można jej było użyć trzeba ją utworzyć za pomocą operatora new , podobnie jak inne normalne klasy.

Page 42: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

42

Przykład delegacji - deklaracja● Poniższy przykład bazuje na założeniu, że mamy do stworzenia pewien system

monitoringu stanu rzek. Każda rzeka ma swoje własne parametry określające maksymalny dopuszczalny poziom oraz informacje o aktualnym poziomie. Uaktualniając poziom rzeki chcemy aby w razie przekroczenia wartości krytycznej lub nawet niebezpiecznego zbliżenia się do tej wartości rzeka wywoływała specjalne metody które jej dostarczymy. Metody będą w stanie odpowiednio obsłużyć daną sytuację wyjątkową.

1 public class River 2 { 3 (...) 4 5 public delegate void AboutToFlood(string msg); 6 public delegate void Flooded(string msg); 7 8 private AboutToFlood aboutToFloodListener; 9 private Flooded floodedListener; 10 11 (...) 12 }

Page 43: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

43

Rejestracja delegacji● Rozwińmy kod i dodajmy do niego dwie metody, które będą rejestrowały metody

do naszych delegacji. ' Rejestrowały ' czyli ustawią atrybuty prywatne tak aby wskazywały na podane metody.

● Rejestracja jest niezbędna ponieważ atrybuty deklarujące delegacje są prywatne. 1 public class River 2 { 3 (...) 4 5 public delegate void AboutToFlood(string msg); 6 public delegate void Flooded(string msg); 7 8 private AboutToFlood aboutToFloodListener; 9 private Flooded floodedListener; 10 11 public void OnAboutToFlood(AboutToFlood aboutToFlood) 12 { 13 aboutToFloodListener = aboutToFlood; 14 } 15 16 public void OnFlooded(Flooded flooded) 17 { 18 floodedListener = flooded; 19 } 20 21 (...) 22 }

Page 44: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

44

Uruchomienie delegacji● Dodajmy jeszcze wywołanie metod zapamiętanych w naszych delegacjach w

momencie wystąpienia sytuacji wyjątkowych, czyli zbliżenia i przekroczenia wartości maksymalnej. - Wyzwolenie delegacji.

1public class River 2{ 3 (...) 4 5 public int Level 6 { 7 set 8 { 9 riverLevel = value; 10 if (riverLevel > riverLevelMax) 11 { 12 // na poczatku sprawdzamy, czy nasza delegacja jest zainicjalizowana 13 if (floodedListener != null) 14 // jeżeli tak, to wywolujemy metode, podajac nazwe delegacji i przekazujac argument 15 floodedListener("The river " + name + " has flooded... "); 16 } else { 19 if (riverLevel > (riverLevelMax - (riverLevelMax / 10))) 20 // podobnie jak poprzednio sprawdzamy, czy delegacja jest zainicjalizowana 21 if (aboutToFloodListener != null) 22 // jezeli jest, to ja wywolujemy 23 aboutToFloodListener("The river " + name + " is about to flood... "); 24 } 25 } 26 get { return riverLevel; } 27 } 28 29 (...) 31}

1. Deklarujemy atrybut w C#.2. Słaby wzorzec projektowy związany z

obiektowym charakterem .NET:if (event != null) …

powodujący powtarzanie się kodu.Czy nie byłoby zgrabniej po prostu

napisać:FloodedListener(„....”), a sama delegacja

powinna zająć się sprawdzeniem, czy jest zarejestrowana metoda obsługi?

Page 45: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

45

Program testujacy... 61 class Program 62 { 63 static void AboutToFlood_Handler(string msg) 64 { 65 Console.WriteLine("Warning! {0}", msg); 66 } 67 68 static void Flooded_Handler(string msg) 69 { 70 Console.WriteLine("Critical! {0}", msg); 71 } 72 73 static void Main(string[] args) 74 { 75 River river = new River("Wisla"); 76 77 Console.WriteLine("Now we exceed the maximum level, but no „ „message is expected, because we have not defined the delegates objects."); 78 for (int i = 1; i < 15; i++) 79 { 80 Console.WriteLine("Setting river level to {0}", i); 81 river.Level = i; 82 } 83 84 // w naszym programie musimy jeszcze zainicjalizowac odpowiednie delegacje, 85 // tworzac dla kazdej metody nowy obiekt delegacji 86 river.OnAboutToFlood( new River.AboutToFlood( AboutToFlood_Handler ) ); 87 river.OnFlooded(new River.Flooded(Flooded_Handler)); 88 89 Console.WriteLine("Now we expect the warning messages."); 90 for (int i = 1; i < 15; i++) 91 { 92 Console.WriteLine("Setting river level to {0}", i); 93 river.Level = i; 94 } 95 96 Console.ReadLine(); 97 } 98 }

1. Definiujemy metody obsługi zdarzeń, które przekażemy do

tworzonych delegacji.

2. Rejestrujemy nowo tworzone obiekty delegacji w klasie River.

Page 46: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

46

Więcej informacji o przykładzie● Dokładniejszy opis przedstawionego przykładu delegacji można znaleźć:

– http://www/wikidyd/AiSD_E/Lab/Dodatkowe_Przyk%C5%82ady/Delegate● Pełny kod do pobrania ze strony, w pliku delegateRiver.rar

● Wynik na ekranie:

Page 47: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

47

Microsoft .NET - Events● Słowo kluczowe event zostało wprowadzone do .NET aby zmniejszyć

ilość tworzonego kodu i zautomatyzować procedury rejestracji i wyrejestrowania delegacji z danych klas. Kompilator, gdy napotka słowo event automatycznie dodaje odpowiednie metody, które są ukryte przed programistą.

● Deklaracja zdarzeń składa się z dwóch etapów:– deklaracji odpowiedniej delegacji (przez co zdefiniujemy typ metody, która będzie

wywoływana w momencie pojawienia się zdarzenia),– deklaracji zdarzenia przy wykorzystaniu wcześniejszej delegacji

public class SenderOfEvents{

public delegate retval AssociatedDelegate(args);public event AssociatedDelegate NameOfEvent;

...}

Page 48: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

48

Przykład deklaracji i uruchamiania zdarzeniapublic class Car{

// Ta delegacja jest uzywana w zdarzeniupublic delegate void CarEventHandler(string msg);// Potrafimy wygenerowac dwa zdarzenia.public event CarEventHandler Exploded;public event CarEventHandler AboutToBlow;public void Accelerate(int delta){

// Czy samochod jest zepsuty?if (carIsDead){

if (Exploded != null)Exploded("Sorry, this car is dead...");

} else {currSpeed += delta;// Prawie zepsuty?if (10 == maxSpeed - currSpeed

&& AboutToBlow != null){

AboutToBlow("Careful! I am going to blow!");}// Nadal w porzadkuif (currSpeed >= maxSpeed)

carIsDead = true;else

Console.WriteLine("->CurrSpeed = {0}", currSpeed);}

}}

1. Procedury obsługi zdarzenia przypisane do delegacji wywołujemy

wpisując nazwę zdarzenia wraz z parametrami.

2. pamiętamy o zadeklarowaniu delegacji, która definiuje sposób

wywołania metody obsługującej dane zdarzenie.

Page 49: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

49

Rejestracja zdarzeń

● W powyższym przykładzie tworzymy obiekt typu delegacja: Car.EngineHandler i przypisujemy mu metodę obsługi o nazwie: CarExplodedEventHandler.

● Następnie za pomocą operatora „+=” dodajemy naszą delegację do kolejki zdarzenia.

● W C# wprowadzono dodatkowe ułatwienie nie wymagające od nas jawnego tworzenia obiektu delegacji. W nowym podejściu kompilator sam automatycznie stworzy dla nas 'ukryty' obiekt delegacji o odpowiednim typie (sam go rozpozna). Naszym zadaniem jest podac jedynie nazwę metody. Wówczas kod skróci się do:

Car.EngineHandler d = new Car.EngineHandler(CarExplodedEventHandler)myCar.Exploded += d;

myCar.Exploded += CarExplodedEventHandler;

Page 50: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

50

Program testującyclass Program{

static void Main(string[] args){

Console.WriteLine("***** Events *****");Car c1 = new Car("SlugBug", 100, 10);// Register event handlers.c1.AboutToBlow += new Car.CarEventHandler(CarIsAlmostDoomed);c1.AboutToBlow += new Car.CarEventHandler(CarAboutToBlow);Car.CarEventHandler d = new Car.CarEventHandler(CarExploded);c1.Exploded += d;Console.WriteLine("\n***** Speeding up *****");for (int i = 0; i < 6; i++)

c1.Accelerate(20);// Remove CarExploded method// from invocation list.c1.Exploded -= d;Console.WriteLine("\n***** Speeding up *****");for (int i = 0; i < 6; i++)

c1.Accelerate(20);Console.ReadLine();

}public static void CarAboutToBlow(string msg){ Console.WriteLine(msg); }public static void CarIsAlmostDoomed(string msg){ Console.WriteLine("Critical Message from Car: {0}", msg); }public static void CarExploded(string msg){ Console.WriteLine(msg); }

}

1

2

3

4

5

Page 51: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

51

events – „wartość dodana”● Wprowadzanie słowa kluczowego event może wydawać się zbyteczne,

skoro delegacje i zdarzenia posiadają podobną funkcjonalność, ale:– zdarzenia event mogą być deklarowane w interfejsach, a zwykłe pola nie

interface ITest{ event MsgHandler msgNotifier; // compiles MsgHandler msgNotifier2; // error CS0525: Interfaces cannot contain fields}

class TestClass : ITest{ public event MsgHandler msgNotifier; // When you implement the interface, you need to implement the event too static void Main(string[] args) { }}

Page 52: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

52

events – „wartość dodana”– wywołanie zdarzenia możliwe tylko z klasy, które je zadeklarowało– nawet klasa pochodna nie może wywoływać takiego zdarzenia

using System;

namespace EventAndDelegate{ delegate void MsgHandler(string s);

class Class1 { public static event MsgHandler msgNotifier; public static MsgHandler msgNotifier2;

static void Main(string[] args) { new Class2().test(); } } class Class2 { public void test() { Class1.msgNotifier("test"); // error CS0070: The event 'EventAndDelegate.Class1.msgNotifier' can only appear on the left hand side of += or -= (except when used from within the type 'EventAndDelegate.Class1') Class1.msgNotifier2("test2"); // compiles fine } }}

Page 53: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

53

events – „wartość dodana”● metody dostępowe podobne do atrybutów (property)● można implementować aspekty związane z rejestracją nowego

słuchacza zdarzeń

event EventHandler IDrawingObject.OnDraw { add { lock (PreDrawEvent) { PreDrawEvent += value; } } remove { lock (PreDrawEvent) { PreDrawEvent -= value; } } }

Page 54: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

54

.NET 3.xJako skutek wzrostu wymagań odnośnie interfejsu użytkownika.

(dokumenty, grafika, wideo)

Jednolity zestaw narzędzi do tworzenia nowoczesnych interfejsów użytkownika zintegrowanego z dokumentami i multimediami.

Wynik doświadczeń związanych z wytwarzaniem oprogramowania dla aplikacji internetowych:

– separacja kompetencji programisty od projektanta GUI.

Obsługa komunikatów podobna do poprzednich wersji .NET

Windows Presentation Foundation: A Unified Approach to Diverse User Interfaces

Page 55: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

55

Komunikaty niestandardowe● Aby wykonać niektóre zadania musimy wykorzystać nieobsługiwane

bezpośrednio przez .NET komunikaty.● Przykładem takiego komunikatu jest informacja o zmianie woluminu (np.

włożenie, usunięcie płyty CD lub pamięci usb):– WM_CHANGEDEVICE

● W .NET są co najmniej dwie metody własnej obsługi komunikatów:– Wykorzystanie filtra komunikatów: IMessageFilter (oficjalnie promowana przez .NET)– przesłonięcie wirtualnej metody WndProc

Page 56: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

56

1. Wykorzystanie IFilterMessage ● Możemy wykorzystać standardowy interfejs .NET: IFilterMessage, w

którym przesłonimy metodę PreFilterMessage.

using System;using System.Windows.Forms;public class MyFilter: IMessageFilter{ public bool PreFilterMessage(ref Message aMessage) { if (aMessage.Msg==WM_AMESSAGE) { //WM_AMESSAGE Dispatched //Let’s do something here //... } // This can be either true or false // false enables the message to propagate to all other // listeners return false; }}

Page 57: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

57

1. Rejestracja filtra komunikatów● Pozostaje nam tylko stworzyć obiekt filtra● I zarejestrować go w obiekcie aplikacji.

● Lub usunąć go...

MyFilter fFilter = new MyFilter();(…)Application.AddMessageFilter(fFilter);(…)

Application.RemoveMessageFilter(fFilter);

Page 58: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

58

2. Przesłonięcie procedury WndProc● Procedurę WndProc możemy przesłonić w klasie potomnej dziedziczącej

z klasy Control lub NativeWindow. Z klasy Control dziedziczymy, gdy chcemy rozszerzyć funkcjonalność jakiejś specyficznej kontrolki.

● W naszym przykładzie dziedziczymy z NativeWindow.

protected override void WndProc(ref Message aMessage){ if (aMessage.Msg==WM_AMESSAGE) { //WM_AMESSAGE Dispatched // Zróbmy coś tutaj... //... }}

Page 59: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

59

Omówienie przykładu

class DeviceVolumeMonitor

NativeWindow_DeviceVolumeMonitor

+ _DeviceVolum eM oni tor(DeviceVolum eM oni tor)

IDisposableDeviceVolumeMonitor

+ DeviceVolum eM oni tor()+ DeviceVolum eM oni tor(IntPtr)+ Dispose() : void+ M askT oDevicePaths(int) : string+ M askT oLogicalPaths(int) : string

«property»+ AsynchronousEvents() : boo l+ Enabled() : boo l

«event»+ OnVolum eInserted() : DeviceVolum eAction+ OnVolum eRem oved() : DeviceVolum eAction

System.Window s.Forms.FormfrmDeviceVolumeMonitor

+ frm DeviceVolum eM oni tor()

-fNative

-fIn ternal-fM oni tor

1. Okno z graficznym interfejsem użytkownika, definiuje metody obsługi

zdarzeń

2. Prywatna klasa dla naszej przestrzeni nazw przechowująca informacje o kodach Win32 API, kodach komunikatów oraz naszą główną pętlę

obsługi komunikatów (dziedziczy z NativeWindow)

3. Publiczna klasa usługowa definiująca zdarzenia oraz zajmująca

się inicjalizacją klasy prywatnej.

Page 60: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

60

Omówienie przykładu● Oryginalny przykład znajduje się na stronie:

http://www.codeproject.com/dotnet/devicevolumemonitor.asp

● Prezentowany przykład składa się z trzech zasadniczych klas:– public class frmDeviceVolumeMonitor : System.Windows.Forms.Form - okno z

graficznym interfejsem użytkownika, definiuje metody obsługi zdarzeń,– internal class _DeviceVolumeMonitor: NativeWindow - prywatna klasa dla naszej

przestrzeni nazw przechowująca informacje o kodach Win32 API, kodach komunikatów oraz naszą główną pętlę obsługi komunikatów (dziedziczy z NativeWindow)

– public class DeviceVolumeMonitor: IDisposable - publiczna klasa usługowa definiująca zdarzenia oraz zajmująca się inicjalizacją klasy prywatnej.

● W załączonym przykładzie implementujemy również asynchroniczny sposób wyzwalania zdarzeń aby nie blokować obsługi natywnych komunikatów. (pomijamy w naszym opisie)

Page 61: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

61

Diagram sekwencjisd DeviceVolumeMonitor

User

_DeviceVolum eM oni torDeviceVolum eM oni torfrm DeviceVo lum eM on i tor

System

Uzytkownik urucham ia ap l ikacje .

DeviceVolum eM oni tor(aHandle)

_DeviceVolum eM oni tor(aM oni tor)

WndProc(aM essage)

in terp retacja kom unika tu

T riggerEvents(a Inserted ,aM ask)

Vo lum eInserted(aM ask)

UpdateUI()

1

2

3

4

5

67

8

9

Page 62: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

62

frmDeviceMonitor public class frmDeviceVolumeMonitor : System.Windows.Forms.Form { DeviceVolumeMonitor fNative;(...) public frmDeviceVolumeMonitor() { InitializeComponent(); // DeviceVolumeMonitor create instance fNative = new DeviceVolumeMonitor(this.Handle); fNative.OnVolumeInserted += new DeviceVolumeAction(VolumeInserted); fNative.OnVolumeRemoved += new DeviceVolumeAction(VolumeRemoved); UpdateUI(); }

[STAThread] static void Main() { Application.Run(new frmDeviceVolumeMonitor()); }

private void VolumeInserted(int aMask) { lbEvents.Items.Add("Volume inserted in "+fNative.MaskToLogicalPaths(aMask)); }

private void VolumeRemoved(int aMask) { lbEvents.Items.Add("Volume removed from "+fNative.MaskToLogicalPaths(aMask)); } }

1. Konstruktor inicjujący obiekt monitorujący zdarzenia, oraz

rejestrujący w nim metody obsługi zdarzeń.

2. Funkcja Main uruchamiana w momencie wystartowania programu.

3. Metody obsługi komunikatów.

Page 63: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

63

DeviceVolumeMonitor public class DeviceVolumeMonitor: IDisposable { (...) public event DeviceVolumeAction OnVolumeInserted; public event DeviceVolumeAction OnVolumeRemoved;

public DeviceVolumeMonitor(IntPtr aHandle) { if (aHandle!=IntPtr.Zero) { fHandle = aHandle; } else { throw new DeviceVolumeMonitorException("Invalid handle!"); } Initialize(); } private void Initialize() { fInternal = new _DeviceVolumeMonitor(this); fDisposed = false;

(...) } internal void TriggerEvents(bool aInserted, int aMask) { if (AsynchronousEvents) { if(aInserted) { OnVolumeInserted.BeginInvoke(aMask,null,null); } else { OnVolumeRemoved.BeginInvoke(aMask,null,null); } } else { if(aInserted) { OnVolumeInserted(aMask); } else { OnVolumeRemoved(aMask); } } } ~DeviceVolumeMonitor() { Dispose(false); } }

Page 64: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

64

_DeviceVolumeMonitor internal class _DeviceVolumeMonitor: NativeWindow { DeviceVolumeMonitor fMonitor;

#region API constants and structures

const int WM_DEVICECHANGE = 0x0219;

public enum DeviceEvent:int { Arrival = 0x8000, //DBT_DEVICEARRIVAL QueryRemove = 0x8001, //DBT_DEVICEQUERYREMOVE QueryRemoveFailed = 0x8002, //DBT_DEVICEQUERYREMOVEFAILED RemovePending = 0x8003, //DBT_DEVICEREMOVEPENDING RemoveComplete = 0x8004, //DBT_DEVICEREMOVECOMPLETE Specific = 0x8005, //DBT_DEVICEREMOVECOMPLETE Custom = 0x8006 //DBT_CUSTOMEVENT } public enum DeviceType:int { OEM = 0x00000000, //DBT_DEVTYP_OEM DeviceNode = 0x00000001, //DBT_DEVTYP_DEVNODE Volume = 0x00000002, //DBT_DEVTYP_VOLUME Port = 0x00000003, //DBT_DEVTYP_PORT Net = 0x00000004 //DBT_DEVTYP_NET }(...) protected override void WndProc(ref Message aMessage) { (...) base.WndProc(ref aMessage); if(aMessage.Msg==WM_DEVICECHANGE && fMonitor.Enabled) { lEvent = (DeviceEvent)aMessage.WParam.ToInt32(); if (lEvent==DeviceEvent.Arrival || lEvent==DeviceEvent.RemoveComplete) { lBroadcastHeader = (BroadcastHeader)Marshal.PtrToStructure(aMessage.LParam,typeof(BroadcastHeader)); if(lBroadcastHeader.Type==DeviceType.Volume) { lVolume = (Volume)Marshal.PtrToStructure(aMessage.LParam,typeof(Volume)); //if((lVolume.Flags & (int)VolumeFlags.Media)!=0) { fMonitor.TriggerEvents(lEvent==DeviceEvent.Arrival,lVolume.Mask); } } } } } }

Page 65: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

65

Program do rejestracji makr związanych

z klawiaturą i myszką

MacroRecorder

Page 66: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

66

Plan prezentacji programu MacroRecorder● Technologie, które można wykorzystać● Struktura projektu● Opis modułu HookManager● Przechwytywanie komunikatów myszki● Przechwytywanie komunikatów klawiatury● Moduł nagrywania komunikatów● Iteracja po komunikatach● Procedura odtwarzania komunikatów● Symulacja klawiatury● Symulacja myszki● Konwersja współrzędnych

Page 67: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

67

Technologie, które można wykorzystać● Symulacja klawiatury i myszki (problemy z TranslateMessage())

– lub lepiej:

● SendMessage() i PostMessage() z komunikatami WM_IME_KEYDOWN itp. (poprawne TranslateMessage())

● SendInput() (okno musi być aktywne)

SendMessage(hwnd, WM_KEYDOWN, wParam, lParam); SendMessage(hwnd, WM_KEYUP, wParam, lParam);

PostMessage(hwnd, WM_KEYDOWN, wParam, lParam); PostMessage(hwnd, WM_KEYUP, wParam, lParam);

SendMessage(hwnd, WM_IME_KEYDOWN, wParam, lParam); SendMessage(hwnd, WM_IME_KEYUP, wParam, lParam);

public void SendKeyDown(Keys vkKey, char key) { Win32.KEYBDINPUT keyb = new Win32.KEYBDINPUT(); keyb.dwFlags = 0; keyb.time = 0; keyb.dwExtraInfo = Win32.GetMessageExtraInfo(); keyb.wVk = (ushort) vkKey; keyb.wScan = key;

Win32.INPUT[] inputs = new Win32.INPUT[1]; inputs[0].type = Win32.INPUT_KEYBOARD; inputs[0].ki = keyb; int isize = Marshal.SizeOf(inputs[0]); Win32.SendInput(1, inputs, isize); }

Page 68: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

68

Struktura projektu

Page 69: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

69

Struktura projektu

● Moduł związany z przechwytywaniem komunikatów klawiatury i myszki

● Moduł związany z – Odtwarzaniem makr– Rejestracją makr– Symulacją klawiatury i myszki

● Procedury i stałe związane wykorzystaniem Win32 API

● Główne okno programu

Page 70: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

70

HookManager● Instalacja uchwytu („haczyka”) do przychwytywania komunikatów

dotyczących myszki:

● oraz klawiatury:

private void InstallMouseHook() { if (hHookMouse == 0) { if (mouseHookProcedure == null) mouseHookProcedure = new Win32.HookProc(HookManager.MouseHookProc);

hHookMouse = Win32.SetWindowsHookEx(Win32.WH_MOUSE_LL, mouseHookProcedure, Win32.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), 0); if (hHookMouse == 0) throw new Exception("SetWindowHookEx failed for InstallMouseHook"); } }

private void InstallKeybHook() { if (hHookKeyb == 0) { if (keybHookProcedure == null) keybHookProcedure = new Win32.HookProc(HookManager.KeybHookProc);

hHookKeyb = Win32.SetWindowsHookEx(Win32.WH_KEYBOARD_LL, keybHookProcedure, Win32.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), 0);

if (hHookKeyb == 0) throw new Exception("SetWindowHookEx failed for InstallKeybHook"); } }

// Callback function for hooks. public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);

Page 71: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

71

Przechwytywanie komunikatów myszkipublic static int MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam) { // Convert the data from unmanaged memory to protected structure (MouseHookStruct). // kParam contains pointer to the unmanaged memory. Win32.MouseHookStruct MyMouseHookStruct = (Win32.MouseHookStruct) Marshal.PtrToStructure(lParam, typeof(Win32.MouseHookStruct));

if (nCode >= 0) { if (CheckProcess()) {

if ( (nCode >= 0) && (recorder != null)) recorder.appendMouseEvent((int)wParam, MyMouseHookStruct.pt.x, MyMouseHookStruct.pt.y);

/* Sample code to process the mouse event message. */ // Create a string variable that shows the current mouse coordinates. String strCaption = " MouseEvent: " + wParam.ToString("d") + " x = " + MyMouseHookStruct.pt.x.ToString("d") + " y = " + MyMouseHookStruct.pt.y.ToString("d");

int mouseCode = (int) wParam; if (mouseCode != Win32.WM_MOUSEMOVE) { System.Console.Out.WriteLine("Myszka: " + strCaption); } System.Console.Out.WriteLine("Myszka: " + strCaption); /**/ } }

return Win32.CallNextHookEx(hHookMouse, nCode, wParam, lParam); }

Page 72: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

72

Przechwytywanie klawiatury

public static int KeybHookProc(int nCode, IntPtr wParam, IntPtr lParam) { if ((nCode >= 0) && (recorder != null)) recorder.appendKeyEvent((int)wParam, Marshal.ReadInt32(lParam));

return Win32.CallNextHookEx(hHookKeyb, nCode, wParam, lParam); }

● Tylko rejestracja w nagrywarce:

Page 73: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

73

Nagrywarka● lista ze zdarzeniami

class MacroRecorder { private List<MMEvent> events = new List<MMEvent>(); (...) public void appendKeyEvent(int wParam, int lParam) { if (started) { events.Add(new MMEvent(wParam, lParam, MMEventType.Keyboard, DateTime.Now.Subtract(_start) )); } }

public void appendMouseEvent( int wParam, int x, int y) { if (started) { int lParam = (x << 16) + y; events.Add(new MMEvent(wParam, lParam, MMEventType.Mouse, DateTime.Now.Subtract(_start) )); } }

(…)}

Page 74: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

74

Iteracja po komunikatach(...) public MMEvent getFirstPlayback() { if (started) throw new Exception("Can not enumerate when recording is ON. Turn OFF recording first.");

playbackStartedTime = DateTime.Now;

enumerator = events.GetEnumerator(); if (enumerator.MoveNext()) { return enumerator.Current; } return null; }

public MMEvent getNextPlaybackWait() { if (started) throw new Exception("Can not enumerate when recording is ON. Turn OFF recording first.");

MMEvent ev = getNextPlaybackNoWait(); if (ev != null) {

System.DateTime dt = playbackStartedTime; dt = dt.Add(enumerator.Current.time); while ( DateTime.Now.CompareTo(dt) < 0) ;

}

return ev; }(...)

Page 75: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

75

Procedura odtwarzania komunikatów

(...) MacroModule.MMSendInput si = new MacroModule.MMSendInput(); MacroModule.MMEvent ev; ev = recorder.getFirstPlayback();

while (ev != null) {

if (ev.type == MacroModule.MMEventType.Keyboard) {

if ( (ev.wParam == Win32.WM_KEYDOWN) || (ev.wParam == Win32.WM_SYSKEYDOWN) ) si.SendKeyDown((Keys)ev.lParam, '\0');

if ( (ev.wParam == Win32.WM_KEYUP) || (ev.wParam == Win32.WM_SYSKEYUP) ) si.SendKeyUp((Keys)ev.lParam, '\0'); }

if (ev.type == MacroModule.MMEventType.Mouse) { int x = ev.lParam >> 16; int y = (ev.lParam << 16) >> 16; si.SendMouseEvent(ev.wParam, x, y); }

ev = recorder.getNextPlaybackWait(); }

(...)

Page 76: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

76

Symulacja klawiaturypublic void SendKeyDown(Keys vkKey, char key) { Win32.KEYBDINPUT keyb = new Win32.KEYBDINPUT(); keyb.dwFlags = 0; keyb.time = 0; keyb.dwExtraInfo = Win32.GetMessageExtraInfo(); keyb.wVk = (ushort) vkKey; keyb.wScan = key;

Win32.INPUT[] inputs = new Win32.INPUT[1]; inputs[0].type = Win32.INPUT_KEYBOARD; inputs[0].ki = keyb; int isize = Marshal.SizeOf(inputs[0]); Win32.SendInput(1, inputs, isize); }

public void SendKeyUp(Keys vkKey, char key) { Win32.KEYBDINPUT keyb = new Win32.KEYBDINPUT(); keyb.dwFlags = Win32.KEYEVENTF_KEYUP; keyb.time = 0; keyb.dwExtraInfo = Win32.GetMessageExtraInfo(); keyb.wVk = (ushort)vkKey; keyb.wScan = key;

Win32.INPUT[] inputs = new Win32.INPUT[1]; inputs[0].type = Win32.INPUT_KEYBOARD; inputs[0].ki = keyb; int isize = Marshal.SizeOf(inputs[0]); Win32.SendInput(1, inputs, isize); }

Page 77: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

77

Symulacja myszkipublic void SendMouseEvent(int msg, int x, int y) { Win32.MOUSEINPUT mouse = new Win32.MOUSEINPUT(); mouse.dx = VirtualXFromAbsoluteX(x); mouse.dy = VirtualYFromAbsoluteY(y); mouse.time = 0; mouse.dwExtraInfo = Win32.GetMessageExtraInfo(); mouse.mouseData = 0;

switch (msg) { case Win32.WM_MOUSEMOVE: mouse.dwFlags = Win32.MOUSEEVENTF_MOVE; break; case Win32.WM_LBUTTONDOWN: mouse.dwFlags = Win32.MOUSEEVENTF_LEFTDOWN; break; case Win32.WM_LBUTTONUP: mouse.dwFlags = Win32.MOUSEEVENTF_LEFTUP; break; default: // Unknown message type. Exiting method. return; }

mouse.dwFlags = mouse.dwFlags | Win32.MOUSEEVENTF_ABSOLUTE; Win32.INPUT[] inputs = new Win32.INPUT[1]; inputs[0].type = Win32.INPUT_MOUSE; inputs[0].mi = mouse; int isize = Marshal.SizeOf(inputs[0]);

Win32.SendInput(1, inputs, isize); }

Page 78: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

78

Konwersja współrzędnych static int VirtualXFromAbsoluteX(int iX) {

double Width = Win32.GetSystemMetrics(Win32.SystemMetric.SM_CXSCREEN); double Val = (((double)iX / Width) * (double)(65535));

double Ceil = Math.Ceiling(Val); double Floor = Math.Floor(Val);

if (Ceil > Floor) { iX = (int)Ceil; } else { iX = (int)Floor; } return (iX); }

static int VirtualYFromAbsoluteY(int iY) {

double Height = Win32.GetSystemMetrics(Win32.SystemMetric.SM_CYSCREEN); double Val = (((double)iY / Height) * (double)(65535));

double Ceil = Math.Ceiling(Val); double Floor = Math.Floor(Val);

if (Ceil > Floor) { iY = (int)Ceil; } else { iY = (int)Floor; } return (iY); }

Page 79: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

79

Qt – Obsługa komunikatów● Tylko część komunikatów jest obsługiwana przez rozszerzenie języka w postaci

preprocesora „moc” – Sloty i Sygnały● Większość metod jest wirtualna; przygotowana do standardowego przesłaniania

(protected virtual)● Qt posiada własną obsługę komunikatów na poziomie aplikacji (procesu)

class Ticker : public QWidget{ Q_OBJECT Q_PROPERTY(QString text READ text WRITE setText)public: Ticker(QWidget *parent = 0, const char *name = 0); void setText(const QString &newText); QString text() const { return myText; } QSize sizeHint() const;protected: void paintEvent(QPaintEvent *event); void timerEvent(QTimerEvent *event); void showEvent(QShowEvent *event); void hideEvent(QHideEvent *event);private: QString myText; int offset; int myTimerId;};

Page 80: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

80

Kod klasy generującej zdarzenia (Sygnały)

#include <QObject> class Counter : public QObject { Q_OBJECT public: Counter() { m_value = 0; } int value() const { return m_value; } public slots: void setValue(int value); signals: void valueChanged(int newValue); private: int m_value; };

(...)

void Counter::setValue(int value) { if (value != m_value) { m_value = value; emit valueChanged(value); } }

(...)

przyklad.h przyklad.cpp

● Załóżmy klasę licznik, która posłuży jako przykład generatora zdarzeń oraz „słuchacza”.

● Na początku fragmenty odpowiadające za „emisję” zdarzeń.

Page 81: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

81

Kod klasy rejestrującej zdarzenia (SLOTY)

#include <QObject> class Counter : public QObject { Q_OBJECT public: Counter(); int value() const { return m_value; } public slots: void setValue(int value); signals: void valueChanged(int newValue); private: int m_value; };

przyklad.h

(...) Counter::Counter() { m_value = 0; connect( wysylacz, SIGNAL(valueChanged(int)), this, SLOT(setValue(int) ); }

void Counter::setValue(int value) { if (value != m_value) { m_value = value; } }

(...)

przyklad.cpp

1. Podpinamy sygnał emitowany przez klasę „wysyłacz”, do naszej metody

obsługi zdarzenia.Uwaga! Qt kontroluje typy!

Page 82: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

82

Qt – przechwytywanie komunikatów● Możemy przesłonić poszczególne wirtualne procedury obsługi:

– np.: mousePressEvent(), keyPressEvent(), paintEvent()

● Możemy przesłonić metodę QObject::event()– Nasza metoda będzie wywołana przed poszczególnymi procedurami obsługi

(musimy pamiętać aby wywołać metode event() z klasy Qobject, bo to ona zajmuje się przekazywaniem zdarzenia do odpowiedniej procedury obsługi)

● Możemy zainstalować filtr komunikatów w pojedynczym obiekcie typu QObject:

– installEventFilter()

● Możemy zainstalować filtr komunikatów na poziomie całej aplikacji:– EventFilter setEventFilter ( EventFilter filter )

● Możemy zaimplementować własna klasę aplikacji i przesłonić metodę notify()

– bool QCoreApplication::notify ( QObject * receiver, QEvent * event ) [virtual]– ta metoda daje możliwość dostępu do absolutnie wszystkich komunikatów,

które otrzymuje aplikacja z danego systemu graficznego

Page 83: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

83

Xlib – Tworzenie okien

Window win;int border_width = 4; /* Border four pixels wide */unsigned int width, height; /* Window size */int x,y; /* Window position */ . ./* Open display, determine screen dimensions */screen_num = DefaultScreen(display); ./* Note that in a real application, x and y would default to 0 but * would be settable from the command line or resource database */x = y = 0;/* Size window with enough room for text */width = display_width/3, height = display_height/4;/* Create opaque window */win = XCreateSimpleWindow(display, RootWindow(display, screen_num), x, y, width, height, border_width, BlackPixel(display, screen_num), WhitePixel(display, screen_num));

Page 84: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

84

Pętla komunikatów X11

int main() {

(…)

XSelectInput(d, w, ExposureMask | KeyPressMask); XMapWindow(d, w);

while (1) { XNextEvent(d, &e); if (e.type == Expose) { XFillRectangle(d, w, DefaultGC(d, s), 20, 20, 10, 10); XDrawString(d, w, DefaultGC(d, s), 50, 50, msg, strlen(msg)); } if (e.type == KeyPress) break; } XCloseDisplay(d); (...)

1. Informujemy serwer X Windowktóre komunikaty chcemy

przechwytywać.

2. Jedna pętla obsługi komunikatów.W systemie X Window okna

zazwyczajsą pojedyncze i kontrolowane

przez pętlę obsługi komunikatówdanego programu. (Nie ma

DispatchMessage())

Page 85: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

85

Przykład pętli komunikatów...XEvent report;Window window1, window2, window3; /* Open display, create windows, etc. *//* Window 1 is a top-level window, window 2 is a child of window 1 */XSelectInput(display, window1, StructureNotifyMask | ExposureMask | ButtonPressMask);XSelectInput(display, window2, ExposureMask);XSelectInput(display, window3, ExposureMask);XMapWindow(display, window1);XMapWindow(display, window2);XMapWindow(display, window3);while (1){ /* Get any type of event on any window; this gets * events on every window for which we have selected * events (three in this case) */ XNextEvent(display, &report); switch (report.type) { case Expose: printf("got an Expose event\n"); /* Redraw contents of windows; note that we can't * use switch because window IDs are not constant */ if (report.xexpose.window == window1) /* Redraw window 1 */; else if (report.xexpose.window == window2) /* Redraw window 2 */; else (report.xexpose.window == window3) /* Redraw window 3 */; break; (...) } /* End switch on event type */} /* End while (1) */

1. Jedna pętla obsługi komunikatów.W systemie X Window okna

zazwyczajsą pojedyncze i kontrolowane

przez pętlę obsługi komunikatówdanego programu. (Nie ma

DispatchMessage())

Page 86: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

86

Komunikaty w Xlib

typedef struct { int type; /* The type of event */ unsigned long serial; /* # of last request processed by server */ Bool send_event; /* True if sent from a SendEvent request */ Display *display; /* Display the event was read from */ Window window; /* Window that receives event */} XAnyEvent;

● Komunikat XEvent to struktura/unia:

● Każdy komunikat posiada XEnyEvent:

typedef union _XEvent { int type; /* Must not be changed; first member */ XAnyEvent xany; XKeyEvent xkey; XButtonEvent xbutton; XMotionEvent xmotion; XCrossingEvent xcrossing; XFocusChangeEvent xfocus; XKeymapEvent xkeymap; XExposeEvent xexpose; XNoExposeEvent xnoexpose; XGraphicsExposeEvent xgraphicsexpose; XVisibilityEvent xvisibility; XCreateWindowEvent xcreatewindow; XDestroyWindowEvent xdestroywindow; XUnmapEvent xunmap; XMapEvent xmap; XMappingEvent xmapping; XMapRequestEvent xmaprequest; XReparentEvent xreparent; XConfigureEvent xconfigure; XGravityEvent xgravity; XResizeRequestEvent xresizerequest; XConfigureRequestEvent xconfigurerequest; XCirculateEvent xcirculate; XCirculateRequestEvent xcirculaterequest; XPropertyEvent xproperty; XSelectionClearEvent xselectionclear; XSelectionRequestEvent xselectionrequest; XSelectionEvent xselection; XColormapEvent xcolormap; XClientMessageEvent xclient;} XEvent;

Page 87: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

87

Xlib cały program... #include <X11/Xlib.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { Display *d; Window w; XEvent e; char *msg = "Hello, World!"; int s; /* open connection with the server */ d = XOpenDisplay(NULL); if (d == NULL) { fprintf(stderr, "Cannot open display\n"); exit(1); } s = DefaultScreen(d); /* create window */ w = XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 100, 100, 1, BlackPixel(d, s), WhitePixel(d, s)); /* select kind of events we are interested in */ XSelectInput(d, w, ExposureMask | KeyPressMask); /* map (show) the window */ XMapWindow(d, w); /* event loop */ while (1) { XNextEvent(d, &e); /* draw or redraw the window */ if (e.type == Expose) { XFillRectangle(d, w, DefaultGC(d, s), 20, 20, 10, 10); XDrawString(d, w, DefaultGC(d, s), 50, 50, msg, strlen(msg)); } /* exit on key press */ if (e.type == KeyPress) break; } /* close connection to server */ XCloseDisplay(d); return 0; }

Page 88: Projektowanie Graficznych Interfejsów Użytkownikaprpw.iem.pw.edu.pl/wyniki.21-3/pgui/L9_komunikaty.pdf · 2011. 12. 7. · Komunikaty stanowią trzon działania graficznych interfejsów

88

Interakcja

● Dziękuję za uwagę.

● Chcemy być coraz lepsi!● Jeżeli coś cię zainteresowało napisz e-maila:

[email protected]

● Jeżeli coś cię bardzo znudziło napisz e-maila:– [email protected]

● Jeżeli zauważyłeś błąd napisz e-maila:– [email protected]