Ir al contenido

Pyzed vs Kivy para apps de escritorio en Python

Kivy y Pyzed comparten la idea básica: dibujar cada widget por su cuenta en la GPU en vez de pedir widgets nativos al sistema operativo. Kivy nació para pantallas táctiles y funciona en Android e iOS, algo que Pyzed no hace. Pyzed está hecho para apps de productividad de escritorio y trae los componentes, temas y herramientas para ellas.

Pyzed Kivy
Plataformas Windows, macOS, Linux Windows, macOS, Linux, Android, iOS
Dibujo wgpu (Vulkan, Metal, DX12, GL), CPU de respaldo OpenGL ES 2
Aspecto Componentes de escritorio modernos, temas, modo oscuro Estilo mínimo propio; Material con KivyMD
Descripción de la interfaz Solo Python Python más el lenguaje KV
Estado Atributos normales, view() se reconstruye al cambiar Propiedades y bindings de Kivy
Detalles de escritorio Menús, atajos, pestañas, árboles, docks, editor de código, varias ventanas Pensado para táctil; las convenciones de escritorio son cosa tuya
Diseñador, recarga Pyzed Design, pyzed dev Herramientas de terceros
Accesibilidad Teclado; sin lectores de pantalla todavía Sin lectores de pantalla
Pyzed
"""Reference app: a counter with -, + and Reset."""
import pyzed as pz
from 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)
Kivy (Python + KV)
"""Reference app: a counter with -, + and Reset."""
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import NumericProperty
KV = """
AnchorLayout:
BoxLayout:
orientation: "vertical"
size_hint: None, None
size: 240, 120
spacing: 8
Label:
text: str(app.count)
font_size: 32
BoxLayout:
spacing: 8
Button:
text: "-"
on_press: app.count -= 1
Button:
text: "+"
on_press: app.count += 1
Button:
text: "Reset"
on_press: app.count = 0
"""
class CounterApp(App):
count = NumericProperty(0)
title = "Counter"
def build(self):
return Builder.load_string(KV)
CounterApp().run()

La versión de Kivy usa un RecycleView, la forma recomendada por Kivy para mostrar listas, con el diseño de la fila en KV.

Pyzed
"""Reference app: add tasks (button or Enter), tick them off, delete them, see how many are left."""
import pyzed as pz
from 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)
Kivy (Python + KV)
"""Reference app: add tasks (button or Enter), tick them off, delete them, see how many are left."""
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import BooleanProperty, ListProperty, NumericProperty, StringProperty
from kivy.uix.boxlayout import BoxLayout
KV = """
<TaskRow>:
size_hint_y: None
height: 40
CheckBox:
size_hint_x: None
width: 40
active: root.done
on_active: app.toggle(root.index, self.active)
Label:
text: root.title
color: (.5, .5, .5, 1) if root.done else (1, 1, 1, 1)
text_size: self.size
valign: "middle"
Button:
text: "Delete"
size_hint_x: None
width: 80
on_press: app.remove(root.index)
BoxLayout:
orientation: "vertical"
padding: 16
spacing: 12
Label:
text: f"{app.left} left"
size_hint_y: None
height: 32
font_size: 20
BoxLayout:
size_hint_y: None
height: 40
spacing: 8
TextInput:
id: entry
multiline: False
on_text_validate: app.add(self)
Button:
text: "Add"
size_hint_x: None
width: 80
on_press: app.add(entry)
RecycleView:
data: app.rows
viewclass: "TaskRow"
RecycleBoxLayout:
orientation: "vertical"
size_hint_y: None
height: self.minimum_height
default_size_hint: 1, None
default_size: None, 40
"""
class TaskRow(BoxLayout):
index = NumericProperty(0)
title = StringProperty("")
done = BooleanProperty(False)
class TodoApp(App):
title = "To-do"
rows = ListProperty([])
left = NumericProperty(0)
def build(self):
return Builder.load_string(KV)
def refresh(self):
self.rows = [{**row, "index": i} for i, row in enumerate(self.rows)]
self.left = sum(not row["done"] for row in self.rows)
def add(self, entry):
if entry.text.strip():
self.rows.append({"title": entry.text.strip(), "done": False})
entry.text = ""
self.refresh()
def toggle(self, index, done):
self.rows[index]["done"] = done
self.refresh()
def remove(self, index):
del self.rows[index]
self.refresh()
TodoApp().run()

Medianas de los benchmarks (Linux, sin GPU: los dos toolkits renderizan con el OpenGL/Vulkan por software de Mesa, así que entre ellos la comparación es justa).

Medición (menor es mejor en todas las filas)Pyzed 1.2.0Kivy 2.3.1
Importar la biblioteca35 ms99 ms
Primer fotograma, 10 filas165 ms258 ms
Primer fotograma, 1.000 filas243 ms1,101 ms
Memoria (RSS), 10 filas151 MB146 MB
Memoria (RSS), 1.000 filas161 MB282 MB
Actualizar 1.000 filas18 ms59 ms
Líneas: contador19 líneas31 líneas
Líneas: lista de tareas31 líneas81 líneas
Kivy Pyzed
App.build() devuelve el widget raíz App.view() devuelve el árbol, cada vez que cambia el estado
NumericProperty, StringProperty, ListProperty atributos normales
Reglas KV (<MiFila>:) funciones de Python que devuelven widgets, o componentes
BoxLayout(orientation=...) pz.row / pz.column
GridLayout, AnchorLayout, FloatLayout pz.grid, pz.center / alineación, pz.stack + pz.anchor_layout
ScrollView, RecycleView pz.scrollable, ui.virtual_list
on_press: app.hacer() on_press=self.hacer
Clock.schedule_interval pz.every(...) en subscriptions()
Animation(...) pz.Animation y transiciones
Window.clearcolor, temas de KivyMD temas
  1. Pasa el KV a view(). Cada regla KV se convierte en una función que recibe datos y devuelve widgets. Los bindings como text: str(app.count) pasan a ser expresiones normales: ui.heading(str(self.count)).
  2. Olvídate de las propiedades. Las propiedades de Kivy existen para que los bindings observen cambios; Pyzed vuelve a ejecutar view() después de cada evento, así que bastan atributos normales.
  3. Repiensa los patrones solo táctiles. En escritorio se esperan menús, atajos de teclado y estados al pasar el ratón, y los componentes de Pyzed los traen.
  • Elige Kivy cuando la misma app deba funcionar en móviles y tablets, o para quioscos táctiles y apps multimedia.
  • Elige Pyzed para apps de escritorio que deban verse y comportarse como software de escritorio moderno, con diseñador, recarga en caliente y herramientas de IA, y sin un segundo lenguaje que aprender.