Diálogos, notificaciones y validación
Una buena app le cuenta al usuario qué pasa: qué está mal en el formulario, qué está a punto de pasar, qué acaba de pasar. Pyzed tiene una pieza lista para cada cosa: errores de campo, diálogos y toasts (pequeñas notificaciones en una esquina).
| Objetivo | Tiempo | Nivel | Necesitas |
|---|---|---|---|
| Un formulario de registro con errores, una confirmación y notificaciones | 30 minutos | Intermedio · Python | Listas y tablas |
Qué vas a aprender
Sección titulada «Qué vas a aprender»- Calcular los errores de validación a partir de tu estado y mostrarlos con
ui.field(..., error=...). - Mostrarlos solo después del primer intento, no mientras el usuario todavía escribe.
- Abrir un diálogo de confirmación sobre la página con
ui.dialog_layeryui.confirm. - Mostrar toasts con
ui.toastsy quitarlos a los pocos segundos sin congelar la ventana.

-
Los errores son una función del estado. Un método devuelve
{campo: mensaje}con lo que esté mal ahora mismo:def errors(self):found = {}if not self.name.strip():found["name"] = "Tell us your name."if "@" not in self.email:found["email"] = "Enter an email such as ada@example.com."if len(self.password) < 8:found["password"] = "At least 8 characters."return found -
Muéstralos bajo cada campo, pero solo cuando el usuario ya intentó enviar (
self.tried). A nadie le gusta ver texto rojo antes de tener la oportunidad:errors = self.errors() if self.tried else {}ui.field("Email", ui.input(self.email, lambda v: setattr(self, "email", v)), error=errors.get("email")) -
Un diálogo de confirmación.
ui.dialog_layer(page, dialog)dibujadialogcentrado sobre la página oscurecida; condialog=Nonese ve solo la página. Así que “¿está abierto el diálogo?” es un atributo:dialog = Noneif self.confirming:dialog = ui.confirm(f"Create an account for {self.email}?", self.create,lambda: setattr(self, "confirming", False), title="Almost there",confirm_label="Create")page = ui.dialog_layer(page, dialog, on_dismiss=lambda: setattr(self, "confirming", False))
ui.confirm sobre la página; hacer clic fuera (on_dismiss) cancela. -
Toasts. Guarda una lista de avisos, dibújalos con
ui.toastsy quita cada uno a los 4 segundos.Command.performespera en un hilo en segundo plano, así que la ventana sigue respondiendo:def notify(self, kind, title, text=""):note_id = next(_ids)self.notes.append((note_id, kind, title, text))return pz.Command.perform(lambda: time.sleep(4), lambda _: self.dismiss(note_id))# en view():return ui.toasts(page, [ui.toast(title, text, kind=kind, on_dismiss=lambda i=i: self.dismiss(i))for i, kind, title, text in self.notes]) -
Júntalo todo. El botón marca
tried, muestra un toast de error si algo está mal o abre el diálogo; confirmar vacía el formulario y muestra un toast de éxito.
Código completo
Sección titulada «Código completo»"""Talking to the user: validation messages, a confirmation dialog and toast notifications.
python examples/tutorial/feedback.py
- ``ui.field(..., error=...)`` shows a message under a field; the errors are computed from the state.- ``ui.dialog_layer(page, dialog)`` puts a dialog (``ui.confirm``, ``ui.alert``...) over the page; ``dialog`` is ``None`` when nothing is open.- ``ui.toasts(page, [...])`` stacks notifications in the corner; ``Command.perform`` waits a few seconds in the background and then removes them.
Walkthrough: https://albakore.github.io/pyzed/tutorials/intermediate/dialogs-feedback/"""
import itertoolsimport time
import pyzed as pzfrom pyzed import ui
_ids = itertools.count()
class Signup(pz.App): title = "Create an account" size = (640, 560)
def __init__(self): self.name = "" self.email = "" self.password = "" self.tried = False # show errors only after the first try self.confirming = False self.notes = [] # (id, kind, title, text) of the visible toasts
# -- validation ----------------------------------------------------------------------- def errors(self): found = {} if not self.name.strip(): found["name"] = "Tell us your name." if "@" not in self.email or "." not in self.email.split("@")[-1]: found["email"] = "Enter an email such as ada@example.com." if len(self.password) < 8: found["password"] = "At least 8 characters." return found
# -- toasts --------------------------------------------------------------------------- def notify(self, kind, title, text=""): note_id = next(_ids) self.notes.append((note_id, kind, title, text)) # wait 4 seconds in a background thread, then remove it (the window never freezes) return pz.Command.perform(lambda: time.sleep(4), lambda _: self.dismiss(note_id))
def dismiss(self, note_id): self.notes = [n for n in self.notes if n[0] != note_id]
# -- actions -------------------------------------------------------------------------- def submit(self): self.tried = True if self.errors(): return self.notify("error", "Check the form", "Some fields need your attention.") self.confirming = True # open the dialog
def create(self): self.confirming = False name = self.name self.name = self.email = self.password = "" self.tried = False return self.notify("success", "Account created", f"Welcome, {name}!")
# -- view ----------------------------------------------------------------------------- def view(self): t = self.resolved_theme() errors = self.errors() if self.tried else {} form = pz.column([ ui.heading("Create an account", level=2), ui.muted("Leave a field empty and press the button to see the messages."), ui.field("Name", ui.input(self.name, lambda v: setattr(self, "name", v), placeholder="Ada Lovelace"), error=errors.get("name")), ui.field("Email", ui.input(self.email, lambda v: setattr(self, "email", v), icon="mail", placeholder="ada@example.com"), error=errors.get("email")), ui.field("Password", ui.input(self.password, lambda v: setattr(self, "password", v), secure=True, on_submit=self.submit), error=errors.get("password"), description="8 characters or more"), pz.row([pz.hspace(), ui.button("Create account", self.submit, variant="solid", icon="user-plus")]), ], spacing=14, padding=28, width="fill") page = pz.container(form, width="fill", height="fill", bg=t.background)
dialog = None if self.confirming: dialog = ui.confirm(f"Create an account for {self.email}?", self.create, lambda: setattr(self, "confirming", False), title="Almost there", confirm_label="Create") page = ui.dialog_layer(page, dialog, on_dismiss=lambda: setattr(self, "confirming", False)) return ui.toasts(page, [ui.toast(title, text, kind=kind, on_dismiss=lambda i=i: self.dismiss(i)) for i, kind, title, text in self.notes])
if __name__ == "__main__": pz.run(Signup)uv run --with pyzed-ui python examples/tutorial/feedback.pypython3 -m venv .venvsource .venv/bin/activatepython -m pip install pyzed-uipython examples/tutorial/feedback.pypy -m venv .venv.venv\Scripts\Activate.ps1python -m pip install pyzed-uipython examples\tutorial\feedback.pypython3 examples/tutorial/feedback.pypy examples\tutorial\feedback.pyEventos de este tutorial
Sección titulada «Eventos de este tutorial»| Widget | Evento | Handler |
|---|---|---|
ui.input (tres campos) |
on_input |
lambda v: setattr(self, "name", v)… |
ui.input (contraseña) |
on_submit |
self.submit |
| Create account | on_press |
self.submit |
ui.confirm |
on_confirm, on_cancel |
self.create, cerrar el diálogo |
ui.dialog_layer |
on_dismiss |
cerrar el diálogo (un clic en el fondo) |
ui.toast |
on_dismiss |
lambda i=i: self.dismiss(i) |
Prueba más eventos
Sección titulada «Prueba más eventos»def on_key(self, combo): if combo == "escape" and self.confirming: # Escape cierra el diálogo self.confirming = False return [] # atendida: la tecla no sigue
def view(self): ... toasts = [ui.toast(title, text, kind=kind, on_dismiss=lambda i=i: self.dismiss(i), on_press=lambda i=i: self.dismiss(i)) # un clic en la tarjeta también lo cierra for i, kind, title, text in self.notes]Siguiente
Sección titulada «Siguiente»- Un componente propio en la vista de componente del diseñador.
- Tareas en segundo plano: barras de progreso y toasts desde hilos.