La gestione degli eventi Antonio Cisternino Programmazione ad Eventi Come abbiamo visto dagli esempi esiste un pattern comune ai vari sistemi per la gestione degli eventi La gestione degli eventi nelle interfacce si articola in: Gestione degli eventi all’interno dell’applicazione Gestione degli eventi del gestore dell’interfaccia grafica Delegate Event Model Questo modello, inizialmente introdotto da Java 1.1, è ormai quello più diffuso nei framework di programmazione di GUI Il modello prevede: Una sorgente degli eventi (es. il bottone) I consumatori degli eventi Delle interfacce (un modo di comunicazione concordato tra il produttore e il consumatore) Schema di funzionamento Consumatori registrati Sorgente Eventi (es. Button) Listeners (interfaccia richiesta dalla sorgente) Il pattern in Java Il delegate event model può essere implementato in vari linguaggi In Java è realizzato utilizzando: Convenzione sui nomi dei metodi per registrare consumatori presso la sorgente (addXXXListener(XXXListener l)) Interfaccia che la sorgente usa per comunicare l’evento (XXXListener) Invocazione dell’evento: la sorgente invoca uno dei metodi dell’interfaccia XXXListener Chi fa cosa Produttore di eventi: addXXXListener removeXXXListener Definisce una classe XXXEvent Definisce l’interfaccia XXXListener Consumatore di eventi Implementa l’interfaccia XXXListener Si registra presso il produttore Il punto di vista dei controlli Il delegate event model, ideale per chi usa controlli, presenta problemi nella gestione degli eventi dei controlli Come si fa a rendere “trasparente”un controllo ad un evento? Ad esempio: il click su una label potrebbe convenire che sia notificato alla finestra che lo contiene Propagazione degli eventi Il bubbling di un evento si ha quando viene passato al contenitore di chi lo ha ricevuto In questo modo si possono implementare i comportamenti di default Si possono anche gestire in modo centralizzato eventi di un controllo composto da finestre Tunneling Framework come WPF (aka Avalon) supportano anche il flusso in direzione inversa al bubbling degli eventi: il tunneling Questo flusso (solitamente seguito dal bubbling) offre la possibilità al contenitore di un controllo di gestire un evento prima che arrivi al figlio Il gestore dell’interfaccia decide, per ogni tipo evento, se è diretto, se fa uso di bubbling, o tunneling e bubbling Implementiamo il bubbling Nei linguaggi orientati ad oggetti il bubbling viene facilmente scaricato sulla gerarchia dell’ereditarietà Nei framework OO normalmente tutti I componenti grafici ereditano da una classe base, ad esempio Component Dalla classe base tutti i componenti ereditano (direttamente o meno) un metodo deputato alla gestione degli eventi Il componente vede passare tutti gli eventi a lui diretti, e può gestirli localmente o delegare Un esempio Java, nella sua prima versione (fino alla versione 1.0.2) aveva un modello di gestione degli eventi come OWL (Borland) e MFC (Microsoft) basato su bubbling La possibilità di invocare il metodo della superclasse consente di delegare la gestione di un evento non gestito (o solo parzialmente gestito) Il dispatch e la classificazione degli eventi viene tipicamente incapsulato da metodi invocati dal metodo che gestisce gli eventi, inclusa la delega Java AWT Event Model class Component { int x, y; bool handleEvent(Event e); } class Button extends Component { String text; bool handleEvent() { } … } class Window extends Component class Frame extends Window Event handling class MyButton extends Button { boolean handleEvent(Event e) { switch (e.type) { case Event.MOUSE_UP: … return true; // Event handled! } default: return super.handleEvent(e); }} Dal bubbling alla delegation I controlli, nella gestione degli eventi, possono implementare l’interfaccia necessaria per essere una sorgente di eventi Per fare questo si possono intercettare gli eventi, ed invocare il metodo dei consumatori registrati La delegation migliora le performance: la gestione degli eventi non necessita l’introduzione di nuove classi L’event loop Chi invoca gli event handler? Un thread dell’applicazione spende una porzione significativa del suo tempo nell’event loop L’event loop ha la seguente struttura: while (m=PickMessage()) DispatchMessage(m) Un qualche metodo (più o meno nascosto) esegue questo ciclo L’event loop Normalmente l’event-loop viene incapsulato in qualche classe (Application.Run per .NET, SwingUtilities.invokeLater in Java) La funzione di dispatch del messaggio si occupa di inoltrare il messaggio al componente dell’applicazione interessato Questo innesca il meccanismo di chiamate che poi va a chiamare gli event handler definiti dall’applicazione Thread or threads? Quanti thread gestiscono l’event loop? A dispetto dell’apparente concorrenza degli eventi il thread della UI è uno Se un event handler lo blocca l’intera interfaccia dell’applicazione smette di funzionare Se quindi è necessario intraprendere un qualche lavoro significativo è bene avviare un thread nell’event handler a cui delegare il compito Troviamo il colpevole Come fa il gestore dell’interfaccia a individuare il destinatario di un evento? Input da tastiera: il controllo che ha il focus riceve il messaggio Spesso il focus si ottiene evidenziando il controllo Usando il tab è possibile spostare il focus su altri controlli La pick correlation consiste nell’associare un evento del puntatore con il controllo a cui è diretto Pick Correlation Come facciamo a sapere quale controllo deve ricevere gli eventi del puntatore? Abbiamo a disposizione le coordinate del punto (x, y), ma non sono sufficienti! Siamo tutti abituati a sovrapporre finestre: qual’è quella visibile? Il gestore dell’interfaccia organizza in modo appropriato le finestre e per ciascuna ricorda lo z-index ovvero un indice che indica l’ordinamento lungo l’asse Z Finestre e Z-Ordering La struttura dati primitiva delle interfacce grafiche (fino ad oggi…) è la finestra: un’area rettangolare dello schermo Tutti i controlli non sono altro che finestre specializzate Per trovare a quale finestra corrisponde un punto (x, y) sullo schermo è necessario trovare tutte le finestre che lo contengono tra queste prendere quella con z-index più alto Le finestre sono organizzate in un albero che consenta l’implementazione della ricerca in modo più efficiente (sfruttando il contenimento) Usare Spy++ Spy++ è un tool Microsoft (ne esistono analoghi per XWindows) che consente di monitorare gli eventi diretti a finestre È anche possibile analizzare la struttura di un’applicazione in termini di finestre Osserviamo gli eventi Se abilitiamo il log degli eventi su un bottone della calcolatrice notiamo che: Gli eventi di paint arrivano in momenti inattesi Ci sono molti tipi di eventi Se invece facciamo il log della finestra vediamo in azione il bubbling: alcuni eventi sui figli sono inviati anche al padre