Revista Do Linux  
EDIÇÃO DO MÊS
  Atualidades
  Beowulf
  CAPA
  Corporativo
  Divirta-se
  Entrevista
  Estudo de Caso
  Interfaces Gráficas
  Mercado
  Programação
  Segurança
  Servidores
  WEB

GTK+ Programador Objeto
Uma introdução à poderosa biblioteca de objetos gráficos, a mesma usada para construir o Gnome


Rodrigo Parra Novo
rodarvus@conectiva.com.br

Como desenvolver um programa com interface gráfica no Linux ?

Esta é normalmente a primeira pergunta feita por um programador que esteja migrando, ou pensando em migrar seus programas para o Linux. São várias as opções. Pode-se utilizar linguagens como C, C++, Java, Perl, Python ou mesmo assembly para os mais radicais. Adicionalmente, é necessario escolher um Widget Set, ou seja, uma biblioteca de ferramentas que permita que o programador especifique coisas como `crie uma janela de tamanho X, e que contenha um botão, contendo o texto Ok' para a linguagem escolhida. Alguns exemplos de Widget Sets são o GTK+, Qt, Tk, fltk e outros. A nossa escolha será a dupla C/GTK+, pela simplicidade e eficiência de ambos.

Podemos definir o GTK+ como um conjunto de widgets (semelhantes aos componentes do Delphi), como botões, caixas de entrada de texto, radio buttons, listboxes, etc., além de várias funções que tornam mais fácil o desenvolvimento do programa. O GTK+ foi desenvolvido na linguagem C e para uso com esta, mas existem wrappers/extensões para várias outras linguagens, como C++, Objective C, Python, Perl, Guile, Pascal, e outras, o que torna esse Widget Set muito flexível para a escolha da linguagem a ser utilizada.

Assume-se nesse artigo que o leitor tenha um conhecimento básico da linguagem C, além dos procedimentos de compilação de um programa. Caso você queira se aprofundar em C antes de prosseguir, recomendamos o clássico C - A linguagem de programação, por Brian W. Kernighan & Dennis M. Ritchie.

Apesar de ter sido desenvolvido em C, o GTK+ tem o paradigma de orientação a objetos como uma característica básica. O que quer dizer que todos os widgets são contidos em objetos (as variáveis da linguagem C) e são acessíveis através dos seus métodos (as funções em C).

O GTK+ pode ser definido em três partes distintas:

  • GLIB - Uma biblioteca de funções de utilidade geral, que pode ser usada independentemente do GTK+. Inclui coisas como um alocador de memória com suporte estendido à depuração, funções para uso de listas ligadas, funções para aumentar a portabilidade do programa, etc.
  • GDK - Um conjunto de funções gráficas, úteis para que o programador não precise lidar direto com a Xlib (Biblioteca gráfica do XFree86).
  • GTK - Contém os Widgets que podem ser utilizados pelo programador, além de toda estrutura para a criação de widgets novos.

Todo e qualquer componente gráfico do GTK+ é definido por um tipo de variável especial, o GtkWidget, que contém informações como nome, cor, posição na tela, etc. Adicionalmente, a nomenclatura das funções do GTK+ é constante, na seguinte forma:

<biblioteca>_<widget-ou-tipo>_<ação>(parâmetros_a_serem_passados),

como em:

janela = gtk_window_new (GTK_WINDOW_TOPLEVEL);

Neste caso, a biblioteca é a GTK, o widget que trabalharemos é um window (uma janela normal) e a ação é a de criar esta janela. Da mesma forma, existe uma função:

gtk_widget_show (janela);

que mostra na tela a janela criada no exemplo anterior. Esta variável janela, que usamos nestes dois exemplos, é do tipo GtkWidget, que é o tipo usado por todos os widgets GTK. A sua declaração é feita da seguinte forma:

GtkWidget *janela;

Além do tipo GtkWidget, vários outros tipos de variáveis são oferecidos pelo GTK+ (Neste caso, pela GLIB especificamente). Alguns deles são: gint, guint, gchar, além de tipos mais avançados como guint64, que garante um inteiro sem sinal, de 64 bits. É recomendado o uso destes tipos oferecidos pela GLIB, pois eles aumentam em muito a portabilidade de qualquer programa que os utilize.

Vamos aproveitar o conhecimento que recebemos agora, para fazer nosso primeiro programa em GTK+. Como todos os primeiros exemplos do mundo, um programa que mostra a string "Hello, World!" na tela. Só que, no nosso caso, dentro de uma janela criada por nós.

***** Começo do programa hello.c *****

#include <gtk/gtk.h>

int main (int argc, char **argv)
{

  GtkWidget *janela;
  GtkWidget *label;

  gtk_init (&argc, &argv);
  janela = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_container_border_width (GTK_CONTAINER (janela), 10);
  label = gtk_label_new ("Hello, World!");
  gtk_container_add (GTK_CONTAINER (janela), label);
  gtk_widget_show (label);
  gtk_widget_show (janela);
  gtk_main ();

  return (0);
}

***** Fim do programa hello.c *****

    
Hello, World!
Você pode compilar o programa acima com o gcc, usando:

# gcc -o hello hello.c `gtk-config --cflags --libs`
O significado dos parâmetros incomuns será explicado depois.

#include <gtk/gtk.h>

Todos os programas GTK+ incluem gtk/gtk.h, que declara variáveis, funções, estruturas e coisas desse tipo que serão usadas em seu programa. A próxima linha:

gtk_init (&argc, &argv);

chama a função gtk_init (gint *argc, gchar ***argv), que é utilizada em todos os programas GTK+. Esta função configura coisas que todo programa para o XFree86 deve ter, como o visual padrão, o colormap, etc. Posteriormente ela remove quaisquer parâmetros internos do GTK+, que o usuário tenha passado ao chamar o programa.

janela = gtk_window_new(GTK_WINDOW_TOPLEVEL);

A próxima linha cria uma janela. O parâmetro GTK_WINDOW_TOPLEVEL diz para o Gerenciador de Janelas controlar quais cores, bordas, etc., serão usadas na janela. Uma janela que não tenha nenhum filho (o nosso caso, até o momento) tem um tamanho padrão de 200x200 pixels, para que possamos trabalhar com ela assim mesmo.

gtk_container_border_width (GTK_CONTAINER (janela), 10);

Aqui nós dizemos para o GTK+ que qualquer widget que venha a ser inserido na nossa janela ficará a uma distância mínima de 10 pixels da borda da janela.

label = gtk_label_new ("Hello, World!");

Criamos um label com o conteúdo "Hello, World"

gtk_container_add (GTK_CONTAINER (janela), label);

e inserimos este botão na nossa janela.

gtk_widget_show (label);
gtk_widget_show (janela);

Após a criação e a configuração de todos os Widgets que serão usados, devemos mostrá-los na tela, com a função gtk_widget_show. Recomenda-se que a janela principal seja o último widget da lista, para que se evite a sensação de "flickering", ou seja, os widgets serem desenhados um a um, depois da janela principal. (Isto é facilmente detectável em computadores lentos e com pouca memória.)

gtk_main ();

Aqui passamos o controle para o GTK+.

A função gtk_main fica parada esperando por eventos que tenhamos configurado antes, timeouts, notificações de E/S em arquivos, etc. No nosso pequeno exemplo todo e qualquer evento é ignorado. Por este motivo, quando você fechar a janela o controle não irá voltar para o shell. (Presssione CTRL+C para sair do programa.)

Vamos agora para um exemplo um pouco mais avançado. Neste exemplo, iremos criar um programa com um botão simples, que quando clicado fecha o programa. O exemplo parece básico, mas mostra uma das características mais importantes do GTK+, o uso dos chamados callbacks, que são funções que executam uma determinada ação quando acionadas.

Para economizar espaço, primeiro mostraremos o programa todo, mas comentaremos apenas as partes que mudarem em relação ao exemplo anterior.


***** Começo do programa callback.c *****

#include <gtk/gtk.h>

void hello (GtkWidget *widget, gpointer data)
{
  g_print ("Hello, World!\n");
}

gint delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
{
  g_print ("delete_event detectado\n");
  return (TRUE);
}

void destroy (GtkWidget *widget, gpointer data)
{
  gtk_main_quit();
}

int main (int argc, char **argv)
{
  GtkWidget *janela;
  GtkWidget *botao;

  gtk_init (&argc, &argv);
  janela = gtk_window_new (GTK_WINDOW_TOPLEVEL);

  gtk_signal_connect (GTK_OBJECT (janela), "delete_event",
                      GTK_SIGNAL_FUNC(delete_event),
		      NULL);

  gtk_signal_connect (GTK_OBJECT (janela), "destroy",
		      GTK_SIGNAL_FUNC(destroy),
		      NULL);

  botao = gtk_button_new_with_label ("Clique aqui para sair");

  gtk_signal_connect (GTK_OBJECT (botao), "clicked",
                      GTK_SIGNAL_FUNC (hello),
                      NULL);

  gtk_signal_connect_object (GTK_OBJECT (botao), "clicked",
                             GTK_SIGNAL_FUNC(gtk_widget_destroy),
			     GTK_OBJECT (janela));

  gtk_container_add (GTK_CONTAINER (janela), botao);
  gtk_widget_show (botao);
  gtk_widget_show (janela);

  gtk_main ();
  return (0);
}

***** Fim do programa callback.c *****

Clique aqui para sair

Lembramos que somente as partes novas para nós serão comentadas.

void hello (GtkWidget *widget, gpointer data)
{
  g_print ("Hello, World!\n");
}

Esta função é um callback do GTK+. Callbacks são caracterizados por retornar sempre void (ou simplesmente não retornar nada) e ter dois parâmetros. O primeiro é a widget à qual o callback está relacionado, e o segundo é um gpointer (basicamente um ponteiro normal), que, usando-se um cast do C, pode ser qualquer tipo de dado. No nosso exemplo, ignoramos os dois parâmetros que são passados, e simplesmente mostramos uma mensagem no callback, para mostrar o seu funcionamento.

gint delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
{
  g_print ("delete_event detectado\n");
  return (TRUE);
}

A função delete_event é um dos eventos do GTK+ (Lembre-se, o GTK+ é um Widget Set orientado a objetos, apesar de ter sido desenvolvido em C.) Abaixo segue a lista dos eventos existentes:

  • event
  • button_press_event
  • button_release_event
  • motion_notify_event
  • delete_event
  • destroy_event
  • expose_event
  • key_press_event
  • key_release_event
  • enter_notify_event
  • leave_notify_event
  • configure_event
  • focus_in_event
  • focus_out_event
  • map_event
  • unmap_event
  • property_notify_event
  • selection_clear_event
  • selection_request_event
  • selection_notify_event
  • proximity_in_event
  • proximity_out_event
  • drag_begin_event
  • drag_request_event
  • drag_end_event
  • drop_enter_event
  • drop_leave_event
  • drop_data_available_event
  • other_event

E, por último, repare que este evento retorna TRUE. Quanto este evento retorna TRUE, o GTK+ ignora a tentativa de sair do programa. Caso você quisesse sair do programa na ocorrência desse envento, seria necessária apenas a substituição do TRUE por um FALSE. Isso é útil quando o programador quer colocar um diálogo perguntando por exemplo `Você realmente deseja sair deste programa ?'

void destroy (GtkWidget *widget, gpointer data)
{
  gtk_main_quit();
}

O Callback "destroy" criado por nós é ativado quando a janela principal do programa é fechada (quando clica no botão). O que ela está fazendo neste caso é simplesmente chamar a função gtk_main_quit(), que finaliza o GTK+. Esta função deve ser chamada em todos programas que utilizem o GTK+, ao finalizar.

gtk_signal_connect (GTK_OBJECT (janela), "delete_event",
                   GTK_SIGNAL_FUNC (delete_event),
		   NULL);

Aqui, apresentamos um conceito novo. A função gtk_signal_connect conecta um evento, neste caso o evento `delete_event' à função delete_event, que definimos acima. O ato de conectar um evento a um callback é uma das características básicas do GTK+, pois permite que associemos algo como um clique num botão a uma ação qualquer que definimos no nosso callback. Note que nessa função são usadas as construções GTK_OBJECT (janela) e GTK_SIGNAL_FUNC (delete_event). Elas são macros do GTK+, que fazem um cast para o tipo desejado, e, no caso da GTK_SIGNAL_FUNC, checa-se se a função delete_event tem realmente dois parâmetros, e se eles são do tipo GtkWidget e gpointer, respectivamente. O último parâmetro da função gtk_signal_connect, que no nosso caso é NULL, é exatamente o parâmetro `gpointer data', que é passado para o nosso callback. Um exemplo de uso desse parâmetro seria passar um ponteiro para uma cadeia de caracteres (string), que seria usada no nosso callback.

gtk_signal_connect (GTK_OBJECT (janela), "destroy",
                    GTK_SIGNAL_FUNC (destroy),
	            NULL);

Aqui temos mais um evento (destroy), conectado ao nosso objeto (a janela). Conectamos esse evento aqui, para demonstrar que podemos conectar qualquer quantidade de eventos a um objeto. O resultado final, no nosso exemplo, é que quando se tenta fechar a janela, clicando no ícone no canto superior direito (ou esquerdo algumas vezes) da janela, é mostrada a mensagem `delete_event detectado', e a tentativa de sair é ignorada. Já no segundo evento conectado ao nosso objeto, nós simplesmente informamos ao GTK+ que, quando o o evento destroy for acionado (ou seja, a janela foi fechada, clicando-se o botão `sair'), se deve chamar a função gtk_main_quit(), que finaliza o GTK+.

botao = gtk_button_new_with_label ("Clique aqui para sair");

Aqui criamos um botão com o texto `Clique aqui para sair'

gtk_signal_connect (GTK_OBJECT (botao), "clicked",
                    GTK_SIGNAL_FUNC (hello),
		    NULL);

Conectamos o evento `clicked' ao callback `hello'. (Novamente: informamos ao GTK+ que quando o botão for clicado, deve-se chamar a função hello, com os parâmetros de sempre)

gtk_signal_connect_object (GTK_OBJECT (botao), "clicked",
                          GTK_SIGNAL_FUNC(gtk_widget_destroy),
			  GTK_OBJECT (janela));

A função gtk_signal_connect_object é útil, por exemplo, neste caso em que conectamos o evento `clicked' do botão à execução do evento `gtk_widget_destroy' da janela, que simplesmente fecha esta janela.

gtk_container_add (GTK_CONTAINER (janela), botao);

Inserimos o botão dentro da janela:

gtk_widget_show (botao);
gtk_widget_show (janela);

Mostramos os dois, lembrando que se deve mostrar a janela principal sempre por último:

gtk_main ();
return (0);

e, pronto, temos o nosso programa funcionando.

Há muito mais sobre GTK+. Caso tenha se interessado, recomendamos a leitura de um dos bons livros sobre GTK+ disponíveis na Internet (Além de terem sido incluídos no CD que acompanha a Revista do Linux - para a sua comodidade).

Na próxima edição, continuaremos com este tutorial em GTK+, quando passaremos à teoria das `boxes' (O modo que o GTK+ utiliza para inserir vários objetos dentro de um widget qualquer), além de outras técnicas avançadas.

No CD da Revista nº2 há farta documentação, em inglês, sobre GTK+ incluindo os itens:

  • Guia de referência da GLIB (glib_reference_guide.tar.gz)
  • Guia de referência do GTK (gtk_reference_guide.tar.gz)
  • Um tutorial sobre GTK+ (gtk_tutorial.tar.gz)
  • O livro GTK+/Gnome Application Development (gtk_gnome_application_development.tar.gz)
 

A Revista do Linux é editada pela Conectiva S/A
Todos os Direitos Reservados.

Política de Privacidade