Creazione applicazione grafica:
Strutturata con le classi
try: import Tkinter as tk # Py2
except ImportError: import tkinter as tk # Py3
class Applicazione:
def __init__ (self):
self.finestra = tk.Tk()
self.finestra.title("Hello world")
def start(self):
self.finestra.mainloop()
app = Applicazione()
app.start()
Strutturata senza classi
try: import Tkinter as tk # Py2
except ImportError: import tkinter as tk # Py3
finestra = tk.Tk()
finestra.title("Hello world")
finestra.mainloop()
Aspetto della finestra
finestra.resizable(False, False) # rende la finestra non ridimensionabile
finestra.overrideredirect(True) # toglie i bordi, non si puo' chiudere con la X
Normalmente la finestra è elastica e diventa grande quanto il suo contenuto,
tuttavia è possibile indicarne a priori la grandezza e la posizione:
finestra.geometry("640x480")
finestra.geometry("%dx%d" % (WIDTH, HEIGHT)) # solo dimensioni
finestra.geometry("%dx%d+%d+%d" % (WIDTH, HEIGHT, X, Y)) # anche posizione
Conoscendo le dimensioni dello schermo è possibile
centrare la finestra nello schermo stesso:
larghezza = finestra.winfo_screenwidth() # larghezza schermo in pixel
altezza = finestra.winfo_screenheight() # altezza schermo in pixel
x = larghezza//2 - WIDTH//2
y = altezza//2 - HEIGHT//2
finestra.geometry("%dx%d+%d+%d" % (WIDTH, HEIGHT, x, y))
Impostare l'icona della finestra su sistemi Windows:
finestra.wm_iconbitmap("icona.ico")
(si possono creare icone 16x16 su:
www.favicon.cc/)
Oggetto Frame, il contenitore:
All'interno della finestra si possono disporre dei widget
(window-objects). L'oggetto Frame (riquadro/cornice) permette di raggruppare
un certo numero di widget all'interno di esso. Questo serve
principalmente per il posizionamento, infatti disponendo il frame
in un altro punto della finestra si spostano assieme ad esso anche
tutti i widget contenuti, ma è utile anche per creare elementi
decorativi, come separatori o riquadri tridimensionali.
Ogni elemento di una applicazione grafica ha un oggetto "genitore"
ed eventualmente uno o più oggetti "figli". Un oggetto
Frame di base ha come genitore la finestra stessa:
frame1 = tk.Frame(form1)
Con questa riga si crea un oggetto di tipo Frame chiamato frame1,
appartenente alla finestra di nome form1. Un frame, come tutti gli altri widget, dispone di un grande
numero di attributi visuali (dimensioni, colori, tipo e dimensione
dei bordi, font ecc. Per impostare (configurare) questi attributi
(chiamati anche option / opzioni) ci sono tre modi, si possono specificare
nell'operazione di creazione dell'oggetto (vengono passati
automaticamente al costruttore dell'oggetto quando viene
istanziato), si può usare il metodo configure (dopo aver
già istanziato l'oggetto), oppure si possono impostare
manualmente uno per uno accedendo direttamente al dizionario
interno dei widget (sempre dopo aver istanziato l'oggetto):
frame1 = tk.Frame(form1, bg="yellow", width=300, height=200)
frame1.configure(bg="yellow", width=300, height=200)
frame1["bg"] = "yellow"
frame1["width"] = 300
frame1["height"] = 200
Per rendere visibile il tutto si deve chiamare
uno dei tre metodi possibili di posizionamento: pack, grid o place:
try: import Tkinter as tk # Py2
except ImportError: import tkinter as tk # Py3
class Applicazione:
def __init__ (self):
self.form1 = tk.Tk()
self.form1.title("Titolo finestra")
self.form1.resizable(False, False)
self.frame1 = tk.Frame(self.form1)
self.frame2 = tk.Frame(self.form1)
self.frame3 = tk.Frame(self.form1)
self.frame4 = tk.Frame(self.form1)
self.frame1.configure(bg="yellow", width=150, height=100)
self.frame2.configure(bg="red", width=150, height=100)
self.frame3.configure(bg="blue", width=150, height=100)
self.frame4.configure(bg="green", width=150, height=100)
self.frame1.grid(row=0, column=0)
self.frame2.grid(row=0, column=1)
self.frame3.grid(row=1, column=0)
self.frame4.grid(row=1, column=1)
def start(self):
self.form1.mainloop()
app = Applicazione()
app.start()
Frame di grandezza fissa o elastici
Normalmente un frame è un oggetto "elastico", può estendersi per
accettare elementi interni più grandi, ma può anche
"collassare" attorno agli oggetti, se vengono inseriti in esso con
i metodi di posizionamento pack e grid. Nell'esempio precedente si
vedono 4 frames rettangolari di identiche dimensioni (specificate
per ciascuno con width e height). Le dimensioni visualizzate sono
quelle volute perchè i frames non contengono nulla.
Provando ad inserire un qualsiasi widget al loro interno, e a
posizionarlo con i metodi pack o grid, i frames si restringono
alle dimensioni del contenuto, e la finestra si restringe
eventualmente alle dimensioni residue dei frames.
self.frame1 = tk.Frame(self.form1)
self.frame2 = tk.Frame(self.form1)
self.frame3 = tk.Frame(self.form1)
self.frame4 = tk.Frame(self.form1)
self.bottone1 = tk.Button(self.frame1, text="PULSANTE")
self.frame1.configure(bg="yellow", width=150, height=100)
self.frame2.configure(bg="red", width=150, height=100)
self.frame3.configure(bg="blue", width=150, height=100)
self.frame4.configure(bg="green", width=150, height=100)
self.frame1.grid(row=0, column=0)
self.frame2.grid(row=0, column=1)
self.frame3.grid(row=1, column=0)
self.frame4.grid(row=1, column=1)
self.bottone1.grid(row=0, column=0)
Per evitare questo si può usare il metodo grid_propagate(False): sul frame che vogliamo "bloccare".
self.frame1.grid_propagate(False)
self.frame1.grid(row=0, column=0)
self.frame2.grid(row=0, column=1)
self.frame3.grid(row=1, column=0)
self.frame4.grid(row=1, column=1)
self.bottone1.grid(row=0, column=0)
Dalla versione 2.6 di Python in poi il metodo grid non centra più
di default i widget nelle celle. Per fare questo bisogna scrivere:
self.frame1.grid_rowconfigure(0, weight=2)
self.frame1.grid_columnconfigure(0, weight=2)
self.bottone1.grid(row=0, column=0)
(Info tratte da:
stackoverflow.com/questions/14946963)
Gli attributi visivi comuni
Dimensioni
Sono specificate con width e height
(larghezza e altezza). Per oggetti non testuali come i Canvas e
i Frame le dimensioni si intendono in pixel, per oggetti
testuali come bottoni, listbox, label le dimensioni si intendono
in caratteri.
Colore
Lo sfondo si identifica con bg, mentre il
primo piano, cioè il colore del testo, si identifica con fg.
I colori possono essere scritti con il loro nome ("yellow"
"blue" ecc) o specificandoli in RGB: bg="#FF0000"
Bordi
I bordi hanno una dimensione
(spessore in pixel) specificato con bd=..., e uno stile
specificato con relief="stile". Gli stili possibili per i bordi
sono:
Testo
Molti widget (come bottoni e
label) possono rappresentare del testo. Lo stile del testo si
può specificare con
font = ("nomedelfont", dimensioni, "stile"). Per essere il più
possibile indipendenti dal sistema operativo usato si dovrebbero
specificare solo famiglie generiche come "serif", "monospace" ecc,
oppure caratteri comuni come "helvetica", "courier", "times" ecc. Lo stile può
essere: "normal", "bold", "roman", "italic", "underline", "overstrike".
self.label1 = tk.Label(self.form1, font=("helvetica", 12, "bold"))
Immagini
Alcuni widget, come label e bottoni, permettono di
rappresentare immagini al posto o assieme al testo. Si deve creare un oggetto
PhotoImage e assegnarlo alla
proprietà image dei widget.
self.immagine1 = tk.PhotoImage(file="logo.gif")
self.bottone1.configure(image=self.immagine1)
Il posizionamento
La griglia
Il metodo grid permette di
posizionare ogni widget all'interno di una cella immaginaria di
una griglia creata sull'elemento genitore. Il metodo grid
applicato ai frames principali di una finestra li allinea in una
griglia relativa all'intera finestra, mentre grid applicato a
widget contenuti in un frame allinea questi widget su una
griglia interna a quel frame. Per posizionare un oggetto
è sufficiente indicare oggetto.grid(row=riga,
column=colonna). I valori di riga e colonna partono da zero.
Celle estese su più righe o colonne
Può essere necessario
estendere un oggetto per più colonne o righe, in questo
caso, oltre ad indicare la sua cella, si indicano anche i
parametri rowspan = numerodirighe o columnspan = numerodicolonne.
try: import Tkinter as tk # Py2
except ImportError: import tkinter as tk # Py3
class Applicazione:
def __init__ (self):
self.form1 = tk.Tk()
self.form1.title("Titolo finestra")
self.form1.resizable(False, False)
self.frame1 = tk.Frame(self.form1)
self.frame2 = tk.Frame(self.form1)
self.frame3 = tk.Frame(self.form1)
self.frame1.configure(bg="yellow", width=150, height=100)
self.frame2.configure(bg="red", width=150, height=100)
self.frame3.configure(bg="blue", width=300, height=100)
self.frame1.grid(row=0, column=0)
self.frame2.grid(row=0, column=1)
self.frame3.grid(row=1, column=0, columnspan=2)
def start(self):
self.form1.mainloop()
app = Applicazione()
app.start()
self.frame1.grid(row=0, column=0)
self.frame2.grid(row=0, column=1, rowspan=2)
self.frame3.grid(row=1, column=0)
Padding
Ogni oggetto visuale dispone di
parametri per regolare il margine esterno (attorno all'oggetto)
o interno (tra il bordo e il contenuto).
Con padx e pady regoliamo il padding esterno, con
ipadx e ipady quello interno. Il margine lasciato dal padding
esterno ha il colore di fondo dell'elemento genitore:
self.frame1.grid(row=0, column=0, padx=10, pady=10)
self.frame2.grid(row=0, column=1, rowspan=2, padx=10, pady=10)
self.frame3.grid(row=1, column=0, padx=10, pady=10)
Estensione
Con il parametro sticky possiamo
fare in modo che un frame si estenda per riempire lo spazio
vuoto nella finestra causato da frame vicini più grandi.
Sticky accetta come valori delle coordinate n (nord) s (sud) w
(ovest) e (est), anche combinate "ns" vuol dire estendi in tutto
lo spazio verticale, "ew" in tutto quello orizzontale, "nsew"
tutto attorno:
self.frame1.grid(row=0, column=0, padx=10, pady=10)
self.frame2.grid(row=0, column=1, rowspan=2, padx=10, pady=10, sticky="ns")
self.frame3.grid(row=1, column=0, padx=10, pady=10)
Label e pulsanti
Due widget molto comuni in un'interfaccia grafica sono
le Label (per rappresentare del testo o delle piccole immagini) e
i pulsanti (Button) da cliccare col mouse (anche loro possono
mostrare delle piccole immagini al loro interno). Nel seguente
esempio si crea una finestra dall'aspetto più classico
usando tre frames orizzontali. Il primo in alto contiene due
pulsanti (nella loro dichiarazione si indica che sono figli di
frame1), quello al centro ha il colore di fondo bianco e una label
(chiamata label1) che serve per mostrare un'immagine (nella
dichiarazione della label è indicato che è figlia di
frame2). La label è posizionata con grid, quindi il frame
deve essere "bloccato" per non collassare attorno alla label
stessa (frame2.grid_propagate(0)). Inoltre affinché non
venga visualizzato un bordo attorno all'immagine mostrata dalla
label, il bordo della label stessa deve essere impostato a 0 (bd=0).
Infine il frame in basso (frame3) contiene solo una label
testuale. L'immagine visualizzata dalla
label1 nel frame2 appare nel frame centrale in quanto va ricordato che grid si applica
riferendosi all'oggetto genitore, la label1 è figlia di frame2,
come frame2 è figlio di form1, quindi grid applicato sulla label1
si riferisce all'area di frame2, mentre grid applicato a frame2 si
rifersice all'area di form1, cioè all'intera finestra).
try: import Tkinter as tk # Py2
except ImportError: import tkinter as tk # Py3
class Applicazione:
def __init__ (self):
self.form1 = tk.Tk()
self.form1.resizable(False, False)
# Frame alto con pulsanti ---------------------------------------------
self.frame1 = tk.Frame(self.form1, bd=1, relief="raised")
self.frame1.grid(row=0, column=0, sticky="we")
self.bottone1 = tk.Button(self.frame1, text="Bottone1")
self.bottone1.grid(row=0, column=0, padx=4, pady=4)
self.bottone2 = tk.Button(self.frame1, text="Bottone2")
self.bottone2.grid(row=0, column=1, pady=4)
# Frame centrale con immagine centrata --------------------------------
self.frame2 = tk.Frame(
self.form1, height=300, width=400,
bd=1, relief="raised", bg="white")
self.frame2.grid_propagate(False)
self.frame2.grid_rowconfigure(0, weight=2)
self.frame2.grid_columnconfigure(0, weight=2)
self.frame2.grid(row=1, column=0)
self.immagine1 = tk.PhotoImage(file="bissio.gif")
self.label1 = tk.Label(self.frame2, image=self.immagine1, bd=0)
self.label1.grid(row=0, column=0)
# Frame basso con label -----------------------------------------------
self.frame3 = tk.Frame(self.form1, bd=1, relief="raised")
self.frame3.grid(row=2, column=0, sticky="we")
self.label2 = tk.Label(self.frame3, text="Barra di stato")
self.label2.grid(row=0, column=0, padx=4, pady=4)
def start(self):
self.form1.mainloop()
app = Applicazione()
app.start()
Canvas
Il canvas è una superficie su cui creare oggetti grafici come
linee, poligoni, rettangoli, ovali, cerchi, testi, immagini ecc.
Ogni elemento grafico disegnato è un oggetto dotato di un identificatore
interno al canvas e di proprietà modificabili (posizione, colori ecc).
Il canvas di tk non è un'area bitmap dove lavorare bit per bit, se serve
lavorare con i singoli bit si può usare un oggetto PhotoImage.
try: import Tkinter as tk # Py2
except ImportError: import tkinter as tk # Py3
# Crea una finestra grafica 640x480
form1 = tk.Tk()
form1.resizable(False,False)
canvas1 = tk.Canvas(
form1, width=640, height=480,
bg="white", highlightthickness=0)
canvas1.grid()
form1.mainloop()
L'area grafica utile è pari alle dimensioni impostate con width e height (il punto in alto
a sinistra ha coordinate x e y pari a 0,0). Se non
si imposta highlightthickness a zero, viene automaticamente disegnato un
bordo di due pixel interno all'area grafica stessa.
I colori predefiniti sono "white", "black", "red", "green", "blue", "yellow", "cyan", "magenta"
(vedere anche
infohost.nmt.edu/tcc/help/pubs/tkinter/colors.html)
Tracciare una linea o più segmenti
(vedere anche
infohost.nmt.edu/tcc/help/pubs/tkinter/create_line.html):
canvas1.create_line(x1, y1, x2, y2)
- L'ultimo pixel della linea non viene disegnato.
- Con il parametro arrow="" si può aggiungere una freccia alle estremità della linea, arrow può essere "first", "last", "both".
- Il colore della riga è nero di default, si può cambiare con il parametro fill="colore"
- Con dash=1 la linea diventa tratteggiata.
- Con width si imposta lo spessore della linea (default 1).
Il plottaggio di un singolo pixel si può simulare tracciando una riga lunga 2 pixel
(di cui solo il primo viene disegnato).
Se si usa una variabile per identificare un oggetto grafico, questo può essere modificato a runtime:
identificatore = canvas1.create_line(2, 25, 101, 25, arrow="both", dash=1)
canvas1.itemconfigure(identificatore, fill="red", width=3)
canvas1.coords(identificatore, 2, 2, 101, 51)
Esempio di tracciamento funzione animato:
Cancellare tutti gli oggetti dal canvas
canvas1.delete("all")
Ottenere lista identificatori di tutti gli oggetti dal canvas
(gli id
sono numeri interi non necessariamente consecutivi):
lista_id = canvas1.find_all()
Esportare il canvas in formato postscript
canvas1.postscript(
colormode="color",
file="nomefile.ps",
rotate=False, x=2, y=2,
width=100, height=50)
(Visualizzatore postscript online:
view.samurajdata.se/)
Metodi PhotoImage
È possibile leggere e modificare i singoli pixel di un oggetto PhotoImage tramite
i metodi get e put. Put nella forma più semplice accetta un colore specificato con una stringa RGB e una
coordinata x,y, mentre nel caso più complesso accetta un'intera stringa di colori suddivisa
in righe e colonne formattata con dei delimitatori {}.
Esempio di creazione di una semplice immagine 8x8 ingrandita con un fattore specificato
in una costante (SCALE). L'immagine viene posizionata all'interno di un canvas e ne viene periodicamente
cambiato il colore agendo direttamente sui pixel.
(info tratte da:
tkinter.unpythonic.net/wiki/PhotoImage)
Funzioni temporizzate
È possibile chiamare funzioni a tempo (metodo after), gestire gli
eventi pendenti (metodo update_idletasks), aggiornare un widget (metodo update):
finestra.after(millisecondi, funzione) ESEMPIO
finestra.update_idletasks()
widget.update()
(info tratte da:
infohost.nmt.edu/tcc/help/pubs/tkinter/universal.html)
Rimuovere e riposizionare widget
Un widget può essere rimosso visualmente dalla finestra (senza distruggerlo) con il metodo forget.
Se ci si sono salvate preventivamente le informazioni di posizionamento è possibile rivisualizzarlo:
info = widget.grid.info()
widget.grid.forget()
widget.grid(info)
Può essere utile intercettare l'evento di chiusura per chiamare una funzione:
finestra.protocol("WM_DELETE_WINDOW", funzione) ESEMPIO
(info tratte da:
effbot.org/tkinterbook/tkinter-events-and-bindings.htm)
Intercettare evento di chiusura
Una finestra si può chiudere da programma chiamando il metodo destroy o cliccando sulla X di chiusura:
finestra.destroy()
Può essere utile intercettare l'evento di chiusura per chiamare una funzione:
finestra.protocol("WM_DELETE_WINDOW", funzione) ESEMPIO
(info tratte da:
effbot.org/tkinterbook/tkinter-events-and-bindings.htm)
↑