Ir al contenido

Pyzed vs Dear PyGui

Dear PyGui envuelve Dear ImGui, el toolkit en modo inmediato que usan los motores de videojuegos para sus herramientas de depuración. Se dibuja por GPU y es muy rápido, y brilla en gráficas en tiempo real, editores de nodos y herramientas internas. Su aspecto es el de «herramienta» de ImGui, y redibuja continuamente, algo que encaja mejor en un panel que cambia en cada fotograma que en un formulario que cambia cuando haces clic.

Pyzed Dear PyGui
Modelo Estado + view() (arquitectura Elm) Elementos creados una vez con etiquetas, cambiados con set_value / configure_item
Redibujado Cuando algo cambia En cada fotograma, por defecto
Aspecto Componentes modernos listos, temas, modo oscuro Estilo de herramienta ImGui, configurable con temas
Texto Shaping completo, cualquier escritura, emoji, texto enriquecido y markdown Fuentes y rangos de glifos que cargas tú
Gráficas Sparklines, barras y donuts, un canvas Excelentes gráficas integradas (ImPlot)
Escritorio Menús, atajos, varias ventanas, docks, editor de código Docking y viewports de ImGui
Diseñador, recarga Pyzed Design, pyzed dev –
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)
Dear PyGui
"""Reference app: a counter with -, + and Reset."""
import dearpygui.dearpygui as dpg
dpg.create_context()
count = 0
def change(step):
global count
count = 0 if step is None else count + step
dpg.set_value("count", str(count))
with dpg.window(tag="main"):
dpg.add_text("0", tag="count")
with dpg.group(horizontal=True):
dpg.add_button(label="-", callback=lambda: change(-1))
dpg.add_button(label="+", callback=lambda: change(1))
dpg.add_button(label="Reset", callback=lambda: change(None))
dpg.create_viewport(title="Counter", width=320, height=200)
dpg.setup_dearpygui()
dpg.show_viewport()
dpg.set_primary_window("main", True)
dpg.start_dearpygui()
dpg.destroy_context()
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)
Dear PyGui
"""Reference app: add tasks (button or Enter), tick them off, delete them, see how many are left."""
import dearpygui.dearpygui as dpg
dpg.create_context()
tasks = [] # [title, done]
def refresh():
dpg.delete_item("list", children_only=True)
for task in tasks:
with dpg.group(horizontal=True, parent="list"):
dpg.add_checkbox(label=task[0], default_value=task[1],
callback=lambda s, value, t=task: (t.__setitem__(1, value), refresh()))
dpg.add_button(label="Delete", callback=lambda s, a, t=task: (tasks.remove(t), refresh()))
dpg.set_value("left", f"{sum(not done for _, done in tasks)} left")
def add():
text = dpg.get_value("entry").strip()
if text:
tasks.append([text, False])
dpg.set_value("entry", "")
refresh()
with dpg.window(tag="main"):
dpg.add_text("0 left", tag="left")
with dpg.group(horizontal=True):
dpg.add_input_text(tag="entry", width=300, on_enter=True, callback=add)
dpg.add_button(label="Add", callback=add)
dpg.add_child_window(tag="list")
dpg.create_viewport(title="To-do", width=420, height=480)
dpg.setup_dearpygui()
dpg.show_viewport()
dpg.set_primary_window("main", True)
dpg.start_dearpygui()
dpg.destroy_context()

Medianas de los benchmarks (Linux, sin GPU: ambos renderizan con los drivers por software de Mesa).

Medición (menor es mejor en todas las filas)Pyzed 1.2.0Dear PyGui 2.3.1
Importar la biblioteca35 ms17 ms
Primer fotograma, 10 filas165 ms133 ms
Primer fotograma, 1.000 filas243 ms152 ms
Memoria (RSS), 10 filas151 MB123 MB
Memoria (RSS), 1.000 filas161 MB125 MB
Actualizar 1.000 filas18 ms6.6 ms
Líneas: contador19 líneas19 líneas
Líneas: lista de tareas31 líneas29 líneas
Dear PyGui Pyzed
create_context, create_viewport, start_dearpygui pz.run(MiApp)
with dpg.window(...) la raíz que devuelve view()
dpg.add_text(tag="x") + dpg.set_value("x", ...) ui.label(self.x): cambia self.x
callback=fn on_press=fn
dpg.group(horizontal=True) pz.row
dpg.child_window pz.scrollable, ui.card, ui.panel
dpg.theme() + bind_item_theme temas y estilos
dpg.add_plot ui.bar_chart, ui.sparkline, o pz.canvas para dibujo propio
  • Elige Dear PyGui para gráficas en tiempo real, editores de nodos, paneles de instrumentos y herramientas de depuración donde el aspecto de ImGui está bien.
  • Elige Pyzed para apps de usuario final: formularios, listas, editores y paneles que deban verse pulidos, con texto de calidad, un diseñador, recarga en caliente y empaquetado.