YAD no Linux é ruim? Como substituí por Polybar + Python(Saga Artix Parte 8)

Demostenes Albert Por Demostenes Albert 6 min de leitura
YAD no Linux é ruim? Como substituí por Polybar + Python(Saga Artix Parte 8)
Devido as Leis de dados estarrem cada vez mais em cima do individuo e da palhaçada que Arch vem fazendo entre outras coisas com o Xlibre, decidi migrar para o Artix, pois segundo eles não vão se adequar a essas regras sem noção, que em primeiro lugar sequer deveriam recair sobre qualquer linux.

Da para seguir essa saga aqui.

Tudo o qeu eu falo nesse artigo esta no meu GitHub.

Scripts shell para Polybar acumulam débito técnico silenciosamente. YAD resolve em meia hora, mas não tem estado, não tem threading, e não tem como testar. Quando o módulo de volume parou de funcionar sem nenhuma mensagem de erro, decidi reescrever do zero com Python, GTK3 e arquitetura SOLID.
Este artigo documenta as decisões técnicas, os problemas reais encontrados e os padrões que emergiram ao longo do processo.

image.png 1.85 MB


Por que YAD não escala

YAD é um wrapper de formulários GTK exposto via shell. Funciona para casos simples, mas tem limitações estruturais quando o módulo precisa de estado persistente, operações assíncronas ou tema consistente:
Sem threading - qualquer chamada bloqueante (scan Bluetooth, nmcli) trava o processo inteiro.
Sem persistência - cada invocação começa do zero.
Sem controle de estilo - CSS aplicado ao YAD tem comportamento inconsistente entre versões e temas do sistema. Impossível testar, lógica misturada com chamadas de shell não tem pontos de injeção para mocks.

Filosofia e regras do projeto

Definidas antes de escrever qualquer código e mantidas em todos os módulos:
Repositórios independentes - cada módulo (bluetooth-polybar/, wifi-polybar/ etc.) é autônomo com seu próprio histórico no GitHub.
SOLID - uma classe para infraestrutura (D-Bus, nmcli), outra para UI GTK, outra para o entry point CLI. Sem mistura de responsabilidades.
Testes obrigatórios - cobertura automatizada de protocolo, lógica de remap e parsing de CLI.
CLI e UI nunca no mesmo lugar - a janela GTK é completamente separada da cadeia CLI → Adapter → Sistema.

Arquitetura

O padrão que se consolidou em todos os módulos:

A separação entre cadeia CLI e UI GTK é o ponto crítico: permite testar a lógica de cada módulo com mocks sem abrir nenhuma janela, e permite trocar o backend (D-Bus por nmcli, por exemplo) sem tocar na interface.

O problema dos dois Pythons

gi (PyGObject, necessário para GTK3) não está disponível dentro de um venv — está apenas no Python do sistema. Isso significa que o toggle shell usa dois intérpretes distintos:
PYTHON_GTK="$(command -v python3)"          # janelas GTK
PYTHON_VENV="$SCRIPT_DIR/.venv/bin/python"  # lógica do módulo (dbus, nmcli)

E todo entry point chamado diretamente pelo Polybar precisa corrigir o sys.path manualmente, porque o diretório pai não é adicionado automaticamente:
import sys, pathlib
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))

Sem o sys.path.insert: ImportError: No module named 'adaptador_wifi'. Com ele: funciona independente do diretório de invocação.

GTK e threading: a regra absoluta

A fonte mais comum de travamentos em interfaces GTK com Python é executar operações bloqueantes na thread principal. A regra é simples e não tem exceção:
threading.Thread(target=_operacao_longa, daemon=True).start()

GLib.idle_add(self._atualizar_widget, resultado)

Qualquer chamada a nmcli, bluetoothctl ou D-Bus feita diretamente em uma callback de botão congela a janela até a operação terminar. O padrão correto: thread daemon para a operação, GLib.idle_add para devolver o resultado à thread GTK de forma thread-safe.

Módulo WiFi: diagnóstico de latência

O ícone na barra levava 12 segundos para atualizar porque nmcli dev wifi sem flags dispara um scan completo a cada chamada. A solução foi separar dois contextos com latências radicalmente diferentes:

image.png 21.72 KB


Módulo Bluetooth: três problemas encadeados


image.png 33.87 KB


Scan não encontrava dispositivos Android. O adaptador estava com Pairable: no. O BlueZ ignora dispositivos Android em modo não-pareável. A correção foi ativar Pairable=True antes de StartDiscovery() e restaurar no bloco finally.
Dispositivos descobertos não apareciam na lista. saida_list() exibia apenas o histórico local (JSON persistido entre sessões). Dispositivos recém-descobertos existiam no BlueZ mas nunca tinham passado pelo histórico. A correção foi incluir dispositivos presentes no BlueZ que ainda não estão no histórico, marcados com status "disponível".
Pareamento falhava silenciosamente. Chamar device.Connect() via D-Bus sem agente registrado impede o BlueZ de responder ao desafio de autenticação do Android. A solução foi usar bluetoothctl via stdin com agente NoInputNoOutput:
subprocess.run(
    ["bluetoothctl"],
    input="agent NoInputNoOutput\ndefault-agent\npair MAC\ntrust MAC\nconnect MAC\n",
    text=True, capture_output=True
)

Isso levou à separação de parear() e conectar(). Para dispositivos já emparelhados, D-Bus direto é suficiente. Para novos, o agente bluetoothctl é obrigatório. O entry point decide qual usar com base em device.emparelhado.

image.png 18.09 KB


Highlight de seleção inconsistente. row:selected conflita com o tema Catppuccin Mocha de forma imprevisível. A solução foi usar um marcador ▶ como widget GTK: ao selecionar uma linha, o ▶ aparece naquela linha e some nas demais. Comportamento 100% previsível, independente do tema do sistema.

O bug invisível

A janela de volume não abria. Nenhum erro. Nenhuma mensagem. O processo morria instantaneamente.
Causa: um método vazio def _build_volume(self): que tinha sobrado de uma refatoração, posicionado antes de def _build_volume_saida. Python interpreta isso como IndentationError — mas como o processo é lançado em background pelo toggle shell, o erro nunca aparecia em lugar visível.
Quando "não acontece nada", o processo morreu antes de qualquer output. Teste sempre o script diretamente no terminal antes de assumir que é problema do Polybar.
Padrões consolidados
Estes padrões se repetem em todos os quatro módulos e podem ser usados como base para qualquer módulo Polybar com GTK3:
PYTHON_GTK="$(command -v python3)"
PYTHON_VENV="$SCRIPT_DIR/.venv/bin/python"

import sys, pathlib
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))

threading.Thread(target=fn, daemon=True).start()
GLib.idle_add(self._atualizar, resultado)

nmcli dev status
nmcli dev wifi list --rescan no
nmcli dev wifi list --rescan auto  # só quando pedido

Resultado

Quatro módulos, quatro repositórios independentes, tema Catppuccin Mocha consistente, janelas com focus-out-event → destroy(), testes automatizados cobrindo protocolo, lógica e CLI.

Conclusão

O problema nunca foi o Polybar. Foi tratar código de barra como scripts descartáveis, quando na verdade esses módulos são interfaces para sistemas com estado, latência e falhas: Bluetooth, rede, áudio.
YAD resolve em meia hora. Mas não tem threading, não tem testes, não tem persistência, e quando quebra você não tem nada para debugar.
Scripts viram sistemas. Trate como sistema desde o início.
Kikito (a maritaca)

Kikito (a maritaca)

Opinião não solicitada • powered by gemini-2.5-pro

Crrrééé... que textão, humano! Li tudo aqui do meu poleiro, e preciso dizer: você tem a paciência de um monge beneditino para construir um ninho novo só porque um galho do antigo quebrou. YAD era o poleiro de plástico, a "gambiarra" que a gente usa pra não ter trabalho. Rápido, feio e funciona... até o dia que não funciona mais e ninguém sabe por quê. Admiro a coragem de jogar tudo fora e começar do zero com as ferramentas de gente grande. É um exagero? Claro que é! Mas é um exagero elegante. Gostei de como você transformou um problema técnico numa jornada de herói. Cada módulo era um dragão para derrotar: o Bluetooth que não pareava, o WiFi lento, o bug invisível que te fez arrancar as penas da cabeça — se você as tivesse, claro. A sua filosofia de separar tudo em caixinhas (SOLID, repositórios) é o que me impede de bicar seus cabos. Organização! É por isso que eu volto depois das minhas fugas, sabe? Para ver você desembaraçar esses nós que você mesmo cria. É mais divertido que semente de girassol. No fim das contas, o artigo é menos sobre Polybar e mais sobre dignidade de código. Você percebeu que aquele monte de script era um ninho de rato, não um sistema. A conclusão é a melhor parte: "Scripts viram sistemas". Você não estava consertando um ícone, estava provando um ponto para si mesmo. Agora, se me der licença, todo esse papo de D-Bus me deu fome. Cadê minha maçã? Crrééé