Paola Magillo, Univestita' di Genova,
Corso di Interfacce Utente per Informatica, a.a. 2002-2003.
STRUTTURA E FUNZIONAMENTO DI UN'INTERFACCIA UTENTE
Richiamo
Finestra finestra = qualsiasi area sullo schermo che viene
gestita come risorsa individuale
- con suoi eventi a cui e' sensibile
- con sua geometria (posizione, dimensione)
- con sue strutture dati accessorie (es. color map)
Le finestre sono organizzate in gerarchia di contenimento,
ci sono finestre a top-level (gestite dal window manager) e sottofinestre.
Struttura di un'interfaccia
Una interfaccia utente grafica e' costituita da:
- una o piu' finestre top-level
- vari dispositivi o elementi di interfaccia o
widget
contenuti nelle finestre top-level, realizzati mediante sottofinestre
Da qui in poi per finestre intendiamo (dove non specificato)
finestre top-level.
Alcuni usano il termine componenti di interfaccia per indicare sia le
finestre che i dispositivi, altri solo per i dispositivi.
Dispositivi di un'interfaccia
Oggetti con aspetto grafico e sensibilita' a particolari eventi
che "popolano" una finestra e permettono interazione con l'utente.
Sono costruiti dal programmatore usando la libreria API del sistema a
finestre (WMS) oppure (di solito) sono forniti da un toolkit per
sviluppo di interfacce.
Terminologia:
- dispositivi
- widget (in X)
- componenti (in Java)
Alcuni usano lo stesso termine per indicare sia le
finestre che i dispositivi.
Tipi di dispositivi
I dispositivi forniti da un toolkit si classificano in diversi tipi
(o classi),
ciascun tipo con certe caratteristiche di aspetto / comportamento.
- Elementi attivi (sensibili a input). Esempi:
- menu' (permettono di immettere una scelta da una lista
di opzioni)
- bottoni (sensibili al click)
- dispositivi per introduzione di testo (campi di input)
- dispositivi per introduzione di valori numerici
(potenziometri lineari, circolari, digitali)
- Elementi inerti (solo aspetto grafico). Esempi:
- etichette testuali
- etichette grafiche (bitmap, pixmap)
- cornici ed altri elementi decorativi
- Aree dedicate a scopi speciali: aree grafiche,
aree per visualizzazione o editing di testi...
Programmazione guidata da eventi
(o Event-driven programming)
Il comportamento di un programma dotato di interfaccia utente grafica
e' di tipo reattivo.
Sono le azioni compiute dell'utente sui dispositivi di input
a determinare il flusso del programma.
Il comportamento del programma consiste in reazioni agli
eventi prodotti dal WMS a seguito delle azioni dell'utente.
Compito dell'interfaccia e' raccogliere l'evento e scatenare la reazione.
La reazione ad un evento puo' comportare:
- cambiamento dello stato interno dell'applicazione
- attivazione di una procedura di calcolo con produzione
di risultati
- visualizzazione di risultati attraverso l'interfaccia
grafica o comunque feedback per confermare
che l'operazione e' stata effettuata
- cambiamento nell'interfaccia grafica: apertura / chiusura /
modifica di finestre, attivazione / disattivazione
di tipi di eventi
- eventuale generazione di eventi diretti ad altre applicazioni
Funzionamento
Schema del funzionamento interno
di un'applicazione guidata da eventi.
NOTA:
Il codice del programma che realizza un'interfaccia che usi
direttamente la API del WMS e' composto di queste parti.
Se l'interfaccia e' realizzata con un toolkit, il codice del programma
che realizza l'interfaccia
non conterra' tutte queste parti. Molte saranno implementate
all'interno del toolkit in modo trasparente al programmatore.
- Inizializzazione
- collegarsi con il WMS
- creare l'interfaccia (definire le
finestre, il loro contenuto, la gerarchia,
gli eventi che si intendono catturare su ognuna...)
- visualizzare l'interfaccia grafica (mappare le finestre)
- Ciclo degli eventi. Ad ogni giro:
- guardare se c'e' un evento
- eseguire la reazione prevista a seconda del tipo di evento e
della finestra/dispositivo in cui si e' verificato l'evento
Interazione bloccante e non bloccante
Riguarda la gestione del ciclo degli eventi.
-
Interazione bloccante
Flusso del programma completamente guidato da eventi.
Finche' non arriva un evento, il programma dorme
(interazione bloccata) e non usa CPU.
Quando riceve un evento, allora si risveglia (riprende l'esecuzione)
per reagire all'evento.
- Interazione non bloccante
Programma oltre a reagire agli eventi esegue anche operazioni autonome,
indipendenti dagli eventi (es: animazione).
Mentre attende che arrivino eventi, nel frattempo
esegue le sue operazioni (consumando CPU).
Se arriva un evento, allora interrompe la sua normale
attivita' per reagire all'evento.
L'attesa puo' essere implementata mediante sospensione dell'esecuzione
(liberando CPU) per un certo tempo massimo prefissato (breve).
Se entro quel tempo non arriva un evento, l'esecuzione
riprende comunque e si esegue la procedura autonoma.
In alcuni pacchetti l'interazione non bloccante e' gestita introducendo
uno speciale evento nullo:
- se non ci sono eventi in coda, viene restituito l'evento nullo
- la reazione all'evento nullo consiste nell'eseguire la procedura
indipendente
Toolkit per lo sviluppo di interfacce grafiche
In generale si possono scrivere interfacce usando la API del WMS,
ma e' lungo, noioso e facile a errori.
Esempio
Per creare una finestra contenente un bottone, dove
quando l'utente preme il bottone l'applicazione termina, devo:
-
chiedere al server una finestra (top-level)
-
chiedere al server una finestra (il bottone) sottofinestra della prima
-
chiedere una font per scrivere sulla seconda finestra (il bottone)
-
chiedere al server di scrivere l'etichetta sul bottone usando la font
-
dichiarare al server che sono interessato a catturare evento
"pressione del mouse" sulla seconda finestra (il bottone)
-
mappare le finestre
-
scrivere un ciclo in cui attendo un evento e, quando arriva
(sara' la pressione del mouse sul bottone perche' non ho dichiarato
interesse per altri eventi), esco dal ciclo e termino
Difficile scrivere interfacce complesse con la API del WMS, troppo
di basso livello.
L'alternativa e' usare un toolkit.
Toolkit: pacchetto che fornisce insieme di elementi di interfaccia
pronti all'uso e insieme di funzioni (API) per utilizzarli nella creazione
di un'interfaccia.
La API del toolkit e' di livello piu' alto rispetto alla API della
libreria di base fornita dal WMS.
Scrivere interfacce usando un toolkit e' piu' facile e veloce:
-
Per creare l'interfaccia basta istanziare widget dei tipi opportuni
e collocarli nelle finestre
-
Ciclo degli eventi e' gestito automaticamente dal toolkit mediante
meccanismo delle procedure callback
Procedure callback
Scopo: stabilire che quando accade un certo tipo di
evento in un certo widget deve si compiere una certa reazione.
Senza un toolkit, il programmatore dovrebbe gestire a mano il ciclo degli
eventi.
Ad ogni evento verificatosi, dovrebbe eseguire una selezione (switch)
in base al tipo di evento e alla finestra in cui e' avvenuto:
- se l'evento e' di tipo TA ed e' avvenuto nella finestra FA,
esegui azione AA
- se l'evento e' di tipo TB ed e' avvenuto nella finestra FB,
esegui azione BB
- se l'evento e' di tipo TC ed e' avvenuto nella finestra FC,
esegui azione CC
- ...
Fattibile solo in programmi piccoli.
I toolkit per sviluppo di interfacce grafiche forniscono un meccanismo
piu' comodo.
Callback: procedura dell'applicazione che viene registrata per
essere eseguita
automaticamente dal sistema al verificarsi di un dato evento in un dato
widget.
Le procedure callback specificano il comportamento dei widget.
In fase di inizializzazione, la procedura viene definita e
registrata cioe' collegata al widget e all'evento in questione.
Il ciclo degli eventi e' gestito automaticamente, dal toolkit:
all'occorrere di un evento in un widget,
chiama la callback registrata per quell'evento e quel widget
(se ne e' stata registrata una).
Il programmatore deve solo:
- scrivere una funzione con parametri e valore di ritorno
conformi a quelli stabiliti dal toolkit per callback a quel tipo di evento
in quel tipo di widget
- associare la funzione come callback al widget per quel tipo di evento
Il toolkit gestira' al suo interno, in modo trasparente
all'applicazione, il ciclo degli eventi:
- guarda l'evento e il widget
- se per quell'evento quel widget ha associata una callback, la esegue
Schema di funzionamento rivisto
Con l'uso di un toolkit, lo schema dell'applicazione diventa:
- Inizializzazione
- dichiarare che si intende usare il toolkit (direttiva
#include in C, import in java...)
- inizializzare il toolkit (esegue collegamento con il WMS)
NOTA: certi toolkit non richiedono inizializzazione esplicita
(es. java)
- creare l'interfaccia (definire le
finestre, i widget, la gerarchia di contenimento...)
- associare callback ai widget per gli eventi di interesse
- visualizzare l'interfaccia grafica (mappare le finestre)
- Ciclo degli eventi.
- chiamare una sola apposita funzione del toolkit, che esegue
automaticamente al suo interno il ciclo degli eventi
NOTA:
in certi toolkit non e' richiesto,
il ciclo degli eventi parte automaticamente non appena
l'interfaccia e' visibile (es. java)
Esempio
Per creare una finestra contenente un bottone, dove
quando l'utente preme il bottone l'applicazione termina
(stesso esempio visto prima), devo:
-
creare una finestra top-level
-
istanziare un bottone (elemento di interfaccia predefinito nel toolkit),
specificando l'etichetta
-
inserirlo nella finestra top-level
-
associare al bottone la procedura che voglio eseguire quando
l'utente aziona il bottone (nel mio caso una procedura che esegue
la terminazione del programma)
-
mappare
Il toolkit si preoccupa di eseguire la serie di richieste al server
necessaria per realizzare il bottone e per gestire gli eventi su di
esso.
La comunicazione col server avviene internamente al toolkit, in modo
trasparente per l'applicazione.
Collocazione del toolkit nello schema client-server:
+-------------------------------+ +--------------+
| CLIENT | | |
| +------------+ +-------+ | (rete) | |
| |applicazione|------|toolkit|---------------- SERVER |
| +------------+ API +-------+ | API | |
| del toolkit | del WMS | |
+-------------------------------+ +--------------+
Interazione non bloccante
Lo schema visto sopra corrisponde a interazione bloccante.
Come si realizza interazione non bloccante, cioe' come posso eseguire
operazioni anche in assenza di eventi ?
-
alcuni toolkit prevedono una
idle callback (callback che scatta quando non ci sono eventi)
-
altri prevedono timer con cui posso scatenare un evento fittizio
ogni tot di tempo, a questo evento fittizio associo callback
(es. java)
Widgets ed eventi
Toolkit fornisce oggetti di finestra o widget
(= window object).
Questi sono gli elementi di interfaccia pronti all'uso
(bottoni, menu'...).
Dal punto di vista dell'utente dell'interfaccia
un widget e' caratterizzato da:
- una o piu' finestre che costituiscono la sua apparenza
- comportamento predefinito rispetto a certi eventi che sono
gestiti internamente a cura del toolkit
- comportamento personalizzabile rispetto a certi altri eventi
che sono gestiti a cura dell'applicazione
Dal punto di vista del programmatore
un widget e' caratterizzato da:
- uno stato, descritto da un insieme di dati propri del widget
- un insieme di funzioni API per interrogare e manipolare lo stato
- un insieme di eventi che possono essere intercettati dal widget
e passati all'applicazione
- un meccanismo per "aggangiare" a tali eventi procedure
dell'applicazione (scritte dal programmatore), che il sistema
chiamera' automaticamente in occasione dell'evento
(procedure callback)
Esempi:
-
Un widget di tipo "bottone semplice" e' costituito da una finestra
ed e' sensibile all'evento click del mouse.
-
Un widget di tipo "bottone con spia luminosa"
e' costituito da una finestra per il bottone ed una per la spia.
L'informazione se la spia e' accesa o spenta fa parte dello stato del
bottone.
Due categorie fondamentali di widget:
- widget di base: servono per presentare
informazioni all'utente e/o ricevere input da questo.
- widget contenitori: servono per raggruppare altri widget
in un contesto grafico.
Mediante i contenitori si struttura la gerarchia di annidamento
dell'interfaccia.
Meccanismo a scatole cinesi.
Con contenitori posso raggruppare widget concettualmente
collegati, disegnare bordi e cornici.
In genere widget di un toolkit sono organizzati in classi
secondo la filosofia object oriented
(questo anche se il toolkit e' scritto in un linguaggio
non object-oriented).
C'e' una classe per ciascun tipo di widget fornito dal toolkit.
Creare un widget = creare un'istanza della classe opportuna.
Gestione degli eventi nei widget
Event dispatching:
in un programma realizzato tramite un toolkit, gli eventi inviati
dal WMS non sono visibili direttamente, ma solo attraverso i widget.
Il toolkit gestisce direttamente la coda degli eventi tramite
un event loop.
Quando arriva un evento dal WMS, il toolkit attiva il widget a cui l'evento
si riferisce (cioe' il widget che corrisponde alla event window).
Widget puo' gestire un evento come:
- evento interno:
widget utilizza evento per cambiare il proprio stato,
senza interessare l'applicazione
- azione:
widget utilizza evento per produrre un evento di widget che
viene passato all'applicazione e
scatena un'azione, implementata da una callback dell'applicazione
Nota: evento di widget puo' essere causato anche da una sequenza
di eventi del WMS (es. doppio click).
Esempi
Un widget bottone del tipo che si evidenzia quando l'utente ci passa
sopra col mouse e scatta quando preme:
-
evento entrata / uscita del mouse dalla finestra che realizza il bottone
e' gestito dal toolkit mediante cambiamento di colore del bottone
-
evento pressione del mouse e' gestito da applicazione stabilendo
callback da invocare
Un widget di tipo campo testuale (per l'immissione di una stringa di testo):
-
evento "pressione del mouse" e' usato internamente al toolkit per
attivare il campo di testo
-
a campo di testo attivo eventi "pressione di caratteri" sono usati
internamente per aggiornare il contenuto del campo
-
evento "pressione del tasto return" e' passato all'applicazione,
che deve avere stabilito una callback per gestirlo (leggere la stringa
immessa...)
In un widget di tipo "slider" (barra di scorrimento per l'immissione di un
valore numerico in un certo intervallo):
-
gli eventi generati dall'utente trascinando il cursore e cliccando
sulle frecce sono gestiti internamente al widget ed usati per
aggiornare la posizione del cursore
-
lo stato interno del widget comprende il valore della posizione corrente
e puo' essere interrogato dall'applicazione
-
l'applicazione puo' agganciare una callback che scatti ogni volta che
l'utente ha terminato di manipolare lo slider (leggere il valore...)
Eventi del toolkit
Gli eventi che toolkit passa all'applicazione non sono necessariamente
gli stessi eventi del sistema a finestre (WMS).
Molti sono eventi a piu' alto livello di astrazione.
Esempi:
-
Click del mouse = sequenza di pressione e rilascio
(eventi del WMS) avvenuti entro un tempo sufficientemente breve
(toolkit se ne accorge guardano il time stamp dei due eventi)
-
Fine interazione su campo di input testuale =
pressione del tasto return (evento che nel WMS e' pari a
pressione di qualsiasi altro tasto, ma il toolkit gli assegna
valore particolare)
Inoltre ci sono eventi che non hanno controparte nel WMS.
Esempio: evento generato da timer (ved. interazione non bloccante).
GUI Designer
Un toolkit puo' fornire un GUI Designer.
Programma interattivo che permette la progettazione di interfacce grafiche
basate su tale toolkit.
- si possono creare finestre, bottoni, slider, menu'
istanziando degli schemi predefiniti (per ogni classe di widget
fornita dal toolkit)
- si possono dimensionare, posizionare tali oggetti nell'interfaccia
usando manipolazione diretta
- si puo' associare direttamente alla pressione di un tasto
in un menu' l'attivazione di sottomenu', apertura di
finestre di dialogo...
Il GUI Designer produce il codice relativo all'interfaccia.
Il programmatore deve solo riempire il corpo delle procedure
callback associate ai widget.