3 points par GN⁺ 2024-08-12 | 1 commentaires | Partager sur WhatsApp
  • Le développement de Textual montre qu’il est possible de créer des animations TUI fluides même sur d’anciens protocoles de terminal, à condition de gérer avec soin le rafraîchissement de l’écran et la synchronisation de la sortie
  • Pour réduire le scintillement, il est important d’écraser le contenu à l’écran sans l’effacer, d’écrire sur la sortie standard en une seule fois et d’utiliser le protocole Synchronized Output
  • La vue dict.items() de Python et @lru_cache sont utiles pour calculer les différences de rendu et optimiser de petites fonctions très fréquemment appelées, tandis que l’implémentation C de CPython soutient les performances du cache
  • Des objets quasi immuables, des schémas en caractères Unicode et fractions.Fraction améliorent respectivement la précision du raisonnement, du cache et des tests, la compréhension de la documentation, et les calculs de mise en page
  • Les Emoji dans le terminal sont difficiles à prévoir à cause des versions d’Unicode, de la largeur des caractères et des différences de rendu pour les séquences à plusieurs code points ; en pratique, les Emoji simples du niveau Unicode 9 sont plus stables

Animation dans le terminal et contrôle du scintillement

  • Les émulateurs de terminal modernes fonctionnent sur d’anciens protocoles, mais beaucoup d’implémentations utilisent des techniques graphiques également employées dans le jeu vidéo
  • Les effets visuels dans le terminal provoquent facilement du scintillement ou du tearing, mais il est possible d’obtenir des animations fluides en respectant quelques principes
  • La première méthode pour réduire le scintillement consiste non pas à faire « clear puis draw », mais à écraser le contenu existant
    • Si l’on efface l’écran avant d’ajouter le nouveau contenu, un frame vide ou partiellement dessiné peut brièvement apparaître
  • La deuxième méthode consiste à écrire les mises à jour sur la sortie standard en un seul write
    • Si l’on appelle file.write plusieurs fois, des mises à jour partielles peuvent devenir visibles pour l’utilisateur
  • La troisième méthode consiste à utiliser le protocole Synchronized Output
    • En indiquant au terminal le début et la fin d’un frame, celui-ci peut s’en servir pour effectuer une mise à jour sans scintillement
  • Textual vise 60 fps, au-delà desquels la différence perceptible n’est pas forcément importante
  • Toutes les animations n’ont pas la même valeur
    • Un effet comme une barre latérale qui glisse depuis la gauche est agréable visuellement, mais n’apporte pas forcément de bénéfice réel à l’expérience utilisateur
    • Un défilement fluide aide à conserver sa position dans de longs textes
    • Pour cette raison, Textual prévoit un mécanisme permettant de désactiver certaines animations

Trouver les différences de rendu avec DictView

  • Dans Python, keys() et items() d’un dict renvoient respectivement KeysView et ItemsView, avec une interface proche des ensembles
  • Le processus de mise en page de Textual génère une carte de rendu, c’est-à-dire une correspondance entre les widgets et leur position à l’écran
  • Dans l’approche précédente, si la position d’un seul widget changeait, l’ensemble de l’écran était rafraîchi inutilement
  • En utilisant la différence symétrique entre deux ItemsView, on peut obtenir les éléments nouveaux ou modifiés
    • Cette opération est traitée au niveau C
    • Textual s’en sert pour repérer les zones de l’écran modifiées quand des propriétés CSS changent, et effectuer des mises à jour optimisées

lru_cache et optimisation des petites fonctions

  • Le décorateur @lru_cache de la bibliothèque standard functools permet de mettre en cache la valeur de retour d’une fonction
  • En définissant maxsize, on évite que le cache ne grossisse indéfiniment
  • Dans CPython, lru_cache est implémenté en C, ce qui le rend rapide aussi bien sur les hits que sur les misses
  • Textual contient de petites fonctions qui ne sont pas lentes en soi, mais sont appelées très fréquemment, et beaucoup se prêtaient bien à la mise en cache
  • En pratique, fixer maxsize entre 1000 et 4000 suffisait généralement à faire basculer la majorité des appels en cache hit
  • Lorsqu’on utilise un cache, il faut vérifier cache_info()
    • Si le cache est efficace, on s’attend à voir hits augmenter plus vite que misses
  • La méthode intersection, qui calcule la zone de recouvrement entre deux rectangles, n’est pas très coûteuse en soi, mais comme elle est appelée des milliers de fois, elle bénéficie de la mise en cache

La simplicité apportée par les objets immuables

  • Python ne possède pas d’objets véritablement immuables au sens strict, mais les tuples, NamedTuple et les dataclasses figées offrent déjà de nombreux avantages
  • La contrainte de ne pas pouvoir modifier un objet peut sembler arbitraire, mais elle s’avère souvent utile dans du vrai code
  • Dans Textual, le code qui utilise des objets immuables est plus facile à raisonner, et se prête mieux au cache comme aux tests
  • La raison essentielle est qu’il devient plus simple d’écrire du code sans effets de bord
    • À l’inverse, lorsqu’on passe une instance de classe à une fonction, il est plus difficile d’éviter les effets de bord de la même manière

Art Unicode et documentation

  • Quand un sujet technique est difficile à expliquer uniquement avec des mots, des schémas réalisés avec des caractères de boîte Unicode peuvent grandement aider à la compréhension
  • Dans les docstrings de Textual, un schéma en caractères de boîte est utilisé pour expliquer une méthode qui divise une zone en quatre sous-zones
  • Ces schémas ne remplacent pas une docstring bien rédigée, mais les deux combinés améliorent la compréhension
  • L’outil utilisé pour créer ces schémas est monodraw
    • Monodraw est réservé à macOS
    • Il existe peut-être des alternatives sur d’autres plateformes

Réduire les erreurs de mise en page avec fractions.Fraction

  • Le module fractions de la bibliothèque standard Python existe depuis Python 2.6
  • Fraction est une autre manière de représenter les nombres et peut, une fois créé, être utilisé à la place des floats
  • Les nombres à virgule flottante ont des limites bien connues
    • Par exemple, 0.1 + 0.1 + 0.1 == 0.3 renvoie False
  • Dans Textual, les erreurs d’arrondi des floats posaient problème dans les mises en page qui divisent l’écran selon plusieurs proportions
    • Dans une situation où un panneau occupe 1/3 de la largeur et où les 2/3 restants sont redécoupés, il pouvait apparaître un espace vide d’un caractère
  • Remplacer les floats par Fraction permet d’éviter ce type d’erreur d’arrondi
    • La somme de trois Fraction(1, 10) est égale à Fraction(3, 10)
  • Dans un exemple où 24 caractères sont répartis en 7 segments, la version en float laissait un caractère manquant, alors que la version en Fraction remplissait toute la longueur

Les difficultés des Emoji dans le terminal

  • La prise en charge des Emoji est un problème constant depuis les débuts de Rich, et Textual en hérite lui aussi
  • Dans un terminal, un caractère occupe généralement une ou deux colonnes, et certains ont une largeur de 0
    • Les caractères CJK prennent deux fois plus d’espace que l’alphabet latin
    • Pour effectuer des opérations de formatage de base comme le centrage ou le dessin de boîtes, il faut connaître la largeur terminal du texte affiché
    • Il n’est donc pas possible de connaître cette largeur avec un simple len(text)
  • La base de données Unicode contient des correspondances pour les caractères simple largeur et double largeur
    • Rich et Textual la consultent pour chaque caractère affiché
    • Cette opération n’est pas légère, mais l’ingénierie et le cache permettent de la rendre suffisamment rapide
  • Les Emoji compliquent encore les choses : même lorsqu’ils sont présents dans la base Unicode, chaque nouvelle version d’Unicode en ajoute de nouveaux, ce qui rend le résultat difficile à prévoir
    • Un nouvel Emoji affiché dans un terminal peut apparaître sur une ou deux colonnes
    • Il peut aussi ne pas être rendu correctement
  • Il n’existe pas de méthode standard fiable pour détecter la version d’Unicode utilisée par un émulateur de terminal
    • Il n’existe pas non plus de variable d’environnement standard
    • Il existe des heuristiques consistant à afficher certaines séquences puis à interroger la position du curseur, mais lors des tests, même en supposant connaître la base Unicode, le terminal rendait les Emoji de façon imprévisible
  • Les Emoji composés de plusieurs code points rendent le problème encore plus complexe
    • Par exemple, 👨🏻‍🦰 est composé de 4 code points
    • Selon le terminal, il peut être rendu comme 4 caractères, 2 caractères, 1 caractère, sur une ou deux colonnes, ou même comme 4 ?
  • En pratique, les Emoji d’Unicode 9 sont relativement stables sur plusieurs plateformes
    • Mieux vaut éviter les nouveaux Emoji et les caractères à plusieurs code points, même s’ils semblent bien s’afficher dans les terminaux actuels

Recrutement chez Textualize

  • Textualize recrute pour construire un framework TUI capable de remplacer certaines fonctions du navigateur
  • Les offres sont disponibles sur Jobs

1 commentaires

 
GN⁺ 2024-08-12
Commentaires sur Hacker News
  • Il est amusant de voir que tous les développeurs de TUI finissent par se heurter à Unicode, et que gérer correctement les caractères internationaux et les emojis devient un projet séparé, de taille comparable ou supérieure au projet TUI initial
    La même chose s’est produite avec rivo/tview, et le paquet rivo/uniseg créé à cette occasion a aussi permis de constater que les mainteneurs d’autres bibliothèques TUI rencontraient exactement le même problème
    Au final, comme la largeur des caractères n’est pas standardisée et que les terminaux sont complexes, chacun invente sa propre solution. L’auteur de l’article ne prend en charge que Unicode 9, alors que la version actuelle d’Unicode est 15.1, donc il est difficile d’être certain que c’est une bonne solution : tôt ou tard, quelqu’un se plaindra qu’un emoji ou un caractère international s’affiche mal

    • Pour l’instant, la meilleure approche consiste à détecter mode 2027 et à l’activer lorsqu’il est pris en charge. On sait alors que le terminal gère correctement les graphèmes
      Je maintiens deux bibliothèques TUI qui utilisent cette technique, et la prise en charge des emojis y est presque excellente. L’une des deux utilise aussi la bibliothèque uniseg
      https://mitchellh.com/writing/grapheme-clusters-in-terminals
    • Je me demande s’il existe déjà une bibliothèque qui normalise tous les emojis en un seul symbole
  • Mon principal reproche à textual, c’est qu’il essaie de devenir React. Je comprends pourquoi, puisque c’est un framework populaire que beaucoup de gens connaissent, mais je ne pense pas que ce soit une bonne manière de construire des interfaces utilisateur
    Le design réactif en lui-même est une approche éprouvée, et fonder l’architecture d’un système sur une méthode connue pour bien fonctionner est une bonne façon de réduire les risques d’un projet
    Mais aller jusqu’à utiliser une variante de CSS me paraît excessif. Le modèle React a déjà cassé à bien des égards les concepts de CSS en privilégiant des composants standardisés, et le fait que les développeurs personnalisent des composants via CSS ressemble plus à un effet secondaire de l’évolution de React qu’à une architecture réellement justifiée
    La dernière fois que je l’ai utilisé, il fallait passer par CSS, et comme il n’y avait pas de bons composants standard, il fallait les créer soi-même. Au lieu d’avoir un composant sous la forme d’une unique classe Python proprement encapsulée, la documentation officielle montrait comment créer un composant de liste et le styliser avec une feuille de style externe
    En Python, il devrait y avoir une façon évidente de faire les choses, une seule si possible. À force de trop coller à React, on a l’impression qu’il imite aussi les pires habitudes de la communauté JavaScript

    • Il n’est pas obligatoire d’utiliser CSS. En réalité, ça a toujours été le cas : tous les styles peuvent être définis dans le code, et la documentation montre pour chaque style un équivalent CSS et Python
      Et cela fait aussi un bon moment qu’il n’est plus vrai qu’il n’y a pas de bons composants standard : https://textual.textualize.io/widget_gallery/
    • Je ne vois pas vraiment en quoi cela donne l’impression de vouloir devenir React. Il essaie clairement de reprendre certains concepts de HTML et CSS, et peut-être un peu de JavaScript, mais justement cela m’a permis d’avancer très vite, sans devoir apprendre de zéro tout un nouveau système d’interface
      Je n’ai pas eu besoin de créer mes propres composants, et je connaissais déjà CSS. Au contraire, ce sont plutôt les petites différences de layout et les écarts par rapport à CSS qui m’ont parfois dérouté
    • Je voudrais un moyen d’embarquer un terminal dans un programme graphique. Pas besoin de prendre en charge une multitude d’émulations de terminal, une seule suffirait, avec macOS en priorité, et d’autres plateformes si possible
      L’idée serait qu’un des composants d’une fenêtre GUI classique soit une fenêtre de terminal. Je me demande si cela existe, ou s’il faut simplement utiliser une vue texte en police à chasse fixe
    • J’aimerais bien qu’on explique davantage en quoi textual essaie de devenir un framework à la React. Je ne vois le mot React nulle part dans l’article
    • Si React, et plus généralement la programmation réactive fonctionnelle, ne sont pas une bonne façon de construire des interfaces utilisateur, quelle serait selon vous la bonne approche ?
  • Cette TUI est jolie, mais j’ai du mal à imaginer une situation où je l’utiliserais réellement et paierais pour ça. Je ne suis peut-être pas dans le bon contexte, mais d’après mon expérience, les gens se satisfont d’outils vraiment minimalistes ou cherchent directement à satisfaire les utilisateurs avec une GUI
    Par exemple, le lien YouTube dans l’article montrait un tableau avec une cellule mise en évidence, mais je ne vois pas pourquoi il faudrait faire ça en TUI. S’il faut déplacer la cellule active mise en évidence, il faudra au final beaucoup d’autres fonctions aussi, et on aura probablement besoin d’une vraie GUI

    • Une des raisons de préférer une interface texte, c’est qu’on peut l’utiliser rapidement, partout, sur n’importe quel ordinateur. Pas besoin d’un VNC lent basé sur la vidéo. Si on utilise déjà le terminal, améliorer cette expérience est aussi une bonne chose, et il y a un marché suffisant pour que cette entreprise le vise
      En revanche, je ne sais pas très bien ce qu’est une « vraie GUI ». Le terminal dispose de standards ouverts largement utilisés, et ces protocoles sont plus standardisés, plus interopérables et mieux pris en charge sur tous les systèmes que n’importe quel framework GUI
      Si on y ajoute des éléments comme la vidéo et qu’on comble quelques lacunes, il semble aussi raisonnable d’imaginer concurrencer le navigateur ou de ramener l’expérience informatique complète dans le terminal
      Bien sûr, si le terminal ajoute la vidéo, cela revient à recopier le pipeline de rendu d’une « vraie GUI », mais cela améliore l’expérience du terminal et, en cas de grand succès, on peut aussi y voir une manière d’apporter la simplicité à la UNIX à la GUI
    • Il existe par exemple des tâches comme la saisie de remboursements médicaux, où les employés connaissent tous les codes par cœur et utilisent encore des consoles AS400 à écran vert
      Ils travaillent très efficacement avec des raccourcis clavier, et chaque fois que quelqu’un essaie de remplacer l’AS400 par une application web moderne, la productivité chute d’un facteur 100
      C’est la même situation que la différence entre un expert de Vim/Emacs et une GUI élégante en apparence mais sans raccourcis clavier
    • Pour l’instant, cela paraît clairement de niche. Si l’on a besoin de quelque chose de plus convivial qu’une ligne de commande pure, mais qu’il est risqué pour une raison ou une autre de proposer une interface web, alors une TUI peut convenir
      On peut laisser l’utilisateur se connecter au serveur en SSH et utiliser l’application TUI comme un shell, même si j’admets que le scénario paraît un peu forcé
      Personnellement, Textual m’a semblé un peu étrange à l’usage, mais c’était mieux que ncurses. Cela dit, ça ne m’a pas donné le résultat que je voulais. Les anciennes applications TUI de style mainframe donnaient une impression d’efficacité remarquable
    • Une vraie GUI, c’est bien, mais quand il faut faire la même chose au travers d’une connexion SSH, c’est une autre histoire
      Parfois cela fonctionne bien, mais dans des cas limités, par exemple avec le X forwarding sur une connexion à forte bande passante. Dans d’autres cas, la GUI devient complètement molle
      Une GUI basée sur le texte semble pouvoir combiner les avantages des deux côtés
  • L’approche « première astuce : ne pas effacer, mais écraser » est la même que celle utilisée pour écrire les jeux d’avant DirectX
    On écrivait directement dans le framebuffer, et au lieu de tout effacer puis redessiner, on redessinait seulement les parties modifiées et ce qu’il y avait autour ou en dessous. C’était parce qu’on n’avait pas le temps de rafraîchir tout l’écran à temps

    • À l’époque, il y avait au moins deux autres techniques
      La première consistait à écrire dans un autre buffer, puis, une fois la frame terminée, à copier tout le buffer d’un coup afin que chaque pixel ne change qu’une seule fois. Ce buffer pouvait souvent se trouver en RAM normale plutôt qu’en mémoire vidéo
      La seconde consistait à écrire dans un autre buffer situé en mémoire vidéo, puis à modifier les registres du matériel graphique pour que les pixels affichés à l’écran soient générés à partir de ce buffer
      Les compromis étaient différents. La copie du buffer entier coûtait cher, alors que le changement de registre d’adresse coûtait peu. Mais les détails des registres pouvaient dépendre du matériel, et il n’existait pas de véritable framework de pilotes graphiques
      Pour simplement faire un « buffer flip », il fallait quand même envoyer les pixels en mémoire vidéo lors du rendu dans le buffer hors écran, et il me semble que l’accès à la mémoire vidéo était plus lent que l’accès à la RAM normale. C’était pratiquement une architecture NUMA, donc selon la fréquence à laquelle les pixels étaient redessinés, il pouvait être globalement plus rapide de faire le rendu en RAM normale, même en comptant la copie finale
    • J’ai appris cette méthode en programmant sur ComputerCraft, elle m’a ensuite servi à plusieurs reprises, et parfois même au travail
      Cela dit, je pense que la possibilité d’indiquer au terminal le début et la fin d’une frame est bien plus puissante
  • Je ne comprends vraiment pas pourquoi les ingénieurs logiciel s’intéressent autant aux TUI. J’aime les bons programmes en ligne de commande, mais les TUI ne m’attirent pas

    • En dehors du web, une TUI est en pratique une autre boîte à outils GUI cross-platform universellement installée
      En plus, il y a un côté hacker. Le style n’est pas fortement séparé du contenu, et les options de style ajustables sont limitées, donc les applications TUI convergent en général vers un style unique. Un style qui évoque les films de hackers et la bonne vieille SF
      Ce n’est pas non plus le terrain préféré des designers d’entreprise, et les sociétés ne concluent pas des ventes auprès des entreprises ou du grand public sur la base d’une TUI. Faute de cette pression commerciale, les TUI sont conçues par des développeurs pour des développeurs
      Du coup, le mème TUI s’auto-renforce. Les développeurs voient et utilisent des TUI en sentant qu’il s’agit généralement d’outils pensés pour d’autres développeurs, puis ils ont envie de créer leur propre TUI
    • C’est une question de goût. Cela dit, pouvoir exécuter l’application via SSH est un gros avantage. Les gens comme moi aiment une expérience rapide et centrée sur le clavier, que l’on pourrait aussi fournir dans une application GUI, mais qu’on ne fournit généralement pas
    • Il n’y a pas beaucoup d’avantages évidents à utiliser une TUI, mais il y a de grands avantages à en écrire une. C’est plus simple à bien des égards
      Le cross-platform est presque gratuit, il n’y a pas besoin de créer une GUI parfaite au pixel près, ce qui est de toute façon impossible, et il n’y a pas besoin d’icônes ni de graphismes. Comme on ne peut pas afficher de graphismes
    • C’est utile quand on s’exécute dans le terminal tout en ayant besoin de plus d’interface utilisateur qu’une simple ligne de commande. Parfois, une TUI est plus rapide qu’une ligne de commande et montre mieux la situation d’ensemble
    • J’utilise beaucoup le CLI, mais certaines tâches se prêtent mieux à une GUI, et la TUI est un entre-deux entre GUI et CLI
      On peut garder son émulateur de terminal avec sa police préférée tout en ayant une interface de type GUI
      Toutes les applications TUI que j’ai utilisées jusqu’ici avaient une faible latence. Les applications GUI sont généralement rapides elles aussi, mais toutes celles que j’ai trouvées péniblement lentes étaient des applications GUI
  • Il existe ascii-draw comme alternative à Monodraw
    https://github.com/Nokse22/ascii-draw

  • Presque tout ce que j’ai vu après TurboVision s’en inspirait, mais manquait en pratique de finition. Quand on utilise vraiment la boîte à outils, c’est mignon, mais on a l’impression que ce n’est pas abouti
    Autrement dit, on pourrait dire que beaucoup de frameworks TUI que j’ai vus étaient des frameworks « très prescriptifs »
    C’est peut-être juste moi qui suis lent et peu créatif, mais depuis mon passage du DOS à Linux dans les années 90, je n’ai jamais été aussi productif qu’à l’époque où je travaillais avec B800
    Il m’a peut-être fallu longtemps pour comprendre la nécessité du double buffering, la différence entre le mode texte local direct et un terminal, jusqu’aux séquences d’échappement. Malgré ça, chaque fois que j’essayais de faire quelque chose moi-même avec ncurses, je finissais presque par abandonner à cause d’un sentiment d’insatisfaction très net. Cela n’avait rien à voir avec ce qu’on pouvait faire au-delà du simple idéal de B800

    • Je me suis mis à la programmation GUI avec Win16, puis Win32, à l’époque de la transition vers WinXP
      Je ne sais pas si ma vision était bouchée à ce point, mais tous les frameworks GUI après la boucle de messages m’ont toujours semblé complètement incompréhensibles
      En particulier, je n’ai jamais compris comment les frameworks orientés objet résolvaient réellement les problèmes d’UI multithread. Sous Win32, je mettais le renderer principal dans un thread, les renderers secondaires construisaient le modèle à côté, puis ne mettaient à jour que les différences. Le code n’est jamais devenu ingérable
    • Je vois ce qu’est Turbo Vision, mais je me demande ce qu’est B800. J’imagine que ce n’est pas le processeur Celeron, et c’est difficile à chercher, il ne semble même pas y avoir de page wiki
  • Si on va lancer kitty, on peut faire bien plus que ça : https://m.youtube.com/watch?v=ft1Q-DwGWIs
    https://notcurses.com/

    • Je préfère WezTerm à Kitty, à cause de l’attitude de l’auteur de Kitty face aux demandes de fonctionnalités et aux pull requests
      Les deux peuvent afficher des graphiques avec le même protocole. Si on a vraiment besoin de graphiques, le terminal n’est presque jamais la bonne solution. Cela dit, pour de petites choses comme des icônes, ça peut parfois être utile
  • C’est intéressant de voir qu’ils recrutent. Je me demande comment ils comptent gagner de l’argent avec un framework TUI

    • Puisqu’ils ont aussi mentionné une exécution sur le web, j’imagine qu’ils essaient d’aider à migrer d’anciens TUI. Il y a pas mal d’argent de conseil dans la maintenance de logiciels d’entreprise anciens
    • Ces postes ont été pourvus puis supprimés plus tard : https://blog.davep.org/2024/03/28/goodbye-textualize.html#go...
    • Le lien de recrutement renvoie une erreur 404
    • Même question. Ils semblent faire des logiciels assez cool, mais les ingénieurs coûtent cher et il existe déjà d’autres frameworks
  • Pour info, j’ai évalué une douzaine de bibliothèques TUI compilées, et FTXUI était la plus simple à utiliser et la plus stable
    https://github.com/ArthurSonzogni/FTXUI
    C’est un bon outil pour créer des tableaux de bord interactifs avec prise en charge du clavier et de la souris à la fois