Fino ad ora abbiamo trattato di programmi di tipo "batch" [Infornata, N.d.t.]. Come ricorderete i programmi possono essere di tipo batch, quando vengono lanciati, eseguono il loro lavoro e terminano; oppure ad eventi, quando vengono lanciati, si mettono in attesa di eventi e rimangono in funzione fino a quando non ricevono l'evento che corrisponde alla richiesta di terminare. Come possiamo creare un programma ad eventi? Affronteremo il problema da due angolazioni, prima simuleremo un ambiente orientato agli eventi, e successivamente creeremo un semplice programma di tipo GUI che usa il sistema operativo e l'ambiente per generare eventi.
Ogni programma ad eventi è strutturato come un ciclo che raccoglie gli eventi e li processa. Gli eventi possono essere generati dal sistema operativo, come accade nel caso di praticamente tutti i programmi di tipo GUI. Oppure può essere il programma stesso che "va in cerca" di eventi, come accade nei sistemi di controllo "embedded" come quelli usati nelle macchine fotografiche ed in altri apparecchi.
Adesso costruiremo un programma che riceve un solo tipo di eventi, la pressione di un tasto della tastiera, e li processa fino a che non riceve un particolare evento di terminazione. Stabiliamo in questo caso che l'evento di terminazione sia la pressione del tasto "barra spaziatrice". Tratteremo tutti gli altri eventi in arrivo in modo molto semplice: visualizzando il codice ASCII del tasto premuto. Per fare ciò useremo il BASIC perché dispone di una funzione per la lettura dei caratteri da tastiera semplice ed elegante: INKEY$.
Innanzi tutto implementiamo la parte centrale del programma principale, quella che attiva la raccolta degli eventi e chiama i sottoprogrammi di gestione degli eventi quando ne riceve uno valido.
' Dichiarazione dei sottoprogrammi per la gestione degli eventi DECLARE SUB EventoTasto (tasto AS STRING) DECLARE SUB EventoFine (tasto AS STRING) ' Ripulisce lo schermo e avverte l'utente di cosa ' deve fare per terminare il programma CLS PRINT "Per terminare premi <SPAZIO>..." PRINT ' Adesso cicla per sempre WHILE 1 tst$ = INKEY$ lung = LEN(tst$) IF lung <> 0 THEN ' invia l'evento alle funzioni per la gestione IF tst$ <> " " THEN CALL EventoTasto(tst$) ELSE CALL EventoFine(tst$) END IF END IF WEND
Notate che nella parte centrale del programma non è necessario sapere come vengono processati gli eventi, questi vengono semplicemente raccolti ed inviati ai gestori degli eventi. L'indipendenza della parte che riceve gli eventi e della parte che li gestisce è un punto chiave della programmazione ad eventi.
Adesso possiamo implementare i due gestori degli eventi. Il primo, EventoTasto visualizza semplicemente il codice ASCII del tasto premuto:
SUB EventoTasto (tasto AS STRING) ' visualizza i tasti validi lung = LEN(tasto) IF lung = 1 THEN 'è un semplice codice ASCII PRINT ASC(tasto) ELSE IF lung = 2 THEN 'è un tasto speciale. Esamina il secondo codice PRINT ASC(MID$(tasto, 2, 1)) END IF END IF END SUB
Il gestore EventoFine è semplice: si limita a terminare il programma!
SUB EventoFine (tasto AS STRING) STOP END SUB
Se il codice che abbiamo scritto dovesse essere usato all'interno di più programmi diversi probabilmente includeremmo una chiamata ad una funzione di inizializzazione all'inizio e ad una funzione di pulizia alla fine. Il programmatore potrebbe così riutilizzare la parte relativa al ciclo, scrivendo le proprie funzioni di inizializzazione, gestione degli eventi e pulizia.
Questo è il modo di funzionare di gran parte degli ambienti di tipo GUI, dato che la parte riguardante il ciclo è racchiusa nell'ambiente di programmazione e le applicazioni sono obbligatoriamente tenute a fornire le funzioni di gestione degli eventi agganciandole in qualche modo al ciclo degli eventi.
Vediamo in pratica tutto ciò esplorando la libreria GUI di python: TKinter.
Per questo esercizio utilizzaremo il "toolkit" [insieme di attrezzi, N.d.t.] Tkinter fornito con Python. Si tratta di una versione per Python del toolkit Tk scritto originariamente come estensione di Tcl e disponibile anche in una versione Perl. La versione Python è orientata agli oggetti e, secondo me, risulta assai più facile da usare di quella originale di tipo procedurale. Non intendo approfondire molto gli aspetti relativi all'interfaccia grafica, ma vorrei invece porre l'accento sullo stile di programmazione, usando Tkinter per gestire il ciclo degli eventi e lasciando al programmatore il compito di creare la interfaccia grafica iniziale e di processare gli eventi via via che si presentano.
Nell'esempio viene creata una classe applicazione AppTasti che crea una GUI nel suo metodo __init__ e lega [In inglese: bind, N.d.t.] il tasto spazio al metodo EventoFine. La classe inoltre definisce, come richiesto, tale metodo EventoFine.
Per quanto riguarda la GUI, questa è costituita semplicemente da un widget [elemento di un'interfaccia grafica, N.d.t.] per l'introduzione di testo la cui funzione di base consiste nel visualizzare i caratteri che vengono introdotti.
La creazione di classi applicazione negli ambienti ad eventi con programmazione ad oggetti è una pratica assai comune perché esiste una forte analogia fra eventi che vengono inviati ad un programma e messaggi che vengono inviati ad un oggetto. I due concetti combaciano molto elegantemente. Una funzione per la gestione di un evento diviene quindi un metodo della classe applicazione.
Dopo aver definito la classe possiamo semplicemente creare un'istanza di essa e quindi inviarle il messaggio mainloop.
Il codice si presenta cosí:
# Si usa from X import * per evitare di dover sempre # specificare tkinter.xxx from Tkinter import * # Si creano la classe applicazione che definisce la GUI # ed i metodi per la gestione degli eventi class AppTasti(Frame): def __init__(self): Frame.__init__(self) self.finTesto = Text(self) self.finTesto.bind("<space>", self.EventoFine) self.finTesto.pack() self.pack() def EventoFine(self,event): import sys sys.exit() # Adesso si crea l'istanza e si attiva il ciclo degli eventi mioProg = AppTasti() mioProg.mainloop()
Nella versione BASIC avevamo visualizzato i codici ASCII dei tasti, invece che il carattere corrispondente come facciamo qui. Niente ci impedisce di catturare gli eventi prodotti dalla pressione dei tasti e fare la stessa cosa. È sufficiente aggiungere al metodo __init__ la seguente istruzione:
self.finTesto.bind("<Key>", self.EventoTasto)
Ed un'altro metodo per gestire il corrispondente evento:
def EventoTasto(self,event): str = "%d\n" % event.keycode self.finTesto.insert(END, str) return "break"
Nota 1: Il codice del tasto viene memorizzato nel campo keycode dell'evento. Per scoprirlo sono stato costretto a guardare nel codice sorgente di Tkinter.py... Ricordate che la curiosità è una qualità fondamentale in un programmatore?
Nota 2: return "break" è una parola magica che chiede a Tkinter di non chiamare il sistema di trattamento degli eventi standard per questo widget. Senza questo comando la finestra di testo mostrerebbe il codice ASCII del tasto seguito dal carattere corrispondente, cosa che qui non vogliamo.
Mi sembra che tutto ciò sia sufficiente per il momento. Lo scopo non è quello di fare un'introduzione a Tkinter, che sarà l'argomento del prossimo capitolo. Inoltre potete trovare numerosi testi sull'uso di TK e di Tkinter.
If you have any questions or feedback on this page send me mail at: alan.gauld@yahoo.co.uk