410 likes | 597 Views
GUI - Úvod. Něco málo teorie Aplikace řízené tokem událostí (Event driven applications) MVC – Model View Controller Win 32 API Okna a zpr á v y. Fronta zpráv. Zpráva. Zpráva. Nové zprávy. Zpráva. Zpráva. Zpráva. Smyčka zpráv. Výběr zprávy. Zpracování zprávy. Odeslání zprávy
E N D
GUI - Úvod • Něco málo teorie • Aplikace řízené tokem událostí (Event driven applications) • MVC – Model View Controller • Win 32 API • Okna a zprávy
Fronta zpráv Zpráva Zpráva Nové zprávy Zpráva Zpráva Zpráva Smyčka zpráv Výběr zprávy Zpracování zprávy Odeslání zprávy (dispatch) Aplikace řízené událostmiEvent driven applications • Také Event driven programming • Používá se např. v OS (přerušení) • GUI
MVCModel View Controller • 1979 - Trygve Mikkjel – SmallTalk (Xerox) • SW architektura, která odděluje • Model = Datový model • View = Interface a zobrazení (např. GUI) • Controller = Řídící logika, reakce na události • Použit např. v • MFC • Od QT od release 4 • NeXTStep • Java SWING (?) • Windows Presentation Foundation (WPF) • Podobné MVC • .Net 3.0 (původně WinFX), nativně na Vistách
MVC • Plné čáry – přímé spojení • Čárkované čáry – nepřímé spojení • Jak to funguje • Vstup od uživatele (např. zmáčknutí tlačítka v GUI) • Controller zpracuje uživatelský vstup (handler nebo callback) • zaktualizuje model dle vstupu • View upraví dle modelu svůj vzhled (GUI) • Model by neměl mít přímé povědomí o View(View si získává data od modelu) • Ale může si zaregistrovat akce pro změny položek modelu (Observer, Listener) • Controller nepředává Model přímo View
GUI – okna, zprávy Win 32 API
Okna Literatura a turoriály • Stránky Microsoftu • MSDN • Knihy o Win 32 API • http://www.stromcode.com/modules.php?name=Glowdot_Tutorials&page=1&tid=1&op=view • http://www.winprog.org/tutorial/ • Google :-)
Okna • Všechno je okno • Včetně olvádacích prvků • controls (prvky GUI) - widgety • Prvky GUI mají svůj handle • většina GUI funkcí používá handle • V C definovány typy pro různé handle • Začínají na H – HICON, HBRUSH .... • Také okno je určeno svým handlem • HWND
Okna • Vytvoření okenní aplikace • Hlavní okno • Registrace třídy okna • Vytvoření okna • Zobrazení okna • Zpracování zpráv • Smyčka událostí • Vybíraní zprávy • Dispatching
Oknočásti okna • Každé okna má určité části • Zobrazení daných částí je řízeno • styly okna • typem okna
Registrace třídy okna • Třída okna (myšleno jako druh okna) • Nemá přímou souvislost s OOP • Nezaměňovat s třídami v C++ nebo v C# • Struktura WNDCLASS nebo WNDCLASSEX • Zavolání RegisterClass nebo RegisterClassEx
Registrace třídy okna WNDCLASSEX WndClsEx; WndClsEx.cbSize = sizeof(WNDCLASSEX); WndClsEx.style = CS_HREDRAW | CS_VREDRAW; WndClsEx.lpfnWndProc = WndProcedure; WndClsEx.cbClsExtra = 0; WndClsEx.cbWndExtra = 0; WndClsEx.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClsEx.hCursor = LoadCursor(NULL, IDC_ARROW); WndClsEx.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); WndClsEx.lpszMenuName = NULL; WndClsEx.lpszClassName = "MojeTestovaciOkno"; WndClsEx.hInstance = hInstance; WndClsEx.hIconSm = LoadIcon(NULL, IDI_APPLICATION); RegisterClassEx(&WndClsEx) • ATOM RegisterClassEx(CONST WNDCLASSEX *lpwcx ); • Vrací ATOM • Všimněte si nastavení velikosti struktury • Definice procedury okna
Vytvoření okna HWND CreateWindow( LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam ); • Vytvoří okno a vrátí handle na něj nebo NULL • lpClassName může být jméno okna nebo atom třídy • Dostupné třídy oken poskytované přímo Windows • COMBOBOX • EDIT • LISTBOX • MDICLIENT • RichEdit • RICHEDIT_CLASS • SCROLLBAR • STATIC
Zničení okna • Okno se zruší pomocí BOOL DestroyWindow( HWND hWnd // handle to window to destroy ); • Zruší okno • Zruší menu • Zruší timery • Vyprázdní frontu zpráv pro dané okno • Zruší také okna, které jsou vlastněné daným oknem a také child okna
Zobrazení okna • Vytvořené okno zobrazíme BOOL ShowWindow( HWND hWnd, // handle to window int nCmdShow // show state ); nCmdShow – SW_HIDE, SW_SHOW, SW_MAXIMIZE, SW_MINIMIZE, … Pokud se nastavíWS_VISIBLEpři volání CreateWindow, tak se okno automaticky zobrazí • Vytvořené okno necháme vykreslit BOOL UpdateWindow( HWND hWnd // handle to window ); • Překreslí client area (pošle zprávu)
Smyčka událostí • Windows doručují zprávy do fronty událostí • Windows nevolají přímo call back funkce • Aplikace si musí zprávy sama vyzvednout • Je možné filtrovat zprávy • Zprávy je pak možné předat daným call back funkcím
Smyčka událostí • Zpráva je • Definována pomocí ID • integer • Definovány v include souborech (WinUser.h) • Má navíc parametry • wParam typu WPARAM (32-bitový integer na Win32) • lParam typu LPARAM (32-bitový integer na Win32) • Parametry jsou specifické pro danou zprávu a můžou mít různý význam • Lze si nadefinovat i vlastní zprávy
Fronta zpráv Zpráva Zpráva Nové zprávy Zpráva Zpráva Zpráva Smyčka zpráv Výběr zprávy Zpracování zprávy Odeslání zprávy (dispatch) Smyčka událostí Smyčka událostí (zpráv) obecně
Smyčka událostí • Ve Windows se smyčka událostí realizuje takto: BOOL code; while((code=GetMessage(&msg,NULL,0,0))!= 0) { if (code==-1) { // nastala chyba } else { TranslateMessage(&msg); DispatchMessage(&msg); } }
Smyčka událostí BOOL GetMessage( LPMSG lpMsg, // message information HWND hWnd, // handle to window UINT wMsgFilterMin, // first message UINT wMsgFilterMax // last message ); • Pokud žádné zprávy nejsou, tak čéká (blokuje) • Do proměnné lpMsg uloží zprávu určenou pro okno hWnd • hWwnd – handle okna • NULL – zprávy pro jakékoliv okno nebo daný thread • wMsgFilterMin a wMsgFilterMax • Přijme jenom zprávy, jejichž ID jsou v daném rozsahu
Smyčka událostí • Formát zprávy je následující typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; } MSG, *PMSG; • pt – souřadnice kurzoru myši v obrazovkových souřadnicích v době, kdy zpráva byla poslána • time – čas v okamžiku poslání zprávy
Smyčka událostí BOOL GetMessage( LPMSG lpMsg, // message information HWND hWnd, // handle to window UINT wMsgFilterMin, // first message UINT wMsgFilterMax // last message ); • Návratová hodnota je BOOL, ale • Vrátí 0: byla poslána zpráva WM_QUIT • Vráti -1: nastala chyba (dá se zjistit pomocí GetLastError) • Vrátí cokoli jiného: přišla zpráva
Smyčka událostí BOOL PeekMessage( LPMSG lpMsg, // message information HWND hWnd, // handle to window UINT wMsgFilterMin, // first message UINT wMsgFilterMax, // last message UINT wRemoveMsg // removal options ); • Funkce podobná GetMesage, ale pokud nejsou žádné zprávy, tak se hned vrátí • Vrátí 0 pokud nejsou žádné zprávy • Poslední parametr určuje, zda-li se má zpráva z fronty odstranit (GetMesage vždy zprávu odstraní)
Smyčka událostí BOOL TranslateMessage( CONST MSG *lpMsg // message information ); • Přeloží zprávu - virtual-keys -> char • WM_KEYDOWN, WM_KEYUP přeloží na WM_CHAR nebo WM_DEADCHAR • WM_SYSKEYDOWN, WM_SYSKEYUP přeloží na WM_SYSCHAR nebo WM_SYSDEADCHAR • Nemodifikuje danou zprávu – pošle novou zprávu • Vrátí 0, pokud zpráva není přeložena • Vrátí nenulovou hodnotu, pokud byla zpráva přeložena
Smyčka událostí LRESULT DispatchMessage( CONST MSG *lpmsg // message information ); • Doručí zprávu proceduře daného okna • Návratová hodnota je dána návratovou hodnotou okenní procedury • Většinou se ignoruje • Pokud je zpráva typu WM_TIMER a lParam není NULL • lParam obsahuje adresu funkce, která se zavolá místo procedury okna
Smyčka událostí Pořadí zpráv, jak se doručují • Sent messages • Posted messages • Input (hardware) messages and system internal events • Sent messages (again) • WM_PAINT messages • WM_TIMER messages
Poslání zprávy BOOL PostMessage( HWND hWnd, // handle to destination window UINT Msg, // message WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ); • Pošle zprávu danému oknu • Zpráva se uloží do smyčky zpráv • Nečeká se na výsledek, program pokračuje hned dále • Vrátí nulu, pokud volání selže
Poslání zprávy LRESULT SendMessage( HWND hWnd, // handle to destination window UINT Msg, // message WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ); • Pošle zprávu danému oknu • Zavolá proceduru okna - nejde přes smyčku zpráv • Pokud je okno ze stejného threadu zavolá proceduru přímo (jako podprogram) • Pokud ne, tak je ji potřeba zpracovat v druhém threadu – uloží se do fronty zpráv • Čeká se, dokud zpráva není zpracována
Procedura okna typedef struct _WNDCLASSEX { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; HICON hIconSm; } WNDCLASSEX, *PWNDCLASSEX; • Zadává se při registraci třídy okna pomocí RegisterClassEx • Položka: WNDPROC lpfnWndProc
Procedura okna LRESULT CALLBACK WndProcedure(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); • Obsluhuje zprávy poslané danému oknu • Většinou implementováno jako velký switch • Pokud procedura okna danou zprávu neobslouží, tak by se měl zavolat standardní handler • return DefWindowProc(hwnd, uMsg, wParam, lParam);
#include <windows.h> LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_CLOSE: { DestroyWindow(hwnd); return 0; } case WM_DESTROY:{ PostQuitMessage(0); return 0; } } return DefWindowProc(hwnd, msg, wParam, lParam); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.lpszMenuName = NULL; wc.lpszClassName = "X36APIClass"; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) COLOR_APPWORKSPACE; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&wc)) return -1; HWND hwnd=CreateWindowEx(WS_EX_CLIENTEDGE,"X36APIClass","API",WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,256, 128,NULL, NULL, hInstance, NULL); if(hwnd==NULL) return -1; ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); BOOL code; MSG msg; while((code=GetMessage(&msg,NULL,0,0))!= 0) { if (code==-1) return -1; TranslateMessage(&msg); DispatchMessage(&msg); } return (int) msg.wParam; }
#include <windows.h> HWND button; LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_CLOSE: { DestroyWindow(hwnd); return 0; } case WM_DESTROY:{ PostQuitMessage(0); return 0; } case WM_COMMAND: { if((HWND) lParam==button) MessageBox(NULL,"Clicked!","Hey",MB_OK); return 0; } } return DefWindowProc(hwnd, msg, wParam, lParam); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.lpszMenuName = NULL; wc.lpszClassName = "X36APIClass"; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) COLOR_APPWORKSPACE; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&wc)) return -1; HWND hwnd=CreateWindowEx(WS_EX_CLIENTEDGE,"X36APIClass","API",WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,256, 128,NULL, NULL, hInstance, NULL); button=CreateWindow("BUTTON","Click!",WS_CHILDWINDOW | WS_VISIBLE | BS_PUSHBUTTON, 10,10,100,25,hwnd, NULL, hInstance, NULL); SetParent(button,hwnd); ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); BOOL code; MSG msg; while((code=GetMessage(&msg,NULL,0,0))!= 0) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int) msg.wParam; }
Dialogy • Součástí aplikace můžou být resources • Lze je definovat v MSVC • Různé typy dat • V programu lze s nimi pracovat
Dialogy • Součástí resource jsou i šablony dialogů • Lze si „naklikat“ dialog v MSVC • Ošetření zpráv se, ale musí udělat ručně (nemluvíme zde o MSFC)
Dialogy HWND CreateDialog( HINSTANCE hInstance, // handle to module LPCTSTR lpTemplate, // dialog box template name HWND hWndParent, // handle to owner window DLGPROC lpDialogFunc // dialog box procedure ); • Každý dialog má okenní proceduru • Má jiný tvar než „normální“ procedura okna • BOOL CALLBACK DialogProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam) • Vrací TRUE pokud byla zpráva zpracována, jinak FALSE • Nesmí se volat DefWindowProc
Dialogy • Pokud používáte dialog z resources • #include "resource.h" • V include souboru jsou konstanty pomocí kterých lze data z reousrces dostat • Makro – MAKEINTRESOURCE(IDD_DIALOG1) • IDD_DIALOG1 = id dialogu CreateDialog(hInstance,MAKEINTRESOURCE(IDD_DIALOG1),NULL,DialogProc);
Dialogy • Aby se správně zpracovávaly klávesové zkratky, je potřeba modifikovat hlavní smyčku událostí while((code=GetMessage(&msg,NULL,0,0))!= 0) { if (code==-1) return -1; if(!IsDialogMessage(dialog, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
Dialogy - příklad • Následující kód vytvoří tento dialog pos stisknutí tlačítka
Dialogy - příklad • Automaticky vygenerovaný soubor resource1.h //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by GUI_HelloWorld1.rc // #define IDD_DIALOG1 101 #define IDC_EDIT1 1004 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1005 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
Dialogy - příklad • Automaticky vygenerovaný soubor resource1.h • Resource soubor (.rc) je textový soubor, který popisuje dané resources //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by GUI_HelloWorld1.rc // #define IDD_DIALOG1 101 #define IDC_EDIT1 1004 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1005 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int show) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.lpszMenuName = NULL; wc.lpszClassName = "X36APIClass"; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) COLOR_APPWORKSPACE; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&wc)) return -1; HWND hwnd=CreateWindowEx(WS_EX_CLIENTEDGE,"X36APIClass","API",WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 256, 128,NULL, NULL, hInstance, NULL); button=CreateWindow("BUTTON","Click!",WS_CHILDWINDOW | WS_VISIBLE | BS_PUSHBUTTON, 10,10,100,25,hwnd, NULL, hInstance, NULL); dialog=CreateDialog(hInstance,MAKEINTRESOURCE(IDD_DIALOG1),NULL,DialogProc); SetParent(button,hwnd); ShowWindow(hwnd, SW_SHOW); UpdateWindow(hwnd); BOOL code; MSG msg; while((code=GetMessage(&msg,NULL,0,0))!= 0) { if(code==-1) return -1; if(!IsDialogMessage(dialog, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int) msg.wParam; }
#include <windows.h> #include "resource1.h" HWND button; HWND dialog=NULL; LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_CLOSE: { DestroyWindow(hwnd); return 0; } case WM_DESTROY:{ PostQuitMessage(0); return 0; } case WM_COMMAND: { if((HWND) lParam==button) { ShowWindow(dialog,SW_SHOW); } return 0; } } return DefWindowProc(hwnd, msg, wParam, lParam); } BOOL CALLBACK DialogProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam) { if(uMsg==WM_COMMAND && ((LOWORD(wParam))==IDOK || (LOWORD(wParam))==IDCANCEL)) { ShowWindow(hwndDlg,SW_HIDE); return TRUE; } return FALSE; }