Componentes propios
Un componente es una clase que agrupa widgets con su lógica, como un componente de React:
- Props: atributos de clase anotados, con su valor por defecto. El padre los pasa en cada render.
- Estado: cualquier otro atributo que asignes. Se conserva entre renders.
render()devuelve los widgets del componente.eventsdeclara los eventos propios que lanza conself.emit(nombre, valor).
import pyzed as pz
class Rating(pz.Component): """Estrellas que se pueden pulsar."""
value: int = 0 # props max: int = 5 events = {"change": "Se eligió una estrella (event.value es la puntuación)."} icon = "star" # cómo aparece en la biblioteca del diseñador
def setup(self): # una vez, antes del primer render self.hover = None # estado
def render(self): visibles = self.hover or self.value return pz.row([ pz.text("★" if i <= visibles else "☆", size=20).on( click=lambda i=i: self.emit("change", i), mouse_enter=lambda i=i: setattr(self, "hover", i), mouse_leave=lambda: setattr(self, "hover", None), ) for i in range(1, self.max + 1) ], spacing=2)Llamar a un componente devuelve un widget, así que va en cualquier sitio donde iría un widget:
class Opinion(pz.App): estrellas = 3
def view(self): return pz.column([ Rating(value=self.estrellas, on_change=lambda e: setattr(self, "estrellas", e.value)), pz.text(f"{self.estrellas} de 5"), ])
examples/components.py tiene
una puntuación con estrellas, un editor de etiquetas y una nota que se puede arrastrar.
Eventos
Sección titulada «Eventos»- Eventos propios: se declaran en
events, como tupla de nombres o como diccionario{nombre: descripción}. El padre los atiende conon_<nombre>=. self.emit(nombre, valor)llama al handler del padre con unpz.Eventque llevavalueysource. Devuelve su resultado desde tu callback, para que unCommando un mensaje lleguen al runtime.- Eventos comunes, como
on_click,on_dragyon_key_down, funcionan en cualquier componente sin escribir nada. Ver Eventos. - Eventos de ciclo de vida:
on_mountyon_unmountavisan al padre cuando el componente aparece y desaparece. style=da estilo al componente como a cualquier widget (Rating(value=3, style="card")), salvo que declare su propia propstyle. Ver Estilos.
Estado y claves
Sección titulada «Estado y claves»El estado también se puede declarar en la clase con pz.state(valor). A diferencia de una prop, el padre
no puede pasarlo, cada instancia empieza con su propia copia del valor (así que listas y diccionarios son
seguros) y el diseñador lo muestra junto a las props:
class Counter(pz.Component): step: int = 1 # prop count: int = pz.state(0) # estado
def render(self): return ui.button(f"{self.count}", lambda: setattr(self, "count", self.count + self.step))El estado se guarda por posición dentro del view() del padre: el segundo Rating de una vista
conserva su propio estado mientras no cambie el orden. Si los componentes salen de una lista cuyos
elementos se mueven, dale a cada uno una key estable:
pz.column([TodoItem(key=tarea.id, todo=tarea) for tarea in self.tareas])Un componente que deja de renderizarse se desmonta y su estado se descarta.
flowchart LR
View["App.view()"] -->|"Rating(value=3)"| Slot{"¿Hay instancia en esta<br/>posición o key?"}
Slot -->|no| New["instancia nueva<br/>setup()"]
Slot -->|sí| Same["la misma instancia<br/>(conserva el estado)"]
New --> Props["se asignan los props"]
Same --> Props
Props --> Changed{"¿cambiaron props,<br/>estado o contexto?"}
Changed -->|sí| Render["render() → widgets"]
Changed -->|no| Last["último dibujo, reutilizado"]
Render -->|la primera vez| Mount["on_mount()"]
View -.->|no se vuelve a renderizar| Unmount["on_unmount()"]
render() solo se ejecuta cuando cambió algo de lo que depende: sus props, su estado, el contexto
que lee, el tema o un componente que lleva dentro. Si no, se reutiliza el último dibujo, así que una
vista con cientos de componentes sigue siendo rápida. Si render() lee otra cosa (la hora, una
variable global), llama a self.refresh() cuando cambie o pon cache = False en la clase. Ver
componentes que solo se dibujan cuando cambian.
Hijos, contexto y estilos
Sección titulada «Hijos, contexto y estilos»Los componentes se anidan como los elementos de HTML y los componentes de React:
class Card(pz.Component): title: str = "" children: list = () # los argumentos posicionales, como los children de JSX stylesheet = { # reglas tipo CSS de este componente (& es el componente) "&": {"background": "panel", "padding": 16, "radius": 8, "border": (1, "border")}, "&:hover": {"border_color": "accent"}, }
def render(self): return pz.box(pz.text(self.title).style(weight="bold"), *self.children, gap=8)
Card(pz.text("Tres mensajes nuevos"), ui.button("Abrir", self.open), title="Bandeja", class_="wide", animate="fade_in_up")- Una prop llamada
childrenrecibe los argumentos posicionales. Las listas se aplanan yNoneyFalsese ignoran. stylesheetañade reglas apz.stylesheetal definir la clase.&representa al componente:"&","&:hover","&.wide".- Todo componente acepta
style=,class_=,id=yanimate=(presets dewidget.animate), salvo que declare props con esos nombres. - El contexto comparte valores con todos los componentes que se dibujan dentro, sin pasar props nivel a nivel:
with pz.provide(user=self.user): page = Dashboard() # cualquier componente de dentro lo lee
class Avatar(pz.Component): def render(self): return ui.avatar(pz.use_context("user").name) # o self.context("user")Un componente también puede compartir valores con lo que dibuja devolviéndolos desde un
método provide(). Los hijos que recibe como argumentos se dibujan antes que el
componente, así que ven el contexto de alrededor, no el suyo. En
examples/web_layout.py hay una página hecha así.
Ciclo de vida
Sección titulada «Ciclo de vida»| Hook | Cuándo |
|---|---|
setup() |
Una vez, antes del primer render. Los props ya están asignados. Inicializa aquí el estado. |
render() |
En cada render del padre. |
on_mount() |
Después del primer render. Puede devolver un Command o un mensaje. |
on_unmount() |
Cuando el componente deja de renderizarse. |
Los hooks se ejecutan después de dibujar la vista, nunca en medio.
En el diseñador
Sección titulada «En el diseñador»Cada subclase de pz.Component aparece en la biblioteca del diseñador:
-
Props: se convierten en propiedades.
boolmuestra un interruptor,intyfloatun campo numérico,Literal[...]un desplegable,listun elemento por línea, y un prop llamadocoloroicontiene su propio selector. Las uniones, opcionales, diccionarios, sets y tuplas (int | None,dict[str, int]…) tienen un editor construido a partir del tipo, con una fila por elemento. Las anotaciones depyzedeligen un editor más completo, como los export hints de Godot:Anotación Editor pz.ColorUn selector de color (token del tema o #rrggbb).pz.IconUn selector de icono. pz.MultilineUn área de texto. Annotated[int, pz.Range(0, 10)]Un campo numérico limitado de 0 a 10 ( stepes opcional).En ejecución son
streintnormales:tint: pz.Color = "accent". -
Eventos propios: se convierten en eventos a los que puedes asignar código.
Estos atributos de clase controlan cómo aparece:
| Atributo | Por defecto |
|---|---|
label |
El nombre de la clase, separado en palabras. |
icon |
"component" (cualquier icono de Lucide). |
category |
"Custom". |
size |
(160, 40), el tamaño al colocarlo en un formulario. |
designer |
True; con False no aparece en la biblioteca. |
type_name |
El nombre de la clase en snake case; el id que se guarda en los formularios. |
El primer párrafo del docstring se muestra como descripción.
Para crear un componente dibujándolo en vez de escribir render(), usa el
modo componente del diseñador.