full circle

32

Upload: ultimusfree

Post on 08-Aug-2015

70 views

Category:

Documents


1 download

DESCRIPTION

non so il numero

TRANSCRIPT

Page 1: Full circle

Full CircleEDIZIONE SPECIALE SERIE PROGRAMMAZIONE

LA RIVISTA INDIPENDENTE PER LA COMUNITÀ LINUX UBUNTU

full circle magazine non è affiliata né sostenuta dalla Canonical Ltd .

EDIZIONE SPECIALE

SERIE PROGRAMMAZIONE

PROGRAMMARE

IN PYTHON

VOLUME 5

Page 2: Full circle

Cos'è Full Circle

Full Circle è una rivista gratuita e

indipendente, dedicata alla famiglia

Ubuntu dei sistemi operativi Linux.

Ogni mese pubblica utili articoli

tecnici e articoli inviati dai lettori.

Full Circle ha anche un podcast di

supporto, il Full Circle Podcast, con

gli stessi argomenti della rivista e

altre interessanti notizie.

Si prega di notare che questaedizione speciale viene fornitasenza alcuna garanzia: né chi hacontribuito né la rivista Full Circlehanno alcuna responsabilità circaperdite di dati o danni chepossano derivare ai computer oalle apparecchiature dei lettoridall'applicazione di quantopubblicato.

Come contattarci

Sito web:http://www.fullcirclemagazine.org/

Forum:http://ubuntuforums.org/forumdisplay.php?f=270

IRC:#fullcirclemagazine suchat.freenode.net

Gruppo editoriale

Capo redattore: Ronnie Tucker(aka: RonnieTucker)[email protected]

Webmaster: Rob Kerfia(aka: admin / [email protected]

Modifiche e CorrezioniMike Kennedy, Lucas Westermann,Gord Campbell, Robert Orsino, JoshHertel, Bert Jerred

Si ringrazia la Canonical e i tantigruppi di traduzione nel mondo.

Ecco a voi un altro 'Specialemonotematico'

Come richiesto dai nostri lettori, stiamo assemblando in edizionidedicate alcuni degli articoli pubblicati in serie.

Quella che avete davanti è la ristampa della serie 'Programmarein Python' parti 27-31 , pubblicata nei numeri 53-59: e già,l'impareggiabile professorGregg Walters si è concesso unapausa durante questo periodo.

Vi preghiamo di tenere conto della data di pubblicazione: leversioni attuali di hardware e software potrebbero esserediverse rispetto ad allora. Controllate il vostro hardware e ilvostro software prima di provare quanto descritto nelle guide diqueste edizioni speciali. Potreste avere versioni più recenti delsoftware installato o disponibile nei repository delle vostredistribuzioni.

Buon divertimento!

Gli articoli contenuti in questa rivista sono stati rilasciati sotto la licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stessomodo 3.0. Ciò significa che potete adattare, copiare, distribuire e inviare gli articoli ma solo sotto le seguenti condizioni: dovete attribuire il lavoroall'autore originale in una qualche forma (almeno un nome, un'email o un indirizzo Internet) e a questa rivista col suo nome ("Full Circle Magazine") econ suo indirizzo Internet www.fullcirclemagazine.org (ma non attribuire il/gli articolo/i in alcun modo che lasci intendere che gli autori e la rivista

abbiano esplicitamente autorizzato voi o l'uso che fate dell'opera). Se alterate, trasformate o create un'opera su questo lavoro dovete distribuire il lavoro risultante con la stessalicenza o una simile o compatibile.Full Circle magazine è completamente indipendente da Canonical, lo sponsor dei progetti di Ubuntu, e i punti di vista e le opinioni espresse nella rivista non sono in alcun

modo da attribuire o approvati dalla Canonical.

Full CircleLA RIVISTA INDIPENDENTE PER LA COMUNITÀ LINUX UBUNTU

Page 3: Full circle

full circle magazine n. 53 7 indice ^

HHOOWW--TTOOScritto da Greg Walters PPrrooggrraammmmaarree iinn PPyytthhoonn -- PPaarrttee 2277

Se avetemai aspettato in filaper comprare unbigliettodel cinema, siete stati incoda. Se avetemai aspettato

nel traffico all'ora di punta, siete statiin coda. Se avetemai aspettato in unufficio pubblico con inmanounodiquei bigliettini che dicevano cheeravate il numero 98 e il tabelloneindicava "serviamo il numero 42",siete stati in coda.

Nelmondodei computer le codesono comuni. Comeutente, lamaggior parte delle volte, non ènecessario preoccuparsene. Sonoinvisibili. Ma se dovete avere a chefare con eventi in tempo reale allora ènecessario conoscerle. Si trattasemplicemente di un dato di unqualche tipo, che attendedi essereprocessato. Una volta entrato nellacoda, vi resta finché non vienerichiesto e quindi viene rimosso. Nonèpossibile recuperare il valore deldato successivo finché non lo si estraedalla coda. Nonpotete, per esempio,recuperare il valore del 15º elementodella coda. Dovete prima accedere airestanti 14 elementi. Una volta chel'accesso è possibile, esce dalla coda.È andato, e ameno che non venga

salvato in una variabile a lungotermine, non èpiù possibilerecuperarlo.

Ci sonomolteplici tipi di code. Lepiù comuni sono FIFO (First In, FirstOut)(primo a entrare, primo a uscire),LIFO (Last In, FirstOut)(ultimo adentrare, primo aduscire), a Priorità eadAnello. Parleremodelle code adanello la prossima volta.

Le code FIFO sonoquelle cheosserviamonella vita di tutti i giorni.Tutti gli esempi fatti prima sono codeFIFO. La primapersona nella fila èservita per prima, si sposta, quindiognuno simuovedi un posto. In unbuffer FIFOnon c'è nessun limite alnumero di elementi che puòcontenere, entro limiti ragionevoli.Vengono impilati in ordine.Quandoun elemento èprocessato, vieneestratto dalla coda e il resto simuoveverso l'uscita di una posizione.

Le code LIFO sonomeno comuni,ma si possono comunque fare esempipresi dalmondo reale.Quello chemiviene inmente più facilmente è unapila di piatti nell'armadio della cucina.Quando i piatti sono lavati e asciugati,

vengono riposti nell'armadio. L'ultimodella fila è il primo che viene presoper essere usato. Tutti gli altri devonoaspettare, forse per giorni, primadiessere utilizzati. È una cosa buona chela coda per il biglietto del cinema siadi tipo FIFO, vero? Come la coda FIFO,entro limiti ragionevoli, non c'ènessun limite alla dimensione di unacoda LIFO. Il primoelemento nellacoda deve aspettaremanmano chenuovi elementi sono estratti dalbuffer (piatti estratti dalla pila) finchérisulta l'ultimo rimasto.

Le code a priorità sonopermoltepersone unpo' più difficili daimmaginare correttamente fuori dalcontesto. Pensate ad una compagniache ha una sola stampante. Tuttiusanoquell'unica stampante. I lavoridi stampa sonogestiti dalla prioritàdel dipartimento. L'ufficio paghe hauna priorità superiore (emenomale)di, diciamo, te, un programmatore.Voi avete una priorità superiore (emenomale) rispetto all'addettoall'accettazione.Quindi, in breve, idati che hannouna priorità superioresonogestiti, ed escono fuori dallacoda, primadi quelli che hannounapriorità inferiore.

FIFO

Le code FIFO sono semplici davisualizzare in termini di dati. Una listapython è una semplicerappresentazionementale.Considerate questa lista...

[1,2,3,4,5,6,7,8,9,10]

Ci sono 10 elementi nella lista. Inquanto tale, potete accedere ai suoielementi tramite un indice. Invece, inuna coda non èpossibile accedereagli elementi attraverso un indice.Dovete gestire il prossimoelementonella coda e la lista non è statica. ÈMOLTOdinamica.Quandonoirichiediamo il prossimoelementonella coda, questo viene rimosso. Cosìusando l'esempio sopra, richiedete un

“Ci sonomolteplici tipi di

code. Lepiù comuni sono

FIFO (First In, FirstOut)(primo

aentrare, primoauscire),

LIFO (Last In, FirstOut)(ultimo

adentrare, primoaduscire),

aPriorità e adAnello.

Page 4: Full circle

full circle magazine n. 53 8 indice ^

HOWTO - PROGRAMMARE IN PYTHON - PARTE 27

elemento dalla coda. Viene restituitoil primoelemento (1) e la coda quindiassomiglia a questa.

[2,3,4,5,6,7,8,9,10]

Richiedetene altri due e ottenete2, quindi 3 e quindi la coda assomigliaa questa.

[4,5,6,7,8,9,10]

Sono sicuro vi siete fatti un'idea.Python fornisce una semplice libreria,sorprendentemente sufficiente,chiamataQueue, che funziona beneper codedi piccola emediadimensione, fino a 500 elementi. Inalto c'è un semplice esempio che lamostra.

In questo esempio, inizializziamola coda (fifo =Queue.Queue()) quindivi inseriamo i numeri da 0 a 4(fifo.put(i)). Quindi usiamo ilmetodointerno .get() per estrarre gli elementidalla coda finché non è vuota,.empty(). Quello che viene restituito è

0,1,2,3,4. Potete anche definire ilnumeromassimodi elementi che lacoda deve contenere inizializzandolacon la dimensione, come in questoesempio.

fifo = Queue.Queue(300)

Una volta che il numeromassimodi elementi è stato caricato,Queueblocca ogni inserimento aggiuntivo.Questo ha come sgradevole effettoquello di far sembrare il programmacome "bloccato". Ilmodopiù sempliceper aggirare il problemaèusare ilcontrolloQueue.full() (in alto adestra).

In questo caso, la coda èimpostata per unmassimodi 12elementi. Quanto aggiungiamoelementi, iniziamo con 0 e arriviamo a11.Quando inseriamo il numero 12,però, il buffer è già pieno. Poichéverifichiamo se il buffer è pienoprimadi inserire un elemento, l'ultimo viene

semplicemente scartato.

Ci sono altre opzioni,mapossonocausare altri effetti secondari, e ce neoccuperemo in un articolo futuro.Quindi, nellamaggior parte delleoccasioni, assicuratevi di usare unacoda senza limiti o di avere comunqueabbastanza spazio per i vostri bisogni.

LIFO

La libreriaQueue supporta anchele code LIFO. Useremo la listaprecedente comeesempio visuale.Impostiamo la nostra coda, apparecomequesta:

[1,2,3,4,5,6,7,8,9,10]

estraiamo tre elementi dalla coda,che appare comequesta:

[1,2,3,4,5,6,7]

Ricordate che in una coda LIFO, glielementi sono rimossi nell'ordineULTIMOaentrare PRIMOauscire.

Ecco il semplice esempiomodificatoper la coda LIFO:

Quando lo eseguiamo, otteniamo"4,3,2,1,0".

Comeper la coda FIFO, avete lapossibilità di impostarne ladimensione, e potete usare ilcontrollo .full().

Priorità

Anche se non è usata spesso, una

import Queuefifo = Queue.Queue()for i in range(5):

fifo.put(i)

while not fifo.empty():print fifo.get()

import Queue

fifo = Queue.Queue(12)for i in range(13):

if not fifo.full():fifo.put(i)

while not fifo.empty():print fifo.get()

import Queuelifo = Queue.LifoQueue()for i in range(5):

lifo.put(i)while not lifo.empty():

print lifo.get()

pq = Queue.PriorityQueue()pq.put((3,'Medium 1'))pq.put((4,'Medium 2'))pq.put((10,'Low'))pq.put((1,'high'))

while not pq.empty():nex = pq.get()print nexprint nex[1]

(1, 'high')high(3, 'Medium')Medium(4, 'Medium')Medium(10, 'Low')Low

Page 5: Full circle

full circle magazine n. 53 9 indice ^

coda a priorità può a volte essereutile. Èmolto simile alle precedenti,madobbiamopassarle una tupla checontiene sia la priorità che il dato.Ecco un esempio che utilizza lalibreriaQueue:

Prima, inizializziamo la coda.Quindi inseriamoquattro elementi.Notate come venga usato il formato(priorità, dato) per inserire i dati. Lalibreria ordina i nostri dati inmanieraascendente in base al valore dipriorità. Quandoestraiamo il dato,questo è in formadi tupla, propriocome l'abbiamo inserito. È possibileusare un indice come indirizzo deldato.Quello che otteniamoè...

Nei primi due esempi, abbiamosemplicemente stampato i dati chevenivano fuori dalla coda.Questo vabeneper questi esempi,ma nellaprogrammazione nelmondo reale,probabilmente è necessario farequalcosa con l'informazione appenaestratta dalla coda, altrimenti èperduta.Quandousiamo 'printfifo.get', il dato è inviato al terminalepere poi essere distrutto. Giusto unacosa da tenere amente.Ora usiamoqualcosa di quello che abbiamogiàimparato su tkinter per creare unprogrammadimostrativo sulle code.Conterrà due cornici. La prima

conterrà (per l'utente) tre pulsanti.Unoper la coda FIFO, unoper la codaLIFOedunoper la coda a priorità. Laseconda cornice conterrà unwidgetdi inserimento, due pulsanti, uno peraggiungere alla coda e unoperestrarre dalla coda, e tre etichette,una chemostra quando la coda èvuota, una chemostra quando la codaè piena, e una chemostra cosa è statoestratto. Scriveremoanchedel codiceper centrare automaticamente lafinestra sullo schermo. In alto sinistrac'è l'inizio del nostro codice.

Qui abbiamogli import e l'iniziodella classe. Comeprima, creiamo lafunzione __init__ con le routineDefineVars, BuildWidgets ePlaceWidgets. Ne abbiamoanche unachiamata ShowStatus (in alto adestra) che... bé,mostra lo stato dellacoda.

Ora creiamo la funzione

HOWTO - PROGRAMMARE IN PYTHON - PARTE 27

import sysfrom Tkinter import *import ttkimport tkMessageBoximport Queue

class QueueTest:def __init__(self,master = None):

self.DefineVars()f = self.BuildWidgets(master)self.PlaceWidgets(f)self.ShowStatus()

def DefineVars(self):self.QueueType = ''self.FullStatus = StringVar()self.EmptyStatus = StringVar()self.Item = StringVar()self.Output = StringVar()# Define the queuesself.fifo = Queue.Queue(10)self.lifo = Queue.LifoQueue(10)self.pq = Queue.PriorityQueue(10)self.obj = self.fifo

def BuildWidgets(self,master):# Define our widgetsframe = Frame(master)self.f1 = Frame(frame,

relief = SUNKEN,borderwidth=2,width = 300,padx = 3,pady = 3

)self.btnFifo = Button(self.f1,

text = "FIFO")self.btnFifo.bind('<ButtonRelease-1>',

lambda e: self.btnMain(1))self.btnLifo = Button(self.f1,

text = "LIFO")self.btnLifo.bind('<ButtonRelease-1>',

lambda e: self.btnMain(2))self.btnPriority = Button(self.f1,

text = "PRIORITY")self.btnPriority.bind('<ButtonRelease-1>',

lambda e: self.btnMain(3))

Page 6: Full circle

full circle magazine n. 53 10 indice ^

DefineVars. Abbiamoquattro oggettiStringVar(), una variabile vuotachiamataQueueType e tre oggetticoda, unoper ciascun tipo di coda concui giocheremo. Per gli scopi diquesta demoabbiamo impostato ladimensionemassimadelle code a 10.Abbiamoanche creato unoggettochiamato obj e lo abbiamoassegnatoalla coda FIFO.Quando selezioniamoun tipo di coda attraverso i pulsanti,assegnamoquest'oggetto alla codache vogliamo. In questomodo, lacoda èmantenuta anche quando sipassa ad un altro tipo (il codice è nellapagina precedente, in basso a destra).

Qui iniziamo la definizione deiwidget. Creiamo la nostra primacornice, i tre pulsanti e le loroconnessioni. Notate che stiamousando la stessa routine per gestire leconnessioni di supporto. Ciascunpulsante invia un valore alla funzionedi callback che denota quale pulsanteè stato premuto. Avremmopotutofacilmente creare una routinededicata per ciascun pulsante.Comunque, dato che i tre pulsanti sioccuperannodi un compito comune,ho pensato fossemeglio lavorarci ingruppo (il codicemostrato a destra).

A seguire (in basso a destra),impostiamo la seconda cornice, il

widget di inserimento e i due pulsanti.L'unica cosa qui che è fuoridall'ordinario è la connessione per ilwidget di inserimento. Colleghiamo lafunzione self.AddToQueue al tastoInvio. In questomodo l'utente nondeve usare ilmouse per aggiungere ildato. Basta inserirlo nelwidget, sevuole.

Qui (pagina seguente, in basso) c'èla definizione degli ultimi trewidget.Tutti e tre sono etichette.Impostiamo l'attributo textvariablealle variabili definiteprecedentemente. Se ricordate,quandoquella variabile cambia, lo faanche l'etichetta. Facciamoqualcosaunpo' differente nell'etichettalblData. Useremoun caratterediverso per evidenziarla quandomostriamo il dato estratto dalla coda.Ricordate che dobbiamo restituire lacornice oggetto così che possa essereusata nella funzionePlaceWidget.

Questo (pagina seguente, alcentro) è l'inizio della funzionePlaceWidget. Notate che inseriamocinque etichette vuote proprioall'inizio della finestra radice. L'hofatto per inserire dello spazio. È unmetodo semplice per "barare" erendere la disposizione della finestrapiù semplice.Quindi impostiamo la

HOWTO - PROGRAMMARE IN PYTHON - PARTE 27

self.f2 = Frame(frame,relief = SUNKEN,borderwidth=2,width = 300,padx = 3,pady = 3

)self.txtAdd = Entry(self.f2,

width=5,textvar=self.Item

)self.txtAdd.bind('<Return>',self.AddToQueue)self.btnAdd = Button(self.f2,

text='Add to Queue',padx = 3,pady = 3

)self.btnAdd.bind('<ButtonRelease-1>',self.AddToQueue)self.btnGet = Button(self.f2,

text='Get Next Item',padx = 3,pady = 3

)self.btnGet.bind('<ButtonRelease-1>',self.GetFromQueue)

self.lblEmpty = Label(self.f2,textvariable=self.EmptyStatus,relief=FLAT

)self.lblFull = Label(self.f2,

textvariable=self.FullStatus,relief=FLAT

)self.lblData = Label(self.f2,

textvariable=self.Output,relief = FLAT,font=("Helvetica", 16),padx = 5

)

return frame

Page 7: Full circle

full circle magazine n. 53 11 indice ^

HOWTO - PROGRAMMARE IN PYTHON - PARTE 27

prima cornice, quindi un'altraetichetta "truffaldina", quindi trepulsanti.

Qui posizioniamo la secondacornice, un'altra etichetta'truffaldina', e i rimanentiwidget.

def Quit(self):sys.exit()

A seguire abbiamo la consuetafunzione quit che chiamasemplicemente sys.exit() (in alto adestra).

Ora la funzione di supporto delpulsante principale, btnMain.Ricordate che stiamo inviando(attraverso il parametro p1)l'informazione relativa al pulsantepremuto. Usiamo la variabileself.QueueType come riferimento al

tipo di coda su cui stiamo lavorando,quindi assegniamo self.obj alla codaappropriata e per finire cambiamo iltitolo della finestra permostrare iltipo di coda che stiamousando. Dopoquesto, stampiamo il tipo di codanella finestra di terminale (non èrealmente necessario farlo) echiamiamo la routine ShowStatus. Aseguire (pagina seguente in alto adestra) creeremo la funzioneShowStatus.

Comepotete vedere, è

def PlaceWidgets(self, master):frame = master# Place the widgetsframe.grid(column = 0, row = 0)l = Label(frame,text='',relief=FLAT,width = 15, anchor = 'e').grid(column = 0, row = 0)l = Label(frame,text='',relief=FLAT,width = 15, anchor = 'e').grid(column = 1, row = 0)l = Label(frame,text='',relief=FLAT,width = 15, anchor = 'e').grid(column = 2, row = 0)l = Label(frame,text='',relief=FLAT,width = 15, anchor = 'e').grid(column = 3, row = 0)l = Label(frame,text='',relief=FLAT,width = 15, anchor = 'e').grid(column = 4, row = 0)

self.f1.grid(column = 0,row = 1,sticky='nsew',columnspan=5,padx = 5,pady = 5)l = Label(self.f1,text='',width = 25,anchor = 'e').grid(column = 0, row = 0)self.btnFifo.grid(column = 1,row = 0,padx = 4)self.btnLifo.grid(column = 2,row = 0,padx = 4)self.btnPriority.grid(column = 3, row = 0, padx = 4)

self.f2.grid(column = 0,row = 2,sticky='nsew',columnspan=5,padx = 5, pady = 5)l = Label(self.f2,text='',width = 15,anchor = 'e').grid(column = 0, row = 0)self.txtAdd.grid(column=1,row=0)self.btnAdd.grid(column=2,row=0)self.btnGet.grid(column=3,row=0)self.lblEmpty.grid(column=2,row=1)self.lblFull.grid(column=3,row = 1)self.lblData.grid(column = 4,row = 0)

def btnMain(self,p1):if p1 == 1:

self.QueueType = 'FIFO'self.obj = self.fiforoot.title('Queue Tests - FIFO')

elif p1 == 2:self.QueueType = 'LIFO'self.obj = self.liforoot.title('Queue Tests - LIFO')

elif p1 == 3:self.QueueType = 'PRIORITY'self.obj = self.pqroot.title('Queue Tests - Priority')

print self.QueueTypeself.ShowStatus()

Page 8: Full circle

full circle magazine n. 53 12 indice ^

HOWTO - PROGRAMMARE IN PYTHON - PARTE 27

abbastanza semplice.Impostiamo lo statoappropriato delle etichettecosì chemostrino se la codache stiamousando èpiena,vuota o qualcos'altronell'intervallo.

La funzioneAddToQueue (paginaseguente, in basso a destra)è anch'essa semplice.Recuperiamo il dato dal boxdi inserimento usando la funzione.get(). Quindi controlliamoper vederese l'attuale tipo di coda è di tipopriorità. In caso affermativo,dobbiamoessere sicuri che sia nelformato corretto. Lo facciamocontrollando se c'è la virgola. Se nonc'è, avvisiamo l'utente attraverso unamessaggio di errore. Se tutto sembracorretto, controlliamoper vedere sela coda attuale è piena. Ricordate, sela coda è piena la funzione diinserimento si blocca e il programmanon risponderà. Se tutto è a posto,aggiungiamo l'elemento alla coda eaggiorniamo il suo stato.

La funzioneGetFromQueue (alcentro a destra) è ancora piùsemplice. Controlliamoper vedere sela coda è vuota così da non incorrerein un problemadi blocco, e, se non loè, estraiamo il dato, lomostriamoe

aggiorniamo lo stato.

Stiamoarrivando alla fine dellanostra applicazione. Ecco la funzioneper centrare la finestra (in alto asinistra). Prima recuperiamo lalarghezza e l'altezza dello schermo.Quindi recuperiamo la larghezza el'altezza della finestra usando lefunzioniwinfo_reqwidth() ewinfo_reqheight()definite in tkinter.Queste funzioni,quando chiamate almomento giusto,restituiscono lalarghezza e l'altezzadella finestra in baseal posizionamento deiwidget. Se lachiamiamo troppopresto, otteniamo idati,ma non sono

def ShowStatus(self):# Check for Emptyif self.obj.empty() == True:

self.EmptyStatus.set('Empty')else:

self.EmptyStatus.set('')# Check for Fullif self.obj.full() == True:

self.FullStatus.set('FULL')else:

self.FullStatus.set('')

def AddToQueue(self,p1):temp = self.Item.get()if self.QueueType == 'PRIORITY':

commapos = temp.find(',')if commapos == -1:

print "ERROR"tkMessageBox.showerror('Queue Demo',

'Priority entry must be in format\r(priority,data)')else:

self.obj.put(self.Item.get())elif not self.obj.full():

self.obj.put(self.Item.get())self.Item.set('')self.ShowStatus()

def GetFromQueue(self,p1):self.Output.set('')if not self.obj.empty():

temp = self.obj.get()self.Output.set("Pulled

{0}".format(temp))self.ShowStatus()

if __name__ == '__main__':def Center(window):

# Get the width and height of the screensw = window.winfo_screenwidth()sh = window.winfo_screenheight()# Get the width and height of the windowrw = window.winfo_reqwidth()rh = window.winfo_reqheight()xc = (sw-rw)/2yc = (sh-rh)/2window.geometry("%dx%d+%d+%d"%(rw,rh,xc,yc))window.deiconify()

Page 9: Full circle

full circle magazine n. 53 13 indice ^

quelli di cui abbiamoveramente bisogno.Quindisottraiamo la larghezza dellafinestra dalla larghezza delloschermoedividiamoper due,e facciamo la stessa cosa perl'altezza.Quindi usiamoquesti dati per chiamare geometry.Nellamaggior parte delle istanze,questo funziona alla perfezione. Però,potrebbero esserci dei casi in cui sianecessario inserire amano larghezzae altezza.

Per finire, instanziamo la finestraradice, impostiamo il titolo di base,instanziamo la classeQueueTest.Quindi chiamiamo root.after, cheattendeXnumerodimillisecondi (inquesto caso tre) dopo che la finestraè stata instanziata e quindi chiama lafunzione center. In questomodo lafinestra è stata completamenteimpostata ed èpronta, così che èpossibile recuperare la larghezza el'altezza. Potrebbe essere necessarioaggiustare unpo' il tempodi attesa.Alcuni computer sonopiù veloci dialtri. Il valore '3' funziona bene sullamiamacchina, nel vostro casopotrebbe variare. Alla finemanonperimportanza, chiamiamomainloopdella finestra per eseguirel'applicazione.

Giocando con le code noterete

chemettendoundato in una coda(diciamo la coda FIFO) e quindipassando adun altro tipo (diciamoLIFO), il dato inserito nella prima codaè ancora lì e ci aspetta. Potetecompletamente oparzialmenteriempire tutte e tre le code, quindiiniziare a giocare con esse. Bene,questo è tutto per questa volta.

Divertitevi con le vostre code. Ilcodice può essere trovato all'indirizzohttp://pastebin.com/5BBUiDce.

GregWalters è il proprietario dellaRainyDay Solutions, LLC, una società diconsulenza in Colorado e programmadal 1972. Ama cucinare, fareescursioni, ascoltaremusica e passareil tempo con la sua famiglia. Il suo sitoweb èwww.thedesignatedgeek.com.

HOWTO - PROGRAMMARE IN PYTHON - PARTE 27

root = Tk()root.title('Queue Tests - FIFO')demo = QueueTest(root)root.after(3,Center,root)root.mainloop() Z e r o D ow n t i m e

Below Zero is a Co-located Server Hosting specialist in the UK.

Uniquely we only provide rack spaceand bandwidth. This makes our servicemore reliable, more flexible, morefocused and more competitively priced.We concentrate solely on the hosting ofCo-located Servers and their associatedsystems, within Scotland's Data Centres.

At the heart of our networking infrastructure is state-of-the-art BGP4routing that offers optimal datadelivery and automatic multihomedfailover between our outstandingproviders. Customers may rest assuredthat we only use the highest quality ofbandwidth; our policy is to pay more forthe best of breed providers andbecause we buy in bulk this doesn't impact our extremely competitivepricing.

At Below Zero we help you to achieve Zero Downtime.

www.zerodowntime.co .uk

Page 10: Full circle

full circle magazine n. 54 7 indice ^

HHOOWW--TTOOScritto da Greg Walters PPrrooggrraammmmaarree iinn PPyytthhoonn -- PPaarrttee 2288

Ci accingiamoa esplorare

altri widgetmessi a

disposizione da tkinter.

Questa volta ci occuperemo

dimenù, combobox, spin box, barra

separatrice, barra di progresso e

notebook. Parliamoneuno alla volta.

Avete di certo visto imenù

praticamente in ogni applicazione che

avete usato. Tkinter rende la loro

procedura di creazioneMOLTO

semplice. I combobox sono simili al

widget lista visto nell’ultimo articolo,

differenziandoseneper il fatto che i

suoi elementi “compaiono” invece di

essere sempre visibili. Il widget spin

box sonoutili per fissare un arco di

valori tra cui scorrere. Per esempio, se

si vuole che l’utente scelga un valore

intero compreso tra 1 e 100possiamo

usare un semplice spin box. Le barre

di progresso rappresentanoun

metodoutilissimopermostrare che la

vostra applicazione non si èbloccatanell’esecuzione di una proceduralunga, come leggere i record di undatabase. Puòmostrare lapercentuale di completamentodell’azione. Esistonodue tipi di questowidget, determinato e indeterminato.Si usa il tipo determinato quando si

conosce a priori il numero di elementida elaborare. Se questo valore èignoto o se in undatomomento èimpossibile conoscere la percentualedi completamento, allora userete laversione indeterminata. Le useremoentrambe. Per finire, il widgetnotebook (owidget a schede) è usatomolte volte nelle schermate diconfigurazione. È possibileraggruppare logicamente una serie diwidget in ciascuna scheda.

Allora, iniziamo. Comeal solito,creeremoun’applicazione base e lapopoleremovia via con i vari widget. Adestra trovate la primaparte dellanostra applicazione. Per lo più laconoscete già.

Salvate il tutto comewidgetdemo2a.py. Ricordate che louseremocomebaseper creare il democompleto. Iniziamoacreare ilmenù.Ecco i passi necessari. Primadefiniamounavariabileper contenere l’istanzadelmenù.Comepergli altriwidgetutilizzati, il formatoè...

OurVariable = Widget(parent,options).

In questo caso stiamousando il

import sysfrom Tkinter import *import ttk# Shows how to create a menuclass WidgetDemo2:

def __init__(self,master = None):self.DefineVars()f = self.BuildWidgets(master)self.PlaceWidgets(f)

def DefineVars(self):pass

E qui ecco la parte inferiore del programma. Ancora, lo avete vistoprecedentemente. Niente di nuovo.

if __name__ == '__main__':def Center(window):

# Get the width and height of the screensw = window.winfo_screenwidth()sh = window.winfo_screenheight()# Get the width and height of the windowrw = window.winfo_reqwidth()rh = window.winfo_reqheight()xc = (sw-rw)/2yc = (sh-rh)/2print "{0}x{1}".format(rw,rh)window.geometry("%dx%d+%d+%d"%(rw,rh,xc,yc))window.deiconify()

root = Tk()root.title('More Widgets Demo')demo = WidgetDemo2(root)root.after(13,Center,root)root.mainloop()

Page 11: Full circle

full circle magazine n. 54 8 indice ^

HOWTO - PROGRAMMARE IN PYTHON - PARTE 28

widgetMenu assegnandolo alla

finestra principale genitore. Lo

facciamonella funzioneBuildWidgets.

Quindi creiamoun altromenù, questa

volta chiamato filemenu.

Aggiungiamo comandi e separatori

quandonecessario. Finiamo

aggiungendo filemenu alla barra dei

menùe ripetiamo la procedura. Nel

nostro esempio abbiamomenubar, i

menù File, Edit eHelp (in alto a

destra). Iniziamo.

A seguire (al centro a destra) ci

concentriamo sulmenù File. Ci

saranno cinque elementi, New,Open,

Save, un separatore e Exit. Useremo il

metodo .add_commandper

aggiungere il comando. Tutto quello

che dobbiamo realmente fare èchiamare ilmetodo condel testo(label = ) e fornire una funzione disupporto per gestire il clic dell’utentesull’elemento. Per finire usiamo lafunzionemenubar.add_cascadeperassociare ilmenù alla barra.

Notate che il comandoExit usaroot.quit per terminare il programma.Nessuna funzione di supporto ènecessaria. A seguire facciamo lostesso per imenùEdit eHelp.

Osservate l’istruzione “tearoff=0”di ciascun gruppodimenù. Secambiaste “0” con “1” ilmenù sarebbe

circondato da una riga tratteggiata ein caso di trascinamento si“separerebbe” creandouna finestrapropria. Benchéquesto possa risultareutile in alcune circostanze, in questocaso è da evitare.

Alla fine,manonper importanza,dobbiamoposizionare ilmenù.Nonoperiamounsemplice posizionamentotramite la funzione .grid().Ricorriamo invece allafunzione parent.config (inbasso a destra).

Tutto questo si trovanella routineBuildWidgets.Ora (prossimapagina, in altoa destra) dobbiamoaggiungere una cornicegenerica e impostarel’istruzione return primadipassare alla funzionePlaceWidgets.

Per finire (prossimapagina, in basso a destra)dobbiamo creare tutte lefunzioni di supporto definiteprecedentemente. Per questa demo,tutto quello che faremoè stamparequalcosa nel terminale usato perlanciare il programma.

Questo è quanto. Salvate ed

def BuildWidgets(self,master):frame = Frame(master)#==============================# MENU STUFF#==============================# Create the menu barself.menubar = Menu(master)

# Create the File Pull Down, and add it to the menu barfilemenu = Menu(self.menubar, tearoff = 0)filemenu.add_command(label = "New", command = self.FileNew)filemenu.add_command(label = "Open", command = self.FileOpen)filemenu.add_command(label = "Save", command = self.FileSave)filemenu.add_separator()filemenu.add_command(label = "Exit", command = root.quit)self.menubar.add_cascade(label = "File", menu = filemenu)

# Create the Edit Pull Downeditmenu = Menu(self.menubar, tearoff = 0)editmenu.add_command(label = "Cut", command = self.EditCut)editmenu.add_command(label = "Copy", command = self.EditCopy)editmenu.add_command(label = "Paste", command = self.EditPaste)self.menubar.add_cascade(label = "Edit", menu = editmenu)# Create the Help Pull Downhelpmenu = Menu(self.menubar, tearoff=0)helpmenu.add_command(label = "About", command = self.HelpAbout)self.menubar.add_cascade(label = "Help", menu = helpmenu)

# Now, display the menumaster.config(menu = self.menubar)#========================================# End of Menu Stuff#========================================

Page 12: Full circle

full circle magazine n. 54 9 indice ^

eseguite il programma. Fate clic su

ciascuna voce deimenù (lasciando

File->Exit per ultimo).

Ora (in basso) ci occupiamodel

combobox. Salvate il file come

widgetdemo2b.py e iniziamo. Gli

import, la definizione della classe e le

funzioni def __init__ sonogli stessi,

comenella parte in basso del

programma. Aggiungeremodue righe

alla funzioneDefineVars. Potete

commentare l’istruzione “pass” o

cancellarla e inserire il codice

seguente (ho incluso la riga di

definizione per chiarezza).

Primadefiniamoun’etichetta che

abbiamo creato prima.Quindi

definiamo la casella combinata.

Usiamo “ttk.Combobox”, definiamo il

genitore e impostiamo l’altezza a 19,

la larghezza a 20 e textvariable a

“self.cmbo1Val”. Ricordate che

abbiamo configurato textvariable

nell’ultimo articolo,ma se l’aveste

dimenticato... il suo testo cambia

sincronizzandosi con il valore del

combobox. Lo definiamo in

DefineVars comeunoggetto di tipo

StringVar.Quindi carichiamo i valori

tra cui vogliamo che l’utente scelga,

definiti anche questi inDefineVars.

Finiamoaccoppiando l’evento virtuale

ComboboxSelected alla funzione

cmbotest che vedremoabreve.

self.f1 = Frame(frame,relief = SUNKEN,borderwidth = 2,width = 500,height = 100)

return frame

Quindi ci occupiamo (come abbiamo fatto più volte) del

posizionamento degli altri widget.

def PlaceWidgets(self,master):frame = masterframe.grid(column = 0, row = 0)

self.f1.grid(column = 0,row = 0,sticky = 'nsew')

def FileNew(self):print "Menu - File New"

def FileOpen(self):print "Menu - File Open"

def FileSave(self):print "Menu - File Save"

def EditCut(self):print "Menu - Edit Cut"

def EditCopy(self):print "Menu - Edit Copy"

def EditPaste(self):print "Menu - Edit Paste"

def HelpAbout(self):print "Menu - Help About"

def DefineVars(self):self.cmbo1Val = StringVar()self.c1Vals = ['None','Option 1','Option 2','Option 3']

Dopo la definizione di self.f1 in BuildWidgets e prima della riga “return frame” inserite il codice

seguente.

# Combo Boxself.lblcb = Label(self.f1, text = "Combo Box: ")self.cmbo1 = ttk.Combobox(self.f1,

height = "19",width = 20,textvariable = self.cmbo1Val)

self.cmbo1['values'] = self.c1Vals# Bind the virtual event to the callbackself.cmbo1.bind("<<ComboboxSelected>>",self.cmbotest)

HOWTO - PROGRAMMARE IN PYTHON - PARTE 28

Page 13: Full circle

full circle magazine n. 54 10 indice ^

Proseguiamoposizionando il

combobox e l’etichetta nelmodulo

(in alto a destra).

Salvate tutto e testate.

Ora salvate come

widgetdemo2c.py e passiamoalla

barra separatrice.Questa èdavveroMOLTO semplice. Nonostantel’ultimo tkinter ne fornisca unwidget,non sonomai riuscito a farlofunzionare. Ecco una semplicesoluzione. Usiamouna cornice conaltezza 2. Gli unici cambiamenti alprogramma saranno la definizionedella cornice in BuildWidgets, dopol’istruzione di associazione del combobox, e il posizionamento della cornicestessa, con la funzionePlaceWidgets.Quindi, in BuildWidgets inseriamo lerighe seguenti (mostrate al centro adestra)...

Ancora una volta, tutto questo loavete già visto. Salvate e testate.Probabilmente dovrete espandere lafinestra più in alto per vedere ilseparatore,madivverrà più chiaro nelprossimopasso. Salvate comewidgetdemo2d.py perché ora toccaallo spin box.

SottoDefineVars aggiungete lariga seguente...

self.spinval = StringVar()

Fino adora sapete che questaserve a recuperare il valore inqualunquemomento vogliamo.Proseguiamoaggiungendounpo’ dicodice alla funzioneBuildWidgets,proprio primadella riga “returnframe” (in basso a destra).

Qui definiamo l’etichetta e ilwidget spin box.Quella dello spin boxè la seguente:

ourwidget = Spinbox(parent,lowvalue, high value, width,textvariable, wrap)

Il valore inferiore (low value)dovrebbe essere chiamato “from_”poiché la parola “from” è un terminechiave e usarlo in questo contestoporterebbe a confusione. I valori“from_” e “to” devonoessere definiticome float. In questo caso vogliamo1comevalore inferiore e 10 comevalore superiore. Per finire, l’opzionewrapdice che se il valore è (nel nostrocaso) 10 e l’utente fa clic sulla frecciarivolta in alto, si passa al valoreinferiore, e così via. Stessa cosa per ilvalore inferiore. Se l’utente fa clic sullafreccia rivolta verso il basso e il valoreè 1, si passa al valore 10. Se impostate“wrap=False”, il widget si interrompeagli estremi.

self.lblcb.grid(column = 0,row = 2)self.cmbo1.grid(column = 1,

row = 2,columnspan = 4,pady = 2)

E per finire inseriamo la funzione di supporto che semplicementestampa nel terminale ciò che l’utente seleziona.

def cmbotest(self,p1):print self.cmbo1Val.get()

self.fsep = Frame(self.f1,width = 140,height = 2,relief = RIDGE,borderwidth = 2)

E in PlaceWidgets inserite questo...

self.fsep.grid(column = 0,row = 3,columnspan = 8,sticky = 'we',padx = 3,pady = 3)

self.lblsc = Label(self.f1, text = "Spin Control:")self.spin1 = Spinbox(self.f1,

from_ = 1.0,to = 10.0,width = 3,textvariable = self.spinval,wrap=True)

HOWTO - PROGRAMMARE IN PYTHON - PARTE 28

Page 14: Full circle

full circle magazine n. 54 11 indice ^

Oraposizioneremo iwidget in

PlaceWidgets (in basso).

Ancora, questo è tutto. Salvate edeseguite.Ora il separatore saràevidente.

Salvate comewidgetdemo2e.py epassiamoalle barre di progresso.

Ancora, abbiamobisognodidefinire alcune variabili, quindi nellafunzioneDefineVars aggiungiamo ilcodice seguente...

self.spinval2 = StringVar()self.btnStatus = Falseself.pbar2val = StringVar()

Dovrebbe essere abbastanza ovviola funzione delle due StringVar.Discuteremodi “self.btnStatus” abreve. Per ilmomentodefiniamo iwidget per questa sezione diBuildWidgets (a destra).

Anchequesta va primadella riga“return frame”. Ciò che stiamofacendoè impostare una cornice nellaquale inserire i widget.Quindi

configuriamodue etichette comeguide. Poi definiamo la primabarra diprogresso.Qui le uniche cose chepotrebbero sembrare strane sonolength,modeemaximum. Length è ladimensione in pixel della barra.Maximumè il valore più grandevisualizzabile. In questo caso è 100visto chemostreremounapercentuale.Mode in questo caso è“indeterminate”. Ricordate, usiamoquestamodalità quandononconosciamo il reale progressodell’azione e vogliamo semplicementenotificare che qualcosa staavvenendo.

Quindi aggiungiamounpulsante(lo avete già fatto prima), un’altraetichetta, un’altra barra di progresso eun altro spin box. Il valoremodeper lasecondabarra di progresso è“determinate”. Useremo il widget spinbox per impostare la “percentuale” dicompletamento.Quindi aggiungete lerighe seguenti (pagina seguente, inalto a sinistra) nella funzionePlaceWidgets.

Per finire aggiungiamodue

self.lblsc.grid(column = 0, row = 4)self.spin1.grid(column = 1,

row = 4,pady = 2)

#=======================================# Progress Bar Stuff#=======================================self.frmPBar = Frame(self.f1,

relief = SUNKEN,borderwidth = 2)

self.lbl0 = Label(self.frmPBar,text = "Progress Bars")

self.lbl1 = Label(self.frmPBar,text = "Indeterminate",anchor = 'e')

self.pbar = ttk.Progressbar(self.frmPBar,orient = HORIZONTAL,length = 100,mode = 'indeterminate',maximum = 100)

self.btnptest = Button(self.frmPBar,text = "Start",command = self.TestPBar)

self.lbl2 = Label(self.frmPBar,text = "Determinate")

self.pbar2 = ttk.Progressbar(self.frmPBar,orient = HORIZONTAL,length = 100,mode = 'determinate',variable = self.pbar2val)

self.spin2 = Spinbox(self.frmPBar,from_ = 1.0,to = 100.0,textvariable = self.spinval2,wrap = True,width = 5,command = self.Spin2Do)

HOWTO - PROGRAMMARE IN PYTHON - PARTE 28

Page 15: Full circle

full circle magazine n. 54 12 indice ^

funzioni per controllare le barre di

progresso (in basso a destra).

La funzione TestPBar controlla

quella indeterminata. In pratica,

avviamoe fermiamoun timer interno

preconfigurato nella barra di

progresso. La riga “self.pbar.start(10)”

imposta il timer a 10millisecondi.

Questo valore faràmuovere la barra

abbastanza velocemente. Sentitevi

liberi di provarne altri. La funzione

Spin2Do semplicemente imposta la

barra di progresso al valore scelto nel

widget spin box. Lo stamperemo

anche nel terminale.

Queste sono tutte lemodifiche.

Salvate e provate.

Ora salvate come

widgetdemo2f.py e occupiamoci del

widget notebook a schede. In

BuildWidgets inserite il codice

seguente (in basso) primadella riga

“return frame”...

Osserviamo cosa abbiamo fatto.

Prima abbiamodefinito una cornice

per il widget notebook. Poi definiamo

il widget. Tutte le opzioni sonoquelle

già viste.Quindi definiamodue cornici

# Progress Barself.frmPBar.grid(column = 0,

row = 5,columnspan = 8,sticky = 'nsew',padx = 3,pady = 3)

self.lbl0.grid(column = 0, row = 0)self.lbl1.grid(column = 0,

row = 1,pady = 3)

self.pbar.grid(column = 1, row = 1)self.btnptest.grid(column = 3, row = 1)self.lbl2.grid(column = 0,

row = 2,pady = 3)

self.pbar2.grid(column = 1, row = 2)self.spin2.grid(column = 3, row = 2)

def TestPBar(self):if self.btnStatus == False:

self.btnptest.config(text="Stop")self.btnStatus = Trueself.pbar.start(10)

else:self.btnptest.config(text="Start")self.btnStatus = Falseself.pbar.stop()

def Spin2Do(self):v = self.spinval2.get()print vself.pbar2val.set(v)

#=======================================# NOTEBOOK#=======================================self.nframe = Frame(self.f1,

relief = SUNKEN,borderwidth = 2,width = 500,height = 300)

self.notebook = ttk.Notebook(self.nframe,width = 490,height = 290)

self.p1 = Frame(self.notebook)self.p2 = Frame(self.notebook)self.notebook.add(self.p1,text = 'Page One')self.notebook.add(self.p2,text = 'Page Two')self.lsp1 = Label(self.p1,

text = "This is a label onpage number 1",

padx = 3,pady = 3)

HOWTO - PROGRAMMARE IN PYTHON - PARTE 28

Page 16: Full circle

full circle magazine n. 54 13 indice ^

chiamate self.p1 e self.p2 che

fungonodapagine. Le due righe

successive (self.notebook.add)

associano le cornici al widget

notebookottenendouna scheda.

Impostiamoanche il testo per le

schede. Per finiremettiamo

un’etichetta sulla pagina numero uno.

Nemetteremouna sulla pagina

numeroduequandoposizioneremo i

controlli, giusto per diletto.

Nella funzionePlaceWidgets

inserite il codice seguente (in basso).

L’unica cosa che potrebbe apparire

strana è l’etichetta di pagina due.Abbiamo combinato la definizione e ilposizionamento nella griglia nello

stesso comando. Lo abbiamo fattonell’applicazione demodell’ultimavolta.

Questo è quanto. Salvate edeseguite.

Come sempre, tutto il codicedell’applicazione completa si trova supastebin, all’indirizzohttp://pastebin.com/qSPkSNU1.

Divertitevi. La prossima volta cioccuperemoancora di database.

self.nframe.grid(column = 0,row = 6,columnspan = 8,rowspan = 7,sticky = 'nsew')

self.notebook.grid(column = 0,row = 0,columnspan = 11,sticky = 'nsew')

self.lsp1.grid(column = 0,row = 0)self.lsp2 = Label(self.p2,

text = 'This is a label on PAGE 2',padx = 3,pady = 3).grid(

column = 0,row = 1)

HOWTO - PROGRAMMARE IN PYTHON - PARTE 28

Z e r o D ow n t i m e

Below Zero is a Co-located Server Hosting specialist in the UK.

Uniquely we only provide rack spaceand bandwidth. This makes our servicemore reliable, more flexible, morefocused and more competitively priced.We concentrate solely on the hosting ofCo-located Servers and their associatedsystems, within Scotland's Data Centres.

At the heart of our networking infrastructure is state-of-the-art BGP4routing that offers optimal datadelivery and automatic multihomedfailover between our outstandingproviders. Customers may rest assuredthat we only use the highest quality ofbandwidth; our policy is to pay more forthe best of breed providers andbecause we buy in bulk this doesn't impact our extremely competitivepricing.

At Below Zero we help you to achieve Zero Downtime.

www.zerodowntime.co .uk

Page 17: Full circle

full circle magazine n. 55 7 indice ^

HHOOWW--TTOOScritto da Greg Walters PPrrooggrraammmmaarree iinn PPyytthhoonn -- PPaarrttee 2299

U n po' di tempo fa, mi fuchiesto di convertire inSQLite un databaseMySQL. Cercando su

Internet una soluzione veloce efacile (e gratuita), non trovai nulla diutile per la mia versione di MySQL.Così decisi di fare "da me".

I l programma MySQLAdministrator vi permette di creareuna copia del database comesemplice file di testo. Moltivisualizzatori SQLite permettono diricreare il database partendo da unfile di questo tipo. Però, ci sonomolte cose che MySQL supportamentre SQLite no. Così questo mesescriveremo un programma diconversione che legge un file diistruzioni sql e crea la versioneSQLite.

Iniziamo dando un'occhiata alfile. Consiste di una sezione che creail database, di una per creareciascuna tabella e di quellaeventuale per i dati. (C'è un'opzioneper esportare solo lo schema dellatabella). In alto a destra c'è unesempio della sezione per creare latabella.

La prima cosa di cui abbiamobisogno è l'ultima riga. Tutto quellodopo le parentesi di chiusura deveessere eliminato. (SQLite nonsupporta il database InnoDB). Inaggiunta a questo, SQLite nonsupporta la riga "PRIMARY KEY". InSQLite si imposta una chiaveprimaria usando "INTEGER PRIMARYKEY AUTOINCREMENT" alladefinizione del campo. L'altra cosache SQLite non digerisce è la parolachiave "unsigned".

Parlando di dati, l' istruzione"INSERT INTO" non è compatibile. I lproblema quì è che SQLite nonpermette inserimenti multipliall' interno della stessa istruzione.Ecco un breve esempio preso da unfile MySQL. Notate (a destra) che ilmarcatore di fine riga è un punto evirgola.

Durante la conversioneignoreremo anche le righe dicommento e le istruzioni CREATEDATABASE e USE. Una voltaottenuto il file SQLite, useremo unprogramma simile a SQLiteDatabase Browser per il processo di

DROP TABLE IF EXISTS `categoriesmain`;CREATE TABLE `categoriesmain` (`idCategoriesMain` int(10) unsigned NOT NULL

auto_increment,`CatText` char(100) NOT NULL default '',PRIMARY KEY (`idCategoriesMain`)

) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULTCHARSET=latin1;

INSERT INTO `categoriesmain`(`idCategoriesMain`,`CatText`) VALUES(1,'Appetizer'),(2,'Snack'),(3,'Barbecue'),(4,'Cake'),(5,'Candy'),(6,'Beverages');

Per renderlo compatibile, dobbiamo cambiare questo da singolaistruzione complessa ad una serie di istruzioni più semplici comesegue:

INSERT INTO `categoriesmain`(`idCategoriesMain`,`CatText`) VALUES (1,'Appetizer');INSERT INTO `categoriesmain`(`idCategoriesMain`,`CatText`) VALUES (2,'Snack');INSERT INTO `categoriesmain`(`idCategoriesMain`,`CatText`) VALUES (3,'Barbecue');INSERT INTO `categoriesmain`(`idCategoriesMain`,`CatText`) VALUES (4,'Cake');INSERT INTO `categoriesmain`(`idCategoriesMain`,`CatText`) VALUES (5,'Candy');INSERT INTO `categoriesmain`(`idCategoriesMain`,`CatText`) VALUES (6,'Beverages');

Page 18: Full circle

full circle magazine n. 55 8 indice ^

HOWTO - PROGRAMMARE IN PYTHON - PARTE 29creazione del database, delle tabellee dei dati.

Iniziamo. Create una cartella peril progetto e un nuovo file python.Chiamatelo MySQL2SQLite.py.

In alto a destra c'è l'istruzioneimport, la definizione della classe ela routine __init__.

Poiché sarà un programma dariga di comando dobbiamo crearel'istruzione "if __name__", ungestore degli argomenti e unaroutine d'aiuto (che informi l'utentesu come utilizzare il programma).Questa andrà inserita alla fine. Tuttoil resto del codice creato andràprima:

def error(message):print >> sys.stderr,

str(message)

In basso c'è il gestore che sioccupa di stampare le istruzionid'uso.

La funzione DoIt() vienechiamata se il programma è eseguitoda solo dalla riga di comando, comeprevisto. Comunque, se volessimo inseguito aggiungerlo come libreria adun altro programma, potremmosemplicemente usare la classe. Quìimpostiamo un numero di variabiliper essere sicuri che tutto funzionicorrettamente. I l codice mostrato inbasso a destra valuta gli argomentipassati e li prepara per le routineprincipali.

Quando avviamo il programmadobbiamo fornire almeno duevariabili. Sono il file di Input e quellodi Output. All'utente sarannodisponibili altre tre opzioni: una è lamodalità debug, affinché ci si renda

#!/usr/bin/env python#====================================# MySQL2SQLite.py#====================================# IMPORTSimport sys#====================================

#====================================# BEGIN CLASS MySQL2SQLite#====================================class MySQL2SQLite:

def __init__(self):self.InputFile = ""self.OutputFile = ""self.WriteFile = 0self.DebugMode = 0self.SchemaOnly = 0self.DirectMode = False

def DoIt():#=======================================# Setup Variables#=======================================SourceFile = ''OutputFile = ''Debug = FalseHelp = FalseSchemaOnly = False#=======================================

if len(sys.argv) == 1:usage()

else:for a in sys.argv:

print aif a.startswith("Infile="):

pos = a.find("=")SourceFile = a[pos+1:]

elif a.startswith("Outfile="):pos = a.find("=")OutputFile = a[pos+1:]

elif a == 'Debug':Debug = True

elif a == 'SchemaOnly':SchemaOnly = True

elif a == '-Help' or a == '-H' or a == '-?':Help = True

if Help == True:usage()

r = MySQL2SQLite()r.SetUp(SourceFile,OutputFile,Debug,SchemaOnly)r.DoWork()

Page 19: Full circle

full circle magazine n. 55 9 indice ^

conto di cosa sta avvenendo manmano che il programma procede;un'opzione per creare solo letabelle e non i dati e una perchiedere aiuto. La riga di comando"normale" per avviare ilprogramma assomiglia a questa:

MySQL2SQLite Infile=FooOutfile=Bar

Dove "Foo" è il nome del fileMySQL e "Bar" è il nome del fileSQLite che vogliamo il programmacrei.

Potete usare anche questaversione:

MySQL2SQLite Infile=FooOutfile=Bar Debug SchemaOnly

che aggiunge l'opzione permostrare i messaggi di debug edesporta solo le tabelle e non i dati.

Per finire se l'utente chiedeaiuto, si passa semplicemente allasezione con le istruzioni d'uso.

Prima di continuare diamoun'occhiata a come vengono gestitigli argomenti nella riga di comando.

Quando un utente inserisce ilnome del programma nel terminale,

il sistema operativo tiene tracciadell'informazione immessa e lapassa al programma che valutal'eventuale presenza di opzioni. Senon è stata inserita nessuna opzione(o argomento) il suo numero è uno,cioè il nome dell'applicazione, nelnostro caso MySQL2SQLite.py.Possiamo accedere a questiargomenti chiamando il comandosys.arg. Se il conteggio è maggioredi uno, usiamo un ciclo for perleggerle. Controlleremo gliargomenti uno alla volta. Alcuniprogrammi richiedono di inserirli inuno specifico ordine. Usando

l'approccio del ciclo for, questipossono essere inseriti in qualunqueordine. Se l'utente non forniscealcun argomento o usa l'argomentoaiuto mostreremo la schermatasull'uso. In alto c'è la relativaroutine.

Continuando, una voltaterminato il controllo degliargomenti istanziamo la classe,chiamiamo la funzione SetUp, cheriempie alcune variabili e quindichiamiamo la routine DoWork.Inizieremo dalla classe (mostratanella prossima pagina, in basso a

destra).

Questa (pagina seguente, inbasso a destra) contiene ladefinizione e la funzione __init__.Quì definiremo le variabili di cuiavremo bisogno più avanti.Ricordate che prima di chiamare lafunzione DoWork bisogna chiamareSetUp. Quì assegneremo i valoricorretti alle variabili vuote. Abbiamoanche la possibilità di non scrivere inun file, utile a scopi di debug, e discrivere semplicemente lo schemasenza i dati, utile per replicare in unnuovo database solo la struttura.

HOWTO - PROGRAMMARE IN PYTHON - PARTE 29

def usage():message = (

'=======================================================================\n''MySQL2SQLite - A database converter\n''Author: Greg Walters\n''USAGE:\n''MySQL2SQLite Infile=filename [Outfile=filename] [SchemaOnly] [Debug] [-H-Help-?\n'

' where\n'' Infile is the MySQL dump file\n'' Outfile (optional) is the output filename\n'' (if Outfile is omitted, assumed direct to SQLite\n'' SchemaOnly (optional) Create Tables, DO NOT IMPORT DATA\n'' Debug (optional) - Turn on debugging messages\n'' -H or -Help or -? - Show this message\n''Copyright (C) 2011 by G.D. Walters\n''=======================================================================\n')

error(message)sys.exit(1)

if __name__ == "__main__":DoIt()

Page 20: Full circle

full circle magazine n. 55 1 0 indice ^

Iniziamo aprendo il file SQL eimpostiamo alcune variabili interne.Definiamo anche alcune stringhecontenenti testo usato da usare piùvolte. Ovviamente se dovremoscrivere nel file di output lodovremo prima aprire e iniziarel'intero processo. Leggeremociascuna riga del file d'ingresso, laprocesseremo e scriveremo il testopotenziale nel file di output. Usiamoun ciclo while forzato per la letturadi ciascuna riga, con un comando diinterruzione quando non è rimastonulla nel file in ingresso. Usiamof.readline() per recuperare la riga dilavoro e assegniamo il suo

contenuto alla variabile"line". Alcune righe possonoessere tranquillamenteignorate. A questo servonole istruzioni if/elif seguitedall' istruzione pass (inbasso).

Possiamo finalmentesmettere di occuparci di cosaignorare e passare a quelleutili. I l processo inizierà nelmomento in cui la rigaconterrà Create Table.Ricordate che abbiamoassegnato a CT il valore"Create Table". Quì (in alto adestra), impostiamo la

HOWTO - PROGRAMMARE IN PYTHON - PARTE 29

#====================================# BEGIN CLASS MySQL2SQLite#====================================class MySQL2SQLite:

def __init__(self):self.InputFile = ""self.OutputFile = ""self.WriteFile = 0self.DebugMode = 0self.SchemaOnly = 0

def SetUp(self, In, Out = '', Debug = False, Schema = 0):self.InputFile = Inif Out == '':

self.writeFile = 0else:

self.WriteFile = 1self.OutputFile = Out

if Debug == True:self.DebugMode = 1

if Schema == 1:self.SchemaOnly = 1

Ora, ci occuperemo della funzione DoWork, dove avviene la "magia".

def DoWork(self):f = open(self.InputFile)print "Starting Process"cntr = 0insertmode = 0CreateTableMode = 0InsertStart = "INSERT INTO "AI = "auto_increment"PK = "PRIMARY KEY "IPK = " INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL"CT = "CREATE TABLE "# Beginif self.WriteFile == 1:

OutFile = open(self.OutputFile,'w')

while 1:line = f.readline()cntr += 1if not line:

break# Ignore blank lines, lines that start with

"--" or comments (/*!)if line.startswith("--"): #Comments

passelif len(line) == 1: # Blank Lines

passelif line.startswith("/*!"): # Comments

passelif line.startswith("USE"):

#Ignore USE linespass

elif line.startswith("CREATE DATABASE "):pass

Page 21: Full circle

full circle magazine n. 55 1 1 indice ^

HOWTO - PROGRAMMARE IN PYTHON - PARTE 29variabile "CreateTableMode" ugualea uno, così da sapere cosa stiamofacendo, poiché la definizione diciascun campo è su una rigaseparata. Quindi prendiamo la riga,rimuoviamo il carattere di ritorno ela teniamo pronta per scriverla nelfile in uscita, e, se richiesto, lascriviamo.

Ora (al centro destra) dobbiamoiniziare ad occuparci di ciascuna rigaall' interno del blocco che crea latabella, gestendo ciascuna riga perfare contento SQLite. Ci sono moltecose che SQLite non supporta.Diamo ancora un'occhiataall' istruzione Create Table di MySQL.

Una cosa che assolutamente daproblemi a SQLite è l'intera ultimariga dopo le parentesi di chiusura.Un'altra è la riga subito sopra, quellacon Primary Key. Un'altra cosa è laparola chiave unsigned nellaseconda riga. Sarà richiesto un po' dicodice (in basso) per superare questiproblemi, ma possiamo farcela.

Prima (terzo inferiore a destra)

controlliamo se la riga contiene"auto increment". Assumeremo chequesta sia la riga della chiaveprimaria, come accade nel 98,6% deicasi, ma potrebbe non esserlo.Comunque noi resteremo sulsemplice. A seguire controlliamo sela riga inizia con ") ". Questo significache si tratta dell'ultima riga delblocco. In questo casoinseriamo semplicementeuna stringa nella variabile"newline" per chiuderepropriamente l'istruzione,azzeriamo la variabileCreateTableMode e, se stiamoscrivendo in un file, scriviamo.

Ora (in basso a destra) usiamol'informazione trovata a propositodella parola chiave auto incremento.Prima eliminiamo dalla riga gli spaziinutili, quindi cerchiamo la posizionedella stringa " int(" (dando per certoche ci sia) all' interno della riga. Lasostituiremo con la frase " INTEGERPRIMARY KEY AUTOINCREMENTNOT NULL". A SQLite non interessala lunghezza dell'intero. Ancora,scriviamo sul file se se richiesto.

Oracercheremo lafrase "PRIMARYKEY ". Notate lospazio extra allafine; è voluto. Incasoaffermativo,

elif line.startswith(CT):CreateTableMode = 1l1 = len(line)line = line[:l1-1]if self.DebugMode == 1:

print "Starting Create Table"print line

if self.WriteFile == 1:OutFile.write(line)

CREATE TABLE `categoriesmain` (`idCategoriesMain` int(10) unsigned NOT NULL auto_increment,`CatText` char(100) NOT NULL default '',PRIMARY KEY (`idCategoriesMain`)

) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=latin1;

elif CreateTableMode == 1:# Parse the line...if self.DebugMode == 1:

print "Line to process – {0}".format(line)

p1 = line.find(AI)if line.startswith(") "):

CreateTableMode = 0if self.DebugMode == 1:

print "Finished Table Create"newline = ");\n"if self.WriteFile == 1:

OutFile.write(newline)if self.DebugMode == 1:

print "Writing Line {0}".format(newline)

elif p1 != -1:# Line is primary key linel = line.strip()fnpos = l.find(" int(")if fnpos != -1:

fn = l[:fnpos]newline = fn + IPK #+ ",\n"if self.WriteFile == 1:

OutFile.write(newline)if self.DebugMode == 1:

print "Writing Line {0}".format(newline)

Page 22: Full circle

full circle magazine n. 55 1 2 indice ^

HOWTO - PROGRAMMARE IN PYTHON - PARTE 29ignoreremo la riga.

elifline.strip().startswith(PK):

pass

Proseguite (in alto a destra)cercando la stringa " unsigned "(anche qui mantenete gli spaziextra) e sostituitela da con " ".

Così termina la routine per crearela tabella. Ora (in basso) cispostiamo alle istruzioni perl'inserimento dei dati. La variabileInsertStart è la frase "INSERT INTO ".La cerchiamo perché MySQLpermette molteplici istruzioni diinserimento in un singolo comando,ma SQLite no. Dobbiamo creareistruzioni separate per ciascunblocco di dati. Impostiamo lavariabile "insertmode" a 1 , inseriamo"INSERT INTO {Table} {Fieldlist}VALUES (" in una variabileriutilizzabile (che chiameremopreambolo), e continuiamo.

Controlliamo per vedere sedobbiamo soltanto lavorare sulloschema. In caso affermativo,possiamo ignorare in sicurezza lesezioni delle istruzioni diinserimento. In caso contrario,dobbiamo occuparcene.

elif self.SchemaOnly == 0:if insertmode == 1:

Controlliamo per vedere se c'è"') ;" o "') ," nella riga. In caso di "') ;",questa sarà l'ultima riga del gruppodi inserimento.

posx = line.find("');")pos1 = line.find("'),")l1 = line[:pos1]

Questa riga controlla la presenzadi apici singoli preceduti dalcarattere di escape e li sostituisce.

line =line.replace("\\'","''")

Se abbiamo un'istruzione dichiusura (");") , questa è la fine

elif line.find(" unsigned ") != -1:line = line.replace(" unsigned "," ")line = line.strip()l1 = len(line)line = line[:l1-1]if self.WriteFile == 1:

OutFile.write("," + line)if self.DebugMode == 1:

print "Writing Line {0}".format(line)

Altrimenti, ci occupiamo della riga.

else:l1 = len(line)line = line.strip()line = line[:l1-4]if self.DebugMode == 1:

print "," + lineif self.WriteFile == 1:

OutFile.write("," + line)

elif line.startswith(InsertStart):if insertmode == 0:

insertmode = 1# Get tablename and field list hereistatement = line# Strip CR/LF from istatement linel = len(istatement)istatement = istatement[:l-2]

if posx != -1:l1 = line[:posx+3]insertmode = 0if self.DebugMode == 1:

print istatement + l1print "------------------------------"

if self.WriteFile == 1:OutFile.write(istatement + l1+"\n")

Altrimenti,colleghiamoilpreamboloalvaloredell' istruzione e finiamo con un punto e virgola.

elif pos1 != -1:l1 = line[:pos1+2]if self.DebugMode == 1:

print istatement + l1 + ";"if self.WriteFile == 1:

OutFile.write(istatement + l1 + ";\n")

Page 23: Full circle

full circle magazine n. 55 1 3 indice ^

Greg Walters è il proprietario dellaRainyDay Solutions, LLC, una società diconsulenza in Colorado e programmadal 1 972. Ama cucinare, fareescursioni, ascoltare musica e passareil tempo con la sua famiglia. Il suo sitoweb è www.thedesignatedgeek.com.

HOWTO - PROGRAMMARE IN PYTHON - PARTE 29dell'insieme di inserimento epossiamo creare l'istruzione perunire il preambolo al valore attualedell'istruzione. Questo è mostratonella pagina precedente, in basso adestra.

Tutto questo funziona (in alto adestra) se l'ultimo valore cheabbiamo nell'istruzione diinserimento è una stringa tra doppiapici. Comunque se l'ultimo valore èdi tipo numerico dobbiamo lavoraredifferentemente. Sarete in grado dicapire ciò che stiamo facendo quì.

Per finire, chiudiamo il file diinput e, se stiamo scrivendo in unfile, chiudiamo anche quello dioutput.

f.close()if self.WriteFile == 1:

OutFile.close()

Una volta ottenuto il fileconvertito, potete usare SQLiteDatabase Browser per ricreare lastruttura del database e i dati.

Questo codice dovrebbefunzionare nel 90% dei casi.Potrebbe esserci qualcosa cheabbiamo tralasciato a causa di altrimotivi, quindi ecco il motivo dellamodalità di debug. Comunque, ho

testato questo su più file e non hoavuto problemi.

Come al solito, il codice è suPasteBin all' indirizzohttp://pastebin.com/cPvzNT7T.

Alla prossima.

else:if self.DebugMode == 1:

print "Testing line {0}".format(line)pos1 = line.find("),")posx = line.find(");")if self.DebugMode == 1:

print "pos1 = {0}, posx = {1}".format(pos1,posx)if pos1 != -1:

l1 = line[:pos1+1]if self.DebugMode == 1:

print istatement + l1 + ";"if self.WriteFile == 1:

OutFile.write(istatement + l1 + ";\n")else:

insertmode = 0l1 = line[:posx+1]if self.DebugMode == 1:

print istatement + l1 + ";"if self.WriteFile == 1:

OutFile.write(istatement + l1 + ";\n")

Page 24: Full circle

full circle magazine n. 58 8 indice ^

HHOOWW--TTOOScritto da Greg D. Walters II nn iizziiaarree PPyytthhoonn -- PPaarrttee 3300

Q uesto mese esploreremoun nuovo disegnatore diGUI, questa volta perTkinter. Molte persone

hanno problemi ad utilizzare Tkinterperché non offre uno strumentoproprio. Benché vi abbia mostratoquanto sia semplice disegnareun'applicazione senza un designer,ora analizzeremo Page. In pratica sitratta di una versione di Visual TCLcon supporto python predefinito. Laversione corrente è la 3.2 che puòessere trovata al seguente indirizzohttp://sourceforge.net/projects/page/files/latest/download.

Prerequisiti

Avete bisogno di TCK/TK 8.5.4 osuccessivo, Python 2.6 o successivo,e pyttk che potete scaricare, se nonlo avete già fatto, dahttp://pypi.python.org/pypi/pyttk.Probabilmente li possedete già tutticon la possibile eccezione di pyttk.

Installazione

Non potete proprio chiedere unaprocedura di installazione più

semplice. Estraete il contenutodell'archivio in una cartella a vostrascelta. Entrate nella cartella edeseguite lo script chiamato"configure". Questo creerà lo scriptlanciatore chiamato "page" cheuserete per fare tutto. Questo èquanto.

Imparare Page

Quando avviate Page, vitroverete di fronte a tre finestre(form). Una è la "launch pad", una èquella con gli strumenti e una èl'editor degli attributi.

Per iniziare un nuovo progettofare clic sul pulsante Toplevel nellafinestra degli strumenti.

Verrà così creata la finestraprincipale. Potete spostarla ovunquenello schermo. Quindi, e a partire da

ora, fate clic su un widget nellafinestra strumenti e ancora clic nellafinestra principale dove lo si vuoleposizionare.

Per il momento inseriamo unpulsante. Fate clic sul pulsanteButton nella finestra strumenti equindi fate clic ovunque nellafinestra principale.

Nella finestra del launch pad fateclic su Window e selezionateAttribute Editor (se non è giàaperto). I l vostro unico pulsantedovrebbe già essere evidenziato,allora spostatelo nella finestra equando rilascerete il pulsante delmouse vedrete che i valori di 'xposition' e 'y position' nella finestradegli attributi cambieranno.

Qui possiamo configurare altriattributi come il testo sul pulsante (osulla maggior parte dei widget),l'alias per il widget (il nome a cuifaremo riferimento nel codice), ilcolore, il nome con cui lochiameremo e via così. Verso il fondoalla finestra degli attributi c'è ilcampo text. In questo caso, si trattadel testo mostrato all'utente per ilpulsante. Cambiamolo da "button" a"Exit". Notate come ora il pulsanteriporti la scritta "Exit". Oraridimensioniamo la finestra permostrare solamente il pulsante che

Page 25: Full circle

full circle magazine n. 58 9 indice ^

HOWTO - INIZIARE PYTHON 30provvederemo a centrare.

Quindi fate clic nella finestraprincipale ovunque tranne che sulpulsante. La finestra degli attributiora mostra gli attributi della finestraprincipale. Cercate il campo "title" ecambiatelo da "New Toplevel 1 " a"Test Form".

Ora, prima di salvare il progetto,dobbiamo creare una cartella percontenere i nostri file. Create lacartella chiamata "PageProjects"dove volete nel disco. Adesso, nellafinestra launch pad, selezionate Filequindi Save As. Navigate fino allacartella PageProjects e, nellafinestra digitate TestForm.tcl e fateclic sul pulsante Salva. Notate che èstato salvato come file tcl, nonpython. Creeremo il file pythonsuccessivamente.

In launch pad trovate la voce di

menu Gen_Python e fate clic.Selezionate Generate Python ecomparirà una nuova finestra.

Page ha generato, come il nomesuggerisce, il codice python e lo haposizionato in una finestra visibile. Inbasso a questa finestra, ci sono trepulsanti... Save, Run e Close.

Fate clic su Save. Se, a questopunto, date un'occhiata alla cartellaPageProjects, vedrete il file python(TestForm.py). Ora fate clic sulpulsante Run. In pochi secondivedrete il progetto avviarsi. I lpulsante non è ancora connesso anulla, così non succederà nientefacendoci clic. Chiudetesemplicemente la finestra con la Xall'angolo. Ora chiudete la console

python con il pulsante Close in bassoa destra.

Nuovamente nella nostra finestra

principale evidenziate il pulsante Exite fate clic destro. Selezionate"Bindings...". Sotto il menù ci sonouna serie di pulsanti.

I l primo a sinistra vi permette di

creare un nuovo legame. Fate clic su"Button-1 ". Questo ci permetterà diinserire il legame per il pulsantesinistro del mouse. Nella finestra adestra digitate "Button1 Click".

Salvate e generate nuovamente ilcodice python. Scorrete verso la finedel file nella console python. Sopra ilcodice "class Test_Form" c'è lafunzione che abbiamo proprio oracreato. Notate che per il momentoviene semplicemente ignorata.Guardate ulteriormente in basso evedrete il codice che crea e controllail pulsante. Tutto è già pronto. Peròdobbiamo ancora dire al pulsantecosa fare. Chiudete la consolepython e continuiamo.

Nel launch pad fate clic suWindow quindi selezionate FunctionList. Qui scriveremo il metodo perchiudere la finestra.

I l primo pulsante a sinistra è

Page 26: Full circle

full circle magazine n. 58 1 0 indice ^

quello Add. Fate clic. Nel campoFunction digitate "py:Button1 Click"e nel campo Arguments inserite "p1 "e cambiate il testo nel riquadro inbasso in...

def Button1Click(p1):sys.exit()

Per finire, fate clic sul segno dispunta.

A seguire dobbiamo legarequesta routine al pulsante.Selezionate il pulsante nella finestra,fate clic destro e selezionate"Bindings...". Come prima, fate clicsul pulsante più a sinistra della barrastrumenti e selezionate Button-1 .Questo è l'evento associato al clicdel pulsante sinistro del mouse. Nelriquadro di testo a destra inserite

"Button1 Click". Assicuratevi di usarele stesse maiuscole/minuscole usateper la funzione appena creata. Fateclic sul segno di spunta sul latodestro.

Ora salvate e generate il codicepython.

Dovreste vedere il seguentecodice verso la fine, ma fuori allaclasse Test_Form...

def Button1Click(p1) :sys.exit()

E l'ultima linea della classedovrebbe essere...

self.Button1.bind('<Button-1>',Button1Click)

Se eseguite il codice e fate clic sulpulsante Exit la finestra dovrebbechiudersi correttamente.

Proseguire

Ora facciamo qualcosa di piùcomplicato. Creeremo una demomostrante i widget disponibili. Primadi tutto chiudiamo Page eriavviamolo. Proseguite creando unnuovo modulo Toplevel. Aggiungetedue frame, uno sopra l'altro eespandeteli fino a occupare l'intera

larghezza della finestra. Nel framesuperiore posizionate un'etichetta e,usando l'editor degli attributi,cambiate il testo in "Buttons:".Quindi, aggiungete due pulsantilungo il piano orizzontale. Cambiateil testo di quello sinistro in "Normal"e di quello destro in "Sunken".Mentre è selezionato il pulsanteSunken, cambiate l'attributo overrelief in "sunken" e chiamatelobtnSunken. Per il pulsante "Normal"scegliete l'alias "btnNormal". Salvate

il progetto come "Demos.tcl".

Proseguite posizionando nelframe inferiore un'etichetta conscritto "Radio Buttons" e quattropulsanti radio come nella immaginein basso. Per finire, posizionate unpulsante Exit sotto il frame inferiore.

Prima di lavorare sui legami,creiamo le funzioni per gestire i clic.

Aprite Function List e create duefunzioni. La prima dovrebbe esserechiamata btnNormalClicked e l'altrabtnSunkenClicked. Assicuratevi diconfigurare i campi Arguments perincludere p1 . Ecco il codice chedovreste avere...

def btnNormalClicked(p1):print "Normal Button Clicked"

def btnSunkenClicked(p1) :print "Sunken Button Clicked"

Aggiungiamo i legami per ipulsanti. Per ciascun pulsante fateclic destro, selezionate "“Bindings..."e aggiungete, come prima, uncollegamento alla funzione cheabbiamo creato. Per il pulsantenormale, dovrebbe essere"btnNormalClicked" e per quelloincavato dovrebbe esserebtnSunkenClicked. Salvate egenerate il codice. Ora testando ilprogramma con l'opzione Run dellaconsole python, facendo clic suqualunque pulsante, non vedreteaccadere nulla. Comunque, chiusal'applicazione, dovreste vederel'output. Questo è normale per Pagee se lo eseguite dalla riga dicomando come normalmente si fa lecose dovrebbero funzionare come cisi aspetta.

HOWTO - INIZIARE PYTHON 30

Page 27: Full circle

full circle magazine n. 58 1 1 indice ^

Greg Walters è il proprietario dellaRainyDay Solutions, LLC, una società diconsulenza in Aurora, Colorado eprogramma dal 1 972. Ama cucinare, fareescursioni, ascoltare musica e passare iltempo con la sua famiglia. Il suo sito webè www.thedesignatedgeek.net.

Ora tocca ai pulsanti radio. Liabbiamo raggruppati in due insiemi. Iprimi due (Radio 1 e Radio 2)saranno l'insieme 1 e gli altri duesaranno l'insieme 2. Fate clic suRadio 1 e nell'editor degli attributiimpostate l'attributo value a zero equello variable a "rbc1 ". Configurarevariable per Radio 2 a "rbc1 " e valuea uno. Fate la stessa cosa per Radio 3e Radio 4 ma per entrambiimpostate variable a "rbc2". Sevolete, potete gestire il clic deipulsanti radio stampndo qualcosanel terminale, ma per ora la cosaimportante è che i due gruppifunzionino. Facendo clic su Radio 1verrà deselezionato Radio 2 senzainfluenzare Radio 3 o Radio 4, stessacosa per Radio 2 e così via.

Per finire dovreste creare unafunzione per il pulsante Exit elegarlo al pulsante così comeabbiamo fatto nel primo esempio.

Se avete seguito quello cheabbiamo realizzato nelle altreapplicazioni Tkinter, dovrestecomprendere il codice mostrato inalto a destra. In caso contrariotornate indietro di qualche numeroper una discussione completa.

Avrete notato come Page renda ilprocesso di creazione molto più

semplice che farlo da soli. Abbiamosolo scalfito la superficie di quelloche Page può fare e inizieremo a farequalcosa di più impegnativo laprossima volta.

I l codice python lo potete trovaresu pastebin all'indirizzohttp://pastebin.com/qq0YVgTb.

Una nota prima di chiudere perquesto mese. Potreste aver notatoche ho saltato un paio di numeri. Èdovuto al fatto che a mia mogliel'anno scorso è stato diagnosticatoun cancro. Nonostante abbia cercatodi non farmi crollare il mondoaddosso, alcune cose sono accadute.Una di queste è che il mio vecchiodominio/sito web all'indirizzowww.thedesignatedgeek.com è

stato prima chiuso per mancatorinnovo e poi venduto a miainsaputa. Ho predispostowww.thedesignatedgeek.net contutto il vecchio materiale. Devosistemare tutte le vecchie cose.Dovrò lavorare duramente il meseprossimo per rimettermi incarreggiata.

Ci vediamo la prossima volta.

HOWTO - INIZIARE PYTHON 30

def set_Tk_var():# These are Tk variables passed to Tkinter and must# be defined before the widgets using them are created.global rbc1rbc1 = StringVar()global rbc2rbc2 = StringVar()def btnExitClicked(p1) :sys.exit()def btnNormalClicked(p1) :print "Normal Button Clicked"def btnSunkenClicked(p1) :print "Sunken Button Clicked"

Il podcast di Ubuntu tratta di tuttele ultime novità e di argomentiriguardanti gli utenti Ubuntu Linuxe i fan del software libero ingenerale. Lo show piacerà ai nuoviutenti così come a quelli piùnavigati. Le nostre discussioniriguardano lo sviluppo di Ubuntusenza troppi tecnicismi. Siamoabbastanza fortunati da avere instudio alcuni ospiti importanti che ciracconteranno in anteprima degliultimi eccitanti sviluppi a cui stannolavorando, in manieracomprensibile! Parliamo inoltredella comunità Ubuntu e di cosa sioccupa.

Lo spettacolo è presentato damembri della comunità UbuntuLinux inglese. Basandosi sul Codicedi Condotta Ubuntu è adatta a tutti.

Lo show è trasmesso in diretta ognidue settimane nella serata dimartedì (ora britannica) ed èdisponibile per il download il giornoseguente.

podcast.ubuntu-uk.org

Page 28: Full circle

full circle magazine n.59 7 indice ^

HHOOWW--TTOOScritto da Greg D. Walters II nn iizziiaarree PPyytthhoonn -- PPaarrttee 3311

D opo il nostro ultimoincontro dovreste avere unadiscreta idea di come usarePage. Se non è così, per

favore leggete l’articolo dello scorsomese. Continueremo questa voltacreando una applicazione perelencare ifile tramite GUI. L’obbiettivo è creareun'applicazione grafica chericorsivamente sfoglia una directory,ricercando file con un insieme diestensioni definite emostrando ilrisultato in una vista ad albero. Perquesto esempio cercheremo file di tipomedia con estensione ".avi", ".mkv",".mv4", "mp3" e ".ogg".

Stavolta il testo può sembrare un po'succinto nella porzione del progetto.Tutto quello che sto per fare è daredelle indicazioni per la disposizione deiwidget e dei valori e attributi necessari,come queste...

Widget

Attribute: Value

Metterò tra apici le stringhe di testosolo quando è necessario. Peresempioperuno dei bottoni, il testo deve essereimpostato a "...".

Ecco come dovrebbe essere la GUIdella nostra applicazione...

Come si può vedere, c'è ilmoduloprincipale, un pulsante peruscire, unacasella per l'inserimento di testo con unpulsante che richiamerà una finestra didialogo per la directory, 5 caselle discelta per la selezione delle estensioni,un pulsante "GO!" per iniziareeffettivamente l’elaborazione e unavista ad albero permostrare il risultato.

Quindi, cominciamo. Avviamo Page ecreiamo un nuovowidget di primolivello. Utilizzando l’editor degli Attributiassegniamo i seguenti attributi.

Alias: SearcherTitle: Searcher

Assicurarsi di salvare spesso. Quandosi salva il file, farlo con il nome"Searcher". Ricordate, Pagemettel’estensione .tcl per noi e quando infinesi genere il codice python, lo salverànello stessa cartella.

Successivamente aggiungiamo unframe. Dovrebbe andare in cima a quelloprincipale. Assegniamo gli attribuiticome indicato di seguito.

Width: 595Height: 55x position: 0y position: 0

In questo frame, aggiungiamo unpulsante. Questo sarà il nostro pulsanteper l‘Uscita.

Alias: btnExitText: Exit

Spostiamolo verso il centro o vicinoal lato destro del frame. Io imposto ilmio con X530 e Y10.

Creiamo un altro frame.

Width: 325Height: 185y position: 60

Ecco qua come apparirà il frame, perdare una guida e andare avanti in questasezione.

Aggiungiamo una etichetta inquesto frame e impostiamo l’attributodel testo a "Path:". Spostiamolo vicinoall’angolo superiore sinistro del frame.

Nello stesso frame aggiungiamo unwidget per l’inserimento.

Alias: txtPathText: FilePathWidth: 266Height: 21

Aggiungiamo un pulsante alla destradelwidget per l’inserimento.

Alias: btnSearchPathText: "..." (no quotes)

Aggiungiamo cinque (5) pulsanti di

Page 29: Full circle

full circle magazine n.59 8 indice ^

HOWTO - INIZIARE PYTHON 31selezione, da mettere nel seguenteordine...

xx xx x

I tre pulsanti per la selezione sullasinistra sono per i file video e i due sulladestra sono per i file audio. Primagestiremo i tre file sulla sinistra e e poi idue sulla destra.

Alias: chkAVIText: ".avi" (no quotes)Variable: VchkAVI

Alias: chkMKVText: ".mkv" (no quotes)Variable: VchkMKV

Alias: chkMV4Text: ".mv4" (no quotes)Variable: VchkMV4

Alias: chkMP3Text: ".mp3" (no quotes)Variable: VchkMP3

Alias: chkOGGText: ".ogg" (no quotes)Variable: VchkOGG

Infine, aggiungiamo in questo frameun pulsante da qualche parte sotto icinque pulsanti di selezione,centrandolo in qualchemodo rispetto alframe.

Alias: btnGoText: GO!

Ora aggiungiamo un ulteriore framesotto all'ultimo frame.

Width: 565Height: 265

Ho posizionato ilmio con X0 Y250.Potreste dover ridimensionare il vostroform principale per renderlo visibileinteramente. All’interno di questo frameaggiungiamo un widgetScrolledtreeview.

Width: 550Height: 254X Position: 10Y Position: 10

Ecco. Abbiamo disegnato la nostraGUI. Ora tutto quello che è rimasto dafare è di creare la lista funzioni ecollegare le funzioni ai pulsanti.

Nella finestra con l’elenco dellefunzioni, premere il pulsante New(quello più a sinistra). Questo faràapparire l’editor di nuove funzioni.Cambiamo il testo nella casella diinserimento Function da "py:xxx" a"py.btnExitClick()". Nella casella perl’inserimento degli argomenti, digitare"p1 ". Nella casella multi linea in basso,cambiare il testo in:

def btnExitClick(p1):

sys.exit()

Notare che non è indentato. Page lofarà pernoi quando creerà il file python.

Successivamente creiamo una altrafunzione chiamata btnGoClick.Ricordarsi di aggiungere il parametropassato con "p1 ". Lasciare l'istruzione"pass". Si cambierà più tardi.

Infine, aggiungiamo una altrafunzione chiamata "btnSearchPath". Dinuovo, lasciare l'istruzione pass.

Al termine, bisogna collegare ipulsanti alle funzioni appena create.

Fare clic con il tasto destro sulpulsante exit creato e selezionare Bind.Si aprirà una grande casella. Fare clic sulpulsante Newbind, poi su Button-1 ecambiare la parola "TODO" nella caselladi inserimento a destra con"btnExitClick". NON inserire le parentesi() qui.

Colleghiamo il pulsante GO abtnGoClick e il pulsante "..." abtnSearchPathClick.

Salvare la GUI e generare il codicepython.

Ora ciò che resta è di creare il codiceche "incolla" insieme la GUI.

Apriamo con l'editorpreferito ilcodice appena generato. Cominciamoad esaminare quello che Page ha creatopernoi.

In cima al file c’è l'intestazionestandard python e una singola istruzionedi importazione per la libreria sys. Poi c’èdel codice abbastanza confuso (a primavista). Questo, essenzialmente, analizzala versione di python con cui si stacercando di eseguire l’applicazione equindi importa la corretta versione dellelibrerie tkinter. Ameno che non si stiausando python 3.x, si possonofondamentalmente ignorare le ultimidue.

Modificheremo la porzione di codice2.x per importare altri moduli tkinter trapoco.

La prossima è la routine"vp_start_gui()". È la routine principaledel programma. Imposta la nostra GUI,le variabili di cui abbiamo bisogno einoltre richiama il ciclo principale tkinter.Si noterà la linea "w= None" al di sottodi questa. Non è indentata e non sisuppone che lo sia.

Le successive sono due routine(create_Searchere destroy_Searcher)

Page 30: Full circle

full circle magazine n.59 9 indice ^

che sono usate per sostituire il cicloprincipale se si sta invocando questaapplicazione come libreria. Nondobbiamo preoccuparci di queste.

Poi c’è la routine "set_Tk_var". Sidefiniscono le variabili tkinter utilizzateche necessitano di essere assegnateprima di creare i widget. È possibilericonoscerle come le variabili di testoper ilwidget di inserimento FilePath e levariabili per le caselle di selezione. Lesuccessive tre routine sono quellecreate usando l’editor di funzioni e unafunzione di inizializzazione "init()".

Eseguiamo ora il programma. Notareche le caselle di selezione sono ingrigitee selezionate. Non è ciò che vogliamoper il rilascio dell'applicazione, cosìcreeremo un po’ di codice perpulirliprima che il form sia mostrato all’utente.L’unica altra cosa funzionante, oltre allecaselle di selezione, è il pulsante Exit.

Andiamo avanti e terminiamo ilprogramma.

Ora diamo una occhiata alla classeche contiene veramente la definizionedella GUI. Che sarebbe la "classSearcher". Qui è dove sono definiti tutti iwidget e disposti nel nostro form.Dovreste ormai avere familiarità conquesto.

Due ulteriori classi che contengono ilcodice per la vista ad albero scorrevolesono create pernoi. Non dobbiamocambiare niente in queste. E’ statocreato tutto da Page pernoi.

Ora torniamo all’inizio del codice einiziamo lemodifiche.

Abbiamo bisogno di importare pochimoduli di libreria in più, così sottol’istruzione "import sys", aggiungiamo...

import os

from os.path import join,getsize, exists

Ora troviamo la sezione che ha lalinea "py2 = True". Come detto prima, èla sezione relativa alle importazioni ditkinterperPython versione 2.x. Sotto a"import ttk", bisogna aggiungere leseguenti istruzioni per supportare lalibreria FileDialog. Serve ancheimportare ilmodulo tkFont.

import tkFileDialog

import tkFont

Poi bisogna aggiungere alcunevariabili alla routine "set_Tk_var()". Infondo alla routine aggiungiamo leseguenti linee...

global exts, FileList

exts = []

FileList=[]

Qui creiamo due variabili gloabali(exts e FileList) che saranno accessibili inseguito nel nostro codice. Entrambesono liste. "exts" è un lista delleestensioni che l’utente seleziona dallaGUI. "FileList" contiene una lista di listedei file corrispondenti trovati quandonoi facciamo la nostra ricerca. Useremoquesta perpopolare ilwidget della vistaad albero.

Poiché il nostro "btnExitClick" è giàstato fatto pernoi da Page, gestiremo laroutine "btnGoClick". Commentiamol’istruzione pass e aggiungiamo il codicein modo tale che somigli a questo…

def btnGoClick(p1) :

#pass

BuildExts()

fp = FilePath.get()

e1 = tuple(exts)

Walkit(fp,e1)

LoadDataGrid()

Questa è la routine che saràchiamata quando l’utente preme ilpulsante "GO!". Lancia una routinedenominata "BuildExts" che crea la listadelle estensioni che l’utente haselezionato. Si ottiene quindi il percorsoche l’utente ha selezionato dalla finestradi dialogo AskDirectory, che vieneassegnato alla variabile fp. Crea quindiuna tupla (ndt elemento di una relazionecon attributi) dalla lista delle estensioni,che è necessaria quando si controllano ifile. Poi lancia una routine denominata"Walkit", passandogli la directory didestinazione e l'estensione tupla.

Infine lancia una routine chiamata"LoadDatagrid".

Successivamente si deve rimpolparela routine "btnSearchPathClick".Commentiamo l'istruzione pass ecambiamo il codice in modo che somiglia questo...

def btnSearchPathClick(p1) :

#pass

path =tkFileDialog.askdirectory()#**self.file_opt)

FilePath.set(path)

La prossima è la routine init. Dinuovo, facciamo in modo che il codice

HOWTO - INIZIARE PYTHON 31

Page 31: Full circle

full circle magazine n.59 1 0 indice ^

somigli a questo...

def init():

#pass

# Fires AFTER Widgets andWindow are created...

global treeview

BlankChecks()

treeview =w.Scrolledtreeview1

SetupTreeview()

Questa crea una variabile globalechiamata "treeview". Poi lancia unaroutine che pulirà le selezioni grigie dallecaselle di selezione, designa la variabile"treeview" a puntare alla vista ad alberoscorrevole nel nostro form e lancia"SetupTreeview" per impostare leintestazioni delle colonne.

Qui c’è il codice per la routine"BlanckChecks" che deve essere laprossima.

def BlankChecks():

VchkAVI.set('0')

VchkMKV.set('0')

VchkMP3.set('0')

VchkMV4.set('0')

VchkOGG.set('0')

Qua, tutto ciò che stiamo facendo èimpostare le variabili (che impostanoautomaticamente lo stato di selezionedelle caselle di selezione) a "0". Sericordate, ogni volta che la casella diselezione è premuta, la variabile èautomaticamente aggiornata. Se lavariabile è cambiata dal nostro codice, lacasella di selezione risponde altrettantobene. Ora (in alto a destra) gestiremo laroutine che costruisce la lista delleestensioni in base a ciò che l’utente hapremuto.

Tornate indietro con lamemoria almio nono articoloin FCM#35. Scrissi del codiceper creare un catalogo di fileMP3. Useremo una versioneaccorciata di quella routine (alcentro destra). Fateriferimento a FCM#35 seavete domande su questaroutine.

Successivamente (in basso adestra) lanciamo la routineSetupTreeview. E’ abbastanzasemplice. Definisce una variabile"ColHeads" con le intestazioni chevogliamo in ciascuna colonnadella vista ad albero. Lo fa tipouna lista. Assegna quindil’attributo intestazione per

ciascuna colonna. Assegna anche lalarghezza della colonna alla dimensionedi questa intestazione.

Infine dobbiamo creare la routine"LoadDataGrid (prossima pagina in altoa destra), dove caricare i dati nella vistaad albero. Ciascuna riga della vista adalbero è una voce nella variabile dellalista FileList. Regoliamo inoltre lalarghezza di ciascuna colonna (di nuovo)per corrispondere alla dimensione dellacolonna dei dati.

Questa è la prima vista dellaapplicazione. Avviamola e vediamo

come l'abbiamo fatta. Notare che se sihanno tanti file da sfogliare, ilprogramma sembra non rispondere.Questo è qualcosa che deve essererisolto. Creeremo una routine per

HOWTO - INIZIARE PYTHON 31

def BuildExts():if VchkAVI.get() == '1':

exts.append(".avi")if VchkMKV.get() == '1':

exts.append(".mkv")if VchkMP3.get() == '1':

exts.append(".mp3")if VchkMV4.get() == '1':

exts.append(".mv4")if VchkOGG.get() == '1':

exts.append(".ogg")

def Walkit(musicpath,extensions):rcntr = 0fl = []for root, dirs, files in os.walk(musicpath):

rcntr += 1 # This is the number of folders we have walkedfor file in [f for f in files if f.endswith(extensions)]:

fl.append(file)fl.append(root)FileList.append(fl)fl=[]

def SetupTreeview():global ColHeadsColHeads = ['Filename','Path']treeview.configure(columns=ColHeads,show="headings")for col in ColHeads:

treeview.heading(col, text = col.title(),command = lambda c = col: sortby(treeview, c, 0))

## adjust the column's width to the header stringtreeview.column(col, width =

tkFont.Font().measure(col.title()))

Page 32: Full circle

full circle magazine n.59 1 1 indice ^

HOWTO - INIZIARE PYTHON 31cambiare il cursore predefinito con unoin stile "orologio" e viceversa per leoperazioni che richiedonomolto tempo,così l’utente lo noterà.

Nella routine "set_Tk_var",aggiungeremo il seguente codice infondo.

globalbusyCursor,preBusyCursors,busyWidgets

busyCursor = 'watch'

preBusyCursors = None

busyWidgets = (root, )

Quello che facciamo qui è definire levariabili globali, inizializzarle e impostarequindi il widget (in busyWidgets) chevogliamo che risponda ai cambiamentidel cursore. In questo caso loimpostiamo a root che è la nostrafinestra completa. Notare che questa èun tupla.

Poi creiamo due routine perimpostare e per ripristinare il cursore.Prima la routine di impostazione, chechiameremo "busyStart". Dopo laroutine "loadDataGrid", inseriamo ilcodicemostrato a centro destra.

Dobbiamo prima verificare se unvalore è stato passato a "newcursor". In

caso contrario, sipreimposta a busyCursor.Poi passiamo attraverso latupla busyWidgets eimpostiamo il cursore aqualsiasi cosa vogliamo.

Ora inseriamo il codicemostrato in basso adestra.

In questa routine,azzeriamosemplicemente il cursoreper i widget nella nostratupla busyWidgetriportandolo alla formapredefinita.

Salviamo edeseguiamo ilprogramma.Dovremmo notarecambiamenti delcursore ogni voltache si scorre unalunga lista di file.

Sebbene questaapplicazione non famolto, mostra comeusare Page per creare un ambiente disviluppo veramente veloce. Dall’articolodi oggi possiamo vedere come, avendoin anticipo una buona progettazionedella GUI, si può rendere il processo di

sviluppo facile e abbastanza indolore.

Il file tcl è salvato in pastebin pressohttp://pastebin.com/AA1kE4Dye ilcodice python presso

http://pastebin.com/VZm5un3e.

Ci vediamo la prossima volta.

def LoadDataGrid():global ColHeadsfor c in FileList:

treeview.insert('','end',values=c)# adjust column's width if necessary to fit each valuefor ix, val in enumerate(c):

col_w = tkFont.Font().measure(val)if treeview.column(ColHeads[ix],width=None)<col_w:

treeview.column(ColHeads[ix], width=col_w)

def busyStart(newcursor=None):global preBusyCursorsif not newcursor:

newcursor = busyCursornewPreBusyCursors = {}for component in busyWidgets:

newPreBusyCursors[component] = component['cursor']component.configure(cursor=newcursor)component.update_idletasks()preBusyCursors = (newPreBusyCursors, preBusyCursors)

def busyEnd():global preBusyCursorsif not preBusyCursors:

returnoldPreBusyCursors = preBusyCursors[0]preBusyCursors = preBusyCursors[1]for component in busyWidgets:

try:component.configure(cursor=oldPreBusyCursors[component])

except KeyError:pass

component.update_idletasks()