In questo ultima parte illustrerò come sono stati utilizzati i vari widgets per realizzare l’editor e l’emulatore. Come vedrete, una volta capito il funzionamento della libreria, sarà molto semplice implemtentare soluzioni per le vostre necessità. Spero che gli esempi siano abbastanza chiari, così da potervi permettere di sperimentare autonomamente la creazione di programmi multipiattaforma con tkinter.
Come potete vedere dal costruttore, oltre alla finestra principale viene passato anche calcolatore (oggetto creato con la classe pdp8 dei precendeit articoli). Questa finestra non è molto complessa, infatti è composta solo dal LabelFrame codice e dal menu in alto “menubar”. Non dimenticate che ogni frame ha la sua griglia (grid).
In create_widgets() possiamo vedere più dettagliatamente come menubar venga arricchito delle varie voci previste dal programma e di come si inserisce un oggetto Text al frame codice.
C’è da notare che all’oggetto Text abbiamo subito associato la barra di scorrimento laterale e che ogni opzione del menù è stata associata ad un comando specifico, ovvero una funzione interna alla classe che ha determinati compiti (non posto il codice delle varie funzioni per non rendere troppo dispersivo l’articolo, si possono comunque cosultare i sorgenti online, visto che ho aggiornato il repository con mercurial).
La finestra dell’emulatore è un pò più complessa, come potete notare dal costruttore:
self.labelprogramc =Label(self.programc, text ='00000000000', relief =SUNKEN, bg ='red')
self.labelprogramc.grid()
self.labelmar =Label(self.mar, text ='00000000000', relief =SUNKEN, bg ='yellow')
self.labelmar.grid()
self.labelmbr =Label(self.mbr, text ='000000000000000', relief =SUNKEN)
self.labelmbr.grid()
self.labelac =Label(self.lac, text ='000000000000000', relief =SUNKEN)
self.labelac.grid()
self.labelvari =Label(self.vari, text ='0', relief =SUNKEN)
self.labelvari.grid()
self.labelvare =Label(self.vare, text ='0', relief =SUNKEN)
self.labelvare.grid()
self.labelopr =Label(self.lopr, text ='000', relief =SUNKEN)
self.labelopr.grid()
self.labelucs =Label(self.unitas, text ='0')
self.labelucs.grid()
self.labelucf =Label(self.unitaf, text ='0')
self.labelucf.grid()
self.labelucr =Label(self.unitar, text ='0')
self.labelucr.grid()
self.labelucint =Label(self.unitaint, text ='0')
self.labelucint.grid()
self.labelnstep =Label(self.nstep, text ='1')
self.labelnstep.grid()
self.labeldelay =Label(self.delays, text =str(self.delay))
self.labeldelay.grid()
self.labeltempo =Label(self.tempo, text =str(self.CD.tempo))
self.labeltempo.grid()
Apparentemente sembra difficile da interpretare, ma questo schemino riassuntivo dei frame principali dovrebbe rendere meglio l’idea della sua implementazione.
Una volta localizzati i frame principali è quasi banale capire l’inserimento dei vari widgets. Molti sotto-frame sono stati inseriti solo per raggiungere un buon compromesso visivo, ma sostanzialmente se ne poteva anche fare a meno.
Da notare che ci siamo portati dietro l’oggetto codice della precedente finestra, così da poter estrapolare il testo al suo interno e darlo in pasto all’Assembler del pdp8. L’oggetto pdp8 è lo stesso creato nel main, quindi ne esiste uno per tutta la sessione del programma ed è raggiungibile sia da Editor (la finestra principale) che da Emulatore (finestra toplevel, secondaria ma di uguale importanza).
Come prima, non metterò il codice di tutte le funzioni presenti perché sono consultabili online; piuttosto vorrei parlarvi della risoluzione di un problema affrontato con la scrittura dell’interfaccia.
Se avete già provato il programma avrete notato che facendo partire l’emulazione è possibile premere altri pulsati ed interagire con l’interfaccia (come in qualsiasi altro programma) mentre è in esecuzione il codice. Questo risultato è stato raggiunto grazie ad un metodo basilare dei widgets per “alarm handlers and other non-event callbacks”, ovverro per gestire eventi che non sono semplicemente associati ad un oggetto.
In questo modo ho potuto ricostruire l’esecuzione temporale del processore secondo un certo delay (che è possobile settare sempre tramite interfaccia grafica) che non tocca minimamente l’implementazione interna del pdp8.
Nei sorgenti troverete per esempio “self.butesegui.after(self.delay, self.esegui)” in Emulatore nel metodo esegui: premuto il tasto ESEGUI (il quale è collegato al medesimo metodo), dopo un certo delay il widget richiamerà la funzione esegui della classe Emulatore, così da permettere la continua esecuzione del codice.
Per il resto, gli unici widgets utilizzati sono Text e Button, mentre i LabelFrame vengono utilizzati per organizzare visivamente l’emulatore, visto che è possibile scrivere un testo di contorno al frame creato. Per evidenziare il testo si è utilizzato un particolare vincolo di Text, ovvero i “tag” che possono essere personalizzati dall’utente.
Conclusioni
Spero che i codici sorgeti disponibili e queste mini-guide esplicative del programma siano utili per la realizzazione di piccoli progetti. Tkinter è molto semplice da utilizzare e per questo non ho speso molto tempo nell’analisi riga per riga del codice visto poiché, a parte l’organizzazione dei frame, l’utilizzo dei widgets è banale e basta consultare bene i manuali online per capire il funzionamento dei vari metodi. C’è da tenere poi conto che 3/4 del tempo è stato speso per il testing, potete quindi immaginare quanto semplice sia il codice.
Con questo concludo la serie di articoli sull’emulatore del pdp8 in python, curioso di ascoltare le vostre critiche, opinioni, suggerimenti e dubbi. In futuro mi piacerebbe convertire l’applicazione in wxPython, visto che quest’ultima ha suscitato di recente il mio interesse.
In Appunti Digitali cerchiamo di esprimere punti di vista informati su questioni che conosciamo, materie che pratichiamo professionalmente o passioni seguite a lungo e con metodo. Nei limiti del poco tempo che possiamo dedicare alla scrittura di articoli, speriamo di aiutarvi a costruirvi un’opinione su materie complesse, informarvi e se possibile divertirvi. I conti del server li paghiamo di tasca nostra e ci riserviamo perciò di moderare i commenti a nostra discrezione.