Lezione 4: Eventi e stato — Rendere l'app interattiva
Obiettivi
Section titled “Obiettivi”- Capire cosa sono gli eventi in Flet
- Gestire click, cambiamenti di testo e altri eventi
- Aggiornare la UI dopo un evento con
page.update() - Usare
page.controlsper gestire dinamicamente i componenti
1. Il concetto di evento
Section titled “1. Il concetto di evento”In un’applicazione desktop/mobile, il programma reagisce a ciò che fa l’utente:
- Clicca un bottone → esegui una funzione
- Scrive in un campo → aggiorna un’altra parte della UI
- Seleziona un’opzione → mostra/nasconde elementi
Questo si chiama programmazione a eventi (event-driven).
La struttura base
Section titled “La struttura base”def nome_funzione(e): # 'e' contiene informazioni sull'evento # fai qualcosa... page.update() # <-- Fondamentale!Il parametro e (evento) contiene informazioni utili come:
e.control— il componente che ha generato l’eventoe.control.value— il valore corrente del componentee.data— dati aggiuntivi dell’evento
2. Evento on_click sui bottoni
Section titled “2. Evento on_click sui bottoni”Esempio 1: gestore esterno
Section titled “Esempio 1: gestore esterno”import flet as ft
def main(page: ft.Page): def bottone_cliccato(e): print("Hai cliccato il bottone!") page.add(ft.Text("Bottone cliccato!", color="green"))
page.add( ft.ElevatedButton("Cliccami", on_click=bottone_cliccato) )
ft.app(target=main)Esempio 2: gestore inline con lambda
Section titled “Esempio 2: gestore inline con lambda”ft.ElevatedButton( "Cliccami", on_click=lambda e: page.add(ft.Text("Click!")))Nota:
lambdaè utile per operazioni semplici. Per logiche più complesse, usa una funzione separata.
3. Stato locale: ricordare i valori tra gli eventi
Section titled “3. Stato locale: ricordare i valori tra gli eventi”Lo stato è l’insieme dei dati che la nostra app ricorda tra un evento e l’altro.
Variabili normali
Section titled “Variabili normali”import flet as ft
def main(page: ft.Page): contatore = 0 # <-- Stato (variabile nella funzione)
def incrementa(e): nonlocal contatore # Serve per modificare la variabile contatore += 1 testo_contatore.value = f"Hai cliccato {contatore} volte" page.update()
testo_contatore = ft.Text("Hai cliccato 0 volte", size=20) bottone = ft.ElevatedButton("+1", on_click=incrementa)
page.add(testo_contatore, bottone)
ft.app(target=main)Importante:
nonlocalserve perchécontatoreè definita nella funzionemaine la modifichiamo dentroincrementa. Senzanonlocal, Python creerebbe una nuova variabile locale.
Usare attributi di oggetti (alternativa più pulita)
Section titled “Usare attributi di oggetti (alternativa più pulita)”import flet as ft
class ContatoreApp: def __init__(self, page: ft.Page): self.page = page self.contatore = 0 self.page.title = "Contatore" self.page.padding = 30
self.testo = ft.Text("0", size=50, weight="bold") self.page.add( self.testo, ft.Row([ ft.IconButton(ft.icons.REMOVE, on_click=self.decrementa), ft.IconButton(ft.icons.ADD, on_click=self.incrementa), ], alignment=ft.MainAxisAlignment.CENTER), )
def incrementa(self, e): self.contatore += 1 self.testo.value = str(self.contatore) self.page.update()
def decrementa(self, e): self.contatore -= 1 self.testo.value = str(self.contatore) self.page.update()
ft.app(target=ContatoreApp)Per il corso useremo principalmente il primo approccio (variabili +
nonlocal), che è più semplice e non richiede classi.
4. page.update() — La chiamata fondamentale
Section titled “4. page.update() — La chiamata fondamentale”page.update() dice a Flet di ridisegnare la pagina con i nuovi valori. Senza questa chiamata, le modifiche ai componenti non si vedono!
# ERRORE: il testo non viene aggiornato!def incrementa(e): contatore += 1 testo.value = str(contatore) # Modifica, ma nessun aggiornamento visivo
# CORRETTOdef incrementa(e): contatore += 1 testo.value = str(contatore) page.update() # Ora la UI si aggiorna!Regola d’oro: Dopo aver modificato qualsiasi proprietà di un componente (
value,color,visible, ecc.), chiama semprepage.update().
5. page.controls: gestire la lista dei componenti
Section titled “5. page.controls: gestire la lista dei componenti”page.controls è una lista di tutti i componenti nella pagina. Possiamo modificarla dinamicamente.
Aggiungere componenti dopo l’avvio
Section titled “Aggiungere componenti dopo l’avvio”import flet as ft
def main(page: ft.Page): page.title = "Aggiungi elementi" page.padding = 30
def aggiungi(e): if input_nome.value: # Aggiunge un nuovo testo alla lista page.add(ft.Text(f"Ciao, {input_nome.value}!", color="blue")) input_nome.value = "" # Svuota il campo page.update()
input_nome = ft.TextField(label="Nome", hint_text="Inserisci un nome") bottone = ft.ElevatedButton("Aggiungi", on_click=aggiungi)
page.add(input_nome, bottone)
ft.app(target=main)Rimuovere componenti
Section titled “Rimuovere componenti”def rimuovi_ultimo(e): if page.controls: # Se ci sono componenti page.controls.pop() # Rimuove l'ultimo page.update()Svuotare e ricostruire
Section titled “Svuotare e ricostruire”def cancella_tutto(e): page.controls.clear() page.add(ft.Text("Lista svuotata!")) page.update()6. Esempio guidato: Lista della spesa dinamica
Section titled “6. Esempio guidato: Lista della spesa dinamica”Mettiamo insieme tutto: eventi, stato, e page.controls:
import flet as ft
def main(page: ft.Page): page.title = "Lista della Spesa" page.padding = 30 page.scroll = ft.ScrollMode.AUTO
# Stato elementi = [] # Lista degli elementi
# Titolo page.add( ft.Text("🛒 Lista della Spesa", size=28, weight="bold"), ft.Text("Aggiungi gli elementi che ti servono", color="grey"), ft.Divider(), )
# Funzioni def aggiungi(e): if input_elemento.value and input_elemento.value.strip(): elemento = input_elemento.value.strip() elementi.append(elemento)
# Aggiunge un testo alla UI page.add( ft.Container( content=ft.Text(f"• {elemento}", size=16), padding=ft.padding.symmetric(vertical=5, horizontal=10), bgcolor="#f9f9f9", border_radius=5, ) )
input_elemento.value = "" input_elemento.focus() # Riporta il focus sul campo page.update()
def cancella_tutto(e): elementi.clear() # Ricostruisce la UI: tiene solo i primi 3 elementi page.controls = page.controls[:3] page.add(ft.Text("Lista svuotata!", color="red", italic=True)) page.update()
# Input e bottoni input_elemento = ft.TextField( label="Nuovo elemento", hint_text="cosa ti serve?", prefix_icon=ft.icons.SHOPPING_CART, on_submit=aggiungi, # Invio come click width=300, )
page.add( ft.Row([input_elemento, ft.ElevatedButton("Aggiungi", on_click=aggiungi)]), ft.Row([ ft.OutlinedButton("Svuota lista", on_click=cancella_tutto, icon=ft.icons.DELETE), ]), ft.Divider(), )
ft.app(target=main)7. Eventi utili dei componenti
Section titled “7. Eventi utili dei componenti”| Componente | Evento | Quando si attiva |
|---|---|---|
ElevatedButton | on_click | Click del bottone |
IconButton | on_click | Click dell’icona |
TextButton | on_click | Click del testo |
TextField | on_change | L’utente digita |
TextField | on_submit | L’utente preme Invio |
Checkbox | on_change | Cambia lo stato |
Switch | on_change | Cambia lo stato |
Dropdown | on_change | Cambia selezione |
Slider | on_change | Cambia valore |
8. Tabella riassuntiva
Section titled “8. Tabella riassuntiva”| Concetto | Codice | Spiegazione |
|---|---|---|
| Evento base | on_click=mia_funzione | Collega un evento a una funzione |
| Parametro evento | def f(e): | Riceve l’evento |
| Componente origine | e.control | Il componente che ha scatenato l’evento |
| Valore corrente | e.control.value | Valore del componente |
| Aggiornamento UI | page.update() | Ridisegna la pagina |
| Aggiungi componente | page.add(componente) | Dopo l’avvio |
| Rimuovi componente | page.controls.pop() | Rimuove l’ultimo |
| Stato con nonlocal | nonlocal x | Modifica variabile esterna |
9. Esercizio autonomo
Section titled “9. Esercizio autonomo”🎯 Esercizio: “Convertitore di temperature”
Section titled “🎯 Esercizio: “Convertitore di temperature””Crea un file convertitore.py che realizzi un convertitore Celsius ↔ Fahrenheit:
- Un titolo “Convertitore di Temperature”
- Un campo di input per inserire la temperatura
- Due bottoni:
- ”→ Fahrenheit”: converte da Celsius a Fahrenheit
- ”→ Celsius”: converte da Fahrenheit a Celsius
- Un testo che mostra il risultato della conversione
- Reset automatico: quando l’utente modifica il valore, il risultato precedente scompare
Formule
Section titled “Formule”- Celsius → Fahrenheit:
F = (C × 9/5) + 32 - Fahrenheit → Celsius:
C = (F - 32) × 5/9
Suggerimenti
Section titled “Suggerimenti”- Usa
float(input.value)per convertire il testo in numero - Gestisci l’errore se l’input non è un numero valido (usa
try/except) - Per il reset automatico, usa
on_changesul TextField - Arrotonda il risultato con
round(valore, 1)
Output atteso
Section titled “Output atteso”┌─────────────────────────────────┐│ Convertitore di Temperature ││ ││ [Inserisci temperatura] ││ ││ [→ Fahrenheit] [→ Celsius] ││ ││ Risultato: 25°C = 77.0°F │└─────────────────────────────────┘