Python 2 - Grafica

Creare e gestire un bottone

Immagine slide
  • Per creare un bottone, dovremo semplicemente definirne il testo e la funzione di risposta, tramite il metodo add_button(<testo>, <funzione>) del frame, che nel nostro esempio si chiama finestra:
    finestra.add_button("Bottone", azione)
  • Abbiamo così creato il nostro bottone. Alla sua pressione verrà invocata la funzione azione, che potrebbe essere scritta così:
    def azione():
    print "Hai premuto il bottone"

Creare e gestire un bottone

Immagine slide
  • Mettendo tutto insieme con la creazione della finestra otteniamo il sorgente completo:
    import simplegui
    def azione():
    print "Hai premuto il bottone"
    finestra = simplegui.create_frame("Finestra", 50, 100)
    finestra.add_button("Bottone", azione)
    finestra.start()

Utilizzare un'Etichetta

Immagine slide
  • Per visualizzare dei dati possiamo utilizzare un'etichetta. Se vogliamo modificarla, potremo assegnarla ad una variabile.
  • Per creare un'etichetta utilizzeremo il metodo add_label(<testo>) di frame, sempre finestra nel nostro esempio:
    risultato = finestra.add_label("Saluto")
  • Per modificarne il testo potremo usare il metodo set_text(<nuovo_testo>) di label:
    risultato.set_text("Ciao " + nome)

Utilizzare un'Etichetta

Immagine slide
  • Se lo aggiungiamo al programma di prima otteniamo:
    import simplegui
    def azione(nome):
    risultato.set_text("Ciao " + nome)
    finestra = simplegui.create_frame("Finestra", 50, 150)
    finestra.add_input("Scrivi il tuo nome", azione, 50)
    risultato = finestra.add_label("Saluto")
    finestra.start()

Aggiungere un Campo

Immagine slide
  • Per ottenere dei dati dall'utente potremo aggiungere dei campi di testo.
  • Per creare un campo useremo il metodo add_input(<richiesta>, <azione>, <dimensione_in_pixel>) di frame, sempre finestra nel nostro esempio:
    finestra.add_input("Scrivi il tuo nome", azione, 50)
  • La funzione <azione> verrà richiamata quando premeremo invio nel nostro campo e riceverà come unico parametro il testo del campo:
    def azione(campo):
    print "Ciao", campo

Aggiungere un Campo

Immagine slide
  • Il programma completo è:
    import simplegui
    def azione(campo):
    print "Ciao", campo
    finestra = simplegui.create_frame("Finestra", 50, 100)
    finestra.add_input("Scrivi il tuo nome", azione, 50)
    finestra.start()

Operare con i Campi

Immagine slide
  • Proviamo ora ad operare con un campo: facciamo un programma che incrementi il valore contenuto in un campo, tramite un bottone.
  • Per prima cosa, al campo non dovrà essere associata nessuna azione. Non potendo omettere la funzione, creeremo una funzione che non fa nulla:
    def nonazione(testo):
    pass
  • Dobbiamo ora leggere nella funzione del bottone il valore del campo. Utilizzeremo il metodo get_text() di field:
    testo = campo.get_text()

Operare con i Campi

Immagine slide
  • Per modificare il testo nel campo faremo esattamente come per l'etichetta:
    campo.set_text(numero)

Operare con i Campi

Immagine slide
  • Vediamo il programma completo:
    import simplegui
    def azione():
    testo = campo.get_text()
    numero = int(testo)
    numero += 1
    campo.set_text(numero)
    def nonazione(testo):
    pass
    finestra = simplegui.create_frame("Finestra", 50, 150)
    campo = finestra.add_input("Scrivi un numero intero", nonazione, 50)
    finestra.add_button("Incrementa", azione)
    finestra.start()

Libreria simplegui

Immagine slide
  • La libreria simplegui crea una finestra standard sulla quale lavorare.
  • Nella parte destra compare un pannello, che potremo dimensionare a piacere, nel quale potremo disegnare.
  • Nella parte sinistra, in alto, troviamo l'area in cui verrà inserita l'interfaccia utente che scriveremo, con bottoni, etichette e campi
  • Per finire, nella parte sinistra, in basso troveremo due riquadri di debug, dove stanno visualizzati gli eventi della tastiera e del mouse

Creare la finestra

Immagine slide
  • Per utilizzare simplegui dovremo per prima cosa importarne la libreria:
    import simplegui
  • Dovremo ora creare una nuova finestra, con il metodo simplegui.create_frame(<titolo>, <larghezza>, <altezza> [,<larghezza_sinistra>) dove <larghezza _sinistra>, opzionale, indica la larghezza della parte sinistra della finestra:
    finestra = simplegui.create_frame("Prova", 300, 200, 10)

Creare la finestra

Immagine slide
  • Con questa istruzione, la finestra è creata, ma non viene attivata: non fa nessuna azione. Per far funzionare la finestra dovremo avviarla con:
    finestra.start()
  • Quindi il codice completo per creare ed attivare la finestra è il seguente:
    import simplegui
    finestra = simplegui.create_frame("Prova", 300, 200, 10)
    finestra.start()

Timer

Immagine slide
  • Sperimentiamo ora l'uso dei timer, scrivendo un piccolo cronometro.
  • Per prima cosa dovremo creare il timer: utilizzeremo la funzione simplegui.create_timer(<periodo_ms>, <azione>):
    timer = simplegui.create_timer(1000, secondo)
  • Il timer creato è fermo. Per avviarlo useremo il metodo start() di timer:
    timer.start()
  • Per fermarlo, il metodo stop() di timer:
    timer.stop()

Timer

Immagine slide
  • Per sapere se il timer è acceso useremo il metodo is_running() di timer:
    if timer.is_running():

Timer

Immagine slide
  • Il programma completo è:
    import simplegui

    cronometro = 0;

    def secondo():
    global cronometro
    cronometro += 1
    def avvia():
    global cronometro
    if not timer.is_running():
    timer.start()
    cronometro = 0
    print "Avviato"
    def ferma():
    if timer.is_running():
    timer.stop()
    print "Fermo:", cronometro
    def intertempo():
    print "Intertempo:", cronometro
    finestra = simplegui.create_frame("Finestra", 50, 150)
    timer = simplegui.create_timer(1000, secondo)
    finestra.add_button("Avvia", avvia)
    finestra.add_button("Ferma", ferma)
    finestra.add_button("Intertempo", intertempo)
    finestra.start()

Esercizio

Immagine slide
  • Creare un programma Python che visualizzi:
    • Un campo con richiesta "Scrivi il tuo nome"
    • Un bottone con titolo "Buongiorno"
    • Un bottone con titolo "Buonasera"
    • Un'etichetta vuota
  • Alla pressione del tasto invio non deve succedere nulla (il corpo della funzione sarà pass)
  • Alla pressione del tasto Buongiorno nell'etichetta dovrà comparire Buongiorno <nome>, dove <nome> è il contenuto del campo

Esercizio

Immagine slide
  • Alla pressione del tasto Buonasera nell'etichetta dovrà comparire Buonasera <nome>, dove <nome> è il contenuto del campo

Accenno alle eccezioni

Immagine slide
  • Il programma appena scritto ha un inconveniente
  • Dato che usa un cast int(<testo>) per convertire il dato inserito, se il dato non è un intero, lancia un'eccezione che chiude la finestra
  • Ovviamente dovremo gestirla tramite un blocco try-catch (try-except in Python):
    try:
    numero = int(testo)
    except ValueError:
    numero = 0
  • Nell'istruzione except abbiamo indicato ValueError, che è l'eccezione lanciata, vista nell'errore

Accenno alle eccezioni

Immagine slide
  • Possiamo avere più clausole except per diverse eccezioni.
  • Possiamo anche avere più eccezioni gestite da una sola except, indicando un set di eccezioni, racchiuso tra parentesi
  • Possiamo intercettare qualsiasi eccezione utilizzando Error nell'istruzione except

Accenno alle eccezioni

Immagine slide
  • Ecco il codice completo:
    import simplegui
    def azione():
    testo = campo.get_text()
    try:
    numero = int(testo)
    except ValueError:
    numero = 0
    numero += 1
    campo.set_text(numero)
    def nonazione(testo):
    pass
    finestra = simplegui.create_frame("Finestra", 50, 150)
    campo = finestra.add_input("Scrivi un numero intero", nonazione, 50)
    finestra.add_button("Incrementa", azione)
    finestra.start()

Testo

Immagine slide
  • Come prima prova, proviamo a scrivere del testo nel nostro pannello.
  • Per farlo utilizzeremo il metodo draw_text(<testo>, (<x>, <y>), <dimensione>, <colore> [, <font>]) di canvas, nel nostro esempio pannello:
    pannello.draw_text("Testo", (10, 60), 60, "Red", "sans-serif")
  • <font> può essere solo "serif", "sans-serif" o "monospace"

Testo

Immagine slide
  • L'esempio completo è:
    import simplegui
    def disegno (pannello):
    pannello.draw_text("Testo", (10, 60), 60, "Red", "sans-serif")
    finestra = simplegui.create_frame("Prova", 300, 200, 5)
    finestra.set_canvas_background('#ffffcc')
    finestra.set_draw_handler(disegno)
    finestra.start()

Linee e forme

Immagine slide
  • Elencherò brevemente i metodi per disegnare sul pannello.
  • Per disegnare un singolo pixel si usa il metodo draw_point((<x>,<y>), <colore>) di canvas:
    pannello.draw_point((120,85), "White")
  • Per disegnare un segmento si usa il metodo draw_line((<x1>,<y1>), (<x2>,<y2>), <spessore>, <colore>) di canvas:
    pannello.draw_line((70,50), (220,30), 3, "RED")

Linee e forme

Immagine slide
  • Si può disegnare una serie di linee connesse con il metodo draw_polyline([(<x1>,<y1>),(<x2>,<y2>)[,(<xn>,<yn>)...]], <spessore>, <colore>) di canvas:
    pannello.draw_polyline([(130,120),(150,80),(170,120)],2, "Black")
  • Si può disegnare un poligono tramite il metodo draw_polygon([(<x1>,<y1>),(<x2>,<y2>)[,(<xn>,<yn>)...]], <spessore_contorno>, <colore_contorno>, [<colore_riempimento>]) di canvas:
    pannello.draw_polygon([(110,130), (150,150), (190,130)], 2, "Black", "Blue")

Linee e forme

Immagine slide
  • Per finire possiamo disegnare cerchi con il metodo draw_circle((<x_centro>,<y_centro>), <raggio>, <spessore>, <colore_circonferenza>, <colore_riempimento>) di canvas:
    pannello.draw_circle((180,85), 20, 2, "Black", "Blue")
  • Non ci sono metodi per disegnare ellissi o archi di cerchio.
  • Un rettangolo è un poligono

Linee e forme

Immagine slide
  • Un esempio completo è:
    import simplegui
    def disegno (pannello):
    pannello.draw_line((70,50), (220,30), 3, "RED")
    pannello.draw_polyline([(130,120),(150,80),(170,120)],2, "Black")
    pannello.draw_polygon([(110,130), (150,150), (190,130)], 2, "Black", "Blue")
    pannello.draw_circle((120,85), 20, 2, "Black", "Blue")
    pannello.draw_point((120,85), "White")
    pannello.draw_circle((180,85), 20, 2, "Black", "Blue")
    pannello.draw_point((180,85), "White")
    pannello.draw_circle((150,100),80, 2, "Black")
    finestra = simplegui.create_frame("Prova", 300, 200, 5)
    finestra.set_canvas_background('#ffffcc')
    finestra.set_draw_handler(disegno)
    finestra.start()

Suoni

Immagine slide
  • Potremmo ora aggiungere un suono al rimbalzo.
  • Caricare un suono è facile come caricare un'immagine: su utilizza il metodo load_sound(<url>) di simplegui:
    bump=simplegui.load_sound("http://www.salvi.mn.it/dati/rimbalzo.mp3")
  • Per avviare la riproduzione si utilizza il metodo play() di sound:
    bump.play()
  • Per sospendere la riproduzione si utilizza il metodo pause() di sound (non l'ho utilizzato nell'esempio)

Suoni

Immagine slide
  • Per fermare la riproduzione e reimpostare all'inizio il suono si utilizza il metodo rewind() di sound:
    bump.rewind()

Suoni

Immagine slide
  • Ecco l'esempio di programma:
    import simplegui
    import random
    larghezza = 300
    altezza = 200
    palla = simplegui.load_image("http://www.salvi.mn.it/dati/palla.png")
    bump=simplegui.load_sound("http://www.salvi.mn.it/dati/rimbalzo.mp3")
    dim_palla = (palla.get_width(), palla.get_height())
    raggio = dim_palla[0] / 2
    x = larghezza / 2
    y = altezza / 2
    vx = random.randrange(1,5)
    vy = random.randrange(1,5)
    def disegno (pannello):
    global x, y, vx, vy
    pannello.draw_image(palla, (raggio, raggio), dim_palla, (x,y), dim_palla)
    x += vx
    y += vy
    if x < raggio or x > larghezza - raggio:
    bump.rewind()
    bump.play()
    vx = -vx
    if y < raggio or y > altezza - raggio:
    bump.rewind()
    bump.play()
    vy = -vy
    finestra = simplegui.create_frame("Prova", larghezza, altezza, 5)
    finestra.set_canvas_background('#000080')
    finestra.set_draw_handler(disegno)
    finestra.start()

Esercizio

Immagine slide
  • Disegnare un campo stellato con la luna in alto a sinistra
  • Per prima cosa, la luna sarà un cerchio posizionato in alto a sinistra, di colore #ffffcc
  • In secondo luogo le stelle saranno dei punti bianchi disposti casualmente
  • Potete scegliere un numero di stelle tra 50 e 150 (magari configurabile)
  • Le stelle devono essere ferme

Immagini

Immagine slide
  • La prima operazione da fare è caricare l'immagine dalla rete con il metodo load_image(<url>) di simplegui:
    palla = simplegui.load_image("http://www.salvi.mn.it/dati/palla.png")
  • Ora dovremo disegnare l'immagine sul nostro pannello. Purtroppo il metodo di disegno è piuttosto complesso.
  • Il metodo draw_image(<immagine>, (<x_centro_img>, <y_centro_img>), (<larg_img>, <alt_img>), (<x_dest>, <y_dest>), (<larg_dest>, <alt_dest>)[, <rotazione_radianti>]) di canvas serve a disegnare un'immagine, ad esempio:
    pannello.draw_image(palla, (raggio, raggio), dim_palla, (x,y), dim_palla)

Immagini

Immagine slide
  • Questo metodo consente di copiare l'immagine sul pannello, per intero o in parte, eventualmente ridimensionandola e ruotandola.
  • (<larg_img>, <alt_img>) indicano la dimensione dell'area copiata. Se sono più piccoli della dimensione dell'immagine, solo una parte verrà copiata.
  • (<x_centro_img>, <y_centro_img>) indicano il centro dell'area copiata. Se si copia l'intera immagine, dovranno corrispondere al centro effettivo dell'immagine. Se si sta copiando solo un pezzo, indicheranno il centro del pezzo copiato, riferito all'intera immagine

Immagini

Immagine slide
  • Se (<larg_dest>, <alt_dest>) non corrispondono a (<larg_img>, <alt_img>) l'immagine o pezzo di immagine verrà allargato o ridotto ed eventualmente deformato, se il rapporto non corrisponde

Immagini

Immagine slide
  • Un esempio completo è:
    import simplegui
    import random
    larghezza = 300
    altezza = 200
    palla = simplegui.load_image("http://www.salvi.mn.it/dati/palla.png")
    dim_palla = (palla.get_width(), palla.get_height())
    raggio = dim_palla[0] / 2
    x = larghezza / 2
    y = altezza / 2
    vx = random.randrange(1,5)
    vy = random.randrange(1,5)
    def disegno (pannello):
    global x, y, vx, vy
    pannello.draw_image(palla, (raggio, raggio), dim_palla, (x,y), dim_palla)
    x += vx
    y += vy
    if x < raggio or x > larghezza - raggio:
    vx = -vx
    if y < raggio or y > altezza - raggio:
    vy = -vy
    finestra = simplegui.create_frame("Prova", larghezza, altezza, 5)
    finestra.set_canvas_background('#000080')
    finestra.set_draw_handler(disegno)
    finestra.start()

Canvas e disegno

Immagine slide
  • Se vogliamo, possiamo cambiare lo sfondo del pannello di disegno con il metodo set_canvas_background(<colore>) di frame, nel nostro caso finestra, dove <colore> è un colore in formato CSS o una costante predefinita:
    finestra.set_canvas_background('#ffffcc')
  • Se però vogliamo disegnare nel pannello, non possiamo farlo direttamente, ma dobbiamo creare una funzione di disegno che verrà chiamata dal sistema a tempo debito.
  • Dobbiamo poi associare questa funzione alla finestra con il metodo add_draw_handler(<funzione_di_disegno>) di frame, nel nostro caso finestra:
    finestra.set_draw_handler(disegno)

Canvas e disegno

Immagine slide
  • La funzione di disegno deve avere un parametro, che rappresenterà il pannello e che useremo per disegnare; per ora preparo una funzione vuota:
    def disegno (pannello):
    pass

Canvas e disegno

Immagine slide
  • Il programma completo è:
    import simplegui
    def disegno (pannello):
    pass
    finestra = simplegui.create_frame("Prova", 300, 200, 5)
    finestra.set_canvas_background('#ffffcc')
    finestra.set_draw_handler(disegno)
    finestra.start()

Input da tastiera

Immagine slide
  • Anche per la gestione della tastiera dobbiamo predisporre delle funzioni di gestione dell'evento (handler)
  • In particolare, per la tastiera ne abbiamo due, uno per la pressione di un tasto l'altro per il rilascio.
  • Per installare il gestore della pressione, useremo il metodo set_keydown_handler(<gestore>) di frame:
    finestra.set_keydown_handler(premuto)
  • Mentre per installare il gestore di rilascio useremo il metodo set_keyup_handler(<gestore>) di frame:
    finestra.set_keyup_handler(rilasciato)

Input da tastiera

Immagine slide
  • In entrambi i gestori abbiamo un parametro che riporta il codice del tasto:
    def rilasciato(key):
    global movimento
    if key == simplegui.KEY_MAP['up'] or key == simplegui.KEY_MAP['down']:
    movimento = 0
  • Per conoscere i codici dei tasti si utilizza la lista KEY_MAP[<nome>] di simplegui. Per conoscerne i nomi si veda http://www.codeskulptor.org/docs.html#Keys

Input da tastiera

Immagine slide
  • Un esempio completo è:
    import simplegui
    import random
    larghezza = 300
    altezza = 200
    racc_sopra = 70
    racc_sotto = 130
    movimento = 0
    def disegno (pannello):
    global movimento, racc_sopra, racc_sotto
    if movimento > 0:
    if racc_sotto < altezza:
    racc_sopra +=1
    racc_sotto += 1
    else:
    movimento = 0
    elif movimento < 0:
    if racc_sopra > 0:
    racc_sopra -= 1
    racc_sotto -= 1
    else:
    movimento = 0
    pannello.draw_line((300, racc_sopra), (300, racc_sotto), 5, "Red")
    def premuto(key):
    global movimento
    if key == simplegui.KEY_MAP['up']:
    movimento = -1
    elif key == simplegui.KEY_MAP['down']:
    movimento = 1
    def rilasciato(key):
    global movimento
    if key == simplegui.KEY_MAP['up'] or key == simplegui.KEY_MAP['down']:
    movimento = 0
    finestra = simplegui.create_frame("Prova", larghezza, altezza)
    finestra.set_canvas_background('#000080')
    finestra.set_draw_handler(disegno)
    finestra.set_keydown_handler(premuto)
    finestra.set_keyup_handler(rilasciato)
    finestra.start()

Input da tastiera

Immagine slide
  • ESERCIZIO: combinare il movimento della racchetta nell'esercizio di prima, aggiungendo anche un conteggio dei rimbalzi sulla racchetta e delle palle perse

Animazioni

Immagine slide
  • Come avrete notato, la funzione di disegno viene chiamata frequentemente, quindi non c'è un modo per forzare un ridisegno e non occorre usare timer.
  • Occorre invece prevedere delle variabili globali di stato ed aggiornarle nella funzione di ridisegno. Con qualche esperimento si potrà trovare l'incremento opportuno in base alla frequenza del ridisego.
  • Potremmo ad esempio facilmente creare una pallina che rimbalzi sui bordi del pannello, partendo con un'angolazione casuale.
  • Utilizzeremo quattro variabili di stato x, y, vx e vy.

Animazioni

Immagine slide
  • La nostra pallina sarà un cerchio.

Animazioni

Immagine slide
  • Un esempio sarà il seguente:
    import simplegui
    import random
    larghezza = 300
    altezza = 200
    raggio = 15
    x = larghezza / 2
    y = altezza / 2
    vx = random.randrange(1,5)
    vy = random.randrange(1,5)
    def disegno (pannello):
    global x, y, vx, vy
    pannello.draw_circle((x,y), raggio, 2, "Yellow", "#ffffc0")
    x += vx
    y += vy
    if x < raggio or x > larghezza - raggio:
    vx = -vx
    if y < raggio or y > altezza - raggio:
    vy = -vy
    finestra = simplegui.create_frame("Prova", larghezza, altezza, 5)
    finestra.set_canvas_background('#000080')
    finestra.set_draw_handler(disegno)
    finestra.start()

Animazioni

Immagine slide
  • Se vogliamo avere un controllo migliore sulla velocità, ed avere velocità inferiori, possiamo includere il modulo time ed utilizzare la funzione time.time() che ritorna un tempo in secondi, ma con i decimali,

Esercizio

Immagine slide
  • Partire dall'esempio della palla che rimbalza
  • Disegnare una riga rossa verticale, sul bordo destro del pannello, spessa 5 pixel, che vada da 70 a 130 pixel dal bordo superiore.
  • Se la palla tocca la linea rossa (racchetta) deve rimbalzare
  • Se tocca il bordo del pannello deve venire rigenerata al centro con velocità casuali

Input da Mouse

Immagine slide
  • Anche per il mouse abbiamo due gestori di evento da scrivere.
  • Il primo, che viene chiamato quando il bottone del mouse viene premuto ed ogni volta che si sposta, mentre è premuto, si installa con il metodo set_mousedrag_handler(<gestore>) di frame:
    finestra.set_mousedrag_handler(trascinamento)
  • Il secondo viene richiamato quando il bottone del mouse viene rilasciato e si installa con il metodo set_mouseclick_handler(<gestore>) di frame:
    finestra.set_mouseclick_handler(pressione)

Input da Mouse

Immagine slide
  • Entrambi i metodi hanno come parametro una tupla con le coordinate del mouse:
    def pressione(posizione):
    global centro, lavorando
    if not lavorando:
    centro = posizione
    else:
    lavorando = False
  • L'esempio che faremo, crea un cerchio centrato nella posizione dove si preme il bottone del mouse e con il bordo dove lo si rilascia

Input da Mouse

Immagine slide
  • Per questo ci servirà la funzione sqrt(numero>) di math per calcoare il raggio:
    raggio = math.sqrt((centro[0]-posizione[0])**2 + (centro[1]-posizione[1])**2)

Input da Mouse

Immagine slide
  • L'esempio completo è:
    import simplegui
    import math
    larghezza = 300
    altezza = 200
    lavorando = False
    centro=None
    raggio=0
    movimento = 0
    def disegno (pannello):
    if centro != None and raggio != 0:
    pannello.draw_circle(centro, raggio, 1, "Red", "Red")
    def pressione(posizione):
    global centro, lavorando
    if not lavorando:
    centro = posizione
    else:
    lavorando = False
    def trascinamento(posizione):
    global raggio, centro, lavorando
    if not lavorando:
    centro = (posizione[0], posizione[1])
    lavorando = True
    raggio = math.sqrt((centro[0]-posizione[0])**2 + (centro[1]-posizione[1])**2)
    finestra = simplegui.create_frame("Prova", larghezza, altezza)
    finestra.set_canvas_background('#000080')
    finestra.set_draw_handler(disegno)
    finestra.set_mouseclick_handler(pressione)
    finestra.set_mousedrag_handler(trascinamento)
    finestra.start()

Input da Mouse

Immagine slide
  • ESERCIZIO: fare in modo che vengano memorizzati i cerchi fatti e nel pannello compaiano tutti
  • PER I PIÙ BRAVI: aggiungere all'esercizio precedente anche l'opzione di cancellare un cerchio fatto cliccandoci sopra
  • PER I SUPERLATIVI: Aggiungere un bottone che cancella tutti i cerchi memorizzati
[any material that should appear in print but not on the slide]