HMI leve em Linux com GTK3 em C: ferramenta de chão sem runtime pesado
Em muitas plantas o PC industrial Linux não precisa de um framework web, nem de um interpretador Python com dezenas de pacotes, para cumprir um papel simples: mostrar estado, aceitar um comando e registrar o que o operador fez. Esse papel é o de ferramenta adjacente ao chão — diagnóstico, ajuste fino, visualização de I/O, lançamento de script de manutenção — e costuma ser confundido com “HMI de linha”. Não é. HMI de linha (painel do fabricante, runtime certificado, integração com CLP) tem outro contrato. O que o repositório c_gui_linux treina é a base nativa para o primeiro tipo: GTK3 em C, com dependência explícita e build reproduzível.
Este texto parte do código real do repo — start.c, startGtkApp.c, makefile, README com libgtk-3-dev — e discute quando essa stack faz sentido ao lado da oficina e da linha, sem inventar métricas de desempenho nem prometer “substitui SCADA”. A disciplina que importa é a mesma da bancada embarcada: saber o que está na máquina, como o binário nasce e o que acontece quando o operador fecha a janela no meio de um comando.
Por que “leve” importa no PC de fábrica
O PC de chão (ou o notebook do técnico) costuma ter restrições práticas que raramente aparecem em tutorial de desktop genérico:
- imagem de SO congelada ou com atualização controlada;
- pouca tolerância a dependências quebradas depois de um
pip installimprovisado; - necessidade de abrir rápido, fechar limpo e não disputar porta com o runtime do CLP;
- preferência por binário único ou quase único, linkado contra bibliotecas do sistema;
- política de TI que bloqueia instaladores “da internet” e favorece pacotes da distro.
GTK3 entra nesse cenário porque já está no ecossistema Debian/Ubuntu via pacotes: o README do repo aponta apt install libgtk-3-dev e sugere checar a versão com dpkg -s libgtk-3-0 | grep 'Version'. Isso não é detalhe cosmética — é a disciplina de saber o que está na máquina antes de escrever widget. Em comissionamento, anotar a versão da lib GTK na imagem é tão útil quanto anotar a versão do firmware do CLP: quando o painel “some” depois de um clone de disco, você sabe se a lib mudou.
Comparado a um empacotamento Python (PyInstaller, venv, wheels), o caminho C+GTK3 troca produtividade de UI por previsibilidade de deploy. Para um painel de três botões e um rótulo de status, essa troca costuma valer. Para um CAD interno com dezenas de telas, provavelmente não. A regra prática de oficina: se o entregável precisa sobreviver a um PC reinstalado só com o ISO da planta e apt, C+GTK3 é candidato forte; se o entregável precisa de grids densos, PDF de OF e menus de engenharia, o estudo PyQt6 (pyqt6_book) é o próximo degrau.
“Leve” aqui não significa “rápido em benchmark inventado”. Significa poucas peças móveis: compilador, pkg-config, libgtk do sistema, makefile. Menos peças móveis = menos surpresa quando o técnico de campo troca o SSD e restaura a imagem.
O que o repositório contém de fato
Árvore mínima:
start.c— janela toplevel com a API clássicagtk_init/gtk_main;startGtkApp.c— esqueleto comGtkApplicatione sinalactivate;makefile—clang+pkg-config --cflags/--libs gtk+-3.0;README.md— propósito de aprendizado e instalação da lib de desenvolvimento.
Não há widget de processo, nem binding Modbus, nem tela de alarme. O valor está na progressão didática: primeiro existir uma janela; depois existir um application object com ciclo de vida correto. Quem espera um “mini SCADA” no clone do repo vai se frustrar — e isso é intencional. O laboratório para no passo em que a toolchain e o loop de eventos estão sob controle.
start.c: a janela que só existe
gtk_init (&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_show_all(window);
gtk_main();
Isso ensina três ideias que qualquer HMI leve precisa internalizar:
- Inicialização do toolkit antes de criar widget.
- Widget como árvore — aqui a árvore tem só a janela, mas o padrão é o mesmo quando você acrescenta
GtkBox,GtkLabel,GtkButton. - Loop de eventos (
gtk_main) — sem ele, a janela nasce e o processo termina; com ele, o processo vive enquanto o operador interage.
O que falta (de propósito) é conectar o sinal destroy da janela a gtk_main_quit. Em um painel de bancada isso aparece logo: fechar o X e deixar o processo zumbi no terminal. Em ferramenta de chão, processo órfão é bug operacional, não detalhe de tutorial. O operador acha que “fechou o diagnóstico”; o processo continua ocupando porta serial ou arquivo de log. Na próxima abertura, “dispositivo ocupado” — e a caça ao fantasma começa.
Hábitos mínimos a acrescentar assim que o start.c compilar:
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);- título e tamanho iniciais (
gtk_window_set_title,gtk_window_set_default_size) para o painel não nascer minúsculo no canto; - um
GtkLabelde status mesmo que diga só “ok / aguardando” — treina o reflexo de estado visível.
startGtkApp.c: o modelo de aplicação
O segundo arquivo muda o contrato:
app = gtk_application_new("in.gtk", G_APPLICATION_FLAGS_NONE);
g_signal_connect (app, "activate", G_CALLBACK(activate), NULL);
ret = g_application_run (G_APPLICATION(app), argc, argv);
g_object_unref (app);
A função activate está vazia (comentário // gtk code comes here). Isso não é falha do estudo — é o ponto em que o aluno passa do “mostrar janela” para o “possuir uma aplicação GApplication”. Em HMI adjacente ao chão, esse modelo ajuda em:
- instância única (evitar dois painéis abertos brigando pelo mesmo recurso);
- argumentos de linha de comando tratados pelo GLib;
- ciclo de vida com
g_object_unrefexplícito.
O application_id ("in.gtk") é placeholder de estudo. Em ferramenta real você usa um ID estável no estilo DNS reverso da empresa — não por estética, mas para o desktop e o D-Bus saberem quem é quem. Dois utilitários com o mesmo ID genérico competem de formas difíceis de debugar em sessão gráfica compartilhada.
Quando activate deixar de ser vazio, o padrão saudável é: criar a GtkWindow, anexar o layout, conectar destroy, e só então gtk_widget_show_all. Misturar criação de widget fora de activate com GtkApplication é caminho clássico para “abre na segunda execução e na primeira não”.
Build: clang, pkg-config e a disciplina do makefile
O makefile compila os dois alvos:
CC = clang
CFLAGS = `pkg-config --cflags gtk+-3.0`
LIBS = `pkg-config --libs gtk+-3.0`
Três decisões práticas:
pkg-configevita hardcodar caminhos de include/lib que mudam entre distros.clangcomo compilador default do estudo — o que importa é a flag set do GTK, não a marca do compilador;gcccom as mesmas flags também resolve.- alvo
cleanremove os binários — hábito pequeno, útil quando a imagem do PC de fábrica é clonada e você não quer deixar artefato de debug.
Para o técnico que versiona ferramenta interna: o makefile é a documentação executável. Se o build não passa em uma máquina limpa com só libgtk-3-dev e clang/gcc, a ferramenta ainda não está pronta para o chão. “Compila no meu notebook com dez PPAs” não conta.
Checklist de aceitação de build (sem métrica inventada — só pass/fail):
- VM ou PC com imagem mínima da planta.
- Instalar apenas o que o README lista (
libgtk-3-deve compilador). makesem editar o makefile.- Abrir os dois binários, fechar pelo X, confirmar que o processo some (
ps/ gerenciador de tarefas). make cleane repetir uma vez — garante que não há lixo escondido.
Se o passo 3 falha, o problema é ambiente ou makefile — não “UI”. Corrigir isso antes de desenhar botão.
Thread, I/O e o loop GTK: onde a oficina quebra o tutorial
O repo não mostra I/O de equipamento. Ainda assim, quem leva GTK para o chão esbarra cedo no mesmo erro: chamar leitura serial bloqueante no callback do botão. O loop GTK para, a janela congela, o operador clica de novo, e você tem fila de eventos e dispositivo em estado indefinido.
Padrão mental (sem inventar API que o repo não tem):
- UI thread só atualiza widget e despacha comando;
- trabalho longo (serial, socket, arquivo grande) fora do callback direto — idle, timeout curto, ou thread com cuidado de voltar à UI só via mecanismos thread-safe do GLib;
- timeout e cancelamento pensados antes do primeiro botão “Ler CLP”.
Isso é o equivalente, em desktop, da separação entre tarefa de ciclo rápido e tarefa de comunicação no firmware. A metáfora ajuda quem vem de PIC/STM32: o gtk_main é o loop cooperativo; bloquear nele é como meter delay longo na ISR.
Estilo visual e legibilidade a distância
GTK3 permite CSS via GtkCssProvider. O repo não traz tema. Em ferramenta de chão, resista à tentação de “bonito de portfolio”:
- contraste alto (texto claro em fundo escuro ou o inverso, mas sem cinza médio em cinza médio);
- fontes grandes o bastante para leitura em pé;
- botões com área generosa — mesmo em mouse, o técnico pode estar de luva fina ou com pressa;
- cores de estado com significado estável (ok / atenção / falha), não paleta decorativa.
Nada disso exige framework novo. Exige disciplina de layout (GtkGrid/GtkBox) e teste sob a iluminação real do painel — lâmpada de LED barata muda percepção de contraste.
Onde encaixa (e onde não encaixa) como HMI de chão
Faz sentido
- painel de diagnóstico local (status de serviço, último log, botão “reiniciar daemon”);
- utilitário de comissionamento que fala com um dispositivo via serial/socket e mostra resultado;
- overlay simples em PC Linux que já roda o resto da stack nativa;
- ensino de eventos, widgets e empacotamento sem misturar Python;
- ferramenta interna versionada em git com build de uma linha na imagem da planta.
Não substitui
- HMI certificado / safety-related da célula;
- SCADA multiestação com histórico e trilha de auditoria completa;
- UI touch rica com animações e temas multiplataforma (aí entram Qt/QML, Kivy, ou runtime comercial);
- portal web multiusuário com autenticação corporativa — outro domínio, outro contrato.
A fronteira honesta: GTK3 em C é excelente para ferramenta adjacente; é um caminho possível para HMI leve de uso interno; não é atalho para painel de segurança. Se o requisito cita norma de máquina, PL/SIL, ou “substitui a IHM do fabricante”, pare e reavalie escopo — o makefile não te salva de requisito errado.
Deploy na imagem da planta
Empacotar para Linux industrial costuma seguir uma destas rotas, em ordem de preferência quando a imagem já tem GTK3:
- Binário + dependência de sistema — o ideal deste estudo:
aptjá resolve GTK; você entrega o executável e um unit file ou atalho.desktopse precisar. - Pacote
.debinterno — útil quando a TI quer instalar via repositório da planta; o makefile atual não gera.deb, mas a disciplina de dependência explícita facilita escrever ocontroldepois. - AppImage / bundle — só se o PC alvo não tiver GTK3 de sistema ou houver conflito de versão grave. Bundle aumenta superfície de atualização e de auditoria.
Evite “copiar a pasta do home do desenvolvedor”. Isso é o equivalente Windows de mandar o venv zipado: funciona uma vez e falha no clone.
Próximos passos naturais a partir deste repo
Sem inventar features que o código ainda não tem, a progressão típica de oficina seria:
- Em
activate, criarGtkWindow, conectardestroy→ sair limpo. - Adicionar
GtkGridouGtkBoxcom labels de estado e botões de comando. - Separar UI de I/O: idle callback ou thread cuidadosa para não travar o loop GTK enquanto espera resposta de equipamento.
- Persistir último estado relevante (arquivo local simples) — o técnico odeia redigitar IP e porta a cada abertura.
- Empacotar com o gerenciador da distro (
.deb) ou AppImage só se o PC alvo não tiver GTK3 de sistema — a maioria dos industriais Linux modernos tem. - Documentar no README da ferramenta: versão da distro, versão do
libgtk-3-0, comando de build, e o que o painel não controla (ex.: não é circuito de emergência).
O repositório para no passo zero de propósito. Quem precisa de formulários longos, PDF de OF e menus de engenharia vai olhar o estudo PyQt6 (pyqt6_book). Quem precisa de touch multiplataforma olha Kivy. Quem precisa de Win32 nativo olha c_gui_win. A família de estudos é deliberada: cada runtime com um contrato claro.
Relação com o irmão Windows
O paralelo com c_gui_win não é coincidência de portfólio: é o mesmo cérebro de SOP. No Linux, pkg-config + GTK; no Windows, Chocolatey + LLVM + WinMain/MessageBox. Nos dois casos:
- validar toolchain com programa mínimo antes da UI “de verdade”;
- makefile como documentação executável;
- honestidade sobre o que o hello não é (ainda não é message loop completo / ainda não é painel de tags).
Quem domina os dois não fica refém de “só roda no meu Ubuntu” ou “só no Visual Studio da fábrica”. Em oficina brasileira misturada — engenharia em Windows, painel em Linux — essa dualidade é operacional, não acadêmica.
Conclusão
c_gui_linux não é um produto de HMI. É o menor laboratório confiável para responder: “consigo entregar uma janela nativa em Linux com dependência de sistema e build de uma linha?” Se a resposta for sim, você tem base para ferramenta de chão adjacente — leve, auditável e sem runtime improvisado. Se a resposta for “preciso de dezenas de telas e relatórios”, o próximo passo não é forçar GTK3: é escolher a stack certa sem abandonar a disciplina de build que este makefile já ensina. O chão de fábrica perdoa UI simples; não perdoa processo zumbi, dependência fantasma e build que só o autor consegue repetir.