Benchmarks de GUI en Python — Pyzed vs tkinter, Qt, Kivy, Flet…
Estos números se midieron, no se estimaron: cada gráfica de esta página se dibuja a partir del archivo JSON que escribió el script de benchmarks, y puedes ejecutar el mismo script en tu máquina. Vienen de una sola máquina sin GPU, así que léelos como una comparación entre toolkits en las mismas condiciones, no como números absolutos para tu ordenador.
Conclusiones principales
Sección titulada «Conclusiones principales»- El arranque es rápido en todos salvo en Flet y, con muchos widgets, en Kivy y Toga. Pyzed muestra una ventana de 10 filas en unos 0,16 s desde el lanzamiento y una de 1.000 filas en unos 0,24 s: construir y distribuir 2.000 widgets más le cuesta unos 80 ms. tkinter, Dear PyGui y Qt arrancan antes; Kivy, Toga y Flet necesitan casi un segundo para 1.000 filas.
- Actualizar 1.000 filas le lleva a Pyzed unos 18 ms desde el cambio de estado hasta la pantalla, aunque reconstruye toda la vista desde Python cada vez. Es algo más rápido que Qt Widgets, unas 3 veces más rápido que Kivy y 9 veces más que Flet; tkinter y Dear PyGui son aún más rápidos. Toga (GTK) necesitó unos 37 s por actualización en esta prueba, porque cada cambio de etiqueta provoca un nuevo cálculo del diseño.
- La memoria depende del renderizador. Con el driver Vulkan por software usado aquí, el renderizador por
GPU de Pyzed ocupa unos 151 MB; la misma app con el renderizador por CPU de Pyzed (tiny-skia, activado con
ICED_BACKEND=tiny-skia) ocupa unos 48 MB, entre tkinter (25 MB) y Qt (72 MB). Los demás toolkits por GPU pagan un coste de driver parecido aquí (Dear PyGui 123 MB, Kivy 146 MB, Flet 268 MB). - Tamaño instalado: unos 30 MB para Pyzed, frente a 238 MB de lo esencial de Qt, 83 MB de Kivy y 51 MB de Flet con su cliente. tkinter, Dear PyGui, Toga (que usa el GTK del sistema) y CustomTkinter (que usa tkinter) son más pequeños.
- Código: la lista de tareas ocupa 31 líneas en Pyzed, la mitad que en PySide6 (62) y muy por debajo de Kivy (81). La de Flet es la más corta (22).
Arranque
Sección titulada «Arranque»Importar es el tiempo de hacer import del toolkit en un proceso de Python nuevo. Primer fotograma es
el tiempo desde que se lanza el proceso de la app hasta que su ventana está en pantalla con su contenido: el
script vigila la pantalla buscando una barra de color en la parte inferior de la ventana.
- tkinterTk 9.0.35.3 ms
- Dear PyGui2.3.117 ms
- Toga0.5.729 ms
- Pyzed1.2.035 ms
- Flet1.0.440 ms
- CustomTkinter6.0.040 ms
- PySide6 (Qt)6.12.068 ms
- Kivy2.3.199 ms
- tkinterTk 9.0.396 ms
- Pyzed (CPU renderer)1.2.0 · tiny-skia118 ms
- Dear PyGui2.3.1133 ms
- PySide6 (Qt)6.12.0135 ms
- CustomTkinter6.0.0162 ms
- Pyzed1.2.0165 ms
- Toga0.5.7221 ms
- Kivy2.3.1258 ms
- Flet1.0.4579 ms
- Dear PyGui2.3.1152 ms
- tkinterTk 9.0.3157 ms
- Pyzed (CPU renderer)1.2.0 · tiny-skia175 ms
- PySide6 (Qt)6.12.0217 ms
- Pyzed1.2.0243 ms
- Toga0.5.7887 ms
- Flet1.0.4994 ms
- Kivy2.3.11,101 ms
Un widget real por fila (una etiqueta y un botón), sin listas virtualizadas. Falta CustomTkinter: su app de 1.000 filas falló (mira las advertencias).
Memoria
Sección titulada «Memoria»Memoria residente (RSS) de los procesos de la app un segundo después del primer fotograma. En Flet incluye el proceso cliente de Flutter que dibuja la ventana.
- tkinterTk 9.0.325 MB
- CustomTkinter6.0.031 MB
- Pyzed (CPU renderer)1.2.0 · tiny-skia48 MB
- Toga0.5.758 MB
- PySide6 (Qt)6.12.072 MB
- Dear PyGui2.3.1123 MB
- Kivy2.3.1146 MB
- Pyzed1.2.0151 MB
- Flet1.0.4268 MB
- tkinterTk 9.0.334 MB
- Pyzed (CPU renderer)1.2.0 · tiny-skia57 MB
- PySide6 (Qt)6.12.083 MB
- Toga0.5.785 MB
- Dear PyGui2.3.1125 MB
- Pyzed1.2.0161 MB
- Kivy2.3.1282 MB
- Flet1.0.4395 MB
Actualizar 1.000 filas
Sección titulada «Actualizar 1.000 filas»Cada 250 ms la app cambia el texto de las 1.000 etiquetas y el color de la barra inferior en la misma actualización. La latencia es el tiempo desde que la app cambia su estado hasta que el color nuevo está en pantalla; 10 actualizaciones por lanzamiento, 7 lanzamientos.
- Dear PyGui2.3.16.6 ms
- tkinterTk 9.0.37.7 ms
- Pyzed (CPU renderer)1.2.0 · tiny-skia15 ms
- Pyzed1.2.018 ms
- PySide6 (Qt)6.12.024 ms
- Kivy2.3.159 ms
- Flet1.0.4166 ms
- Toga0.5.736,619 ms
El eje se corta en 250 ms: la barra de Toga aparece partida y su valor real se muestra a la derecha.
- tkinterTk 9.0.31.9 ms
- Pyzed (CPU renderer)1.2.0 · tiny-skia2.7 ms
- CustomTkinter6.0.02.8 ms
- PySide6 (Qt)6.12.02.8 ms
- Dear PyGui2.3.13.5 ms
- Pyzed1.2.03.8 ms
- Kivy2.3.14.1 ms
- Toga0.5.79.1 ms
- Flet1.0.425 ms
Tamaño instalado
Sección titulada «Tamaño instalado»Cuánto crece un entorno virtual nuevo al instalar el toolkit con sus dependencias. tkinter cuenta los archivos de Tcl/Tk que vienen con Python. Flet incluye el cliente de escritorio que descarga en la primera ejecución. Toga no incluye GTK, que viene del sistema operativo. NiceGUI aparece solo como referencia: dibuja en un navegador web, así que no formó parte de los benchmarks de ventanas.
- CustomTkinter6.0.01.4 MB
- Toga0.5.72.9 MB
- tkinterTk 9.0.39.3 MB
- Dear PyGui2.3.110.0 MB
- Flet1.0.451 MB
- NiceGUI3.18.077 MB
- Kivy2.3.183 MB
- PySide6 (Qt)6.12.0238 MB
Líneas de código
Sección titulada «Líneas de código»Las mismas apps de contador y lista de tareas escritas en cada toolkit (código), contando las líneas que no son vacías, comentarios ni docstrings. Mira Experiencia de desarrollo para las gráficas y qué hacen esas líneas.
Números en bruto
Sección titulada «Números en bruto»Mediana en negrita; debajo, el mínimo–máximo y el número de muestras.
| Biblioteca | Importar (ms) | Primer fotograma, 10 filas (ms) | Primer fotograma, 1.000 filas (ms) | RSS, 10 filas (MB) | RSS, 1.000 filas (MB) |
|---|---|---|---|---|---|
| Pyzed 1.2.0 | 35 34–70 · n=10 | 165 154–691 · n=7 | 243 188–363 · n=7 | 151 150–158 · n=7 | 161 160–161 · n=7 |
| tkinter Tk 9.0.3 | 5.3 5.1–5.7 · n=10 | 96 85–100 · n=7 | 157 152–186 · n=7 | 25 25–25 · n=7 | 34 33–34 · n=7 |
| CustomTkinter 6.0.0 | 40 40–42 · n=10 | 162 152–181 · n=7 | – | 31 31–31 · n=7 | – |
| PySide6 (Qt) 6.12.0 | 68 65–76 · n=10 | 135 127–151 · n=7 | 217 204–247 · n=7 | 72 72–72 · n=7 | 83 83–83 · n=7 |
| Kivy 2.3.1 | 99 96–133 · n=10 | 258 249–276 · n=7 | 1,101 1,022–1,153 · n=7 | 146 146–147 · n=7 | 282 281–282 · n=7 |
| Dear PyGui 2.3.1 | 17 17–19 · n=10 | 133 123–139 · n=7 | 152 145–178 · n=7 | 123 123–123 · n=7 | 125 125–125 · n=7 |
| Flet 1.0.4 | 40 38–42 · n=10 | 579 543–589 · n=7 | 994 923–1,007 · n=7 | 268 265–270 · n=7 | 395 392–403 · n=7 |
| Toga 0.5.7 | 29 28–30 · n=10 | 221 216–259 · n=7 | 887 837–955 · n=7 | 58 58–58 · n=7 | 85 85–85 · n=7 |
| Pyzed (CPU renderer) 1.2.0 · tiny-skia | – | 118 94–163 · n=7 | 175 118–201 · n=7 | 48 47–48 · n=7 | 57 57–58 · n=7 |
| Biblioteca | Actualizar, 10 filas (ms) | Actualizar, 1.000 filas (ms) | Tamaño instalado (MB) | Contador (líneas) | Tareas (líneas) |
|---|---|---|---|---|---|
| Pyzed 1.2.0 | 3.8 2.4–21 · n=70 | 18 12–46 · n=70 | – | 19 | 31 |
| tkinter Tk 9.0.3 | 1.9 1.1–5.9 · n=70 | 7.7 6.6–12 · n=70 | 9.3 | 15 | 39 |
| CustomTkinter 6.0.0 | 2.8 1.7–5.4 · n=70 | – | 1.4 | 14 | 32 |
| PySide6 (Qt) 6.12.0 | 2.8 1.8–9.2 · n=70 | 24 21–38 · n=70 | 238 | 28 | 62 |
| Kivy 2.3.1 | 4.1 2.9–12 · n=70 | 59 46–259 · n=70 | 83 | 31 | 81 |
| Dear PyGui 2.3.1 | 3.5 2.6–6.3 · n=70 | 6.6 4.0–9.2 · n=70 | 10.0 | 19 | 29 |
| Flet 1.0.4 | 25 14–40 · n=70 | 166 145–194 · n=70 | 51 | 17 | 22 |
| Toga 0.5.7 | 9.1 8.4–14 · n=70 | 36,619 36,055–37,464 · n=70 | 2.9 | 19 | 35 |
| Pyzed (CPU renderer) 1.2.0 · tiny-skia | 2.7 1.5–12 · n=70 | 15 13–50 · n=70 | – | – | – |
| NiceGUI 3.18.0 | – | – | 77 | – | – |
Los resultados completos, con cada muestra, están en
benchmarks/results/.
Máquina y versiones
Sección titulada «Máquina y versiones»- Fecha: 2026-10-19
- Sistema: Linux-6.18.44-fc-v139-x86_64-with-glibc2.39
- CPU: Intel(R) Xeon(R) Processor, 4 núcleos; 15.7 GB de RAM (un contenedor en la nube)
- Pantalla: Xvfb (framebuffer virtual de X, sin GPU); OpenGL/Vulkan con llvmpipe/lavapipe de Mesa 25.2.8-0ubuntu0.24.04.4
- Python 3.13.14 (python-build-standalone, con uv); Tk 9.0.3
Toolkits:
Pyzed 1.2.0, tkinter Tk 9.0.3, CustomTkinter 6.0.0, PySide6 (Qt) 6.12.0, Kivy 2.3.1, Dear PyGui 2.3.1, Flet 1.0.4, Toga 0.5.7
- La misma app en todos. El
bench.pyde cada toolkit construye una ventana de 640×480 con una lista desplazable de N filas (una etiqueta y un botón «Open» cada una) y una barra de 40 px abajo pintada con un color centinela. Las listas se construyen de la forma sencilla que documenta cada toolkit, con un widget por fila: sinRecycleViewen Kivy, sinListViewperezoso en Flet, sinvirtual_listen Pyzed. - El primer fotograma se mide desde fuera: el harness lanza el proceso y captura la pantalla más o menos cada milisegundo (con mss) hasta que hay 8.000 píxeles visibles del color centinela.
- La memoria es el RSS del proceso y sus hijos (psutil), un segundo después del primer fotograma.
- Las actualizaciones se miden entre la marca de tiempo de la propia app (
time.monotonic(), impresa justo antes de cambiar su estado) y el momento en que el harness ve el nuevo color de la barra. - Repeticiones: 7 lanzamientos por toolkit y tamaño, 10 actualizaciones por lanzamiento, 10 importaciones; las gráficas muestran la mediana y una línea fina del mínimo al máximo.
- Pyzed (CPU renderer) es la misma app de Pyzed arrancada con
ICED_BACKEND=tiny-skia, que hace que Iced use su renderizador por CPU en vez de wgpu: lo que Pyzed usa en máquinas sin un driver de GPU que funcione. - Tamaño instalado: un
uv venvnuevo,uv pip install --link-mode=copy <paquete>, diferencia de tamaño.
Reprodúcelo
Sección titulada «Reprodúcelo»git clone https://github.com/albakore/pyzedcd pyzeduv venv --python 3.13 .venvsource .venv/bin/activateuv pip install pyzed-ui pyside6 kivy dearpygui "flet[desktop]" toga customtkinter psutil mss numpypython benchmarks/run.pygit clone https://github.com/albakore/pyzedcd pyzeduv venv --python 3.13 .venv.venv\Scripts\Activate.ps1uv pip install pyzed-ui pyside6 kivy dearpygui "flet[desktop]" toga customtkinter psutil mss numpypython benchmarks/run.pygit clone https://github.com/albakore/pyzedcd pyzedpython3 -m venv .venvsource .venv/bin/activatepip install pyzed-ui pyside6 kivy dearpygui "flet[desktop]" toga customtkinter psutil mss numpypython benchmarks/run.pygit clone https://github.com/albakore/pyzedcd pyzedpy -m venv .venv.venv\Scripts\Activate.ps1pip install pyzed-ui pyside6 kivy dearpygui "flet[desktop]" toga customtkinter psutil mss numpypython benchmarks/run.pySin pantalla, arranca Xvfb por su cuenta; en un escritorio, deja las ventanas del benchmark a la vista
mientras se ejecuta. Mira benchmarks/README.md
para las opciones y los paquetes del sistema que necesitan algunos toolkits. Los resultados en otras máquinas
son muy bienvenidos en un issue.
Advertencias
Sección titulada «Advertencias»- Una máquina, sin GPU (ver arriba). Los resultados en Windows y macOS, y con una GPU real, serán distintos.
- El harness comparte la CPU con la app: capturar la pantalla cuesta más o menos un núcleo mientras sondea.
- Tk dibuja widget a widget, así que en tkinter la barra puede aparecer un momento antes de que se hayan dibujado todas las filas; los toolkits por GPU presentan fotogramas completos.
- Dear PyGui redibuja continuamente, así que su latencia de actualización incluye esperar a su siguiente fotograma; además usa CPU en reposo, algo que aquí no se mide.
- CustomTkinter no tiene resultados con 1.000 filas: su app murió en Xvfb con un error de X (BadAlloc o
BadValue en
CreatePixmap) cuando su marco desplazable tenía más de unas 100 filas. - No medidos: wxPython no tiene wheels para Linux en PyPI y compilarlo desde el código fuente no era práctico aquí; NiceGUI se ejecuta en un navegador web, así que medir una ventana de escritorio mediría el navegador.
- Las líneas de código dependen del estilo; las apps siguen las formas documentadas de cada toolkit, y las mejoras son bienvenidas.