GUI - Úvod Něco málo teorie Win 32 API Aplikace řízené tokem událostí (Event driven applications) MVC – Model View Controller Win 32 API Okna a zprávy
Aplikace řízené událostmi Event driven applications Také Event driven programming Používá se např. v OS (přerušení) GUI Fronta zpráv Zpráva Výběr zprávy Odeslání zprávy (dispatch) Smyčka zpráv Zpracování zprávy Nové
MVC Model 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 Jak to funguje Vstup od uživatele (např. zmáčknutí tlačítka v GUI) 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
Win 32 API GUI – okna, zprávy
Okna Literatura a turoriály Stránky Microsoftu MSDN Knihy o Win 32 API http://www.stromcode.com/modules.php?n ame=Glowdot_Tutorials&page=1&tid=1&o p=view http://www.winprog.org/tutorial/ Google :-)
Okna Všechno je okno Prvky GUI mají svůj handle 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 Zpracování zpráv 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 Vrací ATOM 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 Dostupné třídy oken poskytované přímo Windows COMBOBOX 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í Zruší okno Zruší menu Zruší timery 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_VISIBLE př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 Má navíc parametry 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
Smyčka událostí Smyčka událostí (zpráv) obecně Fronta zpráv Nové Zpráva Výběr zprávy Odeslání zprávy (dispatch) Smyčka zpráv Zpracování zprávy Nové
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í Pokud žádné zprávy nejsou, tak čéká (blokuje) 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í Návratová hodnota je BOOL, ale 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í Přeloží zprávu - virtual-keys -> char 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í Doručí zprávu proceduře daného okna 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 Pošle zprávu danému oknu 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 Pošle zprávu danému oknu 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 Obsluhuje zprávy poslané danému oknu 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;
if(!RegisterClassEx(&wc)) return -1; #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 Každý dialog má okenní proceduru 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
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
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;