dal 2015 - visita n. 864
TkInter
TkInter

 

5. Componenti della GUI


5.1. Usare le Lambda

Prima di passare in rassegna operativa i principali widget di Tkinter occorre fare un brevissimo cenno alle Funzioni Lambda, in quanto alcuni degli esempi seguenti ne faranno uso, più che altro per brevità di espressione.
Python, grazie al costrutto lambda, supporta la creazione di funzioni anonime, cioè non collegate ad alcun nome. Non è esattamente equivalente al lambda dei linguaggi per la programmazione funzionale, ma è un concetto molto potente, ben integrato in Python ed usato spesso assieme a concetti funzionali tipici come: filter(), map(), reduce(). Ma, meglio di tante parole, ecco un confronto tra due semplici modi per calcolare il quadrato di x, con una funzione normale f(x) (10) e con una funzione lambda g(x) (20):

def f(x):                                    # (10) funzione tradizionale
  return x**2 
g = lambda x: x**2                           # (20) funzione lambda
print f(7)                                   # (30) stampa a video 49
print g(7)                                   # (40) stampa a video 49

In entrambi i casi (30, 40), passando 7 come argomento, si ha la stampa a video del risultato corretto: 49.
Analizziamo adesso un'altra situazione nella quale la funzione lambda è all'interno di una funzione tradizionale. La funzione si chiama increm ed ha il semplice scopo di incrementare il valore dell'argomento di una quantità costante:

def increm(n):                               # (10) increm è una funzione tradizionale  
  return lambda x: x + n                     # (20)        contenente una funzione lambda
f1 = increm(2)                               # (30) f1 diviene una incrementatrice di 2
f2 = increm(6)                               # (40) f2 diviene una incrementatrice di 6
print f1(42), f2(42)                         # (50) in uscita:  44   48
print increm(22)(33)                         # (60) in uscita:  55


L'esempio seguente mostra l'impiego delle funzioni standard filter(), map(), reduce() applicate ad una lista.

dati = [2, 18, 9, 22, 17, 24, 8, 12, 27]     # (10)
print filter(lambda x: x % 3 == 0, dati)     # (20) in uscita:  [18, 9, 24, 12, 27]
print map(lambda x: x * 2 + 10, dati)        # (30) in uscita:  [14, 46, 28, 54, 44, 58, 26, 34, 64]
print reduce(lambda x, y: x + y, dati)       # (40) in uscita:  139


Ancora un esempio, il calcolo dei numeri primi all'interno di un intervallo.

from math import sqrt
max = 30                                     # (10)
n = range(2,max)                             # (20)
mx = int(sqrt(max)) + 1                      # (30)
for i in range(2,mx):                        # (40)
  n = filter(lambda x: x==i or x%i, n)       # (50)
print n                                      # (60) in uscita: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

Il metodo usato è il classico Crivello di Eratostene. Fissiamo il valore massimo max (10) e creiamo la lista n (20) con i numeri interi su cui operare la ricerca. La (30) determina il valore massimo dei divisori mx da adoperare. Il ciclo for (40), tramite la funzione filter, seleziona i numeri primi.

Sulle funzioni lambda possiamo, in conclusione, fissare alcuni punti:




5.2. Entry, Text

La presentazione di un testo ed il suo trattamento sono fondamentali nel colloquio con l'utente, sia che si tratti di brevi stringhe, sia che si tratti di testi più importanti.
Entry serve per l'input di brevi stringhe di testo e si appoggia alla variabile st istanza di StringVar.

from Tkinter import *

root=Tk()
st=StringVar() 
lb1= Label(root, text="Prova \"entry\"", width=30, fg="blue")
lb1.pack()
ent=Entry(textvariable=st, width=30)
ent.pack()

root.mainloop()

Due sono i metodi indispensabili per operare:   st.get() per la restituzione del testo contenuto nella Entry,   st.set("abcd...xyz") per preimpostare il contenuto della Entry.

Il widget Text serve invece per l'input di testi composti da più righe.

from Tkinter import *

root=Tk()
txt=Text(root,width=30,height=10)
txt.pack()
txt.insert(END,"Testo del widget \"text\"\n\n\
Finche' la barca va,\nlasciala andare ...\n\n\n")
button = Button(txt, text="Uscita", command=root.quit, bd=1)
txt.window_create(INSERT, window=button)

root.mainloop()
	




5.3. Menu, Menubutton

Servono entrambi per la gestione dei menu, pur avendo delle caratteristiche in comune hanno un aspetto ed un comportamento differente.
Menu ha una impostazione più classica. L'esempio e la figura illustrano le caratteristiche salienti. Occorrerà poi sperimentare personalmente le possibili variazioni sul tema.

from Tkinter import *
root = Tk()

def chtitle(selez):    
    root.title(selez)

def chcolor(selez):    
    root.tk_setPalette(selez)

menu1 = Menu(root)                 # (30) Creazione barra menu
mFile = Menu(menu1, tearoff=0)     # (40) Creazione menu File
menu1.add_cascade(label="File", menu=mFile)
mFile.add_command(label="Esci", command=root.quit)

mCasc = Menu(menu1, tearoff=0)
menu1.add_cascade(label="Cascata", menu=mCasc)

mTitle = Menu(mCasc, tearoff=0)
mCasc.add_cascade(label="Titre", menu=mTitle)

mTitle.add_command(label="menu demo", command=lambda : chtitle('menu demo'))
mTitle.add_command(label="Test", command=lambda : chtitle('test'))

mColor = Menu(mCasc)
mCasc.add_cascade(label="Colore", menu=mColor)
mColor.add_command(label="blu", command=lambda : chcolor('blue'))
mColor.add_command(label="rosso", command=lambda : chcolor('red'))

root.config(menu=menu1)

root.mainloop()

Come impostazione predefinita si ha tearoff=1, cioè i sottomenù non sono staccabili. L'opzione tearoff=0 consente quindi di rendere staccabili i sottomenu. Ci si accorge di questo fatto dalla presenza di un segmento tratteggiato nel punto in cui avviene il distacco (vedi figura).

Menubutton è una alternativa per la gestione dei menu più orientata ai pulsanti.

from Tkinter import *
root = Tk()

def chtitle(selez):    
    root.title(selez)

def chcolor(selez):    
    root.tk_setPalette(selez)

mb1=Menubutton(root,text="File",relief="raised")
mb1.m=Menu(mb1,tearoff=0)
mb1.m.add_command(label="Esci", command=root.quit)
mb1.config(menu=mb1.m)
mb1.pack(side="left",pady=4,padx=4)

mb2=Menubutton(root,text="Cascata",relief="raised")
mCasc=Menu(mb2,tearoff=0)
mb2.config(menu=mCasc)

mTitle=Menu(mCasc,tearoff=0)
mCasc.add_cascade(label="titolo",menu=mTitle)
mTitle.add_command(label="menu demo", command=lambda  : chtitle('menu demo'))
mTitle.add_command(label="Test", command=lambda  : chtitle('test'))

mColor=Menu(mCasc)
mCasc.add_cascade(label="Colore",menu=mColor)
mColor.add_command(label="blu", command=lambda : chcolor('blue'))
mColor.add_command(label="rosso", command=lambda : chcolor('red'))

mb2.pack(pady=4)

root.mainloop()




5.4. Canvas, PhotoImage

Lo scopo, se non l'essenza della GUI, è la visualizzazione di immagini che aiutino l'utente a svolgere meglio il suo lavoro. Moltissimi sono i widget orientati alla grafica. Eccone alcuni tipici ed indispensabili.

Canvas costituisce la tela base per disegnare e visualizzare immagini. Da notare subito alcuni dei metodi invocati per tracciare le varie forme geometriche (30, 32, 34, 36).

from Tkinter import *

root = Tk()
canv=Canvas(root, width=200, height=150, bg="#60B7FF")     # (10)
canv.pack(expand = YES, fill = BOTH)                       # (20)
canv.create_rectangle((3,3,148,98), fill="#ffffc0",        # (30)
     outline="red")
canv.create_line((70,80,130,40), fill="blue",width=1)      # (32)
canv.create_oval((30,10,120,30), fill="green",             # (34)
     outline="brown")
canv.create_polygon((40,40, 55,50, 70,40, 60,55, 70,70,    # (36)
     55,60, 40,70, 50,55), fill="brown")
photo=PhotoImage(file="palma.gif")                         # (40)
canv.create_image(150, 110, image = photo, anchor = NW)    # (42)
root.mainloop()

PhotoImage viene utilizzato per la visualizzazione di una immagine in formato .GIF o .PPM, come nell'esempio appena proposto (40, 42). Come supporto, oltre canvas, può utilizzare anche altri widget (Button, Label, ...).
Si noti l'opzione file (40) per associare il nome del file esterno palma.gif. Ma non necessariamente le immagini debbono essere costituite da file esterni, è possibile infatti importarle all'interno del codice tramite una conversione base64, come nell'esempio seguente.

from Tkinter import *

ico1 = """\
R0lGODlhIAAgALMAAAAAAMwAAACZAJmZAAAAmZkAmQCZmZmZmczMzP8AAAD/AP//AAAA//8A/wD/
/////yH5BAEAAAgALAAAAAAgACAAQASuEMmJgL2X6i1BStf3AUqpAA9priWgeeKosvRqcRNGYTfe
wahUzFJruV6xYXHZ4oRkTKPPiTtOf8ksr+cECkXE1iNF41ayI1uYebSgZ1GWFTuER83Vtt2Ev/Z1
V1NzOYE7bm+DgYdPYGtGiR0jYzBQj3KDXl8gM0F7UpGIfJNsbWggcTWlb6hlOZRgrHKGWrFNG4ew
qH0vW6SFFRnAcMG/FXnFXby7xZDGyC9UUxEAADs=
"""

ico2="""\
R0lGODlhIAAgALMAAAAAAMwAAACZAJmZAAAAmZkAmQCZmZmZmczMzP8AAAD/AP//AAAA//8A/wD/
/////yH5BAEAAA8ALAAAAAAgACAAQAS88MlJKwA1a8B6B0mYAEIJLOgCFCyLafADDG9sy7U053d2
eZfRSbVy8SacH+iyFFxMxBNzdbTtetbAIHDZVrGb6RSs8zBAItIQVXT5zGfRqERfq1puHYWHufL3
ZDI0gVhXhEhee4OHPkyMhRePSD9nU0ZfFhxAaU4pbQWYmh9yJFCeeEeicaR1KWyoe2ZonHSdrp81
kTi5TFyOOrq6YABckjDEmI/IxhvFzBbOjMSLOtHSiYLJwzNa1s+PEQAAOw==
"""

root = Tk()
lb1= Label(root, text="pulsante cliccato",
    width=30, fg="blue")
lb1.pack()
ico = [ico1, ico2]
txt = ["palloncini", "ombrello"]
img = PhotoImage(data=ico[0])                              # (10)
b0 = Button(root, image=img, width=100,
    command=(lambda: lb1.configure(text=txt[0])))          # (12)
b0.pack(side="left")
b0.image = img                                             # (14)
img = PhotoImage(data=ico[1])                              # (20)
b1 = Button(root, image=img, width=100,
    command=(lambda: lb1.configure(text=txt[1])))          # (22)
b1.pack(side="left")
b1.image = img                                             # (24)

root.mainloop()

Osservazioni:

La trasformazione dei file immagine (.gif) in codice base64 può facilmente essere effettuata tramite il programma gif2b64.py, riportato di seguito.

#!/usr/bin/env Python
# prg. gif2b64.py 
# 03-08-2007 Edizioni ByteMan
# Converte file binario in stringa di caratteri base64.

import base64
import sys     # Usa il vettore predefinito sys.argv
               # sys.arg[0] nome riga di avvio del programma
               # sys.arg[1] nome del primo argomento
def main():
   if len(sys.argv) < 2:
      print "Sintassi: gif2b64 file.gif"
      return
   nameF = sys.argv[1]
   try:
      print 'data="""\\\n' + base64.encodestring(open(nameF).read()) + '"""'
   except:
      print 'Errore: impossibile aprire il file "' + nameF + '" !!'
      return

main()
Il suo uso è molto semplice, anche se l'output normale avviene su video è possibile generare il file d'uscita tramite la ridirezione dell'output, con il seguente comando:
python gif2b64.py nome.gif > nome.txt



5.5. Checkbutton, Radiobutton

Serve per effettuare delle selezioni cumulabili.

from Tkinter import *

root = Tk()
states = []
for i in range(1,6):
    var = IntVar()
    chk = Checkbutton(root, text=str(i), variable=var)
    chk.pack(side=LEFT, padx=4)
    states.append(var)

root.mainloop()
print map((lambda var: var.get()), states)


from   Tkinter import *

root=Tk()
myvar = IntVar()
for i in range(1,5):
    rdb=Radiobutton(root,text=str(i), variable=myvar, value=str(i))
    rdb.pack(side=LEFT, padx=8)

root.mainloop()
print myvar.get()




5.6. Frame, LabelFrame

Frame è una sorta di contenitore per altri widget. Il suo uso consente di associare blocchi omogenei di widget e sono di aiuto nella preparazione dello schema (layout) grafico dell'interfaccia GUI.

from Tkinter import *

root=Tk()
st1 = "Label rossa su fondo giallo"
frm1=Frame(root)
puls1=Button(frm1,text="Uscita",command=root.quit)
puls2=Button(frm1,text="Puls2")
puls3=Button(frm1,text="Puls3")
lb1= Label(root, text=st1, bg="yellow",
     fg="red", width=30)
puls1.pack(side="left")
puls2.pack(side="left")
puls3.pack()
frm1.pack()
lb1.pack()
root.mainloop()

FrameLabel è come Frame, ma ha una cornicetta ed una scritta in alto.

from Tkinter import *

root = Tk()
st1 = "  Questo Widget e' LabelFrame  "
st2 = "\nQuesta Label\nsi trova dentro LabelFrame\n"
frm1 = Frame(root)
lbfrm1 = LabelFrame(frm1, text=st1)
lbfrm1.pack(fill="both", expand="yes")
lb1 = Label(lbfrm1, text=st2, bg="yellow",
      fg="red", width=30)
lb1.pack()
frm1.pack()
root.mainloop()




5.7. List, Scrollbar

List consente la selezione di Voci da liste predefinite. Da notare che nei widget testo i parametri width ed heigth sono espressi in caratteri, mentre nei widget grafici sono espressi in pixel. Nel caso in esame è importante heigth perchè da esso dipende il numero di Voci visualizzate, e in assenza di barra di scorrimento verticale potrebbero rimanere delle Voci nascoste.

from Tkinter import *

def onSelect(ev):                                          # (10)
    select=listb.get(listb.curselection())                 # (12)
    lab.configure(text=select)                             # (14)
    listb.configure(bg=select)                             # (16)

root=Tk()
listb=Listbox(root, width=30, height=4)                    # (22)
listb.pack()                                               # (42)
lab=Label(root,text="Doppio Click sulla Voce")             # (50)
lab.pack()
for c in ["gray", "red", "yellow",                         # (60)
    "skyblue"]: listb.insert(END,c)
listb.bind('<Double-1>',onSelect)                          # (70)
root.mainloop()

Le istruzioni (22, 42) realizzano la lista, mentre l'istruzione (60) si occupa dell'inserzione degli elementi nella lista e la (70) realizza il collegamento sul doppio click. Quest'ultimo viene gestito dalla funzione (10), in particolare la (12) legge la Voce selezionata nella lista, e le istruzioni (14, 16) operano per cambiare il testo visualizzato sulla label ed il colore di sfondo della listbox.

Scrollbar consente l'aggiunta delle barre di scorrimento ad un widget. Nel caso dell'esempio il widget è rappresentato dalla lista, inizialmente Scrollbar e Listbox non sanno nulla l'uno dell'altro (20, 22), occorre quindi concatenarli informandoli delle rispettive presenze. Ciò viene realizzato con le istruzioni (30, 32) che fissano i rispettivi comandi (command e yscrollcommand). Tutto il resto è identico al programma precedente.

from Tkinter import *

def onSelect(ev):                                          # (10)
    select=listb.get(listb.curselection())                 # (12)
    lab.configure(text=select)                             # (14)
    listb.configure(bg=select)                             # (16)

root = Tk()
sbar = Scrollbar(root, orient=VERTICAL)                    # (20)
listb = Listbox(root, width=30, height=4)                  # (22)
sbar.config(command=listb.yview)                           # (30)
listb.config(yscrollcommand=sbar.set)                      # (32)
sbar.pack(side=RIGHT, fill=Y)                              # (40)
listb.pack()                                               # (42)
lab=Label(root,text="Doppio Click sulla Voce")             # (50)
lab.pack()
for c in ["gray", "blue", "red", "yellow",                 # (60)
    "green", "cyan", "seashell", "ivory",
    "skyblue", "gold"]: listb.insert(END,c)
listb.bind('<Double-1>',onSelect)                          # (70)
root.mainloop()






Corso Base Linux
Asm Linux
Python
GnuPlot
TkInter
wxPython
py-qt4-designer



Esperienza è il nome che tutti danno ai propri errori.
Oscar Wilde

Valid CSS!
pagina generata in 0.001 secondi