Capítulo 4: Avançando


Tipos de dados editar

Algumas coisas que você deve ter percebido nos exemplos anteriores precisam ser explicadas agora. Os tipos gint, gchar, etc. que você vê são typedefs para os tipos int e char, respectivamente, e fazem parte do sistema da GLib. Isso é necessário para contornar a horrível dependência no tamanho de tipos simples de dados, que aparece por causa de cálculos que precisam ser feitos.

Um bom exemplo é o tipo "gint32" que corresponderá sempre a um inteiro de 32 bits, para qualquer plataforma ― seja o alpha de 64 bits ou o i386 de 32 bits. Essas definições são bastante diretas e intuitivas. O código de todas elas está em glib/glib.h (arquivo incluído por gtk.h).

Você também notará a habilidade do GTK de usar o tipo GtkWidget quando a função pede um GtkObject. O GTK foi feito de maneira orientada a objetos, e um widget é um objeto.

Mais sobre tratadores de sinal editar

Vamos dar mais uma olhada na declaração de g_signal_connect().

 gulong g_signal_connect( gpointer object,
                          const gchar *name,
                          GCallback func,
                          gpointer func_data );


Percebeu o valor de retorno do tipo gulong? Ele é uma marca que identifica sua função de callback. Como foi dito acima, você pode ter, por sinal e por objeto, tantos callbacks quanto quiser, e todos serão executados, na ordem em que foram atribuídos.

Essa marca permite que você remova esse callback da lista, usando a função:

 void g_signal_handler_disconnect( gpointer object,
                                   gulong   id );

Então, passando o widget do qual você deseja remover o tratador de sinal e a marca retornada por uma das funções signal_connect, você pode desconectar um tratador de sinal.

Você também pode temporariamente desativar tratadores de sinal com a familia de funções g_signal_handler_block() e g_signal_handler_unblock().

 void g_signal_handler_block( gpointer object,
                              gulong   id );
 
 void g_signal_handlers_block_by_func( gpointer  object,
                                       GCallback func,
                                       gpointer  data );
 
 void g_signal_handler_unblock( gpointer object,
                                gulong   id );
 
 void g_signal_handlers_unblock_by_func( gpointer  object,
                                         GCallback func,
                                         gpointer  data );

Um Hello World otimizado editar

Vamos dar uma olhada num "Hello World" um pouco melhorado, com melhores exemplos de callbacks. Isso nos introduzirá no próximo tópico, empacotando widgets.

 #include <gtk/gtk.h>
    
 /* Nosso novo callback melhorado. Dados passados a essa função  
 * são impressos na stdout. */
 static void callback( GtkWidget *widget,
                      gpointer   data )
 {
    g_print ("Hello again - %s foi pressionado\n", (gchar *) data);
 }
    
 /* Outra callback */
 static gboolean delete_event( GtkWidget *widget,
                              GdkEvent  *event,
                              gpointer   data )
 {
    gtk_main_quit ();
    return FALSE;
 }
    
 int main( int   argc,
          char *argv[] )
 {
    /* GtkWidget é o tipo de dado para widgets */
    GtkWidget *window;
    GtkWidget *button;
    GtkWidget *box1;
    
    /* Esta função é chamada em todas as aplicações GTK. Argumentos da linha
     * de comando são interpretados e retornados à aplicação.*/
    gtk_init (&argc, &argv);
    
    /* Cria uma nova janela */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    
    /* Esta é uma nova chamada, que apenas muda o título da nossa
     * nova janela para "Hello Buttons!"*/
    gtk_window_set_title (GTK_WINDOW (window), "Hello Buttons!");
    
    /* Aqui nós apenas ajustamos um tratador para delete_event que imediatamente
     * sai do GTK. */
    g_signal_connect (G_OBJECT (window), "delete_event",
    G_CALLBACK (delete_event), NULL);
    
    /* Ajusta a largura da borda da janela. */
    gtk_container_set_border_width (GTK_CONTAINER (window), 10);
    
    /* Nós criamos uma caixa para empacotar widgets nela. Descreveremos isso em detalhes
     * na sessão de "empacotamento". A caixa não é realmente visível, ela
     * é apenas utilizada como um instrumento para arranjar widgets. */
    box1 = gtk_hbox_new (FALSE, 0);
    
    /* Coloca a caixa na janela principal. */
    gtk_container_add (GTK_CONTAINER (window), box1);
    
    /* Cria um novo botão com o rótulo "Button 1". */
    button = gtk_button_new_with_label ("Button 1");
    
    /* Agora quando o botão é clicado, chamamos a função "callback"
     * com um apontador para "button 1" como argumento */
    g_signal_connect (G_OBJECT (button), "clicked",
    G_CALLBACK (callback), (gpointer) "button 1");
    
    /* Ao invés de gtk_container_add nós empacotamos o botão na caixa invisível
     * que foi colocada na janela. */
    gtk_box_pack_start (GTK_BOX(box1), button, TRUE, TRUE, 0);
    
    /* Sempre se lembre deste passo, ele diz a GTK que nosso preparo para
     * o botão esta completo, e ele pode ser exibido. */
    gtk_widget_show (button);
    
    /* Execute os mesmos passos para o segundo botão */
    button = gtk_button_new_with_label ("Button 2");
    
    /* Chame a mesma função callback com um argumento diferente, 
     * passando um apontador para o "button 2" dessa vez. */
    g_signal_connect (G_OBJECT (button), "clicked",
    G_CALLBACK (callback), (gpointer) "button 2");
    
    gtk_box_pack_start(GTK_BOX (box1), button, TRUE, TRUE, 0);
    
    /* A ordem em que os botões são exibidos não é realmente importante, mas eu
     * recomento mostrar a janela por último, assim todos aparecem de uma vez. */
    gtk_widget_show (button);
    
    gtk_widget_show (box1);
    
    gtk_widget_show (window);
    
    /* Descanse em gtk_main e espere a diversão começar! */
    gtk_main ();
    
    return 0;
 }

Compile o programa utilizando-se dos mesmos argumentos de ligação do nosso exemplo anterior. Nesse momento prevenimos que não existe um modo fácil de encerrar o programa. Você tem que usar o gerenciador de janelas ou a linha de comando para mata-lo. Bom exercício para o leitor seria criar um terceiro botão "Fechar" para encerrar o programa. Foce pode também resolver alterar as opções do gtk_box_pack_start() enquanto se prepara para a próxima sessão. Tente redimensionar a janela e observe o que acontece.