3 pontos por GN⁺ 2023-12-10 | 1 comentários | Compartilhar no WhatsApp
  • Gooey transforma aplicações de console em Python 3 em aplicações GUI fáceis de usar para o usuário final, funcionando com a adição de uma única linha de decorador a uma função com declaração argparse
  • A instalação básica pode ser feita com pip install Gooey, e ao usar o decorador @Gooey junto com ArgumentParser, o Gooey lê os argumentos e monta uma UI baseada em componentes WX
  • Quando a seleção de widgets padrão não é suficiente, é possível usar GooeyParser como substituto do ArgumentParser e definir o argumento widget para escolher widgets personalizados como FileChooser, DateChooser, PasswordField e Slider
  • O público-alvo não são ferramentas para desenvolvedores que usam CLI diretamente, mas sim scripts de escritório do tipo “executar e pronto” ou ferramentas voltadas a não programadores; ele oferece modos de execução avançado/básico/sem configuração, além de menus, barra de progresso, internacionalização, troca de ícones e empacotamento
  • A validação dinâmica e os eventos de ciclo de vida são marcados como recursos experimentais, optparse não é compatível no momento, e o funcionamento parte de código baseado em ArgumentParser com assinatura opcional de eventos

Como transformar programas de console em GUI

  • Gooey converte aplicações de console em aplicações GUI fáceis de usar para o usuário final
  • O uso principal consiste em aplicar o decorador @Gooey a uma função que contém a declaração argparse
from gooey import Gooey

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

# rest of code
  • O estilo e o comportamento podem ser ajustados com argumentos do decorador
    • advanced: exibir ou não a tela de configurações avançadas
    • language: selecionar tradução baseada em JSON
    • auto_start: pular a tela de configuração e executar imediatamente
    • target: definir o comando a ser executado como subprocesso
    • program_name, program_description: nome e descrição exibidos na GUI
    • default_size: tamanho inicial da GUI
    • dump_build_config, load_build_config: salvar/carregar JSON de configuração de build do Gooey
    • monospace_display: usar fonte monoespaçada na tela de saída

Instalação e exemplos

  • A forma mais fácil de instalar é com pip
pip install Gooey
  • Também é possível instalar clonando o repositório e executando setup.py
git clone https://github.com/chriskiehl/Gooey.git
python setup.py install
  • Scripts de exemplo prontos podem ser obtidos no repositório Gooey Examples
  • O repositório de exemplos serve para explorar vários layouts, widgets e recursos do Gooey

Por que isso é necessário

  • O Gooey foi criado para reduzir o problema de expor o prompt de comando a usuários que não estão familiarizados com ele
  • Se um programa precisa realizar uma ou mais tarefas, ele precisa receber opções, e antes era necessário criar uma GUI manualmente ou explicar como passar argumentos para uma aplicação de console
  • O Gooey permite que desenvolvedores criem programas configuráveis da forma com que já estão acostumados, enquanto oferecem uma GUI para exibição e interação

Usuários adequados e casos inadequados

  • Gooey pode não ser adequado para utilitários feitos para você mesmo ou para outros programadores, nem para ferramentas que geram resultados para serem canalizados para outras aplicações de console
  • É adequado para scripts de escritório para executar, ferramentas que movem dados do ponto A ao B e programas voltados a não programadores
  • O objetivo é criar aplicações complexas e obter a parte de GUI sem custo adicional

Funcionamento interno e mapeamento do argparse

  • O Gooey analisa em tempo de execução as referências a ArgumentParser em scripts Python
  • optparse não é compatível no momento
  • Os argumentos extraídos recebem um tipo de componente de acordo com a action fornecida e são usados na composição da GUI
  • ArgumentParser._actions é mapeado por padrão para os seguintes componentes WX
    • store: TextCtrl
    • store_const, store_true, store_False, version: CheckBox
    • append: TextCtrl
    • count: DropDown
    • Mutually Exclusive Group: RadioGroup
    • choice: DropDown

GooeyParser e widgets personalizados

  • Se a seleção de widgets padrão não for suficiente, você pode usar GooeyParser, um substituto compatível com ArgumentParser
  • GooeyParser oferece o argumento adicional widget para definir o nome do componente a ser exibido
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")
  • Os widgets personalizados compatíveis incluem
    • DirChooser, FileChooser, MultiFileChooser
    • FileSaver, MultiFileSaver
    • DateChooser, TimeChooser
    • PasswordField
    • Listbox
    • BlockCheckbox
    • ColourChooser
    • FilterableDropdown
    • IntegerField, DecimalField
    • Slider
  • Os valores que DateChooser e TimeChooser passam para a aplicação estão sempre em formato ISO, embora partes da GUI possam mostrar valores localizados conforme a configuração do usuário final
  • BlockCheckbox move o bloco de texto para a posição normal e exibe um block_label curto ao lado do controle, para ficar visualmente melhor do que um checkbox inline padrão quando há textos de ajuda longos

Internacionalização

  • O Gooey seleciona o idioma de exibição com o argumento language do decorador
@Gooey(language='russian')
def main():
    ...
  • Todo o texto do programa é armazenado em arquivos JSON externos
  • O suporte a novos idiomas é feito adicionando pares chave/valor no diretório gooey/languages/
  • Atualmente, mais de 18 traduções são fornecidas por padrão

Configurações globais e composição da UI

  • A aparência e o comportamento gerais do Gooey são ajustados com argumentos do decorador
  • As principais configurações incluem
    • encoding: codificação de caracteres exibidos, padrão utf-8
    • advanced: escolher entre tela completa de configurações ou tela simplificada
    • auto_start: executar imediatamente sem configuração
    • target: definir como o Gooey deve executar a si mesmo novamente
    • suppress_gooey_flag: impedir a injeção de parâmetros CLI adicionais ao usar target personalizado
    • fullscreen: iniciar em tela cheia
    • image_dir: diretório de imagens/ícones personalizados
    • language_dir: diretório de arquivos de idioma personalizados
    • disable_stop_button, show_stop_warning: controlar o botão de parar e o aviso durante a execução
    • show_success_modal, show_failure_modal: exibir ou não modais de resumo de sucesso/falha
    • run_validators: executar validação antes de chamar o programa
    • progress_regex, progress_expr: análise e conversão de texto de progresso
    • navigation: TABBED ou SIDEBAR
    • body_bg_color, header_bg_color, footer_bg_color, sidebar_bg_color: configurações de cor
    • terminal_font_family, terminal_font_size, terminal_font_color: configurações de fonte da área de terminal
    • menus: grupos e itens de menu personalizados
    • clear_before_run: apagar a saída anterior ao executar novamente

Layouts e modos de execução

  • O Gooey divide as entradas por padrão em dois grupos: positional e optional
  • Ao usar add_argument_group() do argparse, é possível agrupar entradas logicamente e ajustar o layout de cada grupo
parser = ArgumentParser()
search_group = parser.add_argument_group(
    "Search Options",
    "Customize the search options"
)
search_group.add_argument(
    '--query',
    help='Base search string'
)
  • As opções de layout global compatíveis incluem show_sidebar=True, show_sidebar=False, navigation='TABBED' e tabbed_groups=True
  • Advanced

    • A tela padrão é a de configuração completa/avançada
    • Para a maioria das aplicações, um layout plano que combine com a estrutura da CLI é adequado
    • Para CLIs compostas por vários caminhos ou combinações de pequenas ferramentas, um layout em colunas é mais apropriado
    • O layout em colunas mostra o caminho principal na coluna da esquerda e os argumentos correspondentes na direita
    • No momento, o layout não pode ser definido explicitamente por parâmetro; ele é configurado conforme a existência de subparsers no código
  • Basic

    • Definir advanced=False alterna para a visualização básica
    • É adequado quando o usuário está familiarizado com aplicações de console, mas quer uma interface mais refinada do que um terminal simples
    @gooey(advanced=False)
    def main():
    
    # rest of code
    
  • No Config

    • Definir auto_start=True pula a tela de configuração e vai direto para a área de exibição, executando o programa hospedeiro
    • Isso é usado para melhorar a aparência de pequenos scripts descartáveis
    @Gooey(auto_start=True)
    def main():
        ...
    

Recurso de menu

  • Desde o Gooey 1.0.2, é possível adicionar uma Menu Bar no topo
  • Os menus são definidos como uma lista de mapas no formato @Gooey(menu=[{}, {}, ...])
  • Cada grupo de menu possui name e items
  • Cada item de menu sempre inclui type e menuTitle
  • Os tipos de item de menu compatíveis são
    • AboutDialog: exibe informações do programa, como nome, versão e licença
    • MessageDialog: mostra uma mensagem informativa ao usuário
    • Link: abre a URL especificada no navegador padrão
    • HtmlDialog: exibe um diálogo usando um subconjunto limitado de HTML

Validação dinâmica

  • Dynamic Validation é um recurso experimental e sua API pode mudar ou ser removida
  • O Gooey pode opcionalmente fazer validação prévia antes de enviar a entrada do usuário ao programa
  • Esse recurso aproveita o parâmetro type, disponível na maioria dos tipos de argumento do 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!")
  • A validação não é executada por padrão
  • Para ativá-la, é preciso assinar o evento VALIDATE_FORM
from gooey import Gooey, Events

@Gooey(use_events=[Events.VALIDATE_FORM])
def main():
    ...
  • Atualmente, esse recurso é opt-in porque usa Monkey Patching pesado internamente

Eventos de ciclo de vida e controle da UI

  • Desde o Gooey 1.2.0, o programa expõe hooks de ciclo de vida
  • Esse recurso também é experimental e sua API pode mudar ou ser removida
  • No momento, existem dois hooks principais
    • on_success
    • on_error
  • Ambos são executados após a conclusão do processo
  • Os handlers recebem o objeto Argparse já analisado e o estado atual da UI do Gooey; se retornarem um estado atualizado, ele poderá ser refletido na interface
  • Os handlers são conectados ao criar o GooeyParser
parser = GooeyParser(
    on_success=my_success_handler,
    on_failure=my_failure_handler)
  • Para usar, é necessário assinar explicitamente os eventos
from gooey import Gooey, Events

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

Progresso e exibição de tempo

  • O Gooey pode ler saídas de progresso em texto já existentes para alimentar uma Progress Bar
  • Em casos simples, ele converte o progresso correspondendo a strings como Progress 83% via expressão regular
@Gooey(progress_regex=r"^progress: (\d+)%$")
  • Saídas mais complexas podem ser convertidas com progress_expr, usando os resultados capturados pela regex
@Gooey(progress_regex=r"^progress: (?P<current>\d+)/(?P<total>\d+)$",
       progress_expr="current / total * 100")
  • Se hide_progress_msg=True for definido, o texto de saída que corresponder ao progresso pode ser ocultado no console
  • Com timing_options, é possível exibir tempo decorrido e tempo restante
  • A exibição de tempo só funciona ao usar progress_regex e progress_expr

Ícones e empacotamento

  • O Gooey fornece 6 ícones padrão, e eles podem ser substituídos definindo um diretório de imagens personalizado com o argumento image_dir
  • As imagens são buscadas pelo nome do arquivo, e os nomes que podem ser sobrescritos são
    • program_icon.png
    • success_icon.png
    • running_icon.png
    • loading_icon.gif
    • config_icon.png
    • error_icon.png
  • O empacotamento em executável inclui um exemplo com PyInstaller
  • O procedimento resumido é colocar build.spec na raiz da aplicação, ajustar os valores de APPPNAME, name e pathex de acordo com o projeto e então executar
pyinstaller -F --windowed build.spec

1 comentários

 
GN⁺ 2023-12-10
Comentários no Hacker News
  • Ei, esse é o meu projeto ^_^ Por que ele está no topo do HN?
    Respondendo rapidamente aos comentários sobre argparse: o Gooey já é um projeto bem antigo, e na época em que começou argparse era uma escolha sólida
    Hoje em dia, o Gooey em si funciona com JSON e é desacoplado do argparse, mas quase ninguém criou outras interfaces, então o argparse ainda continua sendo a principal interface “oficial”
    Um ponto interessante é que ele pode chamar não só Python, mas também executáveis arbitrários, então é bem útil: https://chriskiehl.com/article/gooey-as-a-universal-frontend
    Sobre dizerem que o último commit foi há 2 anos: quando a gente envelhece e as prioridades mudam, fica difícil encontrar motivo para continuar mantendo de graça um software de nicho :( Não sei se serve de consolo, mas sempre sinto culpa
    Estranhamente, há grafites “GOOEY” por todos os lados no bairro onde moro, então parecem lembretes permanentes que me fazem lembrar do issue tracker abandonado haha

    • argparse ainda é uma escolha bem boa. É extremamente usado, tem boa documentação e é relativamente simples
      Typer e Click também têm boa usabilidade, mas sinto que a documentação em formato de “tutorial” do Typer é bem difícil de pesquisar quando se quer encontrar respostas como quais argumentos Typer.App() recebe
      O programa em que estou trabalhando agora tem uma estrutura em que o próprio usuário inicia o parsing de argumentos, então argparse se encaixa bem. Quero oferecer ao usuário uma UI baseada em texto como opção, mas acho que o Textualize exigia Click, e mesmo tentando criar uma UI acoplando o Textual diretamente ao código, gastei algumas horas e nem consegui começar
    • É uma ideia realmente excelente. Gosto de interfaces desacopladas da API
      Hoje há frustrações demais causadas pelo fato de não ser possível controlar aplicações programaticamente. Sinceramente, eu gostaria que existisse uma lei dizendo que toda funcionalidade de aplicação deve ter uma API
    • Projeto bacana! Fiquei curioso: por que vocês despejam sys.argv em um arquivo local no diretório de trabalho atual? https://github.com/chriskiehl/Gooey/blob/be4b11b8f27f500e732...
      tmp.txt está longe de ser um nome único; fico me perguntando se estou deixando passar alguma coisa
    • A parte de que “hoje em dia, o Gooey em si funciona com JSON e é desacoplado do argparse” é bacana
      Antes de clicar, só pelo título, pensei: “com certeza ele depende fortemente de argparse”, então fui surpreendido positivamente
      Sou um fã fervoroso de getopt, porque considero a conveniência do usuário muito mais importante do que a conveniência do desenvolvedor. O usuário não deveria precisar se importar com a linguagem de programação ou a biblioteca de parsing de opções que o desenvolvedor escolheu
      Muitas bibliotecas, como argparse, Click e o flag do Go/estilo X11, quebram convenções gravadas na memória muscular coletiva acumulada ao longo de meio século. Mas, com uma camada intermediária, parece possível ter o melhor dos dois mundos
    • O motivo de o Gooey ser tão antigo é que ele foi abandonado. Não digo isso como crítica, é só a realidade
      O pior é que também não surgiu nenhuma alternativa viável para substituí-lo, e nenhum dos dois forks ganhou traction
  • Isto não deveria ser qualificado como “baseado em argparse”?
    argparse é bom para usos simples, mas também há muitas CLIs baseadas em Click, e boa parte das bibliotecas populares de CLI é construída sobre Click. Também existem ferramentas de CLI em Python que não usam nem argparse nem Click, mas essas duas provavelmente estão entre as mais populares
    Foi confirmado que funciona com Click também? Por enquanto só vejo conteúdo relacionado a argparse. Se a resposta for não, a expressão “quase todos” é claramente falsa, e um título mais preciso seria “Transformar quase todo programa de linha de comando Python baseado em argparse em uma aplicação GUI completa”
    Há também o fato de que o último commit significativo foi há mais de 2 anos. Isso por si só não é ruim, mas o número de issues abertas não inspira muita confiança no projeto
    Ainda assim, é um projeto bacana. Gostaria de ver mais coisas assim. Como o README diz, é uma ferramenta multiplicadora de força, permitindo que alguém crie automação via CLI e a compartilhe facilmente com pessoas de escritório sem perfil técnico. É parecido com o papel que o Access teve para bancos de dados
    Assim como o Access, vai bater em limites quando houver escala ou complexidade, mas para usos em pequenos escritórios pode ser “bom o bastante”

    • Fico curioso para saber que deficiências você vê no argparse. Minha tendência é de minimizar dependências externas
    • Usei exatamente para isso no emprego anterior. Automatizei muitas tarefas e criei ferramentas para a equipe, mas a maioria não era programadora Python, então coloquei uma GUI simples em cima dos scripts com Gooey. Funcionou muito bem
    • O que exatamente não dá para fazer com argparse?
  • Posts relacionados:
    Gooey: Turn almost any Python command line program into a GUI application - https://news.ycombinator.com/item?id=27490291 - junho de 2021, 115 comentários
    Gooey: Turn command line programs into GUI applications - https://news.ycombinator.com/item?id=8218785 - agosto de 2014, 74 comentários

  • Seria bom se o sistema operacional e o shell conseguissem entender melhor como executar programas sem depender de uma biblioteca de parsing específica.
    Seria bom se os programas se comunicassem em um formato JSON/proto tipado, e se apenas inspecionando os tipos esperados de entrada e saída fosse possível obter estruturação e autocompletar de comandos no shell, ou até uma GUI completa.
    Hoje, o melhor que parece existir é algo feito com cuidado, meio que enfiando um canudinho. Cada programa precisa exportar arquivos de autocompletar para vários shells, os estilos de flags variam bastante entre programas e bibliotecas de parsing, e, claro, não há GUI.

    • Agora existe uma coisa chamada PowerShell. Respondendo de forma um pouco mais séria, acho que o único sistema operacional que realmente tentou algo assim talvez tenha sido o Plan 9.
    • Você está falando de algo como uma API universal de troca gRPC/CLI com suporte nativo do sistema operacional? Isso poderia ser bem elegante.
      O principal problema do gRPC, porém, parece ser que muitos desenvolvedores, inclusive eu, o acham um pouco mais incômodo de lidar do que soluções que parecem naturais dentro da linguagem.
    • Vale a pena experimentar o PowerShell. Basicamente, ele é o ecossistema .NET da Microsoft moldado como uma linha de comando interativa.
      Não tenho certeza se o PowerShell consegue aproveitar plenamente a tipagem estática que está em seu núcleo, mas sua capacidade de passar objetos pela linha de comando é quase única.
      No Linux, dá para encadear comandos combinando jc(https://github.com/kellyjonbrazil/jc) e jq(https://jqlang.github.io/jq/), mas isso ainda exige duas etapas a mais em comparação com os recursos embutidos do PowerShell.
  • Às vezes, uma GUI parece comida de conforto. Porque dá para simplesmente explorar uma interface GUI, sem a carga cognitiva que pode surgir em uma CLI usada raramente.
    Meio mudando de assunto, há alguns meses descobri por aqui o editor CUDATEXT, que permite usar elementos de GUI arbitrários, como MENU e INPUTS, por meio de uma API Python em um único arquivo. Esses elementos também são usados pelo próprio editor.
    Agora estou usando esses elementos simples de GUI para configuração e gerando um blog diretamente dentro do editor.

  • Algo relacionado para ver também:
    “Textual: um framework leve de aplicações para Python. Crie interfaces de usuário sofisticadas com uma API Python simples e execute apps no terminal e no navegador web” https://github.com/textualize/textual/

  • Quero agradecer por terem criado este excelente software. Gosto do Gooey e já o usei bastante em vários programas.
    Para mim, ele se encaixa perfeitamente naquele ponto em que um script Python faz algo importante, e agora preciso torná-lo utilizável por não programadores.

  • Isso me lembra naked objects. A ideia era que, definindo algumas anotações e um tema em classes Java, uma GUI inteira, ou uma aplicação web de frontend, seria gerada.
    Era uma ideia muito legal, mas, até onde sei, no fim não deu muito certo.

    • O Django também tem uma filosofia parecida. Você define um modelo, ele se reflete diretamente em tabelas do banco de dados, e dá para gerar formulários automaticamente a partir desse modelo.
      Na minha opinião, o problema é que preocupações verticais como apresentação, domínio e infraestrutura acabam acopladas entre si. Quando a representação em tela e a representação no banco de dados do modelo de domínio são simples, tudo bem.
      Mas, se você quer criar uma interface complexa e cheia de recursos de conveniência, esses requisitos precisam vazar para a camada de domínio. Por outro lado, se você quer criar um modelo de domínio complexo, fica impraticável impor invariantes no nível da linguagem, porque qualquer código que tenha uma referência a Model pode mexer nele arbitrariamente.
  • Seria bom se também existisse algo que fizesse o inverso.

    • Seria bom poder configurar uma varredura de parâmetros em uma GUI com base nas informações obtidas do argparse e depois convertê-la de volta em um script de shell que execute e salve os resultados adequadamente.
    • Você quer dizer um editor WYSIWYG que cuspa uma CLI? Parece que seria bem legal, mas inevitavelmente teria algum grau de restrição. Fico curioso para saber se alguém já tentou essa abordagem, mas nenhum exemplo me vem à cabeça.
  • O antigo Macintosh Programmer's Workshop, ou MPW, um shell baseado em texto para o Mac OS dos anos 80 e 90, tinha um recurso parecido chamado Commando para quase todos os comandos.
    As informações do Commando ficavam armazenadas em recursos cmdo. Se o suporte a atributos estendidos não fosse tão irregular, poderíamos fazer algo parecido hoje.
    Pesquisando, vi que o A/UX também tinha Commando. Ao dar dois cliques em um comando de terminal no Finder, aparecia uma caixa do Commando para escolher as opções da CLI, e depois ele era executado em uma janela do shell. Hoje, quando se executa um comando de terminal no Finder, ele simplesmente roda em uma janela do shell sem argumentos.
    https://cohost.org/boredzo/post/804893-i-still-want-a-moder
    http://toastytech.com/guis/aux3.html