Pyzed vs PyQt y PySide (Qt para Python)
Qt es el framework gráfico más completo que puedes usar desde Python, mediante PySide6 (de The Qt Company, LGPL) o PyQt6 (de Riverbank, GPL o comercial). Lo tiene todo: gráficas, multimedia, un Chromium embebido, 3D, impresión, accesibilidad y treinta años en producción. Esa amplitud tiene un peso: una descarga grande, un modelo de objetos de C++ (señales, slots, padres, layouts) y una licencia en la que pensar.
En resumen
Sección titulada «En resumen»| Pyzed | PyQt6 / PySide6 | |
|---|---|---|
| Licencia | MIT | PyQt: GPL v3 o comercial · PySide: LGPL v3 o comercial |
| Modelo | Estado + view(); callbacks de Python normales |
Árbol de objetos, señales y slots, o QML declarativo |
| Aspecto | Componentes modernos y temas, listos | Estilos casi nativos; aspecto moderno con QSS o QML |
| Diseñador | Pyzed Design, genera Python | Qt Designer / Creator (archivos .ui), Qt Design Studio (QML) |
| Recarga en caliente | pyzed dev, conserva el estado |
Vista previa de QML en las herramientas de Qt; el código Widgets necesita reiniciar |
| Amplitud | Lo esencial de una app de escritorio + componentes de IDE | Casi cualquier cosa, con decenas de módulos |
| Móvil | – | PySide6 en Android (vista previa técnica) |
| Accesibilidad | Teclado; sin lectores de pantalla todavía | Soporte completo de lectores de pantalla |
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 sys
from PySide6.QtCore import Qtfrom PySide6.QtWidgets import QApplication, QHBoxLayout, QLabel, QPushButton, QVBoxLayout, QWidget
class Counter(QWidget): def __init__(self): super().__init__() self.setWindowTitle("Counter") self.resize(320, 200) self.count = 0 self.label = QLabel("0", alignment=Qt.AlignmentFlag.AlignCenter) self.label.setStyleSheet("font-size: 24px") buttons = QHBoxLayout() for text, step in (("-", -1), ("+", 1), ("Reset", None)): button = QPushButton(text) button.clicked.connect(lambda _=False, s=step: self.change(s)) buttons.addWidget(button) layout = QVBoxLayout(self) layout.addStretch() layout.addWidget(self.label) layout.addLayout(buttons) layout.addStretch()
def change(self, step): self.count = 0 if step is None else self.count + step self.label.setText(str(self.count))
app = QApplication(sys.argv)window = Counter()window.show()app.exec()La misma lista de tareas
Sección titulada «La misma lista de tareas»En Qt Widgets construyes los widgets una vez y los mantienes sincronizados con tus datos a mano (refresh()
borra y vuelve a crear las filas). En Pyzed solo describes las filas para los datos actuales.
"""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 sys
from PySide6.QtWidgets import (QApplication, QCheckBox, QHBoxLayout, QLabel, QLineEdit, QPushButton, QScrollArea, QVBoxLayout, QWidget)
class Todo(QWidget): def __init__(self): super().__init__() self.setWindowTitle("To-do") self.resize(420, 480) self.tasks = [] # [title, done] self.left = QLabel() self.left.setStyleSheet("font-size: 16px; font-weight: bold") self.entry = QLineEdit() self.entry.returnPressed.connect(self.add) add = QPushButton("Add") add.clicked.connect(self.add) entry_row = QHBoxLayout() entry_row.addWidget(self.entry) entry_row.addWidget(add) self.list = QVBoxLayout() inner = QWidget() column = QVBoxLayout(inner) column.addLayout(self.list) column.addStretch() scroll = QScrollArea() scroll.setWidgetResizable(True) scroll.setWidget(inner) layout = QVBoxLayout(self) layout.addWidget(self.left) layout.addLayout(entry_row) layout.addWidget(scroll) self.refresh()
def add(self): if self.entry.text().strip(): self.tasks.append([self.entry.text().strip(), False]) self.entry.clear() self.refresh()
def toggle(self, task, done): task[1] = done self.refresh()
def remove(self, task): self.tasks.remove(task) self.refresh()
def refresh(self): while self.list.count(): item = self.list.takeAt(0) item.widget().deleteLater() for task in self.tasks: row = QWidget() line = QHBoxLayout(row) check = QCheckBox(task[0]) check.setChecked(task[1]) check.toggled.connect(lambda done, t=task: self.toggle(t, done)) delete = QPushButton("Delete") delete.clicked.connect(lambda _=False, t=task: self.remove(t)) line.addWidget(check, 1) line.addWidget(delete) self.list.addWidget(row) self.left.setText(f"{sum(not done for _, done in self.tasks)} left")
app = QApplication(sys.argv)window = Todo()window.show()app.exec()Medianas de los benchmarks (Linux, sin GPU: renderizado por software). El tamaño
instalado de Qt es solo pyside6-essentials; el paquete pyside6 completo con add-ons es varias veces mayor.
| Medición (menor es mejor en todas las filas) | Pyzed 1.2.0 | PySide6 (Qt) 6.12.0 |
|---|---|---|
| Importar la biblioteca | 35 ms | 68 ms |
| Primer fotograma, 10 filas | 165 ms | 135 ms |
| Primer fotograma, 1.000 filas | 243 ms | 217 ms |
| Memoria (RSS), 10 filas | 151 MB | 72 MB |
| Memoria (RSS), 1.000 filas | 161 MB | 83 MB |
| Actualizar 1.000 filas | 18 ms | 24 ms |
| Líneas: contador | 19 líneas | 28 líneas |
| Líneas: lista de tareas | 31 líneas | 62 líneas |
De los conceptos de Qt a Pyzed
Sección titulada «De los conceptos de Qt a Pyzed»| Qt | Pyzed |
|---|---|
QApplication + QMainWindow + app.exec() |
class MiApp(pz.App) + pz.run(MiApp) |
QVBoxLayout, QHBoxLayout, QGridLayout |
pz.column, pz.row, pz.grid |
QLabel, QPushButton, QLineEdit, QCheckBox |
ui.label, ui.button, ui.input, ui.checkbox |
clicked.connect(slot) |
on_press=metodo |
| señales propias | mensajes a update(), o pz.post(mensaje) desde un hilo |
QTimer |
pz.every(...) en subscriptions() |
QThread / QRunnable |
callbacks async def, generadores o hilos normales |
QDockWidget, QTabWidget, QSplitter |
ui.workspace, ui.tab_bar, ui.split |
QTreeView / QTableView + modelos |
ui.tree, ui.table, ui.virtual_list desde listas de Python |
| Hojas de estilo QSS | temas y pz.Style |
QShortcut, QAction |
on_key(combo), comandos y keys="ctrl-s" en botones |
Archivos .ui de Qt Designer |
Archivos .pzform de Pyzed Design con manejadores en Python |
Consejos de migración
Sección titulada «Consejos de migración»- Empieza por los datos, no por los widgets. Haz una lista de lo que guardan tus subclases de
QWidget(self.items,self.actual…) y conviértelo en atributos de tuApp. Todo lo que los copia a los widgets (setText,setChecked, reinicios de modelos) desaparece. - Convierte los slots en métodos. Un slot conectado con
clicked.connect(self.guardar)pasa a seron_press=self.guardar. - Sustituye los modelos por listas. Un
QAbstractListModelsobre una lista de Python pasa a ser la propia lista, másui.virtual_listcuando es larga. - Quédate con Qt donde dependas de él. Si la app necesita
QtWebEngine,QtMultimedia, impresión o 3D, Qt sigue siendo la herramienta adecuada para esa app.
Cuándo elegir cada uno
Sección titulada «Cuándo elegir cada uno»- Elige Qt cuando necesites su amplitud, una app accesible con lectores de pantalla hoy, o una estabilidad probada durante décadas en un equipo grande, y su tamaño y su licencia encajen con tu proyecto.
- Elige Pyzed cuando quieras una app de escritorio con aspecto moderno, con mucho menos código y ceremonia, licencia MIT permisiva, una instalación mucho más pequeña, recarga en caliente y ayuda de IA integrada en el flujo de trabajo.