Tutorial en 5 pasos
Cinco archivos cortos en examples/tutorial/.
Cada uno se ejecuta solo y añade una idea al anterior, así que puedes parar después de cualquier paso y seguir
teniendo un programa que funciona.
| Objetivo | Tiempo | Nivel | Necesitas |
|---|---|---|---|
| De una ventana vacía a una app de notas, en Python puro | 45 minutos | Básico · Python | Pyzed instalado (Instalación) |
Qué vas a aprender
Sección titulada «Qué vas a aprender»- Cómo funciona una app de Pyzed: una clase
App, su estado y unview()que describe la ventana. - Dos formas de reaccionar al usuario: mensajes enviados a
update()y callbacks normales. - Layout, componentes listos para usar y temas.
- Trabajo lento con
async defsin congelar la ventana. - Atajos de teclado, un panel dividido redimensionable y una segunda ventana.
uv run --with pyzed-ui python examples/tutorial/step1_hello.pypython3 -m venv .venvsource .venv/bin/activatepython -m pip install pyzed-uipython examples/tutorial/step1_hello.pypy -m venv .venv.venv\Scripts\Activate.ps1python -m pip install pyzed-uipython examples\tutorial\step1_hello.pypython3 examples/tutorial/step1_hello.pypy examples\tutorial\step1_hello.py1. Una ventana
Sección titulada «1. Una ventana»Una app es una clase que hereda de pz.App y define view(). view() devuelve un árbol de
widgets (pz.text, pz.column, pz.row, pz.container…). Pyzed la vuelve a llamar después de
cada cambio, así que nunca modificas widgets: describes cómo se ve el estado actual. Atributos
de clase como title y size configuran la ventana.

"""Tutorial, step 1: a window with some text.
python examples/tutorial/step1_hello.py"""
import pyzed as pz
class Hello(pz.App): title = "Hello" size = (400, 200)
def view(self): # view() returns a tree of widgets; Pyzed calls it again after every change. return pz.center(pz.text("Hello, Pyzed!", size=28))
if __name__ == "__main__": pz.run(Hello)2. Estado y mensajes
Sección titulada «2. Estado y mensajes»El estado son atributos normales de tu objeto. Los widgets reciben qué hacer de dos formas:
- Un valor (
ui.button("Greet", "greet")): se envía aupdate(message). - Un callable (
ui.button("Reset", self.reset)): se llama directamente.
Los callbacks que traen datos reciben el valor nuevo: on_input=lambda text: ...,
on_toggle=lambda checked: ..., on_change=lambda value: ....

"""Tutorial, step 2: state, messages and callbacks.
python examples/tutorial/step2_state.py"""
import pyzed as pzfrom pyzed import ui
class Greeter(pz.App): title = "Greeter" size = (420, 260)
def __init__(self): # State is plain attributes on your object. self.name = "" self.greetings = 0
def update(self, message): # Widgets given a plain value (here the string "greet") send it to update(). if message == "greet" and self.name: self.greetings += 1
def view(self): return pz.container( pz.column([ # Callbacks that receive data get the new value: here the input's text. ui.input(self.name, lambda text: setattr(self, "name", text), placeholder="Your name", on_submit="greet"), ui.button("Greet", "greet", variant="solid"), # A callable is called directly, no update() needed. ui.button("Reset", self.reset, variant="ghost"), ui.label(f"Hello, {self.name}! ({self.greetings})" if self.greetings else "Nobody greeted yet."), ], spacing=12), padding=24, )
def reset(self): self.name, self.greetings = "", 0
if __name__ == "__main__": pz.run(Greeter)3. Layout, componentes y temas
Sección titulada «3. Layout, componentes y temas»pyzed.ui trae componentes listos para usar (button, input, select, number_field,
settings_group, callout…) que toman sus colores del tema activo. theme puede ser un
Theme, el nombre de un tema incluido (pz.themes.names()) o pz.themes.auto(...) para seguir
al sistema. self.resolved_theme() devuelve el Theme en uso. Más en Temas.

"""Tutorial, step 3: layout, ready-made components and themes.
python examples/tutorial/step3_layout.py"""
import pyzed as pzfrom pyzed import ui
class Settings(pz.App): title = "Settings" size = (640, 420) theme = "One Dark" # any name from pz.themes.names(), a Theme, or pz.themes.auto()
def __init__(self): self.font_size = 14 self.wrap = True self.language = "Python"
def view(self): t = self.resolved_theme() # the Theme in use, handy for colors return pz.container( pz.column([ pz.row([ui.heading("Settings", level=2), pz.hspace(), ui.select(pz.themes.names(), t.name, lambda n: setattr(self, "theme", n), width=200)], align="center"), ui.settings_group("Editor", [ ui.field("Font size", ui.number_field(self.font_size, lambda v: setattr(self, "font_size", v), min=8, max=32)), ui.field("Soft wrap", ui.switch(self.wrap, lambda v: setattr(self, "wrap", v)), description="Wrap long lines at the window edge"), ui.field("Language", ui.select(["Python", "Rust", "Go"], self.language, lambda v: setattr(self, "language", v), width=140)), ]), ui.callout("Tip", "Every component reads its colors from the theme.", tint="hint"), ], spacing=16), padding=24, width="fill", height="fill", bg=t.background, )
if __name__ == "__main__": pz.run(Settings)4. Async
Sección titulada «4. Async»El trabajo lento no debe bloquear la ventana. Command.perform acepta funciones async def, que
corren en el loop de asyncio de Pyzed; el resultado llega a tu callback en el hilo de la interfaz.
Mientras carga, una suscripción pz.every redibuja la ventana para animar el spinner. También
puedes devolver corrutinas o generadores desde update y los callbacks: lo explica la guía
Async y suscripciones.

"""Tutorial, step 4: async work without freezing the window.
python examples/tutorial/step4_async.py"""
import asyncioimport random
import pyzed as pzfrom pyzed import ui
async def fetch_quote(): await asyncio.sleep(1.2) # pretend this is a network request (aiohttp, httpx...) return random.choice(["Simple is better than complex.", "Readability counts.", "Now is better than never."])
class Quotes(pz.App): title = "Quotes" size = (480, 260)
def __init__(self): self.quote = "Press the button." self.loading = False
def load(self): self.loading = True # Return a Command.perform: fetch_quote runs on Pyzed's asyncio loop and its # result is passed to self.loaded on the UI thread. return pz.Command.perform(fetch_quote, self.loaded)
def loaded(self, quote): self.quote, self.loading = quote, False
def subscriptions(self): # Redraw ~30 times per second while loading so the spinner turns. return [pz.every(1 / 30, lambda: None)] if self.loading else []
def view(self): return pz.container( pz.column([ ui.label(self.quote, size="lg"), pz.row([ui.button("New quote", self.load, disabled=self.loading, variant="solid"), ui.spinner() if self.loading else None], spacing=10, align="center"), ], spacing=16), padding=24, )
if __name__ == "__main__": pz.run(Quotes)5. Todo junto
Sección titulada «5. Todo junto»Una app de notas: lista lateral, editor con vista previa de Markdown en un ui.split
redimensionable, un atajo (on_key recibe combinaciones como "ctrl-n") y una segunda ventana
(pz.Window) que se abre con return About().open() y comparte el estado de la app. Más en
Varias ventanas.

"""Tutorial, step 5: a small notes app that puts it all together.
python examples/tutorial/step5_notes.py
A list of notes, a code editor with a live Markdown preview in a resizable split,a command (ctrl-n) and a separate "About" window."""
import pyzed as pzfrom pyzed import ui
class About(pz.Window): title = "About Notes" size = (320, 180) resizable = False
def view(self): return pz.container(pz.column([ui.heading("Notes", level=3), ui.muted("Built with Pyzed."), ui.button("Close", self.close)], spacing=10), padding=20, width="fill", height="fill", bg=self.resolved_theme().background)
class Notes(pz.App): title = "Notes" size = (1000, 640)
def __init__(self): self.notes = {"Welcome": "# Welcome\n\nWrite **Markdown** on the left.", "Ideas": "- One\n- Two"} self.current = "Welcome" self.ratio = 0.5
def new_note(self): name = f"Note {len(self.notes) + 1}" self.notes[name] = f"# {name}\n" self.current = name
def on_key(self, combo): if combo == "ctrl-n": self.new_note()
def view(self): t = self.resolved_theme() sidebar = pz.container( pz.column([ pz.row([ui.label("Notes", weight="semibold"), pz.hspace(), ui.icon_button("plus", self.new_note, tooltip="New note (ctrl-n)"), ui.icon_button("info", lambda: About().open(), tooltip="About")], align="center"), *[ui.list_item(name, lambda n=name: setattr(self, "current", n), icon="file-text", selected=name == self.current) for name in self.notes], ], spacing=2), padding=8, width=220, height="fill", bg=t.panel, ) # The editor's id is the note name, so each note keeps its own cursor and undo history. editor = ui.editor(self.current, self.notes[self.current], language="md", wrap=True, on_change=lambda text: self.notes.__setitem__(self.current, text)) preview = pz.container(pz.scrollable(pz.container(ui.markdown(self.notes[self.current]), padding=20), height="fill"), width="fill", height="fill", bg=t.background) return pz.row([sidebar, ui.split(editor, preview, ratio=self.ratio, on_resize=lambda r: setattr(self, "ratio", r))])
if __name__ == "__main__": pz.run(Notes)Eventos de este tutorial
Sección titulada «Eventos de este tutorial»| Paso | Widget | Evento | El handler recibe |
|---|---|---|---|
| 2 | ui.input |
on_input |
El texto nuevo, en cada tecla. |
| 2 | ui.input, ui.button |
on_submit, on_press |
Nada. Aquí son el mensaje "greet", que va a update. |
| 3 | ui.number_field, ui.switch, ui.select |
on_change, on_toggle, on_select |
El número, True/False, la opción. |
| 4 | subscriptions() |
un temporizador | pz.every(segundos, mensaje) manda el mensaje a update. |
| 5 | on_key(combo) |
cada tecla | La combinación, como "ctrl-n". |
| 5 | ui.editor, ui.split |
on_change, on_resize |
El texto; la parte del primer panel. |
El paso 2 maneja sus eventos en dos estilos a la vez: la caja llama a una lambda que cambia self (imperativo) y el
botón manda el mensaje "greet" a update (declarativo). El mismo saludador solo con mensajes:
from dataclasses import dataclass
@dataclassclass Typed: text: str
class Greeter(pz.App): def __init__(self): self.name, self.greetings = "", 0
def update(self, message): match message: case Typed(text): self.name = text case "greet" if self.name: self.greetings += 1
def view(self): return pz.column([ ui.input(self.name, Typed, placeholder="Tu nombre", on_submit="greet"), ui.button("Saludar", "greet", variant="solid"), ], spacing=12, padding=24)Mira Imperativo, declarativo y funcional para la tercera forma.
Siguientes pasos
Sección titulada «Siguientes pasos»- Lista de tareas y el mini editor de código, más detallados.
- Enlace de datos en el diseñador, si prefieres dibujar tus formularios.
- Ejemplos: recreaciones de las demos de Iced (game of life, reloj, cronómetro, multi-ventana, imágenes, QR, estilos, paneles…).