Programação Gráfica em C: Gnome Tool Kit (GTK+)

Se você gostou da pequena série de textos sobre programação gráfica usando a Win32 API (aqui, aqui e aqui), saiba que apenas arranhei o assunto, mas toda a base está lá… Até mesmo para ambientes diferentes como X11, no Linux e Unixes em geral. É claro que a coisa toda é um pouco mais simples e diferente nesses outros ambientes.

No caso do Linux (e Unixes) o ambiente gráfico não é integrado ao sistema operacional. Trata-se de um software “servidor” ou daemon. A aplicação literalmente envia comandos para o daemon pedindo para desenhar uma janela, movê-la, mudar de tamanho, pintar uma área e o daemon manda mensagens de volta… Em essência, é o que o Windows também faz. Mas, no X11, as janelas são desenhadas por um desktop environment manager, que é um módulo que roda sobre o daemon X11. Existem vários: Gnome, Unity, Wayland, Cinnamon, MATE, KDE, XFCE, WindowMaker, Motif etc.

Os dois desktop managers mais famosos e mais aceitos são Gnome e KDE e, por isso, as duas bibliotecas mais usadas para desenvolvimento “em janelas” para Linux são o GTK+ e o Qt. O primeiro é uma conjunto de bibliotecas para serem usadas em C. A outra, é uma class library, um conjunto de bibliotecas para serem usadas em C++. Neste aspecto, o GTK+ é mais parecido com a Win32 API do que o Qt. Eis um “hello, world” com o GTK+:

#include <stdlib.h>
#include <gtk/gtk.h>

int main(int argc, char *argv[])
{
  GtkWidget *mainWindow;
  GtkWidget *label;

  gtk_init(&argc, &argv);

  // Cria a janela, ajusta o tamanho, o título e
  // conecta o evento "destroy" à função gtk_main_quit.
  mainWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_default_size(GTK_WINDOW(mainWindow),
                              320, 200);
  gtk_window_set_title(GTK_WINDOW(mainWindow), 
                       "Hello App");
  gtk_signal_connect(GTK_OBJECT(mainWindow),
                     "destroy",
                     GTK_SIGNAL_FUNC(gtk_main_quit),
                     NULL);

  // Cria o label e ajusta o texto.
  label = gtk_label_new(NULL);
  gtk_label_set_text(GTK_LABEL(label), "Hello, world!");

  // Adiciona o label à janela.
  // Mostra a janela e seus filhos.
  gtk_container_add(GTK_CONTAINER(mainWindow), label);
  gtk_widget_show_all(mainWindow);

  // Loop principal.
  gtk_main();

  return EXIT_SUCCESS;
}

Para compilar é sempre bom usar um utilitário que nos dará a lista de diretórios onde acharmos os headers e as libraries:

$ pkg-config --cflags gtk+-2.0
-pthread -I/usr/include/gtk-2.0 -I/usr/lib/x86_64-linux-gnu/gtk-2.0/include \
-I/usr/include/gio-unix-2.0/ -I/usr/include/cairo -I/usr/include/pango-1.0 \
-I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pixman-1 \
-I/usr/include/libpng12 -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/libpng12 \
-I/usr/include/pango-1.0 -I/usr/include/harfbuzz -I/usr/include/pango-1.0 \
-I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include \
-I/usr/include/freetype2

$ pkg-config --libs gtk+-2.0
-lgtk-x11-2.0 -lgdk-x11-2.0 -lpangocairo-1.0 -latk-1.0 -lcairo \
-lgdk_pixbuf-2.0 -lgio-2.0 -lpangoft2-1.0 -lpango-1.0 -lgobject-2.0 -lglib-2.0 \
-lfontconfig -lfreetype

Impressionante, não? GTK+ exige um porrilhão (como sempre, essa é uma especificação de grandeza! hehe) de diretórios contendo arquivos header e um punhado de bibliotecas! Para compilarmos o nosso programinha podemos misturar as opções --cflags e --libs em uma linha só, assim:

$ gcc -O3 -s -o hello hello.c `pkg-config --cflags --libs gtk+-2.0`

Estou usando as opções -O3 e -s para otimizações e eliminar símbolos não necessários no arquivo binário final. O resultado da execução de hello é esse:

Agora, mova a janela, mude o tamanho, minimize, maximize… tá tudo lá num pequeno executável de 10 KiB!

O que pode surpreender é o aparente uso de Orientação a Objetos em um código em C. Os macros GTK_WIDGET, GTK_WINDOW e GTK_LABEL são apenas type castings para que a função não reclame que estamos passando o tipo errado e, ao mesmo tempo, todo objeto visível é um widget (GtkWidget).

Para tornar as coisas mais rápidas, a identificação de cada objeto é, de fato, um ponteiro que aponta para ele próprio. Nada de “handles”, como no Win32. E, embora pudéssemos criar nosso próprio loop de mensagens, GTK+ fornece a função gtk_main() que faz isso por nós. Ele permite registrar callbacks para hooks no loop, como exemplo, quando o loop estiver esperando por mensagens, pode executar uma função idle

Ahhh… quanto ao pkg-config, use a opção --list-all para ver todas as bibliotecas que ele consegue obter as listas de headers e libs

Por que gtk_init() toma ponteiros para argc e argv?

A função gtk_init() pode receber opções da linha de comando diretamente para ele… No caso do X11, por exemplo, a opção --display indica o daemon onde as janelas serão criadas (pode estar em outra máquina, por exemplo!)… A função retira da lista de parâmetros aqueles que são destinados apenas ao GTK+!

Um aviso sobre o sistema de coordenadas usado no GTK+:

Se você já mexeu com Applets em JAVA, deve se lembrar da antiga biblioteca AWT (Abstract Widget Toolkit). Ele foi “copiado” de bibliotecas como XLib e GTK+… Os containers do GTK+ (GtkWindow, por exemplo) usam o sistema de coordenadas “geográfico” por default, onde, ao inserir um único widget-filho ele é colocado no “centro”, ocupando a área útil toda… mas, podemos colocá-lo ao norte, ao sul, ao leste ou oeste, assim como era feito no AWT.

Isso não significa que não podemos usar um sistema de coordenadas cartesiano, mas, para isso, precisaríamos incluir o container GtkFixed na nossa janela e esse container é o que permite posicionamento cartesiano… A maioria das aplicações feitas com GTK+ não fazem isso…

Existe um utilitário para desenhar janelas?

Sim… existe o Glade:

Screenshot do Glade

Ele gera um arquivo XML contendo a descrição de sua janela e o GTK+ possui funções que interpretam esse XML e montam a janela completa (GtkBuilder). É a perfeita implementação do MVC. O View é apenas o arquivo XML contendo a parte visual e o código que lida com ele é o seu Model e Controller. Num outro artigo explico como usá-lo…

Gtk+ é portável!

Um detalhe final é que GTK+ é portável. Existe o GTK+ para Linux, OS/X, FreeBSD, mas também tem para Windows! Isso quer dizer que você pode criar programas gráficos para todas as plataformas com o mesmo conjunto de bibliotecas. No caso do Windows, terá que usar o MinGW para compilar com o GCC e carregar junto um caminhão de DLLs que colocará em C:\Windows\System32 (aqui é mostrado como)… Note que é perfeitamente possível compilar sua aplicação com GTK+ usando o Visual Studio.

Note que, no Windows, não é necessário usar aquela construção biruta de WinMain(). O bom e velho main funciona!

GTK+ 2.0 e GTK+ 3.0

Existem duas implementações da biblioteca porque existem, hoje, dois “Gnomes” (versão 2.x e 3.x) diferentes… O ‘G’ do GTK vem de Gnome e é para desenvolver aplicações para ele que a biblioteca existe. Clique nos links para obter documentação de referência a ambas as versões: GTK+ 2.0 e GTK+ 3.0. Note, também, que a GLib é parte integrante e, portanto, uma pancada de algoritmos está à sua disposição (listas, árvores etc)…

Anúncios