Pyzed vs Flet para apps de escritorio en Python
Flet te deja construir apps Flutter en Python. Tu código Python se ejecuta en un proceso y envía el árbol de controles a un cliente Flutter que lo dibuja, en el escritorio, en la web o en un móvil. Ese alcance es la gran fortaleza de Flet. Pyzed dibuja en el mismo proceso que tu código Python, con un renderizador en Rust, y se centra en el escritorio.
En resumen
Sección titulada «En resumen»| Pyzed | Flet | |
|---|---|---|
| Arquitectura | Un proceso: Python + renderizador en Rust (Iced) | Proceso Python + proceso cliente Flutter, comunicados por un protocolo |
| Plataformas | Windows, macOS, Linux | Windows, macOS, Linux, web, Android, iOS |
| Aspecto | Estilo de editor de código moderno, denso, pensado para escritorio | Material 3 o Cupertino, pensado para móvil |
| Modelo | Estado + view() reconstruido desde los datos |
Controles mutables + page.update(); Flet 1.0 añade componentes declarativos con hooks |
| Empaquetado | pyzed build (PyInstaller), sin SDK extra |
flet build (requiere el SDK de Flutter) |
| Diseñador | Pyzed Design | – |
| Recarga en caliente | Conserva el estado de la app | flet run reinicia la app al guardar |
| Accesibilidad | Teclado; sin lectores de pantalla todavía | Lectores de pantalla mediante la semántica de Flutter |
El mismo contador
Sección titulada «El mismo contador»"""Reference app: a counter with -, + and Reset."""
import pyzed as pzfrom pyzed import ui
class Counter(pz.App): title = "Counter" size = (320, 200)
def __init__(self): self.count = 0
def add(self, step): self.count += step
def view(self): return pz.center(pz.column([ ui.heading(str(self.count)), pz.row([ ui.button(icon="minus", on_press=lambda: self.add(-1)), ui.button(icon="plus", on_press=lambda: self.add(1), variant="solid"), ui.button("Reset", lambda: setattr(self, "count", 0), variant="ghost"), ], spacing=8), ], spacing=16, align="center"))
pz.run(Counter)"""Reference app: a counter with -, + and Reset."""
import flet as ft
def main(page: ft.Page): page.title = "Counter" page.window.width, page.window.height = 320, 200 count = ft.Text("0", size=24)
def change(step): count.value = "0" if step is None else str(int(count.value) + step) page.update()
page.add(ft.Column([ count, ft.Row([ ft.IconButton(ft.Icons.REMOVE, on_click=lambda e: change(-1)), ft.IconButton(ft.Icons.ADD, on_click=lambda e: change(1)), ft.TextButton("Reset", on_click=lambda e: change(None)), ], alignment=ft.MainAxisAlignment.CENTER), ], horizontal_alignment=ft.CrossAxisAlignment.CENTER))
ft.run(main)La misma lista de tareas
Sección titulada «La misma lista de tareas»"""Reference app: add tasks (button or Enter), tick them off, delete them, see how many are left."""
import pyzed as pzfrom pyzed import ui
class Todo(pz.App): title = "To-do" size = (420, 480)
def __init__(self): self.tasks = [] # [title, done] self.draft = ""
def add(self): if self.draft.strip(): self.tasks.append([self.draft.strip(), False]) self.draft = ""
def toggle(self, task, done): task[1] = done
def row(self, task): return pz.row([ ui.checkbox(task[1], lambda done: self.toggle(task, done)), ui.label(task[0], color="text_muted" if task[1] else "text", width="fill"), ui.icon_button("trash-2", lambda: self.tasks.remove(task)), ], spacing=8, align="center")
def view(self): left = sum(not done for _, done in self.tasks) return pz.column([ ui.heading(f"{left} left", level=3), pz.row([ ui.input(self.draft, lambda text: setattr(self, "draft", text), on_submit=self.add), ui.button("Add", self.add, variant="solid"), ], spacing=8), pz.scrollable(pz.column([self.row(t) for t in self.tasks], spacing=4), height="fill"), ], spacing=12, padding=16)
pz.run(Todo)"""Reference app: add tasks (button or Enter), tick them off, delete them, see how many are left."""
import flet as ft
def main(page: ft.Page): page.title = "To-do" page.window.width, page.window.height = 420, 480 tasks = ft.Column(scroll=ft.ScrollMode.AUTO, expand=True) left = ft.Text("0 left", size=18, weight=ft.FontWeight.BOLD)
def refresh(_=None): left.value = f"{sum(not row.controls[0].value for row in tasks.controls)} left" page.update()
def remove(row): tasks.controls.remove(row) refresh()
def add(_): if entry.value.strip(): row = ft.Row([ft.Checkbox(label=entry.value.strip(), on_change=refresh, expand=True)]) row.controls.append(ft.IconButton(ft.Icons.DELETE, on_click=lambda e: remove(row))) tasks.controls.append(row) entry.value = "" refresh()
entry = ft.TextField(expand=True, on_submit=add) page.add(left, ft.Row([entry, ft.Button("Add", on_click=add)]), tasks)
ft.run(main)Medianas de los benchmarks (Linux, sin GPU: renderizado por software en ambos).
La memoria de Flet incluye su proceso cliente de Flutter, que es donde vive la ventana. Su tamaño instalado
cuenta los paquetes de Python más el cliente de escritorio que flet descarga en la primera ejecución.
| Medición (menor es mejor en todas las filas) | Pyzed 1.2.0 | Flet 1.0.4 |
|---|---|---|
| Importar la biblioteca | 35 ms | 40 ms |
| Primer fotograma, 10 filas | 165 ms | 579 ms |
| Primer fotograma, 1.000 filas | 243 ms | 994 ms |
| Memoria (RSS), 10 filas | 151 MB | 268 MB |
| Memoria (RSS), 1.000 filas | 161 MB | 395 MB |
| Actualizar 1.000 filas | 18 ms | 166 ms |
| Líneas: contador | 19 líneas | 17 líneas |
| Líneas: lista de tareas | 31 líneas | 22 líneas |
De los conceptos de Flet a Pyzed
Sección titulada «De los conceptos de Flet a Pyzed»| Flet | Pyzed |
|---|---|
ft.run(main) con main(page) |
pz.run(MiApp) con una clase pz.App |
page.add(...), page.update() |
devuelve el árbol desde view(); sin actualizar a mano |
ft.Column, ft.Row, ft.Container |
pz.column, pz.row, pz.container |
ft.Text, ft.Button, ft.TextField, ft.Checkbox |
ui.label, ui.button, ui.input, ui.checkbox |
on_click=manejador(e) |
on_press=metodo (sin argumento de evento) |
page.theme_mode = ft.ThemeMode.DARK |
theme = "One Dark" o pz.themes.auto(...) |
page.run_task(corrutina) |
devuelve una corrutina desde un callback |
ft.ListView |
ui.virtual_list |
ft.NavigationRail, ft.Tabs |
paneles de ui.workspace, ui.tab_bar |
Consejos de migración
Sección titulada «Consejos de migración»- Lleva el estado de los controles a la app. Los valores que lees de los controles (
campo.value,casilla.value) pasan a ser atributos, actualizados por los callbacks de los controles. - Borra las llamadas a
page.update(). Pyzed redibuja después de cada callback. - Espera una interfaz más densa. El espaciado de Material está pensado para el tacto; los componentes de
Pyzed tienen el tamaño de un editor de escritorio. Usa variantes
size="lg"o estilos si quieres más aire.
Cuándo elegir cada uno
Sección titulada «Cuándo elegir cada uno»- Elige Flet cuando un mismo código deba funcionar en la web y en móviles, o quieras los widgets Material de Flutter.
- Elige Pyzed para apps pensadas para escritorio: un solo proceso, sin SDK de Flutter para empaquetar, un diseñador visual, recarga en caliente que conserva el estado y un aspecto denso y profesional.