Sunteți pe pagina 1din 12

Ce este 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).

PPE: istorie
Î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.

Handlerul de eveniment și coordonatorul (dispatcher)


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.
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).

PPE: 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.

PPE pentru aplicații server, instrumente ale SO pentru realizarea multiplexării cu


exemple de implementări.
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.
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.

Exemple de implementări
Servere Web:
• Node.js
• Nginx
• lighttpd
• tornada
Proxy servere:
Squid

PPE: 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");
}

PPE: Limbaje de programare cu exemplul C#


Î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.

Cum funcționează programarea orientată pe evenimente


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").
Reprezentarea schematică a paradigmei event-driven progamming, pseudo-
codul unui 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

Coada de evenimente. Programarea interfeței grafice a utilizatorului


Î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.
#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;
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);
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 ;
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) ;

ISTORIA WINDOWS. CE ADUCE NOU WINDOWS. Conceptele şi fundamentele


GUI. Consecvenţa privind interfaţă cu utilizatorul. Avantajul oferit de
multitasking. Gestionarea memoriei. Interfaţa grafică independentă de
dispozitiv.
1. Interfaţa grafică cu utilizatorul (GUI)
2. Apelurile de funcţii
3. Programarea orientată pe obiecte
4. Arhitectura bazată pe mesaje
5. Procedura de fereastră
User Interface
Conexiune între computer și utilizator
Doua tipuri:
1. Linie de comanda
2. GUI: Grafic (vizual)

Pentru desenarea zonei client - funcţiile din interfața pentru dispozitivele grafice (GDI)
– DrawText
– TextOut (hdc, x, y, psString, iLength);

Apelurile de funcţii. Arhitectura bazată pe mesaje. Procedura de fereastră.


WinMain şi WndProc.
1. LoadIcon - încarcă o pictogramă
2. LoadCursor - încarcă un indicator pentru mouse
3. GetStockObject - obţine un obiect grafic
4. RegisterClassEx - înregistrează o clasă de fereastră
5. CreateWindow - creează o fereastră pe baza unei clase
6. ShowWindow - afişează o fereastră pe ecran
7. UpdateWindow - cere unei ferestre să se redeseneze
8. GetMessage - preia un mesaj din coada de mesaje.
9. TranslateMessage - converteşte unele dintre mesaje
10. DispatchMessage - trimite un mesaj
11. PlaySound - redă un fişier de sunet
12. BeginPaint - iniţiază o operaţie de desenare a ferestrei
13. GetClientRect - obţine dimensiunile zonei client
14. DrawText - afişează un text
15. EndPaint - încheie o operaţie de desenare
16. PostQuitMessage - inserează un mesaj de încheiere
17. DefWindowProc - execută operaţiile prestabilite
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
Atributele contextului de dispozitiv. Salvarea contextului de dispozitiv. Funcţiile
LineTo, Polyline şi PolylineTo, PolyPolyline, Arc, PolyBezier şi PolyBezierTo,
Rectangle, Ellipse, RoundRect, Chord şi Pie. Атрибуты DC. Сохранение DC.
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.

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

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 ;

Structura de informaţii pentru desenare


typedef struct tagPAINTSTRUCT
{
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
}
PAINTSTRCUT;

 Windows completează câmpurile când programul apelează funcţia


BeginPaint
 fErase valoarea TRUE - Windows a şters fondul dreptunghiului invalid
 rcPaint al structurii PAINTSTRUCT este o structură de tip RECT
 rcPaint din structura PAINTSTRUCT - dreptunghi „de decupare" (clipping
rectangle) - Windows restricţionează desenarea în interiorul dreptunghiului.