3 points par GN⁺ 2023-12-10 | 1 commentaires | Partager sur WhatsApp
  • Gooey transforme des applications console Python 3 en applications GUI faciles à utiliser pour les utilisateurs finaux, en ajoutant simplement une ligne de décorateur à une fonction contenant une déclaration argparse
  • L’installation de base se fait avec pip install Gooey, et en utilisant le décorateur @Gooey avec ArgumentParser, Gooey lit les arguments et construit une interface basée sur des composants WX
  • Quand la sélection de widgets par défaut ne suffit pas, on peut remplacer ArgumentParser par GooeyParser et préciser l’argument widget pour choisir des widgets personnalisés comme FileChooser, DateChooser, PasswordField ou Slider
  • Le public visé n’est pas tant les outils pour développeurs manipulant directement la CLI, mais plutôt les scripts bureautiques de type « exécuter puis terminé » ou les outils destinés à des non-programmeurs ; Gooey prend en charge les modes d’exécution avancé / basique / sans configuration, ainsi que les menus, l’affichage de progression, l’internationalisation, le changement d’icônes et le packaging
  • La validation dynamique et les événements de cycle de vie sont indiqués comme des fonctions expérimentales ; optparse n’est pas pris en charge actuellement, et l’outil part du principe d’un code basé sur ArgumentParser avec abonnement optionnel aux événements

Comment transformer un programme console en GUI

  • Gooey convertit les applications console en applications GUI faciles à manipuler pour les utilisateurs finaux
  • L’usage principal consiste à ajouter le décorateur @Gooey à une fonction contenant une déclaration argparse
from gooey import Gooey

@Gooey
def main():
  parser = ArgumentParser(...)

# rest of code
  • Le style et le comportement peuvent être ajustés via les arguments du décorateur
    • advanced : afficher ou non l’écran de configuration avancée
    • language : choisir une traduction basée sur JSON
    • auto_start : ignorer l’écran de configuration et lancer immédiatement
    • target : définir la commande à exécuter dans un sous-processus
    • program_name, program_description : nom et description affichés dans la GUI
    • default_size : taille initiale de la GUI
    • dump_build_config, load_build_config : enregistrer / charger la configuration de build Gooey en JSON
    • monospace_display : utiliser une police à chasse fixe pour l’écran de sortie

Installation et exemples

  • La méthode d’installation la plus simple est pip
pip install Gooey
  • Il est aussi possible de cloner le dépôt puis d’installer via setup.py
git clone https://github.com/chriskiehl/Gooey.git
python setup.py install
  • Des scripts d’exemple prêts à l’emploi sont disponibles dans le dépôt Gooey Examples
  • Ce dépôt d’exemples sert à parcourir différents layouts, widgets et fonctionnalités de Gooey

Pourquoi est-ce utile

  • Gooey a été conçu pour réduire le problème consistant à exposer une invite de commande à des utilisateurs peu familiers avec celle-ci
  • Lorsqu’un programme doit effectuer une ou plusieurs tâches, il a besoin d’options ; jusque-là, il fallait soit créer directement une GUI, soit expliquer comment passer des arguments à une application console
  • Gooey permet aux développeurs de créer des programmes configurables avec des méthodes qui leur sont familières, tout en fournissant une interface GUI pour l’affichage et l’interaction

Utilisateurs adaptés et cas moins pertinents

  • Gooey peut ne pas convenir aux utilitaires destinés à soi-même ou à d’autres programmeurs, ni aux outils produisant des résultats à chaîner vers d’autres applications console
  • Il convient en revanche aux scripts bureautiques, aux outils de transfert de données d’un point A à un point B, et aux programmes destinés à des non-programmeurs
  • L’objectif est de créer des applications complexes tout en obtenant la partie GUI sans coût supplémentaire

Fonctionnement interne et correspondance avec argparse

  • Gooey analyse à l’exécution les références à ArgumentParser dans le script Python
  • optparse n’est pas pris en charge actuellement
  • Les arguments extraits se voient attribuer un type de composant en fonction de l’action fournie, puis sont utilisés pour construire la GUI
  • ArgumentParser._actions est mappé par défaut vers les composants WX suivants
    • store : TextCtrl
    • store_const, store_true, store_False, version : CheckBox
    • append : TextCtrl
    • count : DropDown
    • Mutually Exclusive Group : RadioGroup
    • choice : DropDown

GooeyParser et widgets personnalisés

  • Si la sélection de widgets par défaut ne suffit pas, on peut utiliser GooeyParser, un remplacement direct de ArgumentParser
  • GooeyParser fournit un argument nommé supplémentaire, widget, qui permet de préciser le nom du composant à afficher
from gooey import Gooey, GooeyParser

@Gooey
def main():
  parser = GooeyParser(description="My Cool GUI Program!")
  parser.add_argument('Filename', widget="FileChooser")
  parser.add_argument('Date', widget="DateChooser")
  • Les widgets personnalisés pris en charge incluent
    • DirChooser, FileChooser, MultiFileChooser
    • FileSaver, MultiFileSaver
    • DateChooser, TimeChooser
    • PasswordField
    • Listbox
    • BlockCheckbox
    • ColourChooser
    • FilterableDropdown
    • IntegerField, DecimalField
    • Slider
  • Les valeurs transmises à l’application par DateChooser et TimeChooser sont toujours au format ISO, même si certaines parties de la GUI peuvent afficher des valeurs localisées selon les paramètres de l’utilisateur final
  • BlockCheckbox déplace le bloc de texte à sa position normale quand le texte d’aide est long, pour une présentation plus claire qu’avec la case à cocher en ligne par défaut, et affiche un block_label court à côté du contrôle

Internationalisation

  • Gooey sélectionne la langue d’affichage via l’argument language du décorateur
@Gooey(language='russian')
def main():
    ...
  • Tous les textes du programme sont stockés dans des fichiers JSON externes
  • Pour ajouter une nouvelle langue, il faut ajouter des paires clé/valeur dans le répertoire gooey/languages/
  • Plus de 18 traductions sont actuellement fournies par défaut

Configuration globale et composition de l’interface

  • L’apparence et le comportement globaux de Gooey se règlent via les arguments du décorateur
  • Les principales options incluent
    • encoding : encodage des caractères affichés, utf-8 par défaut
    • advanced : choisir l’écran de configuration complet ou une vue simplifiée
    • auto_start : lancer immédiatement sans configuration
    • target : définir comment Gooey se relance lui-même
    • suppress_gooey_flag : empêcher l’injection de paramètres CLI supplémentaires lors de l’utilisation d’un target personnalisé
    • fullscreen : démarrer en plein écran
    • image_dir : répertoire d’images / d’icônes personnalisées
    • language_dir : répertoire de fichiers de langue personnalisés
    • disable_stop_button, show_stop_warning : contrôler le bouton d’arrêt et l’avertissement pendant l’exécution
    • show_success_modal, show_failure_modal : afficher ou non des fenêtres récapitulatives de succès / d’échec
    • run_validators : exécuter les validations avant l’appel du programme
    • progress_regex, progress_expr : analyser et convertir le texte de progression
    • navigation : TABBED ou SIDEBAR
    • body_bg_color, header_bg_color, footer_bg_color, sidebar_bg_color : configuration des couleurs
    • terminal_font_family, terminal_font_size, terminal_font_color : réglages de police pour l’affichage terminal
    • menus : groupes et éléments de menu personnalisés
    • clear_before_run : effacer la sortie précédente lors d’une nouvelle exécution

Layouts et modes d’exécution

  • Gooey répartit par défaut les entrées en deux groupes : positional et optional
  • Avec add_argument_group() d’argparse, on peut regrouper les entrées de façon logique et ajuster le layout de chaque groupe
parser = ArgumentParser()
search_group = parser.add_argument_group(
    "Search Options",
    "Customize the search options"
)
search_group.add_argument(
    '--query',
    help='Base search string'
)
  • Les options de layout global prises en charge incluent show_sidebar=True, show_sidebar=False, navigation='TABBED', tabbed_groups=True
  • Advanced

    • L’écran par défaut est l’écran de configuration full/advanced
    • Pour la plupart des applications, un layout plat correspondant à la structure de la CLI est adapté
    • Pour les CLI composées de plusieurs chemins ou de petits outils assemblés, un layout en colonnes convient mieux
    • Le layout en colonnes affiche le chemin principal dans la colonne de gauche et les arguments correspondants dans la colonne de droite
    • Le layout actuel ne peut pas être explicitement défini par paramètre ; il est déterminé selon la présence ou non de subparsers dans le code
  • Basic

    • En définissant advanced=False, on passe à la vue basique
    • Ce mode convient lorsque les utilisateurs sont familiers des applications console mais souhaitent une interface plus soignée qu’un simple terminal
    @gooey(advanced=False)
    def main():
    
    # rest of code
    
  • No Config

    • Avec auto_start=True, on passe directement à la zone d’affichage sans écran de configuration, puis le programme hôte est exécuté
    • Cela sert à améliorer l’apparence de petits scripts ponctuels
    @Gooey(auto_start=True)
    def main():
        ...
    

Fonctionnalités de menu

  • Depuis Gooey 1.0.2, on peut ajouter une barre de menu en haut de la fenêtre
  • Les menus se définissent sous la forme @Gooey(menu=[{}, {}, ...]), c’est-à-dire une liste de maps
  • Chaque groupe de menu possède name et items
  • Chaque élément de menu contient toujours type et menuTitle
  • Les types d’éléments de menu pris en charge sont
    • AboutDialog : afficher les informations sur le programme, comme le nom, la version ou la licence
    • MessageDialog : afficher un message informatif à l’utilisateur
    • Link : ouvrir l’URL indiquée dans le navigateur par défaut
    • HtmlDialog : afficher une boîte de dialogue à partir d’un sous-ensemble limité de HTML

Validation dynamique

  • Dynamic Validation est une fonctionnalité expérimentale dont l’API peut changer ou être supprimée
  • Gooey peut éventuellement effectuer une validation préalable des saisies utilisateur avant de les transmettre au programme
  • Cette fonctionnalité exploite le paramètre type, utilisable avec la plupart des types d’arguments Argparse
def must_be_exactly_ten(value):
    number = int(value)
    if number == 10:
        return number
    else:
        raise TypeError("Hey! you need to provide exactly the number 10!")
  • La validation n’est pas exécutée par défaut
  • Pour l’activer, il faut s’abonner à l’événement VALIDATE_FORM
from gooey import Gooey, Events

@Gooey(use_events=[Events.VALIDATE_FORM])
def main():
    ...
  • Cette fonctionnalité repose en interne sur un monkey patching important, ce qui explique son caractère actuellement opt-in

Événements de cycle de vie et contrôle de l’interface

  • Depuis Gooey 1.2.0, le programme expose des hooks de cycle de vie
  • Cette fonctionnalité est elle aussi expérimentale, et son API peut changer ou être supprimée
  • Il existe actuellement deux hooks principaux
    • on_success
    • on_error
  • Ces deux hooks sont exécutés une fois le processus terminé
  • Les handlers reçoivent l’objet Argparse parsé et l’état courant de la GUI Gooey ; s’ils renvoient un état mis à jour, celui-ci peut être répercuté dans l’interface
  • Les handlers se connectent lors de la création du GooeyParser
parser = GooeyParser(
    on_success=my_success_handler,
    on_failure=my_failure_handler)
  • Pour les utiliser, il faut s’abonner explicitement aux événements
from gooey import Gooey, Events

@Gooey(use_events=[Events.ON_SUCCESS, Events.ON_ERROR])
def main():
    ...

Progression et affichage du temps

  • Gooey peut lire une sortie texte de progression existante pour piloter une barre de progression
  • Dans les cas simples, une chaîne comme Progress 83% peut être reconnue via une expression régulière puis convertie en progression
@Gooey(progress_regex=r"^progress: (\d+)%$")
  • Pour des sorties plus complexes, progress_expr permet de transformer les résultats de la regex
@Gooey(progress_regex=r"^progress: (?P<current>\d+)/(?P<total>\d+)$",
       progress_expr="current / total * 100")
  • Si hide_progress_msg=True est spécifié, la sortie texte correspondant à la progression peut être masquée dans la console
  • timing_options permet d’afficher le temps écoulé et le temps restant
  • L’affichage du temps ne fonctionne que lors de l’utilisation de progress_regex et progress_expr

Icônes et packaging

  • Gooey fournit six icônes par défaut, que l’on peut remplacer en définissant un répertoire d’images personnalisé via l’argument image_dir
  • Les images sont recherchées par nom de fichier, et les noms remplaçables sont les suivants
    • program_icon.png
    • success_icon.png
    • running_icon.png
    • loading_icon.gif
    • config_icon.png
    • error_icon.png
  • Un exemple de packaging d’exécutable avec PyInstaller est fourni
  • La procédure résumée consiste à placer build.spec à la racine de l’application, à adapter APPPNAME, name et pathex au projet, puis à exécuter la commande suivante
pyinstaller -F --windowed build.spec

1 commentaires

 
GN⁺ 2023-12-10
Avis sur Hacker News
  • Euh, c’est mon projet ^_^ Pourquoi est-il en haut de HN ?
    Pour répondre brièvement aux commentaires sur argparse : Gooey est désormais un projet assez ancien, et au moment où il a commencé, argparse était un choix solide.
    Aujourd’hui, Gooey lui-même fonctionne avec du JSON et est découplé d’argparse, mais comme presque personne n’a créé d’autres interfaces, argparse reste encore l’interface « officielle » principale.
    Détail amusant : il peut appeler non seulement Python, mais aussi n’importe quel exécutable, ce qui le rend assez utile : https://chriskiehl.com/article/gooey-as-a-universal-frontend
    Quant au fait que le dernier commit remonte à deux ans, quand on vieillit et que les priorités changent, il devient difficile de trouver une raison de continuer à maintenir gratuitement un logiciel de niche :( Je ne sais pas si c’est consolant, mais je me sens toujours coupable.
    Étrangement, il y a des graffitis « GOOEY » un peu partout dans mon quartier, ce qui fait comme un rappel permanent de l’issue tracker laissé à l’abandon, haha.

    • argparse reste un très bon choix. Il est extrêmement répandu, bien documenté et relativement simple.
      Typer et Click sont aussi agréables à utiliser, mais je trouve que la documentation façon « tutoriel » de Typer est assez difficile à parcourir quand on cherche une réponse du genre : quels arguments prend Typer.App() ?
      Le programme sur lequel je travaille en ce moment a une structure où l’utilisateur lance lui-même le parsing des arguments, donc argparse convient bien. J’aimerais proposer aux utilisateurs une UI en mode texte, mais il me semble que Textualize nécessitait Click, et même en essayant de brancher directement Textual dans le code pour créer l’UI, j’y ai passé des heures sans même réussir à démarrer.
    • C’est une idée vraiment excellente. J’aime les interfaces découplées de l’API.
      Il y a aujourd’hui trop de frustrations dues au fait qu’on ne peut pas contrôler les applications par programmation. Honnêtement, j’aimerais qu’il existe une loi imposant une API pour toutes les fonctionnalités d’une application.
    • Super projet ! J’ai une question : pourquoi dumper sys.argv dans un fichier local du répertoire de travail courant ? https://github.com/chriskiehl/Gooey/blob/be4b11b8f27f500e732...
      tmp.txt est loin d’être un nom unique ; je me demande si quelque chose m’échappe.
    • La partie « Aujourd’hui, Gooey lui-même fonctionne avec du JSON et est découplé d’argparse » est excellente.
      Avant de cliquer, rien qu’au titre, je me disais : « ça va forcément être fortement dépendant d’argparse », et j’ai été agréablement surpris.
      Je suis un fervent fan de getopt, parce que je considère que le confort de l’utilisateur est bien plus important que celui du développeur. L’utilisateur ne devrait pas avoir à se soucier du langage de programmation ou de la bibliothèque de parsing d’options choisis par le développeur.
      argparse, Click, le flag de Go / le style X11 et beaucoup d’autres bibliothèques cassent des conventions ancrées dans un demi-siècle de mémoire musculaire collective. Mais avec une couche intermédiaire, on dirait qu’on peut avoir le beurre et l’argent du beurre.
    • Si Gooey est très ancien, c’est parce qu’il a été laissé à l’abandon. Je ne dis pas ça pour blâmer qui que ce soit, c’est simplement la réalité.
      Le pire, c’est qu’aucune alternative viable n’a émergé pour le remplacer, et qu’aucun des deux forks n’a vraiment gagné en traction.
  • Est-ce qu’il ne faudrait pas préciser « basé sur argparse » ?
    argparse est bien pour les usages simples, mais il existe aussi beaucoup de CLI basées sur Click, et une bonne partie des bibliothèques CLI populaires sont construites au-dessus de Click. Il existe aussi des outils CLI Python qui ne sont ni argparse ni Click, même si ces deux-là sont probablement parmi les plus populaires.
    A-t-on vérifié que ça fonctionne aussi avec Click ? Pour l’instant, je ne vois que des éléments liés à argparse. Si la réponse est non, l’expression « presque tous » est manifestement fausse, et un titre plus exact serait « Transformer presque tous les programmes Python en ligne de commande basés sur argparse en applications GUI complètes ».
    Il y a aussi le fait que le dernier commit significatif remonte à plus de deux ans. Ce n’est pas mauvais en soi, mais vu le nombre d’issues ouvertes, ça ne donne pas énormément confiance dans le projet.
    Cela dit, c’est un beau projet. J’aimerais voir davantage de choses comme ça. Comme le dit le README, c’est un outil démultiplicateur de force : quelqu’un peut créer une automatisation en CLI et la partager facilement avec des personnes non techniques dans un bureau. C’est un peu le rôle qu’Access a joué pour les bases de données.
    Comme avec Access, dès qu’il y aura de l’échelle ou de la complexité, on atteindra ses limites, mais pour de petits usages de bureau, cela peut être « suffisamment bon ».

    • Je suis curieux de savoir ce que tu trouves insuffisant dans argparse. De mon côté, j’ai plutôt tendance à minimiser les dépendances externes.
    • Je l’ai utilisé exactement pour ça dans mon précédent poste. J’automatisais beaucoup de tâches et créais des outils pour l’équipe, mais comme la plupart n’étaient pas programmeurs Python, j’ajoutais une GUI simple au-dessus des scripts avec Gooey. Ça marchait très bien.
    • Qu’est-ce qu’on ne peut pas faire exactement avec argparse ?
  • Articles liés :
    Gooey: Turn almost any Python command line program into a GUI application - https://news.ycombinator.com/item?id=27490291 - juin 2021, 115 commentaires
    Gooey: Turn command line programs into GUI applications - https://news.ycombinator.com/item?id=8218785 - août 2014, 74 commentaires

  • Ce serait bien que les systèmes d’exploitation et les shells puissent mieux comprendre la façon dont les programmes s’exécutent, sans dépendre d’une bibliothèque de parsing particulière
    Ce serait bien que les programmes communiquent dans un format JSON/proto typé, et qu’en inspectant simplement les types d’entrée et de sortie attendus, on puisse obtenir une structuration et de l’autocomplétion des commandes shell, voire une GUI complète
    Pour l’instant, le mieux qu’on semble pouvoir faire ressemble à quelque chose bricolé prudemment à la paille. Chaque programme doit exporter des fichiers d’autocomplétion pour plusieurs shells, les styles de flags varient fortement selon les programmes et les bibliothèques de parsing, et bien sûr il n’y a pas de GUI

    • Il y a maintenant quelque chose qui s’appelle PowerShell. Plus sérieusement, le seul système d’exploitation qui ait vraiment tenté ce genre de chose est probablement Plan 9
    • Tu parles d’une sorte d’échange gRPC / API CLI généraliste avec prise en charge native par l’OS ? Ça pourrait être assez élégant
      Cela dit, le principal problème de gRPC est sans doute que beaucoup de développeurs, moi y compris, le trouvent un peu plus lourd à manier que les solutions qu’on utilise naturellement dans un langage
    • Ça vaut le coup d’essayer PowerShell. C’est, en gros, l’écosystème .NET de Microsoft façonné en ligne de commande interactive
      Je ne sais pas si PowerShell peut exploiter pleinement le typage statique qui en constitue le cœur, mais sa capacité à faire circuler des objets en ligne de commande est presque unique
      Sous Linux, on peut chaîner des commandes en combinant jc(https://github.com/kellyjonbrazil/jc) et jq(https://jqlang.github.io/jq/), mais cela demande encore deux étapes de traitement de plus par rapport aux fonctionnalités intégrées de PowerShell
  • Une GUI, c’est parfois comme de la comfort food. On peut simplement parcourir une interface graphique, sans la charge cognitive que peut imposer une CLI qu’on utilise rarement
    C’est un peu à côté, mais j’ai découvert ici l’éditeur CUDATEXT il y a quelques mois ; il permet d’utiliser, via une API Python dans un seul fichier, des éléments de GUI arbitraires comme MENU ou INPUTS. Ce sont les mêmes éléments que ceux utilisés par l’éditeur lui-même
    Désormais, j’utilise ces éléments graphiques simples pour configurer des choses et générer mon blog directement dans l’éditeur

  • À voir aussi :
    « Textual : un framework d’application léger pour Python. Créez des interfaces utilisateur sophistiquées avec une API Python simple, et exécutez vos apps dans le terminal et dans un navigateur web » https://github.com/textualize/textual/

    • Il existe aussi quelque chose de similaire à Gooey : https://github.com/Textualize/trogon « Transformer facilement des CLI Click en puissantes applications de terminal »
    • Waouh, comment est-ce possible ? L’exemple dans le terminal est bien plus détaillé que ce que j’imaginais
    • Il semble que l’ajout de la prise en charge de Textual à camply ait été assez simple. Ce n’est pas moi qui l’ai implémenté, je contribue seulement au projet de temps en temps : https://juftin.com/camply/command_line_usage/#tui
      camply : https://juftin.com/camply/
  • Je voulais dire merci d’avoir créé cet excellent logiciel. J’aime Gooey et je l’ai beaucoup utilisé dans plusieurs programmes
    Pour moi, il correspond parfaitement à ce cas précis où un script Python fait quelque chose d’important, et où il faut maintenant le rendre utilisable par des non-programmeurs

  • Ça me fait penser aux naked objects. L’idée était qu’en définissant quelques annotations et un thème sur des classes Java, on générait une GUI complète, ou une application web front-end
    C’était une idée très élégante, mais à ma connaissance elle n’a finalement pas vraiment pris

    • Django suit aussi une philosophie similaire. On définit un modèle, il se reflète directement dans des tables de base de données, et on peut générer automatiquement des formulaires à partir de ce modèle
      À mon avis, le problème est que des préoccupations verticales comme la présentation, le domaine et l’infrastructure se retrouvent couplées. Quand la représentation visuelle et la représentation en base de données du modèle de domaine sont simples, ça va
      Mais si l’on veut construire une interface complexe avec beaucoup de fonctionnalités de confort, ces exigences doivent fuiter dans la couche domaine. À l’inverse, si l’on veut construire un modèle de domaine complexe, il devient irréaliste d’imposer les invariants au niveau du langage, car n’importe quel code ayant une référence vers Model peut le modifier arbitrairement
  • Ce serait bien d’avoir aussi l’inverse

    • Ce serait bien de pouvoir configurer un balayage de paramètres dans une GUI à partir des informations tirées d’argparse, puis de le reconvertir en script shell qui exécute et enregistre correctement les résultats
    • Tu veux dire un éditeur WYSIWYG qui recrache une CLI ? Ça pourrait être assez chouette, mais il y aurait forcément un certain nombre de contraintes. Je me demande si quelqu’un a déjà tenté cette approche, mais aucun exemple ne me vient à l’esprit
  • Dans l’ancien Macintosh Programmer's Workshop, ou MPW, le shell textuel de Mac OS dans les années 80 et 90, il existait une fonctionnalité similaire appelée Commando pour presque toutes les commandes
    Les informations de Commando étaient stockées dans des ressources cmdo. Si la prise en charge des attributs étendus n’avait pas été aussi inégale, on pourrait encore faire quelque chose de similaire aujourd’hui
    En cherchant, j’ai vu qu’A/UX avait aussi Commando. Quand on double-cliquait sur une commande de terminal dans le Finder, une boîte Commando permettant de choisir les options CLI s’ouvrait, puis la commande s’exécutait dans une fenêtre shell. Aujourd’hui, quand on lance une commande de terminal depuis le Finder, elle s’exécute simplement dans une fenêtre shell, sans argument
    https://cohost.org/boredzo/post/804893-i-still-want-a-moder
    http://toastytech.com/guis/aux3.html