Sunteți pe pagina 1din 86

Programarea

Programarea pilotată
pilotată
de
de evenimente
evenimente
Event driven programming
Formalități

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


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

07/13/20 07:46 PM 2
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

07/13/20 07:46 PM 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
recepționa evenimentele pe măsura apariției și a le
procesa, folosind o procedură adecvată de tratare.
Este o extensie a programării controlate de întreruperi,
de tipul celor folosite în SO 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).

07/13/20 07:46 PM 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.

07/13/20 07:46 PM 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.
A fost accelerată de introducerea interfeței grafice
de utilizator (GUI), adoptată pe scară largă pentru
utilizare în SO și aplicațiile utilizatorilor finali.

07/13/20 07:46 PM 6
Despre POO
Una dintre ideile fundamentale care stau în spatele POO
este 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 entității, 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.

07/13/20 07:46 PM 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ă.

07/13/20 07:46 PM 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 propriu al evenimentului (ex., "clic-stânga").

07/13/20 07:46 PM 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ă, va fi generat un
eveniment.
Dacă există o eroare hardware sau software, va fi generat un
eveniment.

07/13/20 07:46 PM 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.

07/13/20 07:46 PM 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 Close pentru a termina programul).

07/13/20 07:46 PM 12
Relația dintre evenimente, planificator și
codul care tratează evenimente

07/13/20 07:46 PM 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

07/13/20 07:46 PM 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.
Firul 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.

07/13/20 07:46 PM 15
Tableta de la ora 10
Programarea procedurală PPE
Computer-Centric User-Centric
Programul centrat pe calculator Programul centrat pe utilizator
Program Runs as Programmer Computer User Determines the
Intended Order of Actions
Programul este executat conform Utilizatorul stabilește ordinea
intenției programatorului acțiunilor
Programmer Sets the Order of Programs are Interactive
Action
Programatorul stabilște ordinea Programele sunt interactive
acțiunilor
Programmer Controls the Flow of Control is Determined at
Program’s Flow Runtime
Programatorul controlează fluxul User Clicks Mouse / Presses
de calcul Key
Object in Scene Moves to
create a Condition

07/13/20 07:46 PM 16
Event
• Something that happens
• Can be caused by computer or user
– Mouse Click
– Key Press
– System Clock Tick
• Can occur differently each time the
program is executed
• Events are WORLD Level

07/13/20 07:46 PM 17
Event Handling
• Response to an event
• Action or Sequence of Actions carried
out when Event Occurs (Methods)
• Events TRIGGER response
• Programmer must think of all possible
events
– Plan a Response for Each (Event Handler)

07/13/20 07:46 PM 18
Linking
• Event Handling Methods must be
linked to the Specific Event

Event Occurs which causes the Event


Handling Method to execute

07/13/20 07:46 PM 19
Testing
• Testing of Event Driven Programs is much
more difficult
– Programmer does not know flow of program
– Flow of Program changes every time program is
executed
– User’s Actions are difficult to pre-determine
• Use Incremental Development and Testing
– Write a Method
– Test it
– Write a Method
– Test it
– Etc.

07/13/20 07:46 PM 20
Domenii de aplicare
PPE, de regulă, este utilizată în trei cazuri:
1.atunci când sunt construite interfețe utilizator
(inclusiv cele grafice pentru aplicații desktop);
2.pentru crearea aplicațiilor server în cazul în care,
dintr-un motiv sau altul, generarea de procese de
serviciu este nedorită;
3.pentru programarea jocurilor în care trebuie
controlate multe obiecte.

07/13/20 07:46 PM 21
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ă;
•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 calculator este un automat finit.
Programarea cu fire de execuție este necesară pentru
cei care nu știu cum sa programeze automate finite".

07/13/20 07:46 PM 22
Utilizarea în aplicații server
Aplicația server prin 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.

07/13/20 07:46 PM 23
Utilizarea în aplicații server
Exemple de implementări
Servere Web:
•Node.js
•Nginx
•lighttpd
•tornada

Proxy servere:
Squid

07/13/20 07:46 PM 24
Utilizare în aplicații desktop
Evenimentele și gestionarea lor sunt 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");
}

07/13/20 07:46 PM 25
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.

07/13/20 07:46 PM 26
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.

07/13/20 07:46 PM 27
PROVOCAREA
PROGRAMATORULUI
Filozofia de proiectare a programelor - să facem
lucrurile mai uşoare pentru utilizatori
Principiul NNEMBDOMG
Eroi necunoscuţi de la Microsoft

07/13/20 07:46 PM 28
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ă

07/13/20 07:46 PM 29
User Interface
Connection between the computer and the user
Two types:
•Command Line
•GUI: Graphical (Visual)

07/13/20 07:46 PM 30
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

07/13/20 07:46 PM 31
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

07/13/20 07:46 PM 32
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)

07/13/20 07:46 PM 33
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

07/13/20 07:46 PM 34
07/13/20 07:46 PM 35
Ce nu ne place aici?
„Hello, world!":
#include <stdio.h>
main ()
{
printf ("Hello, world\n");
}

07/13/20 07:46 PM 36
#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;

07/13/20 07:46 PM 37
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);

07/13/20 07:46 PM 38
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 ;
}
07/13/20 07:46 PM 39
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
07/13/20 07:46 PM 40
Rularea programului HELLOWIN

07/13/20 07:46 PM 41
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

07/13/20 07:46 PM 42
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

07/13/20 07:46 PM 43
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

07/13/20 07:46 PM 44
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

07/13/20 07:46 PM 45
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

07/13/20 07:46 PM 46
Structuri de date
Structura Semnificaţie
MSG Structura mesajului
WNDCLASSEX Structura clasei de fereastră
PAINTSTRUCT Structură pentru desenare
RECT Dreptunghi

07/13/20 07:46 PM 47
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

07/13/20 07:46 PM 48
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)

07/13/20 07:46 PM 49
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

07/13/20 07:46 PM 50
07/13/20 07:46 PM 51
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

07/13/20 07:46 PM 52
Afişarea
Afişarea textului
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ă

07/13/20 07:46 PM 54
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.

07/13/20 07:46 PM 55
Interfaţa GDI

Pentru desenarea zonei client - funcţiile din


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

07/13/20 07:46 PM 56
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.

07/13/20 07:46 PM 57
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

07/13/20 07:46 PM 58
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 ;
07/13/20 07:46 PM 59
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
}
 rcPaint din structura PAINTSTRUCT -
PAINTSTRCUT;
dreptunghi „de decupare" (clipping
rectangle) - Windows restricţionează
desenarea în interiorul dreptunghiului.
07/13/20 07:46 PM 60
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

07/13/20 07:46 PM 61
Obţinerea unei variabile
handle DC

În timpul prelucrării altor mesaje


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

07/13/20 07:46 PM 62
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
contextul de dispozitiv.
07/13/20 07:46 PM 63
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
07/13/20 07:46 PM definit ca un bloc de pixeli 64
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
07/13/20 07:46 PM 65
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;
ReleaseDC (hwnd, hdc);
return
07/13/20 07:460;
PM 66
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!

07/13/20 07:46 PM 67
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",
...
}

07/13/20 07:46 PM 68
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)
{
static char szAppName[] =
"SysMets1”;
HWND hwnd;
07/13/20 07:46 PM 69
MSG msg;
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) ;
07/13/20 07:46 PM 70
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 ;
}07/13/20 07:46 PM 71
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 ;

07/13/20 07:46 PM 72
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;

07/13/20 07:46 PM 73
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);
return 0;
07/13/20 07:46 PM 74
SYSMETS1
case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, iMsg, wParam, lParam);
}

07/13/20 07:46 PM 75
Fereastra afişată de
SYSMETS1

07/13/20 07:46 PM 76
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;
07/13/20 07:46 PM 77
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ă
07/13/20 07:46 PM în zona client: 78
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

07/13/20 07:46 PM 79
Domeniul şi poziţia
„domeniu" (pereche de
Poziţia 0 numere întregi care
Poziţia 1 reprezintă valorile maximă
Poziţia 2 ş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) ;
07/13/20 07:46 PM 80
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ă care o
conţine.

07/13/20 07:46 PM 81
Răspunderea pt întreţinerea și actualizarea

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

07/13/20 07:46 PM 82
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.
07/13/20 07:46 PM 83
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 ;
07/13/20 07:46 PM 84
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 ;

07/13/20 07:46 PM 85
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

07/13/20 07:46 PM 86