Sunteți pe pagina 1din 81

Programarea pilotată

de evenimente
Event driven programming
Formalități

Prelegeri – 30/14 ore = 4/2 ore/săpt.


Lucr. lab. – 30/14 ore = 2/1 ore/săpt.
Evaluare intermediară – săptămâna 4
O atestare - săptămâna 6
Examen final (toate lab. susţinute!!!!!)

2/26/2020 6:59:43 2
AM
Literatura
Charles Petzold. Programarea în Windows 95, - Editura Teora,
Bucureşti, 1996, 1062 pp.
V.Beşliu. Ciclu de prelegeri la disciplina “Programarea în
Windows”, Varianta de calculator, UTM,
http://elearning.utm.md/moodle/
V.Beşliu ş.a. Îndrumar metodic pentru lucrări de laborator la
disciplina “Programarea în Windows”, UTM, 2006, 68 pp.
Doru Turturea. Programarea aplicaţiilor Windows în limbajul C.
Editura Tehnică, Bucureşti, 1995, 540 pp.
Peter Norton, Paul Yao. Windows 3.1 Advanced Programming
Techniques. – Sybex, 1992
Florica Moldoveanu, Gabriel Hera. Programarea aplicaţiilor
Windows, - Editura Teora, Bucureşti, 1994

2/26/2020 6:59:43 AM 3
Programarea pilotată de evenimente
Paradigmă de programare în care execuția programului este
determinată de evenimente: o acțiune a unui utilizator cum ar fi
un click de mouse, apăsarea de taste, un mesaj din sistemul de
operare sau din alt program.
O aplicație bazată pe evenimente este concepută pentru a
detecta evenimentele pe măsură ce acestea apar și apoi pentru a
le procesa, folosind o procedură adecvată de tratare a lor.
Ideea este o extensie a programării controlate de întreruperi,
de tipul celor folosite în sistemele de operare sau în sistemele
încorporate.
Programele bazate pe evenimente pot fi scrise în orice limbaj de
programare, deși unele limbaje sunt proiectate special pentru a
facilita acest tip de programare și oferă medii integrate de
dezvoltare (IDE).

2/26/2020 6:59:43 AM 4
Scurt istoric
Înainte event-handlerele erau implementate ca subrutine în cadrul
programului procedural. Fluxul de execuție a programului era
determinat de către programator și controlat de rutina principală a
aplicației. Programul trebuia foarte bine structurat.
Într-un program controlat de evenimente nu există un flux de control.
Rutina principală conține o buclă de dispecerizare a evenimentelor
(dispecer de evenimente), care atunci când apare un eveniment
apelează procedura adecvată de tratare a acestuia.
Deoarece codul pentru bucla de dispecerizare a evenimentelor este
furnizat de obicei de mediul sau cadrul de dezvoltare bazat pe
evenimente și în mare parte este invizibil pentru programator,
percepția programatorului asupra aplicației este cea a unei colecții de
rutine de tratare a evenimentelor. Programatorii obișnuiți cu
programarea procedurală uneori consideră că trecerea la un mediu
pilotat de evenimente solicită un efort mental considerabil.

2/26/2020 6:59:43 AM 5
Schimbarea accentului
Trecerea de la programarea procedurală la
programarea pilotată de evenimente a pornit odată
cu introducerea limbajelor de programare orientate
pe obiecte și a noilor metodologii de dezvoltare de la
sfârșitul anilor 1970 și a fost accelerată de
introducerea interfeței grafice de utilizator (GUI),
adoptată pe scară largă pentru utilizare în sistemele
de operare și aplicațiile utilizatorilor finali.

2/26/2020 6:59:43 AM 6
Despre POO
Una dintre ideile fundamentale care stau în spatele POO
- reprezentarea entităților programabile ca obiecte.
Exemplu: program care modelează executarea comenzilor clienților
într-o companie include obiecte precum "client", "comanda" sau
"item al comenzii". Un obiect include atât datele (atributele)
atașate unei entități, acțiunile (metodele) care pot fi utilizate
pentru a accesa sau modifica atributele entității, cât și
evenimentele care pot determina invocarea metodelor entității.

2/26/2020 6:59:43 AM 7
Legătura dintre POO și PPE
Strânsă legătură!
De exemplu, obiectele de pe o formă Visual C++ (denumite în
mod obișnuit controale) pot fi clasificate în clase (clasa Buton,
clasa TextBox, etc.) și mai multe instanțe pot apărea pe una și
aceeași formă. Fiecare clasă va avea atribute (proprietăți),
care pot fi comune tuturor obiectelor de tipul respectiv (de
exemplu BackgroundColour, Width, etc.) și va defini o listă de
evenimente la care un obiect va răspunde.
Metodele (event-handlerele) pentru gestiunea evenimentelor
sunt furnizate ca șabloane la care programatorul trebuie doar
să adauge codul care efectuează acțiunea necesară.

2/26/2020 6:59:43 AM 8
Cum funcționează PPE
Elementul central al unei aplicații pilotate de evenimente este
partea programului care recepționează evenimentele (dispecerul)
și transmite fiecare eveniment manipulatorului (handler,
procedură de tratare) propriu. Dispecerul rămâne activ până când
va întâlni un eveniment (de exemplu, "End_Program") care îl va
determina să închidă aplicația.
În anumite circumstanțe, dispecerul poate întâlni un eveniment
pentru care nu există un handler adecvat. În funcție de natura
evenimentului, dispecerul poate fie să îl ignore, fie să genereze o
excepție. Într-un mediu de programare pilotat de evenimente,
evenimentele standard sunt de obicei identificate utilizând ID-ul
obiectului afectat de eveniment (ex., numele unui buton de
comandă dintr-o formă) și ID-ul evenimentului (ex., "clic-stânga").

2/26/2020 6:59:43 AM 9
Ce sunt evenimentele
• Acțiuni efectuate de utilizator în timpul executării programului,
• Mesaje generate de sistemul de operare,
• Mesaje generate de o altă aplicație,
• O întrerupere generată de un periferic sau hardware de sistem.

Dacă utilizatorul apasă un buton de mouse sau tasta Enter, va fi


generat un eveniment.
Dacă descărcarea unui fișier este finalizată, aceasta generează un
eveniment.
Dacă există o eroare hardware sau software, va fi generat un
eveniment.

2/26/2020 6:59:43 AM 10
Cine gestionează evenimentele
Evenimentele sunt gestionate de operatorul central de
evenimente (dispecer sau planificator) - ciclu care rulează
continuu în fundal și așteaptă să se întâmple evenimente.
Când are loc un eveniment, dispecerul trebuie să determine
tipul de eveniment și să apeleze handlerul corespunzător.
Informațiile transmise handlerului evenimentului de către
dispecer vor varia, dar vor include date suficiente pentru a
permite codului care tratează evenimentul să ia toate măsurile
necesare.

2/26/2020 6:59:43 AM 11
Cine tratează evenimentele
Handlerele de evenimente - mici blocuri de cod procedural care
tratează un eveniment.
Vor produce un răspuns vizual pentru a informa sau direcționa
utilizatorul, pot schimba starea sistemului.
Starea sistemului cuprinde atât datele utilizate de sistem (de
exemplu, valoarea unui câmp al BD), cât și starea interfeței
utilizatorului (ex.: care obiect de pe ecran deține cursorul de
intrare sau care este culoarea de fundal a unei casete text).
Un event-handler poate chiar să declanșeze un alt eveniment
care va solicita un alt event-handler (atenție la bucle infinite!).
Un event-handler poate determina ca în anumite situații
așteptarea să fie eliminată (de exemplu, când utilizatorul face
clic pe butonul Quit pentru a termina programul).

2/26/2020 6:59:43 AM 12
Relația dintre evenimente, planificator și
codul care tratează evenimente

2/26/2020 6:59:43 AM 13
Cum ar putea funcționa un planificator
do forever: // the main scheduler loop
get event from input stream
if event type == EndProgram:
quit // break out of event loop
else if event type == event_01:
call event-handler for event_01 with event parameters
else if event type == event_02:
call event-handler for event_02 with event parameters
...
else if event type == event_nn:
call event-handler for event_nn with event parameters
else handle unrecognized event // ignore or raise exception
end loop

2/26/2020 6:59:43 AM 14
Coada de evenimente
Într-un sistem bazat pe evenimente, frecvența apariției evenimentelor
poate fi mare. Dispecerul și event-handlerele s-ar putea să nu poată
face față tuturor evenimentelor imediat ce acestea apar.

Soluția este de a plasa evenimentele neprocesate într-un fir de


așteptare până când acestea vor putea fi preluate pentru a fi tratate.

Existența firului de așteptare garantează că toate evenimentele vor fi


tratate la un moment dat și într-o anumită ordine. Lungimea firului și
timpul necesar procesării evenimentelor depind de viteza procesorului,
capacitatea memoriei operative și numărul de alte aplicații executate în
același timp (nivelul de multitasking - concurența pentru aceleași
resurse de sistem).

2/26/2020 6:59:43 AM 15
Domenii de aplicare
PPE, de regulă, este utilizată în trei cazuri:

•pentru crearea aplicațiilor server în cazul în care, dintr-un motiv


sau altul, generarea de procese de serviciu este nedorită;
•atunci când sunt construite interfețe utilizator (inclusiv cele
grafice pentru aplicații desktop);
•pentru programarea jocurilor în care trebuie controlate multe
obiecte.

2/26/2020 6:59:43 AM 16
Utilizarea în aplicații server
PPE este utilizată pentru a rezolva problema scalării atunci când
există mai mult de 10.000 de conexiuni simultane. În serverele
construite pe modelul "un fir pe o conexiune", problemele de
scalabilitate apar din următoarele motive:
•cheltuieli excesive pentru structurile de date ale sistemului de
operare necesare pentru a descrie o sarcină (segmentul de stare a
sarcinii, stiva);
•cheltuieli excesive pentru comutarea contextelor.

Condiția filosofică pentru refuzarea modelului serverelor cu fire de


execuție poate fi declarația lui Alan Cox: "Un computer este un
automat finit. Programarea cu fire de execuție este necesară pentru
cei care nu stiu cum sa programeze automate finite".

2/26/2020 6:59:43 17
AM
Utilizarea în aplicații server
Aplicația server pentru PPE este implementată într-un apel de sistem, care
primește evenimentele simultan din mai mulți descriptori (multiplexare).
La procesarea evenimentelor utilizate doar operațiile de I/O fără blocare:
nici un descriptor nu împiedică procesarea evenimentelor din alți descriptori.
Multiplexarea conexiunilor
Pot fi utilizate următoarele instrumente ale SO:
•select (UNIX). Scalare slabă - lista descriptorilor reprezentată ca bitmap;
•poll și epoll (Linux);
•kqueue (FreeBSD);
•/dev/poll (Solaris);
•IO completion port (Windows);
•POSIX AIO în prezent numai pentru operațiile I/O de disc;
•io submit și eventfd pentru operațiile I/O de disc.

2/26/2020 6:59:43 AM 18
Utilizarea în aplicații server
Exemple de implementări
Servere Web:
•Node.js
•Nginx
•lighttpd
•tornada

Proxy servere:
Squid

2/26/2020 6:59:43 19
AM
Utilizare în aplicații desktop
Evenimentele și gestionarea evenimentelor - esențiale pentru implementarea
interfeței grafice de utilizator.
Exemplu: interacțiunea programului cu evenimente de la mouse. Apăsarea
butonului stâng al mouse-ului determină o întrerupere a sistemului care
declanșează o procedură specifică în interiorul sistemului de operare. În
această procedură, fereastra de sub cursorul mouse-ului este căutată. Dacă
se găsește o fereastră, atunci acest eveniment este trimis la coada de
așteptare a mesajelor din această fereastră.
În funcție de tipul ferestrei, pot fi generate evenimente suplimentare.

În C# codul de tratare a evenimentului poate arăta astfel:


private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Butonul a fost apăsat");
}

2/26/2020 6:59:43 AM 20
Limbaje de programare
În C# evenimentele sunt implementate ca element al limbajului.
Exemplu de declarație a unui eveniment:
public class MyClass
{
public event EventHandler MyEvent;
}
Aici, EventHandler este un delegat care definește tipul procedurii de
tratare a evenimentului. Abonarea la eveniment este realizată astfel:
myClass.MyEvent += new EventHandler (Handler);
Aici myClass este o instanță a clasei MyClass, Handler este procedura
de tratare a evenimentului.

2/26/2020 6:59:43 21
AM
Limbaje de programare
Diferite limbaje de programare suportă PPE în grad diferit. Cel mai
complet nivel de susținere a evenimentelor îl au următoarele limbaje
(lista este incompletă):
•Perl (evenimente și daemoni DAEMON cu prioritățile lor PRIO),
•Delphi (limbaj de programare),
•ActionScript 3.0,
•C# (evenimente event),
•JavaScript (acțiunile utilizatorului).

Celelalte limbaje, în majoritatea lor, susțin evenimentele ca mecanism


pentru tratarea excepțiilor.

2/26/2020 6:59:43 22
AM
PROVOCAREA
PROGRAMATORULUI
Filozofia de proiectare a programelor - să facem
lucrurile mai uşoare pentru utilizatori
Principiul NNEMBDOMG
Eroi necunoscuţi de la Microsoft

2/26/2020 6:59:43 23
AM
CE ADUCE NOU WINDOWS
Interfaţa grafică cu utilizatorul (GUI)
Apelurile de funcţii
Programarea orientată pe obiecte
Arhitectura bazată pe mesaje
Procedura de fereastră

2/26/2020 6:59:43 24
AM
User Interface
Connection between the computer and the user
Two types:
• Command Line
• GUI: Graphical (Visual)

2/26/2020 6:59:43 25
AM
Command Line Interfaces
• User types commands, must remember valid commands
• Results Scroll by
• Text-based
• “Interactive” but hard to use
• Only kind of interface available until 1970s

2/26/2020 6:59:43 26
AM
History of GUIs
• DARPA SRI (late 60s)
• Xerox PARC Alto (early 70s)
• Microcomputers (late 70s to present)
– PC (DOS command line)
– Apple Lisa, Macintosh
• First real microcomputer GUI
– Microsoft Windows
• Many versions
• We’ll emphasize GUI Programming for Microsoft
Windows in this course

2/26/2020 6:59:43 27
AM
Other GUI-Windowing Systems
Sun Microsystems: Java
– AWT
– Swing
– Platform independent
– JDK is free
The X Window System
– Developed at MIT, late 1980s
– Networked graphics programming interface
– Independent of machine architecture/OS (but
mostly used under UNIX/LINUX)

2/26/2020 6:59:43 28
AM
Classic Windows Processing
user events
via
keyboard or mouse

win32 API calls

windows

implements process
WinMain
message queue windows
messages and messages
implements
message
message loop
routing

provides
win32 API application
messages
functionality

register windows WinProc


"class"
implements
create window message application function calls
show window handling
update window for
get message application
dispatch message
windows
messages
controls

2/26/2020 6:59:43 29
AM
2/26/2020 6:59:43 30
AM
Ce nu ne place aici?
„Hello, world!":
include <stdio.h>
main ()
{
printf ("Hello, world\n");
}

2/26/2020 6:59:43 31
AM
#include <windows.h>
LRESULT CALLBACK WndProc (HWND, UINT,
WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance,
HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static char szAppName[] = "HelloWin”;
HWND hwnd;
MSG msg;
WNDCLASSEX wndclass;
wndclass.cbSize = sizeof (wndclass);
wndclass.style = CS_HREDRAW|CS_VREDRAW;

2/26/2020 6:59:43 32
AM
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
wndclass.hbrBackgr=(HBRUSH) GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
RegisterClassEx (&wndclass);

2/26/2020 6:59:43 33
AM
hwnd = CreateWindow (szAppName, // window class name
"The Hello Program", // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL) ; // creation parameters
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;

while (GetMessage (&msg, NULL, 0, 0))


{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
2/26/2020 6:59:43 34
AM
LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam,
LPARAM lParam)
{
HDC hdc ;
PAINTSTRUCT ps ;
RECT rect ;
switch (iMsg)
{
case WM_CREATE :
PlaySound ("hellowin.wav", NULL, SND_FILENAME | SND_ASYNC);
return 0 ;
case WM_PAINT :
hdc = BeginPaint (hwnd, &ps);
GetClientRect (hwnd, &rect);
DrawText (hdc, "Hello, Windows 95! ", -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
}
Programul HELLOWIN
2/26/2020 6:59:43 35
AM
Rularea programului HELLOWIN

2/26/2020 6:59:43 36
AM
Privire generală

Fişierul conţine numai două funcţii:


WinMain
WndProc
Funcţia WinMain reprezintă punctul de intrare în program.

WndProc este „procedura de fereastră" a ferestrei

Nici o instrucţiune nu apelează direct funcţia WndProc

2/26/2020 6:59:43 37
AM
Apelurile de funcţii
LoadIcon - încarcă o pictogramă
LoadCursor - încarcă un indicator pentru mouse
GetStockObject - obţine un obiect grafic
RegisterClassEx - înregistrează o clasă de fereastră
CreateWindow - creează o fereastră pe baza unei clase
ShowWindow - afişează o fereastră pe ecran
UpdateWindow - cere unei ferestre să se redeseneze
GetMessage - preia un mesaj din coada de mesaje.
TranslateMessage - converteşte unele dintre mesaje

2/26/2020 6:59:43 38
AM
Apelurile de funcţii
DispatchMessage - trimite un mesaj
PlaySound - redă un fişier de sunet
BeginPaint - iniţiază o operaţie de desenare a ferestrei
GetClientRect - obţine dimensiunile zonei client
DrawText - afişează un text
EndPaint - încheie o operaţie de desenare
PostQuitMessage - inserează un mesaj de încheiere
DefWindowProc - execută operaţiile prestabilite

2/26/2020 6:59:43 39
AM
Identificatori cu majuscule
CS_HREDRAW, DT_VCENTER, WM_CREATE, IDC_ARROW,
WM_DESTROY, CW_USEDEFAULT, IDI_APPLICATION,
SND_ASYNC, WS_OVERLAPPEDWINDOW …

Prefix Categorie
CS Opţiune pentru stilul clasei
IDI Număr de identificare pentru o pictogramă
IDC Număr de identificare pentru un cursor
WS Stil de fereastră
CW Opţiune de creare a unei ferestre
WM Mesaj de fereastră
SND Opţiune pentru sunete
DT Opţiune de desenare a textului

2/26/2020 6:59:43 40
AM
Noi tipuri de date
UINT unsigned int
PSTR - pointer la un şir de caractere, char*
WPARAM - UINT
LPARAM - LONG
WinMain este de tipul WINAPI
WndProc este de tipul CALLBACK

2/26/2020 6:59:43 41
AM
Structuri de date
Structura Semnificaţie
MSG Structura mesajului
WNDCLASSEX Structura clasei de fereastră
PAINTSTRUCT Structură pentru desenare
RECT Dreptunghi

2/26/2020 6:59:43 42
AM
Variabile handle
Identificator Semnificaţie
HINSTANCE Variabilă handle a unei „instanţe" - programul însuşi

HWND Variabilă handle a unei ferestre

HDC Variabilă handle a unui context de dispozitiv

2/26/2020 6:59:43 43
AM
Notaţia ungară
Prefix Tip de date
c char
by BYTE (unsigned char)
n short
i int
x, y int (folosit pentru coordonate)
cx, cy int (dimensiuni pe x si y, c de la „contor")
b sau f BOOL (int); f vine de la „flag" (indicator)

2/26/2020 6:59:43 44
AM
Notaţia ungară
Prefix Tip de date
w WORD (unsigned short)
l LONG (long)
dw DWORD (unsigned long)
fn funcţie
s şir de caractere
sz şir de caractere terminat cu zero
h variabilă handle
p pointer

2/26/2020 6:59:43 45
AM
2/26/2020 6:59:43 46
AM
Rezumat
Punctul de intrare în program
Înregistrarea clasei de fereastră
Crearea ferestrei
Afişarea ferestrei
Ciclul de mesaje
Procedura de fereastră
Prelucrarea mesajelor
Redarea unui fişier de sunet
Mesajul WM_PAINT
Mesajul WM_DESTROY

2/26/2020 6:59:43 47
AM
Afişarea textului
Mesajul WM_PAINT

• diferența dintre fereastra aplicației şi zona client


• funcţiile GDI
• afișarea liniilor de text
• fontul prestabilit
• desenarea şi redesenarea
• primul mesaj WM_PAINT
• Windows informează că o parte a zonei client trebuie să
fie actualizată

2/26/2020 6:59:43 49
AM
Dreptunghiuri valide şi
invalide

Procedura ferestrei este pregătită să


actualizeze zona client când primeşte mesajul
WM_PAINT

•deseori este necesară numai reactualizarea unei


porţiuni mai mici
•Windows păstrează în interior o „structură cu
informaţii pentru desenare“ (PAINTSTRUCT) pentru
fiecare fereastră.
•la recepţionarea mesajului WM_PAINT, procedura
ferestrei poate obţine coordonatele dreptunghiului
invalid.

2/26/2020 6:59:43 50
AM
Interfaţa GDI

Pentru desenarea zonei client - funcţiile din


interfața pentru dispozitivele grafice (GDI)
– DrawText
– TextOut (hdc, x, y, psString, iLength);

2/26/2020 6:59:43 51
AM
Contextul de dispozitiv
O structură de date întreținută de interfața GDI.
hdc - calea de acces a ferestrei la funcțiile GDI.
Atunci când vrea să deseneze …
O parte dintre valorile din DC sunt atribute grafice.

2/26/2020 6:59:43 52
AM
Obţinerea unei variabile
handle DC

BeginPaint

EndPaint
PAINTSTRUCT ps
funcţia BeginPaint completează câmpurile structurii ps.

Valoarea returnată de funcţia BeginPaint este variabila


hdc
– HDC hdc;

EndPaint eliberează hdc

2/26/2020 6:59:43 53
AM
Obţinerea unei variabile
handle DC
case WM_PAINT :
hdc = BeginPaint (hwnd, &ps) ;
[apelarea unor funcţii GDI]
EndPaint (hwnd, &ps) ;
return 0 ;

DefWindowProc prelucrează mesajele WM_PAINT:


case WM_PAINT:
BeginPaint (hwnd, &ps) ;
EndPaint (hwnd, &ps) ;
return 0 ;
2/26/2020 6:59:43 54
AM
Structura de informaţii pentru desenare

typedef struct tagPAINTSTRUCT


{
HDC hdc;  Windows completează câmpurile când
BOOL fErase; programul apelează funcţia BeginPaint
RECT rcPaint;  fErase valoarea TRUE - Windows a
BOOL fRestore; şters fondul dreptunghiului invalid
BOOL fIncUpdate;
 rcPaint al structurii PAINTSTRUCT
BYTE rgbReserved[32];
este o structură de tip RECT
}
PAINTSTRCUT;  rcPaint din structura PAINTSTRUCT -
dreptunghi „de decupare" (clipping
rectangle) - Windows restricţionează
desenarea în interiorul dreptunghiului.
2/26/2020 6:59:43 55
AM
Limitele unui dreptunghi
invalid

Pentru desenarea în afara


dreptunghiului rcPaint în timpul
prelucrării mesajului WM_PAINT:
InvalidateRect (hWnd, NULL, TRUE);
înaintea apelării funcţiei
BeginPaint.

Dacă ultimul parametru are


valoarea FALSE, fondul nu este
şters şi desenul va fi făcut peste
ceea ce există deja

2/26/2020 6:59:43 56
AM
Obţinerea unei variabile
handle DC

În timpul prelucrării altor mesaje


hdc = GetDC(hwnd);
[apelarea unor funcţii GDI]
ReleaseDC(hwnd, hdc);

2/26/2020 6:59:43 57
AM
Funcţia TextOut

TextOut (hdc, x, y, psString,


iLength);
• hdc - variabila handle a DC
• psString - pointer la un şir,
• iLength - numărul de caractere.
• x şi y - începutul şirului de
caractere.

Coordonatele GDI - „coordonate


logice“:
moduri de mapare - controlează
transformarea coordonatelor
logice, transmise funcţiilor GDI, în
coordonate fizice ale pixelilor
afişaţi pe ecran, definite în
2/26/2020de
contextul 6:59:43
dispozitiv. 58
AM
Fonturi

Fontul de sistem
În DC este definit fontul pe care Windows îl
foloseşte pentru scrierea textului în zona
client: prestabilit este SYSTEM_FONT
Categorii
– font cu dimensiune fixă
– font cu dimensiune variabilă

Fontul sistem este un font de tip „rastru“: fiecare


caracter este definit ca un bloc de pixeli
2/26/2020 6:59:43 59
AM
Fonturi
Dimensiunile unui caracter
Sunt obţinute cu GetTextMetrics
TEXTMETRIC tm;
hdc = GetDC(hwnd);
GetTextMetrics(hdc, &tm)
;
ReleaseDC(hwnd, hdc);

TEXTMETRIC conţine câmpuri care


descriu lăţimea unui caracter:
– tmAveCharWidth (lăţimea medie
a literelor mici)
– tmMaxCharWidth (lăţimea celui
mai mare caracter al fontului)
Lăţimea medie a majusculelor: 150%
din valoarea tmAveCharWidth
2/26/2020 6:59:43 60
AM
Informații despre
metrica textului
Dimensiunile fontului sistem nu se modifică în timpul unei sesiuni
Windows: GetTextMetrics în WM_CREATE
Vrem să afişăm mai multe linii de text una sub alta. Obţinem valorile
pentru înălţimea şi lăţimea caracterelor:
static int cxChar, cyChar;

case WM_CREATE:
hdc = GetDC (hwnd);
GetTextMetrics (hdc, &tm);
cxChar = tm.tmAveCharWidth;
cyChar = tm.tmHeight +
tm.tmExternalLeading;
AM ReleaseDC (hwnd, hdc);
2/26/2020 6:59:43 61
Fomatarea și extragerea
textului
int iLenght;
char szBuffer [40];

[alte Iinii de program]

iLenght = sprintf (szBuffer, "The sum of %d and %d is %d", nA, nB, nA +


nB);
TextOut (hdc, x, y, szBuffer, iLength);

Sau:
TextOut (hdc, x, y, szBuffer, sprintf (szBuffer, "The sum of %d and %d is %d", nA, nB,
nA + nB));

Dacă nu afişăm numere în virgulă mobilă, folosim wsprintf în locul sprintf.


Aceeaşi sintaxă, dar este inclusă în Windows!

2/26/2020 6:59:43 62
AM
Să punem totul la un loc
GetSystemMetrics(iIndex) returnează informaţii despre dimensiunea
unor elemente grafice din Windows, cum ar fi pictograme, cursoare, bare
de titlu sau bare de derulare.
#define NUMLINES ((int) (sizeof sysmetrics / sizeof sysmetrics [0]))
struct
{
int iIndex ;
char* szLabel ;
char* szDesc ;
}
sysmetrics [ ] =
{
SM_CXSCREEN, "SM_CXSCREEN", "Screen width in pixels",
SM_CYSCREEN, "SM_CYSCREEN", "Screen height in pixels",
SM_CXVSCROLL, "SM_CXVSCROLL", "Vertical scroll arrow width",
...
}

2/26/2020 6:59:43 63
AM
SYSMETS1
#include <windows.h>
#include <string.h>
#include "sysmets.h"
LRESULT CALLBACK WndProc (HWND,
UINT,
WPARAM, LPARAM);
int WINAPI WinMain (HINSTANCE
hInstance,
HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
AM
{
2/26/2020 6:59:43 64
SYSMETS1
WNDCLASSEX wndclass ;
wndclass.cbSize = sizeof (wndclass) ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject
(WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION) ;
RegisterClassEx (&wndclass) ;
2/26/2020 6:59:43 65
AM
SYSMETS1

hwnd = CreateWindow (szAppName, "Get System Metrics No. 1",


WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}2/26/2020 6:59:43 66
AM
SYSMETS1
LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg,
WPARAM wParam, LPARAM lParam)
{
static int cxChar, cxCaps, cyChar ;
char szBuffer[10] ;
HDC hdc ;
int i;
PAINTSTRUCT ps ;
TEXTMETRIC tm ;

2/26/2020 6:59:43 67
AM
SYSMETS1
switch (iMsg)
{
case WM_CREATE
hdc = GetDC(hwnd);
GetTextMetrics(hdc, &tm);
cxChar = tm.tmAveCharWidth;
cxCaps = (tm.tmPitchAndFamily&1?3:2)*cxChar/2;
cyChar = tm.tmHeight + tm.tmExternalLeading;
ReleaseDC(hwnd, hdc);
return 0;

2/26/2020 6:59:43 68
AM
SYSMETS1
case WM_PAINT
hdc = BeginPaint (hwnd, &ps) ;
for (i = 0 ; i < NUMLINES ; i++)
{
TextOut(hdc, cxChar, cyChar*(1+i), sysmetrics[i].szLabel, strlen
(sysmetrics[i].szLabel));
TextOut(hdc, cxChar+22*cxCaps, cyChar*(1+i), sysmetrics[i].szDesc,
strlen(sysmetrics[i].szDesc));
SetTextAlign (hdc, TA_RIGHT | TA_TOP) ;
TextOut(hdc, cxChar+22*cxCaps+40*cxChar, cyChar*(1+i), szBuffer,
wsprintf (szBuffer, "%5d“, GetSystemMetrics
(sysmetrics[i].iIndex)));
SetTextAlign (hdc, TA_LEFT | TA_TOP);
}
EndPaint (hwnd, &ps);
return2/26/2020
0; 6:59:43 69
AM
SYSMETS1
case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, iMsg, wParam, lParam);
}

2/26/2020 6:59:43 70
AM
Fereastra afişată de
SYSMETS1

2/26/2020 6:59:43 71
AM
Dimensiunea zonei client
Prelucrarea mesajului WM_SIZE în procedura de fereastră:

lParam - lăţimea zonei client în LoWORD


înălţimea - în cuvântul mai semnificativ (HiWORD).

Codul pentru prelucrarea acestui mesaj arată astfel:


static int cxClient, cyClient;
[alte linii de program]
case WM_SIZE
cxClient = LOWORD (lParam);
cyClient = HIWORD (lParam);
return 0;
2/26/2020 6:59:43 72
AM
Dimensiunea zonei client
LOWORD şi HIWORD sunt definite în fişierele antet din
Windows. Mesajul WM_SIZE este urmat de un mesaj
WM_PAINT, deoarece la definirea clasei am specificat
următorul stil al clasei de fereastră:
CS_HREDRAW | CS_VREDRAW
Acest stil cere SO să forţeze redesenarea ferestrei de fiecare
dată când se modifică dimensiunea verticală sau orizontală a
acesteia.

Putem calcula numărul de linii de text afişate în zona client:


cyClient / cyChar
Această valoare poate fi zero dacă zona client este prea mică.

Putem calcula numărul aproximativ de caractere care pot fi


afişate pe orizontală
2/26/2020 6:59:43 în zona client: 73
AM
cxClient / cxChar
Barele de derulare

„Casetă de derulare”…
Perspectiva programatorilor este diferită de cea a
utilizatorilor
Includem în fereastra aplicaţiei o bară de derulare
orizontală sau verticală: WS_VSCROLL şi/sau
WS_HSCROLL în stilul de fereastră din apelul
funcţiei CreateWindow
Windows se ocupă de modul de utilizare a mouse-ului
pentru barele de derulare, dar barele de derulare
ale ferestrelor nu au o interfaţă automatizată cu
tastatura
2/26/2020 6:59:43 74
AM
Domeniul şi poziţia
„domeniu" (pereche de
Poziţia 0 numere întregi care
Poziţia 1 reprezintă valorile
Poziţia 2 maximă şi minimă)
Poziţia 3
„poziţie" (punctul în care
Poziţia 4
se află caseta de derulare
în domeniul asociat barei
Poziţia 0 Poziţia 1 Poziţia 2 Poziţia 3 Poziţia 4 de derulare).
SetScrollRange (hwnd, iBar, iMin, iMax, bRedraw) ;
SetScrollPos (hwnd, iBar, iPos, bRedraw) ;
2/26/2020 6:59:43 75
AM
Răspunderea pt întreţinerea și actualizarea
Sistemul de operare:
• Tratează operaţiile executate cu
mouse-ul asupra barei de derulare.
Afişează în video invers zona pe care
utilizatorul execută clic.
• Mută caseta de derulare atunci când
utilizatorul o trage cu ajutorul
mouse-ului.
• Trimite mesaje din partea barei de
derulare către procedura de
fereastră
2/26/2020 6:59:43
care o conţine. 76
AM
Răspunderea pt întreţinerea și actualizarea

Programul:
• Iniţializarea domeniului barei de
derulare.
• Prelucrarea mesajului primit de la bdd.
• Actualizarea poziţiei casetei.

2/26/2020 6:59:43 77
AM
Mesaje de la barele de
derulare
WM_VSCROLL,
WM_HSCROLL
Cuvântul mai puţin semnificativ
al wParam - acţiunea efectuată

Numere care corespund unor


identificatori SB: SB_LINEUP,
SB_PAGEUP, SB_PAGEDOWN,
SB_LINEDOWN,
SB_ENDSCROLL
SB_THUMBTRACK sau
SB_THUMBPOSITION
cuvântul mai semnificativ al
parametrului conţine poziţia
curentă pe bara de derulare.
2/26/2020 6:59:43 78
AM
Procedura de fereastră
LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
static int cxChar, cxCaps, cyChar, cyClient, iVscrollPos ;
char szBuffer[10] ;
HDC hdc ;
int i, y ;
PAINTSTRUCT ps ;
TEXTMETRIC tm ;
switch (iMsg)
{
case WM_CREATE :
hdc = GetDC (hwnd) ;
GetTextMetrics (hdc, &tm) ;
cxChar = tm.tmAveCharWidth ;
cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;
cyChar = tm.tmHeight + tm.tmExternalLeading ;
ReleaseDC (hwnd, hdc) ;
SetScrollRange (hwnd, SB_VERT, 0, NUMLINES, FALSE) ;
SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ;
return 0 ;

case WM_SIZE :
cyClient = HIWORD (lParam) ;
return 0 ;
2/26/2020 6:59:43 79
AM
Procedura de fereastră
case WM_VSCROLL :
switch (LOWORD (wParam))
{ case SB_THUMBPOSITION :
case SB_LINEUP : iVscrollPos = HIWORD (wParam) ;
iVscrollPos -= 1 ; break ;
break ;
case SB_LINEDOWN : default :
iVscrollPos += 1 ; break ;
break ; }
case SB_PAGEUP : iVscrollPos = max (0, min (iVscrollPos, NUMLINES));
iVscrollPos -= cyClient / if (iVscrollPos != GetScrollPos (hwnd, SB_VERT))
cyChar; {
break ; SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE);
case SB_PAGEDOWN : InvalidateRect (hwnd, NULL, TRUE);
iVscrollPos += cyClient / }
cyChar ; return 0;
break ;

2/26/2020 6:59:43 80
AM
SYSMETS 3
1. Să derulăm ecranul pană când ultima linie de text devine
vizibilă. Programul va calcula un nou domeniu pentru bara de
derulare (şi probabil şi o nouă poziţie a casetei de derulare)
în timpul prelucrării mesajului WM_SIZE. Domeniul barei de
derulare este calculat în funcţie de numărul de linii de text,
de lăţimea textului şi de dimensiunea zonei client. Ca
rezultat se obţine un domeniu mai mic - numai atât cât este
nevoie pentru afişarea textului care iese în afara zonei
client
2. Eliminarea barei din fereastră dacă nu mai este necesară
3. SYSMETS3 să prelucreze şi operaţiile SB_THUMBTRACK

2/26/2020 6:59:43 81
AM

S-ar putea să vă placă și