<<
^
>>

Introdução à programação em Gtk+

Primeiro exemplo prático

Programar em Gtk+ envolve muitas técnicas e conceitos diferentes que procuraremos entender durante esse capítulo. Mas antes de falar em teoria, vamos criar nossa primeira janela com Gtk+. Vamos digitar o código a seguir em um arquivo chamado ola-gtk.c:

#include <gtk/gtk.h> int main (gint argc, gchar **argv) { GtkWidget *janela; gtk_init (&argc, &argv); janela = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW(janela), "Oi, Gtk!"); gtk_widget_show (janela); gtk_main (); }

Como podemos perceber, o arquivo cabeçalho incluído aqui é gtk/gtk.h. Já estamos usando a biblioteca Gtk+ e suas funções, mas continuamos usando coisas da GLib, que são automaticamente incluídas pela biblioteca Gtk+.

Para compilar o código acima, usamos o seguinte comando:

$ gcc -o ola-gtk ola-gtk.c `pkg-config --libs --cflags gtk+-2.0` $

Depois de rodar o programa você notará que uma janela simples, de título "Oi, Gtk!", de 200 por 200 pixels e que não fechará se você tentar fechá-la aparecerá ;). Ainda veremos como fazê-la fechar como outras janelas, aumentar seu tamanho e outras cositas más, mas por agora vamos só entender os conceitos desse código simples.

Para começar, todo programa que usa Gtk+ precisa aceitar argumentos, ou seja, precisa ter argc e argv como parâmetros da função main (). Isso porque a função gtk_init () (que tem de ser chamada antes de qualquer outra função da biblioteca Gtk+) interpreta algumas opções específicas da biblioteca que você pode consultar na página de manual gtk-options(7).

Algo importante que talvez deva ser dito antes de continuarmos é que a Gtk+ usa um modelo de orientação de objetos. Alguns conceitos de orientação a objetos como o de herança se aplicam aqui. Na linhas seguintes à que chama a função gtk_init () criamos uma janela, definimos um título para ela e a mostramos. Vejamos o código para ficar mais fácil comentar:

janela = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW(janela), "Oi, Gtk!"); gtk_widget_show (janela);

Na primeira linha a janela é criada e o ponteiro janela passa a representá-la. Note que janela é um GtkWidget, que é uma "classe" que representa um elemento gráfico qualquer. Essa classe é derivada de outras. Não vem ao caso falar da "filiação" das classes nesse momento, mas você pode conferir essa informação na referência da API. O importante aqui é notar que normalmente quando criamos um elemento gráfico qualquer, o que a função "construtora" retorna é quase sempre um GtkWidget. GTK_WINDOW_TOPLEVEL é o tipo de janela que queremos criar: uma janela comum. Outra opção seria GTK_WINDOW_POPUP, que não tem decorações do gerenciador de janelas e possui outras características.

Na segunda linha definimos o título da janela. Essa linha serve para perceber uma convenção importante do Gtk+. Vejamos:

gtk_window_set_title (GTK_WINDOW (janela), "Oi, Gtk!"); | | ^^^^^^^^^ | | | | | | -> argumento do método | | | -> objeto | | -> método do objeto | -> classe ou módulo -> proteção de namespace

Com essas informações já temos até como traduzir essa chamada para uma linguagem que tenha "açúcar sintático" específico de orientação a objetos. Veremos isso mais à frente. Agora nos importa perceber que, para funções que são específicas de determinado elemento, precisamos fazer casting do objeto de GtkWidget para sua própria classe (GtkWindow, no caso - já dá pra notar a convenção no estilo da glib aparecendo, também). Para isso usamos nesse caso a macro GTK_WINDOW, que trata de fazer checagem de tipo por nós.

Na outra linha usamos a função gtk_widget_show () para mostrar efetivamente a janela. Podemos tirar algumas lições aqui também. Note que a classe dessa função é widget. Isso significa que o método show vale para qualquer elemento gráfico (GtkWidget) e por isso mesmo não precisamos fazer casting. Outra lição é a de que qualquer elemento gráfico que criemos só será mostrado para o usuário caso peçamos explicitamente.

A chamada à função gtk_main () faz com que o programa entre em seu looping principal de tratamento de eventos, que estudaremos com alguns detalhes na próxima seção.

Looping principal de eventos

Vimos na seção anterior que, no final do código de um programa escrito com a biblioteca Gtk+ nós chamamos a função gtk_main (). Essa função é responsável por fazer o programa entrar em um loop infinito que trata os eventos e desenha os elementos gráficos.

Veremos que para que o programa seja capaz de atualizar sua exibição, executar funções temporizadas e tratar eventos ele precisa estar nesse looping. As implicações disso são razoavelmente claras: se você tiver uma função que gaste muito em seu programa, a interface vai ficar "travada" a menos que você permita que o loop principal do programa rode explicitamente.

Isso vai ficar claro se digitarmos e rodarmos o seguinte programa:

#include <gtk/gtk.h> int main (gint argc, gchar **argv) { GtkWidget *janela; gtk_init (&argc, &argv); janela = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_widget_show (janela); while (gtk_events_pending ()) gtk_main_iteration (); gtk_window_set_title (GTK_WINDOW(janela), "Titulo aqui!"); sleep (10); return 0; }

Note que esse programa não tem uma chamada à função gtk_main (), mas usa duas novas funções que ainda não vimos: a gtk_events_pending () retorna verdadeiro se houver quaisquer eventos que precisam ser tratados pelo Gtk+. No nosso caso, criamos uma janela e pedimos que fosse mostrada com gtk_widget_show (). Isso, então é o que está pendente no momento. Com aquele while, então, fazemos com que o Gtk+ faça uma iteração de seu looping principal (gtk_main_iteration ()) enquanto houve eventos pendentes e, com isso, garantimos que a janela seja mostrada antes de continuar.

Notaremos, no entanto que, ao rodar esse programa, o título da janela não é modificado. Isso porque a função gtk_window_set_title () é chamada mas o Gtk+ não tem oportunidade de efetivar a mudança, já que seu looping principal não tem mais nenhuma iteração até o final do programa, quando a janela some.

Como vimos, a função gtk_main () inicia um looping principal. Você pode iniciar outro looping principal dentro de uma função callback (conceito que veremos na próxima seção) qualquer para impedir que o programa passe daquele ponto até que uma determinada ação ou evento seja acionado. Nesse caso a ação ou evento que deve funcionar como gatilho para liberar a execução do código deve chamar a função gtk_main_quit () que é responsável por fazer o looping infinito terminar. É essa função que chamamos para finalizar um programa, também, já que temos um nível de looping infinito logo antes do return final do programa. Veremos mais sobre isso nas seções seguintes.

Entender o funcionamento do looping principal é um grande passo para desenvolver e aplicar técnicas que melhoram a usabilidade dos programas, e também para entender o funcionamento deles.

Orientação a eventos: sinais e callbacks

Programação de interfaces gráficas normalmente involve programação orientada a eventos. Cliques de mouse, pressionamentos de teclas e outros "acontecimentos" disparam processos. Há também processos que não devem depender de "ações" ou "acontecimentos" explíticos. Desse tipo de processos nós trataremos mais tarde . Falaremos por enquanto de tratamento de eventos, em sua maioria causados pelo usuário.

O Gtk+ chama os eventos de "sinais". Para tratarmos um sinal temos de associar ou, em gtkês, conectá-lo a uma função que chamamos de callback. Uma callback é, então, uma função cujo propósito é tratar um sinal.

Vamos então melhorar nosso programa, escrito na primeira seção desse capítulo para que seja capaz de terminar se o usuário fechar a janela:

#include <gtk/gtk.h> gboolean sair_do_programa (GtkWidget *janela, GdkEvent *event, gpointer data) { gchar *mensagem = (gchar*)data; printf ("%s\n", mensagem); gtk_main_quit (); return FALSE; } int main (gint argc, gchar **argv) { GtkWidget *janela; gtk_init (&argc, &argv); janela = gtk_window_new (GTK_WINDOW_TOPLEVEL); g_signal_connect (G_OBJECT(janela), "delete-event", G_CALLBACK(sair_do_programa), "Por hoje é só!"); gtk_window_set_title (GTK_WINDOW(janela), "Oi, Gtk!"); gtk_widget_show (janela); gtk_main (); return 0; }

Agora sim nosso programinha fecha! Vamos ver o que fizemos. A função g_signal_connect () faz com que a função callback sair_do_programa () seja executada quando o programa receber um sinal delete-event. Esse é o sinal emitido quando o usuário aperta o "X" da janela, por exemplo. Note que fazemos casting da nossa janela para G_OBJECT, que é, na verdade, a classe mãe de todos os objetos Gtk+ e é fornecida pela GLib. A própria função g_signal_connect, os mais atentos já devem ter percebido, é da GLib. Aqui encontramos mais uma aplicação das facilidades da GLib: todo o sistema de tratamento de sinais do Gtk+ é feito por ela.

Passamos como terceiro argumento nossa função callback e como quarto argumento um dado qualquer (pode-se passar NULL caso não se tenha necessidade de passar nada).

A função sair_do_programa () recebe como primeiro argumento o elemento gráfico que recebeu o evento, como segundo o evento e terceiro o dado passado quando a conectamos ao sinal. Essa assinatura está definida pelo sinal, na verdade. Você pode conferir os sinais que cada elemento gráfico tem associados a ele olhando o final da página referente a ele na referência da API. Importante notar que o sinal pode estar definido na classe do elemento ou qualquer classe de que ela derive. Por exemplo, o sinal "delete-event", muito embora o estejamos usando em uma GtkWindow pertence à classe GtkWidget. Portanto, observe a árvore de herança do elemento ao escolher um sinal!

Não trataremos do GdkEvent agora. Vamos investigar o conteúdo da função. Antes de mais nada, o gpointer é convertido para gchar* e impresso na tela. Logo depois a função gtk_main_quit () é chamada, o que significa que na próxima iteração do looping principal ele sairá. Finalmente a função retorna FALSE (ou seja, 0). Isso é importante porque, no caso desse sinal você pode impedir que outras callbacks sejam chamadas para tratar esse mesmo sinal retornando TRUE. Isso não é regra geral: a maioria dos sinais esperam callbacks que não retornem nada (retorno void), então fique atento à documentação.

<<
^
>>