gui - Úvod
DESCRIPTION
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 - PowerPoint PPT PresentationTRANSCRIPT
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
Aplikace řízené událostmiEvent driven applications
• Také Event driven programming– Používá se např. v OS (přerušení)– GUI
Fronta zprávZprávaZprávaZprávaZpráva
Zpráva
Výběr zprávy
Odeslání zprávy
(dispatch)
Smyčkazpráv
Zpracovánízprávy
Novézprávy
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
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?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
• ATOM RegisterClassEx(CONST WNDCLASSEX *lpwcx );
• Vrací ATOM• Všimněte si nastavení velikosti struktury• Definice procedury 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)
Vytvoření okna• 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
HWND CreateWindow( LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam);
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ímeBOOL 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 vykreslitBOOL 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
Smyčka událostí
Smyčka událostí (zpráv) obecně
Fronta zprávZprávaZprávaZprávaZpráva
Zpráva
Výběr zprávy
Odeslání zprávy
(dispatch)
Smyčkazpráv
Zpracovánízprávy
Novézprávy
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ávyBOOL 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ávyLRESULT 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 oknatypedef 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 oknaLRESULT 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)
DialogyHWND 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
//{{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
//{{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
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;
}