3 puntos por GN⁺ 2024-08-12 | 1 comentarios | Compartir por WhatsApp
  • Durante el desarrollo de Textual se comprobó que es posible crear animaciones TUI fluidas incluso sobre protocolos de terminal antiguos, pero hay que manejar con cuidado la forma de actualizar la pantalla y sincronizar la salida
  • Para reducir el parpadeo, es importante sobrescribir en vez de limpiar la pantalla, escribir de una sola vez en la salida estándar y aprovechar el protocolo Synchronized Output
  • La vista dict.items() de Python y @lru_cache son útiles para calcular diferencias de renderizado y optimizar funciones pequeñas de alta frecuencia, y la implementación en C de CPython respalda el rendimiento del caché
  • Los objetos casi inmutables, los diagramas con caracteres de caja Unicode y fractions.Fraction mejoran respectivamente la precisión en razonamiento, caché y pruebas, comprensión de la documentación y cálculo de layouts
  • Los Emoji en terminal son difíciles de predecir por las diferencias en la versión de Unicode, el ancho de los caracteres y el renderizado de múltiples code points, así que en la práctica los Emoji simples del nivel de Unicode 9 son más estables

Animaciones en terminal y control del parpadeo

  • Los emuladores de terminal modernos funcionan sobre protocolos antiguos, pero muchas implementaciones usan técnicas gráficas que también se emplean en videojuegos
  • Los efectos visuales en terminal tienden a sufrir parpadeo o tearing, pero si se cumplen ciertas condiciones se pueden lograr animaciones fluidas
  • La primera forma de reducir el parpadeo es no hacer “clear y luego draw”, sino sobrescribir el contenido existente
    • Si se borra la pantalla y luego se agrega el contenido nuevo, pueden verse brevemente frames vacíos o parcialmente dibujados
  • La segunda forma es escribir la actualización en la salida estándar con una sola llamada de escritura
    • Si se llama varias veces a file.write, el usuario podría ver actualizaciones parciales
  • La tercera forma es usar el protocolo Synchronized Output
    • Si se le indica a la terminal el inicio y el fin de un frame, la terminal puede usar esa información para hacer actualizaciones sin parpadeo
  • Textual toma 60fps como referencia, y por encima de eso la diferencia visible puede no ser muy grande
  • No todas las animaciones tienen el mismo valor
    • Efectos como una barra lateral que se desliza desde la izquierda se ven bien, pero quizá no aporten una mejora real a la experiencia de usuario
    • El desplazamiento suave sí ayuda a mantener la ubicación actual dentro de textos largos
    • Por eso, Textual planea incluir un mecanismo para desactivar ciertas animaciones

Encontrar diferencias de renderizado con DictView

  • keys() e items() de un dict de Python devuelven respectivamente KeysView e ItemsView, y ofrecen una interfaz parecida a la de los conjuntos
  • En el proceso de layout de Textual se genera un mapa de renderizado, que es una relación entre Widget y posición en pantalla
  • En el enfoque anterior, aunque solo cambiara la posición de un Widget, se refrescaba toda la pantalla de forma innecesaria
  • Si se usa la diferencia simétrica entre dos ItemsView, se pueden obtener los elementos nuevos o modificados
    • Esta operación se procesa a nivel C
    • Textual la usa para encontrar las regiones de pantalla modificadas cuando cambian propiedades CSS y así hacer actualizaciones optimizadas

lru_cache y optimización de funciones pequeñas

  • @lru_cache de la biblioteca estándar functools es un decorador para almacenar en caché los valores de retorno de una función
  • Si se define maxsize, se puede limitar el crecimiento indefinido del caché
  • La implementación de lru_cache en CPython usa C, por lo que es rápida tanto en aciertos como en fallos de caché
  • En Textual hay funciones pequeñas que no son lentas por sí mismas, pero se llaman con mucha frecuencia, y muchas resultaron buenas candidatas para caché
  • En general, establecer maxsize entre 1000 y 4000 fue suficiente para que la mayoría de las llamadas terminaran en aciertos de caché
  • Al usar caché, conviene revisar cache_info()
    • Si el caché es efectivo, lo esperable es que hits crezca más rápido que misses
  • El método intersection, que calcula la parte superpuesta entre dos áreas rectangulares, no hace un trabajo grande por llamada, pero al ejecutarse miles de veces se beneficia del caché

La simplicidad que aportan los objetos inmutables

  • En Python no existen objetos verdaderamente inmutables en sentido estricto, pero con tuple, NamedTuple y frozen dataclass se pueden obtener muchas ventajas
  • La restricción de no poder modificar un objeto puede parecer una limitación arbitraria, pero en el código real suele ayudar bastante
  • En Textual, el código que usa objetos inmutables es más fácil de razonar, y además favorece el caché y las pruebas
  • La razón principal es que facilita escribir código sin efectos secundarios
    • Cuando se pasa una instancia de clase a una función, es más difícil evitar efectos secundarios de la misma forma

Arte Unicode y documentación

  • Cuando un tema técnico es difícil de explicar solo con palabras, los diagramas hechos con caracteres de caja Unicode ayudan mucho a entender la documentación
  • En los docstrings de Textual se usan diagramas con caracteres de caja para explicar un método que divide un área en cuatro subáreas
  • Estos diagramas no reemplazan un docstring bien escrito, pero usados en conjunto pueden mejorar la comprensión
  • Como herramienta para crear diagramas se usa monodraw
    • Monodraw es exclusivo de macOS
    • Puede haber alternativas en otras plataformas

Reducir errores de layout con fractions.Fraction

  • El módulo fractions de la biblioteca estándar de Python existe desde Python 2.6
  • Fraction es otra forma de representar números y, una vez creado, puede usarse en lugar de float
  • Los números de punto flotante tienen limitaciones bien conocidas
    • Por ejemplo, 0.1 + 0.1 + 0.1 == 0.3 da False
  • En Textual, los errores de redondeo de punto flotante daban problemas en layouts que dividen la pantalla en varias proporciones
    • En una situación donde un panel ocupa 1/3 del ancho y luego se vuelve a dividir el 2/3 restante, podía aparecer un espacio vacío de un carácter
  • Si se reemplaza float por Fraction, se evita este tipo de error de redondeo
    • La suma de tres Fraction(1, 10) es igual a Fraction(3, 10)
  • En el ejemplo de dividir 24 caracteres en 7 partes, la versión con float se quedaba corta por un carácter, pero la versión con Fraction completaba todo el largo

Las dificultades de los Emoji en terminal

  • El soporte de Emoji ha sido un problema constante desde los inicios de Rich, y Textual heredó ese mismo problema
  • En terminal, los caracteres normalmente ocupan una celda o dos, y algunos tienen ancho 0
    • Los caracteres CJK ocupan el doble de espacio que el alfabeto occidental
    • Para hacer formato básico, como centrar texto o dibujar cajas, hay que conocer el ancho real del texto en la terminal
    • Por eso, len(text) no basta para saber cuánto espacio ocupará en terminal
  • La base de datos Unicode incluye un mapeo de caracteres de ancho simple y doble
    • Rich y Textual consultan esa base de datos para cada carácter que imprimen
    • No es una tarea liviana, pero con ingeniería y caché se puede volver lo bastante rápida
  • Aunque los Emoji estén incluidos en la base de datos Unicode, cada nueva versión de Unicode agrega nuevos Emoji y eso vuelve impredecibles los resultados
    • Un Emoji nuevo puede mostrarse en terminal como de una celda o de dos
    • También puede no renderizarse correctamente
  • No existe un método estándar y confiable para detectar qué versión de Unicode usa un emulador de terminal
    • Tampoco hay una variable de entorno estándar
    • Existen heurísticas como imprimir ciertas secuencias y preguntar la posición del cursor, pero en las pruebas, incluso asumiendo conocer la base de datos Unicode, la terminal renderizaba los Emoji de forma impredecible
  • Los Emoji compuestos por múltiples code points complican aún más el problema
    • Por ejemplo, 👨🏻‍🦰 está formado por 4 code points
    • Algunas terminales pueden renderizarlo como 4 caracteres, 2 caracteres, 1 carácter, una celda o dos, o incluso como 4 ?
  • En la práctica, usar los Emoji de Unicode 9 resulta relativamente estable en varias plataformas
    • Aunque los Emoji nuevos y los compuestos por múltiples code points se vean bien en la terminal actual, conviene evitarlos

Textualize está contratando

  • Textualize está contratando para crear un framework TUI capaz de reemplazar parte del rol del navegador
  • La información de vacantes está en Jobs

1 comentarios

 
GN⁺ 2024-08-12
Opiniones de Hacker News
  • Es interesante que todos los desarrolladores de TUI terminan chocando con Unicode, y que manejar correctamente caracteres internacionales y emojis se vuelve un proyecto aparte tan grande como el proyecto TUI original, o incluso más
    En rivo/tview pasó lo mismo, y el paquete rivo/uniseg que se creó como resultado mostró que los mantenedores de otras bibliotecas TUI también enfrentan el mismo problema
    Al final, como el ancho de los caracteres no está estandarizado y las terminales son complejas, cada quien inventa su propia solución. El autor del artículo solo soporta Unicode 9, pero Unicode ya va en 15.1, así que es difícil estar seguro de que sea una buena solución, porque algún día aparecerán quejas de que cierto emoji o carácter internacional se renderiza mal

    • Lo mejor por ahora es detectar mode 2027 y activarlo cuando esté soportado. Así se sabe que la terminal maneja correctamente los grafemas
      Mantengo dos bibliotecas TUI que usan esta técnica, y el soporte de emojis ha sido casi excelente. Una de ellas también usa la biblioteca uniseg
      https://mitchellh.com/writing/grapheme-clusters-in-terminals
    • Me pregunto si ya existe alguna biblioteca que normalice todos los emojis como un símbolo único
  • Mi mayor queja sobre textual es que intenta ser como React. Entiendo por qué, ya que es un framework popular con el que mucha gente está familiarizada, pero no lo considero una buena forma de construir interfaces de usuario
    El diseño reactivo en sí es un camino probado, y apoyar el diseño del sistema en enfoques que se sabe que funcionan bien es una buena forma de reducir el riesgo del proyecto
    Pero sumar a eso una variante de CSS me parece excesivo. El modelo de React, al favorecer componentes estandarizados, ya rompió en muchos aspectos los conceptos de CSS, y que los desarrolladores personalicen componentes con CSS parece más un efecto colateral de la evolución de React que una arquitectura legítima
    La última vez que lo usé había que usar CSS, y no había buenos componentes estándar, así que tuve que crearlos yo mismo. En vez de que un componente fuera una clase de Python bien encapsulada, la documentación oficial proponía crear un componente de lista y decorarlo con una hoja de estilos externa
    En Python debería haber una forma obvia de hacer algo, y preferiblemente solo una. Al seguir demasiado de cerca a React, parece que también adopta las peores malas prácticas de la comunidad JavaScript

    • No es obligatorio usar CSS. De hecho, es así desde hace tiempo: todos los estilos se pueden configurar desde código, y la documentación incluye el código equivalente en CSS y Python para cada estilo
      Parece que hace bastante que no revisas lo de que no hay buenos componentes estándar: https://textual.textualize.io/widget_gallery/
    • No estoy seguro de sentir que intenta ser React. Está claro que busca usar conceptos de HTML y CSS, y quizá algunos de JavaScript, pero para mí eso permitió avanzar muy rápido porque no tuve que aprender desde cero un sistema de UI completamente nuevo
      Tampoco tuve que crear mis propios componentes, y CSS ya lo conocía. Más bien, algunas diferencias de layout y las partes que se apartan de CSS fueron lo que a veces me resultó confuso
    • Quiero una forma de embeber una terminal dentro de un programa gráfico. No necesito soportar montones de emulaciones de terminal; con una basta. macOS es la prioridad, y sería bueno que también funcione en otras plataformas
      Imagínalo como un componente dentro de una ventana GUI normal que es una ventana de terminal. Me pregunto si existe algo así, o si simplemente debería usar una vista de texto con fuente monoespaciada
    • Me gustaría que explicaran mejor en qué sentido textual intenta ser como el framework React. En ninguna parte del artículo aparece la palabra React
    • Si React, y la programación reactiva funcional en general, no son una buena forma de construir UI, me pregunto cuál consideran que sí lo es
  • Esta TUI es bonita, pero me cuesta imaginar una situación en la que realmente la use y hasta pague por ella. Puede que no esté en el entorno adecuado, pero según mi experiencia, la gente se conforma con herramientas realmente minimalistas o directamente intenta satisfacer a los usuarios con una GUI.
    Por ejemplo, el enlace de YouTube del artículo mostraba una tabla que resaltaba celdas, pero no entiendo por qué eso tendría que hacerse como TUI. Si hay que mover la celda activa resaltada, al final se necesitarían muchas otras funciones y probablemente terminaría haciendo falta una GUI en serio.

    • Una razón para preferir una UI de texto es que se puede usar rápido en cualquier computadora, en cualquier lugar. No hace falta un VNC lento basado en video. Si ya estás usando la terminal, también está bien mejorar esa experiencia, y hay un mercado lo suficientemente grande como para que esta empresa apunte a él.
      Dicho eso, no tengo muy claro qué sería una “GUI en serio”. En la terminal hay estándares abiertos ampliamente usados, y esos protocolos son más estándar e interoperables que cualquier framework de GUI, además de estar soportados en todos los sistemas.
      Si a esto se le agregan elementos como video y se cubren algunos huecos, también parece razonable competir con el navegador o llevar toda la experiencia de cómputo a la terminal.
      Claro que, si la terminal agrega video, en cierto sentido estaría copiando el pipeline de renderizado de una “GUI en serio”, pero sería una mejora de la experiencia de terminal y, si tiene mucho éxito, también podría verse como llevar la simplicidad estilo UNIX a las GUI.
    • Hay trabajos, por ejemplo la carga de facturación médica, donde los empleados conocen todos los códigos de facturación y todavía usan consolas AS400 de pantalla verde.
      Trabajan de forma muy eficiente con atajos de teclado, y cada vez que alguien intenta reemplazar AS400 por una app web moderna, la productividad cae 100 veces.
      Es la misma situación que la diferencia entre un experto en Vim/Emacs y una GUI que se ve pulida pero no tiene atajos de teclado.
    • Hoy definitivamente parece un nicho. Si se necesita algo más amigable que una línea de comandos pura, pero por alguna razón es riesgoso ofrecer una UI web, una TUI podría encajar.
      Se podría hacer que el usuario entre al servidor por SSH y use la app TUI como si fuera un shell, aunque admito que es un escenario algo forzado.
      Personalmente, Textual me resultó un poco raro de usar, aunque fue mejor que ncurses. Aun así, no me dio el resultado que quería. Las viejas aplicaciones de TUI estilo mainframe se sentían extremadamente eficientes.
    • Una GUI en serio también está bien, pero la cosa cambia cuando hay que hacer lo mismo a través de una conexión SSH.
      A veces funciona bien, pero es algo limitado, como cuando se usa X forwarding en una conexión con mucho ancho de banda. En otros casos, la GUI se vuelve completamente torpe.
      Una GUI basada en texto parece poder quedarse con lo mejor de ambos mundos.
  • La idea de “el primer truco es no borrar, sino sobrescribir” es la misma forma en que se escribían los juegos anteriores a DirectX.
    Se escribía directamente en el framebuffer y, en vez de borrar todo y volver a dibujar, se redibujaban las partes cambiadas y lo que estaba alrededor o debajo. Era porque no había tiempo para actualizar toda la pantalla a tiempo.

    • En esa época había al menos dos técnicas más.
      La primera era escribir en otro búfer y, al final del frame, copiar todo el búfer de una sola vez para que cada píxel cambiara solo una vez. Ese búfer normalmente podía estar en RAM común, no en RAM de video.
      La segunda era escribir en otro búfer dentro de la RAM de video y cambiar registros del hardware gráfico para que los píxeles mostrados en el monitor se generaran desde ese búfer.
      Los compromisos eran distintos. Copiar todo el búfer era caro, y cambiar registros de dirección era barato. Pero los detalles de los registros podían depender del hardware, y tampoco había un framework de drivers gráficos en condiciones.
      Para hacer simplemente “buffer flipping”, incluso al renderizar en un búfer fuera de pantalla había que enviar los píxeles a la RAM de video, y según recuerdo el acceso a la RAM de video era más lento que a la RAM normal. En la práctica era una arquitectura NUMA, así que, dependiendo de cuántas veces se repintaran los píxeles, renderizar en RAM normal podía ser más rápido en conjunto incluso contando la copia final.
    • Aprendí este método programando en ComputerCraft, después me resultó útil muchas veces y ocasionalmente también lo usé en el trabajo.
      Aun así, creo que la capacidad de indicarle a la terminal el inicio y el fin de un frame es mucho más potente.
  • De verdad no entiendo por qué a los ingenieros de software les interesan tanto las TUI. Me gustan los buenos programas de línea de comandos, pero las TUI no me atraen.

    • Una TUI es, fuera de la web, básicamente otro toolkit de GUI multiplataforma instalado universalmente.
      Además tiene una estética hacker. El estilo no está fuertemente separado del contenido, y las opciones de estilo ajustables son limitadas, así que las apps TUI suelen converger hacia un solo estilo. Ese estilo recuerda a películas de hackers y a ciencia ficción cool.
      Tampoco es un terreno que les encante a los diseñadores corporativos, y las empresas no cierran ventas a compañías o consumidores usando una TUI como argumento. Al no existir esa presión comercial, las TUI las diseñan desarrolladores para desarrolladores.
      Por eso el meme de las TUI se refuerza a sí mismo. Los desarrolladores ven y usan TUI, sienten que normalmente son herramientas pensadas para desarrolladores, y eso les da ganas de crear su propia TUI.
    • Es una cuestión de gustos. Aun así, poder ejecutar una app a través de SSH es una gran ventaja. A personas como yo nos gusta una experiencia rápida y centrada en el teclado, algo que las apps GUI podrían ofrecer, pero normalmente no ofrecen.
    • No hay muchos beneficios obvios al usar una TUI, pero sí hay grandes ventajas al escribir una. En varios aspectos es más fácil.
      Lo multiplataforma sale casi gratis, no hace falta crear una GUI perfecta al píxel —y de entrada es imposible—, y tampoco se necesitan íconos ni gráficos, porque no se pueden mostrar gráficos.
    • Es útil cuando se ejecuta dentro de la terminal y se necesita más interfaz de usuario que la línea de comandos. A veces una TUI es más rápida que la línea de comandos y muestra mejor el panorama completo.
    • Uso mucho la CLI, pero para algunas tareas una GUI encaja mejor, y una TUI es un punto intermedio entre GUI y CLI.
      Puedes seguir usando tu emulador de terminal con la fuente que prefieras y, al mismo tiempo, tener una interfaz parecida a una GUI.
      Todas las apps TUI que he probado hasta ahora tenían baja latencia. Las apps GUI por lo general también son rápidas, pero todas las apps que me resultaron frustrantemente lentas eran apps GUI.
  • Como alternativa a Monodraw existe ascii-draw.
    https://github.com/Nokse22/ascii-draw

  • Casi todo lo que vi después de TurboVision estaba inspirado en él, pero en la práctica le faltaba madurez. Cuando probabas bien el toolkit, era simpático, pero se sentía incompleto
    Dicho de otra forma, podría decirse que muchos frameworks TUI que vi eran frameworks “muy opinados”
    Quizá sea que soy torpe y poco creativo, pero desde que pasé de DOS a Linux en los 90, nunca volví a ser tan productivo como cuando trabajaba con B800
    Es posible que me haya tomado mucho tiempo entender la necesidad del doble buffering, la diferencia entre el modo texto directo local y una terminal, e incluso las secuencias de escape. Aun así, cada vez que intentaba hacer algo directamente con ncurses, casi terminaba abandonándolo por una sensación clara de insatisfacción. Era algo completamente distinto de lo que se podía hacer con el ideal simple de B800

    • Aprendí programación GUI con Win16 y luego Win32, en la época en que se estaba pasando a WinXP
      No sé si mi visión estaba tapada como una ciudad entera, pero los frameworks GUI posteriores al message pump siempre me resultaron imposibles de entender
      En particular, nunca entendí cómo los frameworks orientados a objetos resolvían realmente los problemas de UI multihilo. En Win32 ponía el renderer principal en un thread, mientras renderers secundarios construían el modelo al costado y luego actualizaban solo las diferencias. El código no crecía hasta volverse inmanejable
    • Entiendo qué es Turbo Vision, pero me intriga qué es B800. Supongo que no se refiere al procesador Celeron, y además es difícil de buscar y no parece tener página en la wiki
  • Si vas a ejecutar kitty, puedes hacer mucho más que eso: https://m.youtube.com/watch?v=ft1Q-DwGWIs
    https://notcurses.com/

    • Prefiero WezTerm a Kitty por la actitud del autor de Kitty frente a los pedidos de funcionalidades y los pull requests
      Ambos pueden mostrar gráficos usando el mismo protocolo. Si realmente necesitas gráficos, una terminal casi nunca es la solución correcta. Aunque a veces sirve para cosas pequeñas, como íconos
  • Es interesante que estén contratando. Me pregunto cómo piensan ganar dinero con un framework TUI

    • Como al final también mencionan ejecutarlo en la web, supongo que quieren ayudar a migrar TUI antiguos. Hay bastante dinero en consultoría para mantenimiento de software empresarial antiguo
    • Esos puestos se cubrieron y luego se eliminaron: https://blog.davep.org/2024/03/28/goodbye-textualize.html#go...
    • El enlace de contratación da error 404
    • Me pregunto lo mismo. Parece que hacen software bastante genial, pero los ingenieros son caros y ya existen otros frameworks
  • Como referencia, evalué unas 12 bibliotecas TUI compilables, y FTXUI fue la más fácil de usar y la más estable
    https://github.com/ArthurSonzogni/FTXUI
    Es una buena herramienta para crear dashboards interactivos con soporte tanto para teclado como para mouse