programaÇÃo gtk

38
PROGRAMAÇÃO GTK+ GTK+ é um toolkit multi-plataforma para a criação de interfaces gráficas. Ele foi desenvolvido para o GIMP. Por isso foi batizado de GIMP toolkit, com abreviação GTK+. GTK+ e Qt suplantaram outros toolkits e hoje são os dois conjuntos de widgets mais usados para a plataforma X11. O GTK+ é muito popular, sendo usado em um grande número de aplicações e no ambiente de desktop GNOME (que por sua vez também é muito popular). Licenciado sob a licença GNU LGPL, GTK+ é software livre e integra o projeto GNU. Este curso aborda uma introdução a programação GTK+ utilizando a linguagem C e como pré requisito, é necessário ter noções da linguagem C/C++. A versão da API GTK+ utilizada neste curso será a GTK+ 2.0 Duração: 1 semana. Carga Horária: 4 horas/dia Lição 1 - Pré Requisitos GTK+ Introdução - O que é o GTK+? GTK (GIMP Toolkit) é uma biblioteca usada para a criação de interfaces gráficas. Este está licenciado sob a licença LGPL, e você pode desenvolver softwares abertos e livres, ou até mesmo aplicativos comerciais não livre sem precisar pagar nenhuma taxa de licenciamento ou royalties. Por que o nome GIMP Toolkit? Originalmente, esta biblioteca foi escrita para o desenvolvimento do software GIMP (GNU Image Manipulation Program), porém hoje está sendo usada em vários projetos de softwares. GNU Network Object Model Environment (GNOME) project, um ambiente Desktop utilizado como padrão em várias distribuições Linux, é um destes projetos de sucesso. O GTK foi desenvolvido em cima da biblioteca GDK e Gdk-pixbuf. GDK (GIMP Drawing KIT) basicamente é uma abstração sobre funções de mais baixo nível para o acesso de operações de janelas (ex.: biblioteca Xlib para o sistema X windows) e Gdk-pixbuf que é uma biblioteca de manipulação de imagens. GTK é uma biblioteca desenvolvida com idéias de orientação objeto. Apesar de ter sido completamente escrita em C, ela foi implementada usando a idéia de classes e funções callback (ponteiro para funções). Qual linguagem será utilizada? Como já afirmado na ementa do curso, a linguagem utilizada será o C. Existem também outras linguagens que utilizam o "core" (núcleo) da biblioteca GTK, como por exemplo C++, Guile, Perl, Python, TOM, Ada95, Objective C, Free Pascal, Eifeel, Java e C#. Este curso não abordará as outras linguagens e sugiro a procura

Upload: api-3838125

Post on 07-Jun-2015

1.373 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: ProgramaÇÃo Gtk

PROGRAMAÇÃO GTK+

GTK+ é um toolkit multi-plataforma para a criação de interfaces gráficas. Ele foi desenvolvido para o GIMP. Por isso foi batizado de GIMP toolkit, com abreviação GTK+.

GTK+ e Qt suplantaram outros toolkits e hoje são os dois conjuntos de widgets mais usados para a plataforma X11. O GTK+ é muito popular, sendo usado em um grande número de aplicações e no ambiente de desktop GNOME (que por sua vez também é muito popular).

Licenciado sob a licença GNU LGPL, GTK+ é software livre e integra o projeto GNU.

Este curso aborda uma introdução a programação GTK+ utilizando a linguagem C e como pré requisito, é necessário ter noções da linguagem C/C++. A versão da API GTK+ utilizada neste curso será a GTK+ 2.0

Duração: 1 semana. Carga Horária: 4 horas/dia

Lição 1 - Pré Requisitos GTK+

Introdução - O que é o GTK+?

GTK (GIMP Toolkit) é uma biblioteca usada para a criação de interfaces gráficas. Este está licenciado sob a licença LGPL, e você pode desenvolver softwares abertos e livres, ou até mesmo aplicativos comerciais não livre sem precisar pagar nenhuma taxa de

licenciamento ou royalties.

Por que o nome GIMP Toolkit?

Originalmente, esta biblioteca foi escrita para o desenvolvimento do software GIMP (GNU Image Manipulation Program), porém hoje está sendo usada em vários projetos de softwares. GNU Network Object Model Environment (GNOME) project, um ambiente

Desktop utilizado como padrão em várias distribuições Linux, é um destes projetos de sucesso.

O GTK foi desenvolvido em cima da biblioteca GDK e Gdk-pixbuf. GDK (GIMP Drawing KIT) basicamente é uma abstração sobre funções de mais baixo nível para o acesso de operações de janelas (ex.: biblioteca Xlib para o sistema X windows) e Gdk-pixbuf

que é uma biblioteca de manipulação de imagens.

GTK é uma biblioteca desenvolvida com idéias de orientação objeto. Apesar de ter sido completamente escrita em C, ela foi implementada usando a idéia de classes e funções callback (ponteiro para funções).

Qual linguagem será utilizada?

Como já afirmado na ementa do curso, a linguagem utilizada será o C. Existem também outras linguagens que utilizam o "core" (núcleo) da biblioteca GTK, como por exemplo C++, Guile, Perl, Python, TOM, Ada95, Objective C, Free Pascal, Eifeel, Java e C#. Este curso não abordará as outras linguagens e sugiro a procura de sua documentação pela Internet. Essas linguagens, em sua maioria, nada mais é do que uma "casca" de abstração para que possa utilizar a sintaxe de outras linguagens. No fundo acaba

sendo o puro GTK+ e você encontrará muitas vezes referências para tutoriais GTK+ em C.

Este curso não irá abordar TODAS as possibilidades de construção de aplicações em GTK. A idéia é passar o máximo suficiente para que usando a documentação existente pela Internet, conseguir criar aplicações com interfaces gráficas mais complexas e

robustas.

É interessante que vocês reportem qualquer problema que encontrarem durante o curso. Qualquer dúvida ou sugestão para melhorar o curso será bem recebido e para enviar seu recado, procure o fórum de dúvidas gerais encontrada neste link.

Page 2: ProgramaÇÃo Gtk

Pré requisitos

Para poder compilar um programa GTK+ é necessário instalar a sua biblioteca com suas dependências.

Existem duas formas de instalação : por pacotes pré compilados e instalação por código fonte.

Instalação por Pacotes pré Compilados

Em sistemas Debian/Ubuntu, podemos instalar utilizando a ferramenta apt-get. Abra um terminal e digite: sudo apt-get install libgtk2.0-0 libgtk2.0-dev

Instalação por Código Fonte

Vale ressaltar que para os "puristas", a compilação exige um pouco mais de trabalho, pois além do código fonte do gtk+2.0, é necessário também sempre prestar bastante atenção nas suas dependências para compilação.

Primeiramente, vamos baixar todos os pacotes necessários para a compilação pelo site a seguir: GTK+ -> ftp://ftp.gtk.org/pub/gtk/v2.8/

Dependências -> ftp://ftp.gtk.org/pub/gtk/v2.8/dependencies/

Obs.: Esta é a versao 2.8 do GTK+ que será utilizada no curso.

Duas ferramentas são necessárias para o processo de construção/compilação: pkg-config e GNU make. pkg-config é uma ferramenta de ajuda usada para facilitar a compilação de aplicativos com as suas dependências de

bibliotecas. Isso ajuda a inserção de opções corretas do compilador pela linha de comando para que aplicações usem o formato de opções como a seguir: gcc teste.c -o teste `pkg-config --libs --cflags glib-2.0`, ao invés de usar valores numéricos ou letras para que o compilador saiba onde está a biblioteca glib-2.0 (por exemplo).

Você poderia pensar em usar a versão do makefile a seu gosto, porém eles tendem a ter incompatibilidades. Logo é recomendado a instalação do GNU make (caso você ainda não o tenha no computador). O nome da ferramenta é "gmake" ao invés de "make".

Três bibliotecas que GTK+ depende, são mantidas pela equipe do GTK+. São elas: GLib, Pango e ATK. A biblioteca GLib provê o núcleo de funcionalidades não gráficas como tipos de dados de alto nível, manipulação de

Unicode, objetos e sistema de tipagem para programas C. GLib pode ser encontrado aqui! Pango é a biblioteca que fornece a possibilidade de manipulação de textos internacionais (várias linguagens). Pango

pode ser encontrado aqui!!! ATK é uma ferramenta para acessibilidade. Ele fornece um conjunto de intefaces genéricas que permite acessibilidade

tecnológica para necessidades especiais, como leitores de tela para interargir com a interface gráfica. ATK pode ser encontrado aqui!!!

A biblioteca GNU libiconv é necessário para compilar GLib caso seu sistema não possua a função iconv() que serve para a conversao entre as codificações de caracteres. A maioria dos sistemas modernos já devem ter esta função como padrão.

A biblioteca libintl do pacote GNU gettex é necessário caso seu sistema não tenha a funcionalidade do gettext(), que é a capacidade de manipulação de traduções de banco de mensagens.

As bibliotecas para carregar imagens JPEG, PNG e TIFF são necessárias para compilar GTK+. Você provavelmente já deve ter estas bibliotecas instaladas, caso não tenha, as versões que você precisa estão no diretório de dependência na página dos arquivos do GTK+. (Antes de instalar estas bibliotecas pelo código fonte, é bom observar se já não existe um pacote pré-compilado para o seu sistema).

As bibliotecas para o sistemas X windows são necessárias para compilar as bibliotecas Pango e GTK+. Você provavelmente já deve ter instalado estas bibliotecas no sistema, mas é possível que você tenha que instalar o ambiente de desenvolvimento para estas bibliotecas que o fornecedor disponobiliza ao seu sistema operacional.

A biblioteca fontconfig provê a biblioteca Pango com um modo padrão de localizar fontes e relacionar eles pelos seus nomes.

Cairo é uma biblioteca gráfica que suporta gráfico vetorial e composição de imagens. Pango e GTK+ utilizam bastante cairo para seus desenhos.

Compilando e Testando GTK+

Antes de ilustrar os passos para a compilação, vale comentar novamente que caso você tenha como instalar as bibliotecas por algum repositório de softwares, seria bem mais prático, fácil e rápido fazer por meio dele. O interessante de uma instalação

rápida das bibliotecas faria com que você possa testar mais rapidamente os exemplos do curso e não perder tempo em coisas fora do objetivo principal: programação GTK+.

Caso você queira fazer isso manualmente ou que sua distribuição Linux não permite a instalação por meio de algum repositório, continue lendo as instruções.

Primeiramente, tenha certeza de que você tenha todas as dependências instaladas: pkg-config; GNU make; bibliotecas JPEG, PNG e TIFF; FreeType; e caso necessário para seu sistema, libiconv e libintl. Para detalhes especiais de como compilar estes pacotes,

Page 3: ProgramaÇÃo Gtk

a documentação dos pacotes individuais são encontradas nos seus respectivos sites. Em sistemas Linux, provavelmente você já terá todos estes pacotes instalados, menos o pkg-config.

Depois de baixar todos os arquivos necessários, compile e instale as bibliotecas GTK+ da seguinte forma: GLib, Pango, ATK, e GTK+. Para cada biblioteca, siga os passos de "configure, make e make install". Se estiver instalado tudo certinho, vai funcionar tudo de forma suave e você, rapidamente, poderá testar sua própria aplicação GTK+.

Obs.(1): Lembre-se que para a instalação dos pacotes de código fonte, geralmente seguem os passos: tar -zxvf nomedopacote.tar.gz

./configure make

make install

Obs.(2): Se algum dos scripts configure falhar ou o make falhar, observa atentamente a mensagem de erro; a maioria dos casos irá fornecer informação útil do que ocorreu de errado. Geralmente a mensagem de erro e fazendo a pergunta certa ao google, irá aparecer vários sites de pessoas que já tiveram estes mesmos problemas. Por último caso, se você se perder no google, pode

perguntar ao monitor do curso "eu! " para te passar algumas dicas!

Compilando Aplicações GTK+ em Ambiente Unix-like

Para compilar as aplicações GTK+, você precisa informar ao compilador onde achar os arquivos cabeçalho e suas bibliotecas. Isto é feito usando o utilitário pkg-config.

Abra um terminal shell e digite: pkg-config --cflags gtk+-2.0

Você irá encontrar algo semelhante a: -I/usr/include/gtk-2.0 -I/usr/lib/gtk-2.0/include -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pango-1.0

-I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include

Digite também: pkg-config --libs gtk+-2.0

Você também irá encontrar algo semelhante a: -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgdk_pixbuf-2.0 -lm -lpangocairo-1.0 -lfontconfig -lXext -lXrender -lXinerama -lXi -

lXrandr -lXcursor -lXfixes -lpango-1.0 -lcairo -lX11 -lgobject-2.0 -lgmodule-2.0 -ldl -lglib-2.0

Dessa forma, você pode utilizar estas saídas de textos para colocar como parâmetro de configuração na hora de compilar algum aplicativo GTK+, sem o medo de esquecer algum destes parâmetros.

Logo, uma forma de utilizar estes textos na hora de compilar seria com o comando a seguir: gcc `pkg-config --cflags --libs gtk+-2.0` hello.c -o hello

Note que, o que estiver entre ` ` são os comandos que colocamos no começo deste tópico, e que serão automaticamente transformados para string (onde tem a localidade das bibliotecas) e utilizadas na compilação.

Ou seja, em vez de utilizarmos: gcc -I/usr/include/gtk-2.0 -I/usr/lib/gtk-2.0/include -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pango-1.0

-I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgdk_pixbuf-2.0 -lm -lpangocairo-1.0 -lfontconfig -lXext -lXrender -lXinerama -lXi -lXrandr -lXcursor -lXfixes -lpango-1.0 -lcairo -lX11 -lgobject-2.0 -lgmodule-

2.0 -ldl -lglib-2.0 hello.c -o hello

Podemos poupar os dedos e digitar menos para a compilação, ou seja, digitando: gcc `pkg-config --cflags --libs gtk+-2.0` hello.c -o hello

Ps.: Pode ser que você não tenha o pacote pkg-config e ao tentar digitar isto no terminal, irá aparecer:

bash: pkg-config: command not found

Neste caso instale o pacote pkg-config: sudo apt-get install pkg-config

Questão: Podemos afirmar que GTK+ é um conjunto de bibliotecas para interfaceamento gráfico, a qual fornece apenas funções em linguagem C/C++, não tendo nenhum outro suporte a outras linguagens.

Lição 2 - Passos Iniciais

Page 4: ProgramaÇÃo Gtk

A sua Primeira Janela

Vamos primeiramente criar uma janela simples de tamanho 200x200 pixels, mas sem implementarmos ainda nenhuma funcionalidade, por enquanto. Copie o código a seguir em um arquivo teste1.c (utilize o editor de texto a seu gosto, gedit, kate,

vim, emacs, etc...)

#include <gtk/gtk.h>

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

GtkWidget *window;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

gtk_widget_show(window);

gtk_main();

return 0;}

Para compilar o arquivo, digite:

* gcc teste1.c -o teste `pkg-config --cflags --libs gtk+-2.0`

e execute o programa:

* ./teste1

Isso irá mostrar na tela uma janela feita em GTK+! PARABÉNS!!!

Agora tente fechar a janela.... ops! Ela não fecha.... então, seja grosseiro e force a saída do programa dando ctrl-c no seu terminal (onde você iniciou o programa).

Detalhando o que são cada parte do código, observe:

Inclusão da Biblioteca GTK+;#include <gtk/gtk.h>

Função Principal;int main(int argc, char *argv[]){

Cria uma estrutura (widget) na qual é a janela principal;GtkWindow *window;

Esta função inicializa a biblioteca, seta os manipuladores de sinais padrões;(explicarei adiante) e testa os parâmetros passados pela linha de comando para controle;

gtk_init(&argc,&argv);

Cria um widget para a janela principal; Observe que window é um GtkWidget, que é uma "classe" que representa um elemento gráfico qualquer. Essa

Page 5: ProgramaÇÃo Gtk

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.

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

Este é um loop principal que irá rodar até que o aplicativo seja fechado;gtk_main();

Retorno da função principal main.return 0;

}

WIDGETS?!? O que são estes "Widgets" (Leia widgêeets)que estão sendo citados constantamente nos comentários?

Na programação GUI (Graphics User Interface), um widget é um elemento de interface para controle, na qual um usuário pode interagir com este mesmo. Exemplos de Widgets são janelas, botões e caixas de texto. Widgets as vezes são consideradas como simulações virtuais de controles considerados "físicos", ou seja, um botão virtual você consegue clicar com um cursor de mouse e em contrapartida um botão físico você pode pressionar com o dedo (botão do mouse). Widgets são geralmente empacotados em um toolkit widget (kit ferramentas). Programadores usam estes widgets para construir interfaces gráficas.

Botão Olá Mundo!

Vamos criar agora um botão escrito "hellllo" Este botão ao ser clicado, irá imprimir no terminal uma mensagem de texto e fechar.

Ps.: Para um bom aprendizado, no começo apenas teste (digite) o código para que se familiarize com a estrutura e as funções do gtk+. Com o tempo (a medida que o curso passa) vários ítens vão se esclarecendo até que se torne intuitivo e natural criar

softwares em GTK+! Confie, digite e teste!~

Código fonte + Comentários:

/* * Exemplo de um Botão Olá Mundo!!!

**/

#include <gtk/gtk.h>

/** Essa é uma função para chamada de retorno (callbacks), os argumentos de "data"

* estão sendo ignorados e explicarei melhor "callbacks" depois.**/

static void hello(GtkWidget *widget,gpointer data)

{g_print("Ola Mundo!!\n");

Page 6: ProgramaÇÃo Gtk

}

/* GTK irá emitir um sinal "destroy". Retornando TRUE significa que* você não irá querer que a janela seja destruída. Isto é útil para

* aqueles tipos de janelas popup que fazem confirmação de * fechamento de janelas - "Você realmente deseja sair?"

*/

static gboolean delete_event(GtkWidget *widget,GdkEvent *event,

gpointer data){

g_print("callback delete_event!!!\n");return TRUE;

}

/** Outro callback tratando a saída do programa.

*/

static void destroy (GtkWidget *widget,gpointer data)

{g_print("Funcao destroy!!!\n");

gtk_main_quit();}

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

// GtkWidget é o tipo de armazenamento para os widgets //GtkWidget *window;GtkWidget *button;

// Esta função inicializa a biblioteca, seta os manipuladores de sinais padrões //gtk_init(&argc,&argv);

// Cria uma nova janelawindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);

/** Quando é dada para janela um sinal "delete_event" (fornecido pelo gerenciador de* janela, geralmente por uma opção de "fechar", ou por uma opção do menu), nós

* fazemos uma chamada para a função delete_event() como está definido acima. O* dado passado para a função neste caso é NULL e é ignorado dentro da função

* callback delete_event();*/

g_signal_connect(G_OBJECT(window), "delete_event",G_CALLBACK(delete_event),NULL);

/** Aqui nós conectamos o evento "destroy" para o tratador de sinal. Este evento

* acontece quando nós chamamos a função gtk_widget_destroy() pela janela (clique * para fechar a janela no canto direito superior) ou então se nós retornamos

* FALSE na chamada "delete_event";*

* Será explicado melhor sobre "Propagação de sinal" e TRUE e FALSE dentro de um* tratador de sinal. Tudo será esclarecido mais adiante.

**/

g_signal_connect(G_OBJECT(window), "destroy",G_CALLBACK(destroy),NULL);

// Seta a grossura da borda de uma janelagtk_container_set_border_width(GTK_CONTAINER(window),10);

// Cria um botão novo escrito "helllllo!"button = gtk_button_new_with_label("helllllo");

/** Novamente um tratador de sinal! Desta vez é para o botão. Quando um botão

* recebe este sinal "clicked", será chamado a função hello() definida acima.* Podemos ver que temos NULL, que é um argumento NULO enviado para a função

* callback hello().*

Page 7: ProgramaÇÃo Gtk

*/g_signal_connect(G_OBJECT(button),"clicked",

G_CALLBACK(hello), NULL);

/** Outro sinal.. este sinal causa a destruição da janela ao ser clicado. Esta chama a

* função gtk_widget_destroy(window) para tal e novamente, o sinal de destruir pode * vir daqui(clique no botão) ou então do gerenciador de janelas (botões de fechar -

* altF4, etc).**/

g_signal_connect_swapped(G_OBJECT(button), "clicked",G_CALLBACK(gtk_widget_destroy),

G_OBJECT(window));

// Esta função empacota o botão dentro de um container, neste caso o// container seria o "window" (janela principal criada).gtk_container_add(GTK_CONTAINER(window),button);

// A última etapa é mostrar o botão na telagtk_widget_show(button);

// e também, lógico, devemos mostrar a janelagtk_widget_show(window);

/** Todas as aplicações GTK devem ter uma chamada gtk_main(). O controle do

* programa termina aqui e esta função fica esperando algum evento acontecer * (clique de mouse, movimento do mouse, tecla pressionada, etc)

**/

gtk_main();

return 0;}

Vocês têm observado o uso constante de g_signal_connect e algumas funções chamadas de "callback" acima. A seguir iremos passar pelos conceitos de "signal" e "callback" (teoria dos sinais e chamadas de funções).

Observem a estrutura da função ilustrada no código total acima:

button = gtk_button_new_with_label("helllllo");

Subdividindo a linha acima, temos:

...button ...-> objeto GtkWidget

...= gtk_ ... -> proteção namespace

...button ... -> classe ou módulo

..._new_with_label... -> método (Ação)

...("helllllo"); -> argumentos do método

Todos os comandos seguem mais ou menos essa estrutura, primeiramente se for criação de algum widget, temos que associar para um objeto GtkWidget. Logo a seguir temos que classe que você estará manipulando e mais adiante temos a ação na qual será feita seguidos de seus argumentos.

Resumindo temos com bastante frequência: instância (objeto) -> classe -> ação(método) -> argumentos

Tendo essa idéia, fica mais fácil lembrar os comandos, pois todos seguem mais ou menos essa estrutura. (Pelo menos foi a forma que encontrei para "decorar" os comandos).

Signals e Callbacks

A biblioteca hoje encarregada em cuidar do sistema de sinais foi movida do GTK para o GLib, a partir da versão 2.0. A única diferença encontrada nas funções é que em vez de ter um prefixo "gtk_", elas têm agora prefixo "g_". Não irei abordar neste

curso detalhes da relação entre o sistema de sinais do GLib 2.0 e o GTK 1.2. Como ilustrado na ementa do curso, estamos estudando a versão 2.0 do GTK+.

Iremos após explicar a teoria dos signals e callbacks, mostrar em detalhes cada parte do código anterior do Ola Mundo.

GTK é um kit de ferramentas (toolkit) baseado em eventos, ou seja, os softwares irão sempre permanecer dormindo na função gtk_main() até que algum evento ou controle seja passado para uma função apropriada. Esta passagem de controle é feita

usando a idéia dos "signals" (sinais) e se você conhece algo sobre sistemas Unix-like, este possui um sistema de sinais

Page 8: ProgramaÇÃo Gtk

semelhantes ao que GTK+ usa. Embora o GTK+ não utilize diretamente "signals" do sistema operacional, a terminologia e a idéia são as mesmas.

Quando um evento ocorre como um clique do mouse em um botão, um sinal apropriado será emitido pelo "widget" na qual você pressionou. Desta forma que o GTK faz a maioria dos seus trabalhos. Existem sinais que todo widget herda como "destroy", e

alguns que só algumas widgets possuem como "toggled" encontrada em botões "toggled" (Aqueles botões que afundam).

Para que um botão faça alguma ação, nós setamos um tratador de sinal para capturar o sinal e chamar alguma função para resolver o problema. Isto é feito usando uma função no formato abaixo:

gulong g_signal_connect( gpointer *objeto,

const gchar *nome,GCallback funcao,

gpointer func_dado );

O primeiro argumento é o widget que irá enviar o sinal;O segundo argumento é o nome do sinal que você quer capturar;

O terceiro argumento é a função que irá ser chamada assim que o sinal for capturado e...;O último argumento é o dado que você quer passar para a função.

A função especificada no 3o argumento é chamado de "função callback" (ou só callback), e ela tem o seguinte formato:

void callback_func( GtkWidget *widget,... /* outros argumentos possíveis de aparecer */

gpointer dado_callback );

O primeiro argumento aponta para o widget que emitiu o sinal e o último argumento aponta para o dado que foi enviado pela função g_sinal_connect() (conector de sinais) mostrado acima.

Vale ressaltar que o formato mostrado acima é apenas para mostrar um caso geral, pois muitos widgets especificam sinais que exigem parâmetros diferenciados, mudando a "cara" dessa função callback.

Outra chamada para conectar sinais, que você pode reparar no nosso programa do olá mundo, é no formato a seguir:

gulong g_signal_connect_swapped( gpointer *objeto,const gchar *nome,GCallback funcao,

gpointer *dado_callback );

g_signal_connect_swapped() é a mesma coisa do g_signal_connect(), a diferença é que a instância na qual o sinal está emitindo e o dado são trocados ao se chamar um trador de sinal. Ou seja, quando você usar esta função, o primeiro argumento será qual o

dado (widget) que irá emitir o sinal e o último seria o objeto passado para a função.

void callback_func( gpointer callback_data,... /* other signal arguments */

GtkWidget *widget);

Geralmente não se cria callbacks para este tipo de função, mas muitas vezes é usado para chamar uma função que aceita um widget simples ou um objeto como parâmetro, quando o sinal é emitido por algum objeto. No nosso exemplo anterior, nós conectamos o sinal "clicked" no botão e chamamos um gtk_widget_destroy() em cima da janela. Se seu callback necessitar mais dados, utilize g_signal_connect ao invés do g_signal_connect_swapped().

Eventos

Além dos mecanismos de sinais desritos acima, existem outros mecanismos de eventos e que podem ser associados a callbacks. Os eventos possíveis são:

event button_press_event

button_release_event scroll_event

motion_notify_event delete_event destroy_event expose_event

key_press_event key_release_event enter_notify_event leave_notify_event configure_event

Page 9: ProgramaÇÃo Gtk

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 visibility_notify_event

client_event no_expose_event

window_state_event

Não iremos detalhar todos os eventos neste curso, mas a medida que forem colocados novas idéias e algoritmos, iremos passar por algumas delas.

Como ilustrado acima, com o objetivo de conectar uma função callback em um destes eventos, você deve usar a função g_signal_connect() com um destes eventos como parâmetro. As funções callback para eventos têm uma ligeira diferença

comparado aos dos sinais.

gint callback_func( GtkWidget *widget, GdkEvent *event,

gpointer callback_data );

GdkEvent é uma estrutura "Union" a qual seu tipo depende de qual dos eventos acima ocorreu. Para que nos informe qual evento foi emitido, cada uma das possíveis alternativas têm um tipo associado a ele. Outros componentes da estrutura do evento irão depender do tipo do evento emitido e podem ser classificados nos tipos a seguir:

o GDK_NOTHING o GDK_DELETE o GDK_DESTROY o GDK_EXPOSE

o GDK_MOTION_NOTIFY o GDK_BUTTON_PRESS o GDK_2BUTTON_PRESS o GDK_3BUTTON_PRESS o GDK_BUTTON_RELEASE

o GDK_KEY_PRESS o GDK_KEY_RELEASE o GDK_ENTER_NOTIFY o GDK_LEAVE_NOTIFY o GDK_FOCUS_CHANGE o GDK_CONFIGURE

o GDK_MAP o GDK_UNMAP

o GDK_PROPERTY_NOTIFY o GDK_SELECTION_CLEAR

o GDK_SELECTION_REQUEST o GDK_SELECTION_NOTIFY o GDK_PROXIMITY_IN o GDK_PROXIMITY_OUT o GDK_DRAG_ENTER o GDK_DRAG_LEAVE o GDK_DRAG_MOTION o GDK_DRAG_STATUS o GDK_DROP_START

o GDK_DROP_FINISHED o GDK_CLIENT_EVENT

o GDK_VISIBILITY_NOTIFY o GDK_NO_EXPOSE o GDK_SCROLL

o GDK_WINDOW_STATE o GDK_SETTING

Page 10: ProgramaÇÃo Gtk

Então, para conectar uma função callback para um destes eventos, devemos usar um comando do tipo:

g_signal_connect (G_OBJECT (button), "button_press_event",

G_CALLBACK (button_press_callback), NULL);

Estabelecemos button como um Widget Button e que quando o mouse estiver sobre ele e o botão for pressionado, a função button_press_callback() irá ser chamado. Esta função é declarada como:

static gboolean button_press_callback( GtkWidget *widget,GdkEventButton *event,

gpointer data );

Observe que declaramos o segundo argumento da função como tipo GdkEventButton, já que sabemos qual evento ocorreu para esta função ser chamada.

O valor retornado para esta função indica se o evento irá ser propagado adiante pelo mecanismo de manipulação de eventos GTK. Retornando TRUE indica que o evento já foi tratado e não irá ser propagado para os outros tratadores. Retornando FALSE indica que mesmo executando a função, o tratamento de eventos irá continuar atuando normalmente (propagando o sinal). Este processo de propagação será explicado com mais detalhes futuramente.

Para mais detalhes dos tipos de dado GdkEvent, veja no final do curso uma listagem destes mesmos.

Há também bibliotecas chamadas GDK selection (seleção) e drag-and-drop (arraste e solte) que também emitem um conjunto de eventos que refletem no GTK. Este também usa a teoria dos sinais explicada anteriormente.

São eles: selection_received

selection_get drag_begin_event drag_end_event drag_data_delete

drag_motion drag_drop

drag_data_get drag_data_received

As técnicas de seleção, arrastar e soltar, não irão ser mostradas neste curso, por ser uma técnica mais avançada, porém é facilmente encontrada documentações nos links da página inicial deste curso.

Aguardem! Até o momento talvez muitas coisas ainda não estejam claras, mas procurem copiar (decorando se possível) os comandos e compile. Achem os erros e tentem perguntar no fórum a respeito deles e com isso vocês irão progredindo aos poucos em relação a interface gráfica. Esta é uma das formas que procuro utilizar para estudar assuntos de programação.

Agora que expliquei um pouco mais da teoria, o código mostrado provavelmente estará mais claro. Com intuito de esclarecer todos os pontos do código, irei explicar com mais detalhes cada parte do código novamente. Aqui temos a inclusão da biblioteca necessária para usarmos as funções, estruturas e constantes fornecidas pelo GTK. #include <gtk/gtk.h> Aqui temos uma função

callback chamada "hello()" que irá imprimir na tela o texto "Ola Mundo!\n" sempre que capturar algum sinal associado a ela. static void hello(GtkWidget *widget, gpointer data) { g_print("Ola Mundo!\n"); } Temos aqui outra função callback chamada

"delete_event()" que imprimirá na tela o texto "callback delete_event!!!\n". Observe que esta função é bem similar ao de cima, mas com a diferença do retorno TRUE. Este já foi explicado anteriormente, mas para relembrar, o retorno TRUE indica que o sinal que esta função está tratando, não irá se propagar por todas as funções, ou seja, seria a idéia de que o sinal já foi tratado! static

gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer data) { g_print("callback delete_event!!!\n"); return TRUE; } Esta função callback irá destruir todas as janelas e terminar o aplicativo. static void destroy (GtkWidget *widget,

gpointer data) { g_print("funcao destroy\n"); gtk_main_quit(); } Vale a pena ressaltar que em nenhuma das funções acima foi utilizado o quot;data"entre os parâmetros das funções. Ou seja, mesmo se contiver alguma informação, estas funções o

ignorarão. Logo abaixo temos a função principal clássica de programas em C. int main(int argc, char *argv[]) { As duas linhas

abaixo são ponteiros para tipos de dados GtkWidget. Já foi explicado o que é um widget e não irei comentar novamente. GtkWidget *window; GtkWidget *button; Aqui nós iniciamos um ambiente gtk. Várias coisas são feitas nessa parte do código para

que possamos usar corretamente gkt+. gtk_init(&argc,&argv); Temos na linha de baixo a criação de uma janela gtk. 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. window = gtk_window_new(GTK_WINDOW_TOPLEVEL); A seguir são dois exemplos de conexão de um manipulador de sinais em um objeto.

Neste caso estamos conectando sinais em objeto janela. Os sinais especificados para serem capturados são "delete_event" e "destroy". O primeiro é emitido quando você clica para fechar a janela no canto direito (x) ou quando é passado uma widget da

janela para ser destruído por uma função gtk_widget_destroy(). G_OBJECT e G_CALLBACK são macros que fazem a tipagem (casting) e checka se está com a tipagem certa, além de ajudar na legibilidade do código. g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event),NULL); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy),NULL); A função seguinte especifica a espessura da borda desta janela. gtk_container_set_border_width(GTK_CONTAINER(window),10); Temos uma criação de um botão escrito "helllllo" dentro. button = gtk_button_new_with_label("Helllllo"); Aqui novamente temos conexões de sinais com suas respectivas funções callback. O 1o está conectando um sinal "clicked" que é emitido quando algum usuário clica no botão e a função que trata isso é o "hello()" (especificada acima). g_signal_connect(G_OBJECT(button),"clicked",

Page 11: ProgramaÇÃo Gtk

G_CALLBACK(hello), NULL); As funções callbacks são chamadas na ordem que você criar as conexões. Esta conexão é um pouco diferente, ela passa trocado a posição do . g_signal_connect_swapped(G_OBJECT(button), "clicked",

G_CALLBACK(gtk_widget_destroy), G_OBJECT(window)); g_signal_connect_swapped() é o mesmo que g_signal_connect() exceto por um aspecto: a instância na qual o sinal é emitido e os dados (definidos pela chamada) serão invertidos quando o tratador for

chamado. Então, quando essa função for usada para conectar sinais, o callback deve ser da forma mostrada anteriormente. O objeto é normalmente um widget. No entanto, não costumamos definir callbacks para a função g_signal_connect_swapped(). Eles são normalmente usados para chamar, quando um sinal é emitido em algum outro objeto, uma função do GTK que aceita apenas

um widget ou objeto como argumento. Neste exemplo, conectamos ao sinal "clicked" no botão, mas chamamos gtk_widget_destroy() na janela. Se seus callbacks precisam de dados adicionais, use g_signal_connect() no lugar de g_signal_connect_swapped(). gtk_container_add(GTK_CONTAINER(window),button); Esta acima é uma "chamada de

empacotamento", que será explicada a fundo mais adiante, na lição sobre Empacotando widgets; mas é razoavelmente fácil de ser entendida. Ela simplesmente diz ao GTK que o botão deve ser colocado na janela, onde ele será exibido. Note que um

"container" GTK só pode conter um widget; mas há outros widgets — que serão descritos mais tarde — que são desenhados para dispor vários widgets de várias maneiras. gtk_widget_show(button); Agora já temos tudo configurado da maneira que queremos que seja. Com todos os tratadores de sinal no lugar, e o botão colocado na janela no lugar que deverá estar, mandamos o GTK mostrar ("show") os widgets na tela. O widget da janela é mostrado por último para que a janela apareça inteira de uma vez —

em vez de vermos primeiro a janela aparecer e depois o botão se formar dentro da janela. Claro que, com um exemplo tão simples, você dificilmente (ou nunca) notaria essa diferença. gtk_widget_show(window); E, é claro, chamamos a função

gtk_main(), que aguarda eventos do servidor X e irá coordenar as emissões de sinais quando esses eventos forem detectados. gtk_main(); Agora, quando clicarmos o botão do mouse num botão do GTK, esse widget permitirá o sinal "clicked". Para

podermos usar essa informação, nosso programa cria um tratador de sinal para interceptar esse sinal, despachando a função de nossa escolha. No nosso exemplo, quando o botão que criamos é apertado, a função hello() é chamada com um argumento NULL, e depois o próximo tratador para esse sinal é chamado — a função gtk_widget_destroy(), passando a ela o widget da janela como argumento, e destruindo esse widget. Com isso, a janela emite o sinal "destroy", que é interceptado e faz chamar nossa função

callback destroy(), que simplesmente sai do GTK. Outro caminho é usar o gerenciador de janelas para fechar a janela, o que causará a emissão do sinal "delete_event". Isso chamará nosso tratador de sinal "delete_event". Nele, se retornarmos TRUE, a janela será deixada como está e nada irá acontecer. Retornando FALSE, fazemos o GTK emitir o sinal "destroy" que, é claro,

chama o callback "destroy", deixando o GTK. return 0; }

Tipos de dados

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

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 família 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 );

Teste o programa a seguir e escolha uma resposta abaixo: o que este programa faz?

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

Page 12: ProgramaÇÃo Gtk

static void clicado(GtkWidget *widget,

gpointer data){g_print("O que você está fazendo?!");

gtk_main_quit();}

static void delete_event(GtkWidget *widget,gpointer data){gtk_main_quit();

}

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

GtkWidget *window;GtkWidget *button;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

g_signal_connect(G_OBJECT(window), "destroy",G_CALLBACK(delete_event), NULL);

button = gtk_button_new_with_label("OPA!");

g_signal_connect(G_OBJECT(button),"clicked",G_CALLBACK(clicado),NULL);

gtk_container_add(GTK_CONTAINER(window),button);

gtk_widget_show(button);

gtk_widget_show(window);

gtk_main();

return 0;}

Cria uma janela com um botão que ao ser clicado, sai do programa.

Lição 3 - Empacotando Widgets

O que é Empacotamento de Widgets?

Quando você está querendo criar um aplicativo, você naturalmente irá querer colocar mais de um widget dentro de uma janela. Nosso primeiro exemplo apenas usava um widget, por isso podíamos usar simplesmente a função gtk_container_add() para "empacotar" um widge dentro de uma janela. Mas com certeza vocês estão me perguntando, como e quando irei precisar colocar mais de um widget em uma janela? Além disso, como que você irá controlar em que lugar seu widget irá ficar em sua janela? Esta lição irá responder estas perguntas e fazer você entender melhor a idéia central deste conceito.

Page 13: ProgramaÇÃo Gtk

O empacotamente basicamente é feito criando caixas que nada mais são do que containers invisíveis. Nestas podemos empacotar nossos widgets em duas formas básicas, por uma caixa horizontal ou uma caixa vertical. Quando estamos empacotando widgets em uma caixa horizontal, os objetos são inseridos horizontalmente da esquerda para a direita ou da direita para a esquerda, dependendo da forma que o chamamos. Em uma caixa vertical, widgets são empacotados de cima para baixo ou vice versa. É interessante comentar que a combinação destas caixas dentro ou do lado de outras caixas, podem criar o layout que você desejar.

Para criar uma caixa horizontal, nós chamamos a função gtk_hbox_new(), e para caixas verticais, gtk_vbox_new(). A função gtk_box_pack_start() irá iniciar do topo a colocação dos widgets em uma caixa vertical e da esquerda para a direita, em uma caixa horizontal. gtk_box_pack_end() irá fazer o oposto, empacotando de baixo para cima em uma caixa vertical e da direita para a esquerda uma caixa horizontal. Usando estas funções, permite-nos alinhar a direita ou a esquerda nossos widgets e pode misturar estes alinhamentos de qualquer forma para ilustrar o efeito desejado. A maioria dos exemplos irei utilizar gtk_box_pack_start().

Um objeto pode ser um outro container ou um widget. De fato, muitos widgets são literalmente um container. Até mesmo o botão pode ser usado como container, pois se você observar, podemos colocar uma palavra dentro da caixa de botão. Usando as chamadas listadas anteriormente, GTK irá saber onde colocar os widgets e por isso poderá automaticamente adaptar seu tamanho. Além disso existem muitas funções e opções para explicitar para GTK como os widgets irão se comportar em seus containers e isso cria uma infinidade de opções permitindo boa flexibilidade no controle da posição destas.Devido a flexibilidade explicada anteriormente, a teoria do empacotamento no GTK irá se mostrar de certa forma confusa. Temos várias opções e não é totalmente intuitivo como que ficará o posicionamento final após compilado o programa.

A prática

Depois de uma introdução teórica, vamos partir para a prática explicitando uma função.

void gtk_box_pack_start( GtkBox *box,GtkWidget *child,gboolean expand,

gboolean fill,guint padding );

O que são estes parâmetros?

Primeiramente temos um parâmetro *box. Esta é a caixa na qual você irá colocar seus widgets para serem empacotados. E o que irá ser colocado na caixa? O segundo argumento: *child. Logo adiante iremos usar como exemplo botões que irão se colocados em uma caixa.

O parâmetro expand é um chaveador que controla se você irá querer que o objeto se expanda para que ocupe todo o espaço da caixa ou não. Caso você não queira que o widget expanda, ela irá encolher para a forma padrão e você poderá deslocar a posição dela dentro da caixa.

O parâmetro fill controla uma espécie de reserva de espaço para um widget. Caso seja ativado (TRUE), o espaço irá ser reservado para que seja totalmente de um widget. Caso esteja desativado (FALSE), você poderá especificar um espaçamento vazio entre o objeto widget e a caixa (como se fosse uma borda em volta do objeto). O tamanho desta borda é especificado pelo último argumento padding.

Nós já temos agora a função que mostra em qual caixa empacotar, o que empacotar e de que forma os objetos irão ficar na caixa. Mas, cade a caixa? Temos que criá-la.

Para criar uma caixa (horizontal ou vertical), utilize a função:

GtkWidget *gtk_hbox_new ( gboolean homogeneous,gint spacing );

GtkWidget *gtk_vbox_new ( gboolean homogeneous,gint spacing );

O parâmetro homogeneous controla se os objetos dentro da caixa irão ter o mesmo tamanho (comprimento e largura por exemplo). Quando ativado este parâmetro, o comportamento da caixa funcionaria como se o parâmetro expand do gtk_box_pack() estivesse ativado.

Curioso que a função da caixa também tem um parâmetro de espaçamento semelhante ao da função ilustrada anterior. Qual a diferença?

O espaçamento é o que está entre os objetos e padding é o que está entre osdois lados de um objeto.

Observe a imagem a seguir:

Page 14: ProgramaÇÃo Gtk

A seguir está a imagem da janela e um código contendo quase todas as possibilidades acima.

Para executar digite ./nomeArquivo num

tal que num = 1, 2 ou 3

Para num = 1

Para num = 2

Page 15: ProgramaÇÃo Gtk

Para num = 3

#include <stdio.h>

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

static gboolean delete_event( GtkWidget *widget,GdkEvent *event,

gpointer data ){

gtk_main_quit ();return FALSE;

}

/* Cria um hbox preenchido com botões. Argumentos para as varíaveis são passados dentro desta função e não mostramos a caixa, mas sim todas as coisas internas a ela.*/

static GtkWidget *make_box( gboolean homogeneous,gint spacing,

gboolean expand,gboolean fill,

guint padding ){

GtkWidget *box;GtkWidget *button;

char padstr[80];

/* Cria um hbox novo com as configurações apropriadas (parâmetros monogeneous e spacing)box = gtk_hbox_new (homogeneous, spacing);

/* Cria uma série de botões com suas respectivas configurações*/button = gtk_button_new_with_label ("gtk_box_pack");

gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);gtk_widget_show (button);

button = gtk_button_new_with_label ("(box,");gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);

gtk_widget_show (button);button = gtk_button_new_with_label ("button,");

gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);gtk_widget_show (button);

/* Cria um botão com um label dependendo do valor do expand */

if (expand == TRUE)button = gtk_button_new_with_label ("TRUE,");

elsebutton = gtk_button_new_with_label ("FALSE,");

gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);gtk_widget_show (button);

/* Aqui a criação do botão é como acima, mas de uma forma mais concisa.*/button = gtk_button_new_with_label (fill ? "TRUE," : "FALSE,");

gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);gtk_widget_show (button);

sprintf (padstr, "%d);", padding);button = gtk_button_new_with_label (padstr);

gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);gtk_widget_show (button);

return box;}

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

GtkWidget *window;GtkWidget *button;GtkWidget *box1;

Page 16: ProgramaÇÃo Gtk

GtkWidget *box2;GtkWidget *separator;

GtkWidget *label;GtkWidget *quitbox;

int which;

/* Não esqueça o init nunca. */gtk_init (&argc, &argv);

if (argc != 2) {

fprintf (stderr, "usage: packbox num, where num is 1, 2, or 3.\n");exit (1);

}

which = atoi (argv[1]);

/* Criando a nossa janela GTK */window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

/* Nunca esqueça de conectar os sinais a janela principal. */g_signal_connect (G_OBJECT (window), "delete_event",

G_CALLBACK (delete_event), NULL);

gtk_container_set_border_width (GTK_CONTAINER (window), 10);

/* A seguir uma caixa vertical (vbox) usada para empacotar caixas horizontais dentro dela, isto nos permite empilhar várias caixas horizontais preenchidas com botões.*/

box1 = gtk_vbox_new (FALSE, 0);

/* Qual exemplo mostrar? 1, 2 ou 3? As imagens dos exemplos estão antes desse código.*/switch (which) {

case 1:

/* Cria um lable novo. */label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");

/* Alinha o label para o lado esquerdo. Iremos discutir sobre esta função e outros na seção sobre os atributos de um Widget. */gtk_misc_set_alignment (GTK_MISC (label), 0, 0);

/* Empacota o label dentro de uma caixa vertical (vbox box1). Lembre-se que widgets são adicionados dentro de uma vbox colocando de cima para baixo. */

gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);

/* Mostre o label! */gtk_widget_show (label);

/* Chama nossa função box - homogeneous = FALSE, spacing = 0,* expand = FALSE, fill = FALSE, padding = 0 */box2 = make_box (FALSE, 0, FALSE, FALSE, 0);

gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);gtk_widget_show (box2);

/* Chama nossa função box - homogeneous = FALSE, spacing = 0,* expand = TRUE, fill = FALSE, padding = 0 */box2 = make_box (FALSE, 0, TRUE, FALSE, 0);

gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);gtk_widget_show (box2);

/* Argumentos são: homogeneous, spacing, expand, fill, padding */box2 = make_box (FALSE, 0, TRUE, TRUE, 0);

gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);gtk_widget_show (box2);

/* Cria um separador. */separator = gtk_hseparator_new ();

/* Empacota este separador na vbox. */gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);

gtk_widget_show (separator);

/* Cria outro label e o mostra. */label = gtk_label_new ("gtk_hbox_new (TRUE, 0);");

gtk_misc_set_alignment (GTK_MISC (label), 0, 0);gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);

gtk_widget_show (label);

Page 17: ProgramaÇÃo Gtk

/* Argumentos são: homogeneous, spacing, expand, fill, padding */box2 = make_box (TRUE, 0, TRUE, FALSE, 0);

gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);gtk_widget_show (box2);

/* Argumentos são: homogeneous, spacing, expand, fill, padding */box2 = make_box (TRUE, 0, TRUE, TRUE, 0);

gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);gtk_widget_show (box2);

/* Outro separadorr. */separator = gtk_hseparator_new ();

/* Os três últimos argumentos são:* expand, fill, padding. */

gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);gtk_widget_show (separator);

break;

case 2:/* Cria um label novo. Lembre-se que box1 é o vbox criado no começo do código perto da função principal main()*/

label = gtk_label_new ("gtk_hbox_new (FALSE, 10);");gtk_misc_set_alignment (GTK_MISC (label), 0, 0);

gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);gtk_widget_show (label);

/* Argumentos são: homogeneous, spacing, expand, fill, padding */box2 = make_box (FALSE, 10, TRUE, FALSE, 0);

gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);gtk_widget_show (box2);

/* Argumentos são: homogeneous, spacing, expand, fill, padding */box2 = make_box (FALSE, 10, TRUE, TRUE, 0);

gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);gtk_widget_show (box2);

separator = gtk_hseparator_new ();

/* Os 3 últimos argumentos desta função são:* expand, fill, padding. */

gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);gtk_widget_show (separator);

label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");gtk_misc_set_alignment (GTK_MISC (label), 0, 0);

gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);gtk_widget_show (label);

/* Argumentos são: homogeneous, spacing, expand, fill, padding */box2 = make_box (FALSE, 0, TRUE, FALSE, 10);

gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);gtk_widget_show (box2);

/* Argumentos são: homogeneous, spacing, expand, fill, padding */box2 = make_box (FALSE, 0, TRUE, TRUE, 10);

gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);gtk_widget_show (box2);

separator = gtk_hseparator_new ();

/* Os 3 últimos argumentos desta função são: expand, fill, padding. */gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);

gtk_widget_show (separator);break;

case 3:/* Aqui demonstra a utilização do gtk_box_pack_end() para alinhar a direita os widgets. Primeiramente vamos criar uma caixa

como criamos antes. */box2 = make_box (FALSE, 0, FALSE, FALSE, 0);

/* Cria um label e coloca-o no final. */label = gtk_label_new ("end");

/* Empacotando usando gtk_box_pack_end(), logo ele irá colocar do lado direito do hbox, criada pela chamada do make_box() anterior.*/

gtk_box_pack_end (GTK_BOX (box2), label, FALSE, FALSE, 0);

/* Mostre-me o label! */gtk_widget_show (label);

Page 18: ProgramaÇÃo Gtk

/* Empacota box2 dentro de box1 (o vbox lembra dele ? */gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);

gtk_widget_show (box2);

/* Outro separador. */separator = gtk_hseparator_new ();

/* Aqui iremos explicitamente setar o tamanho do separador para 400 pixels comprimento e 5 pixels de altura. A idéia é que ohbox criado também tem mais o menos 400 pixels de comprimento e o label "end" irá ficar separado dos outros labels dentro

do hbox. Porém, todos os widgets dentro do hbox irão ficar empacotado o mais próximo possível. */gtk_widget_set_size_request (separator, 400, 5);

/* Empacotando o separador dentro da vbox (box1) */gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);

gtk_widget_show (separator);}

/* Outro hbox... Existe a liberdade de você criar quantos quiser! */quitbox = gtk_hbox_new (FALSE, 0);

/* Botão de saída (quit). */button = gtk_button_new_with_label ("Quit");

/* Configurando o sinal para terminar o programa quando o botão for clicado*/g_signal_connect_swapped (G_OBJECT (button), "clicked",

G_CALLBACK (gtk_main_quit),G_OBJECT (window));

/* Empacota o botão dentro da caixa quitbox. Os últimos argumentos são:* expand, fill, padding. */

gtk_box_pack_start (GTK_BOX (quitbox), button, TRUE, FALSE, 0);

/* Empacotando o quitbox dentro do vbox (box1) */gtk_box_pack_start (GTK_BOX (box1), quitbox, FALSE, FALSE, 0);

/* EMpacotando o vbox (box1) preenchido de widgets, dentro da nossa janela principal */gtk_container_add (GTK_CONTAINER (window), box1);

/* Mostre-me tudo o que falta! */gtk_widget_show (button);gtk_widget_show (quitbox);

gtk_widget_show (box1);

/* Mostrando a janela ao fim, para que tudo seja mostrado de uma vez. Caso você mande mostrar a janela antes, poderá aparecer um a um cada um dos componentes de seu programa. */

gtk_widget_show (window);

/* Finalmente o loop principal gtk. */gtk_main ();

/*O controle do gtk termina aqui quando gtk_main_quit() é chamado e não quando você usa exit(). */return 0;

}Vamos olhar uma outra forma de empacotamento - TABELAS! Essas são extremamente úteis em certa situações. Usando tabelas, você cria uma grade na qual poderá colocar widgets em cada um dos espaços.

A primeira coisa a fazer é criar a tabela. Ou seja, a função:

GtkWidget *gtk_table_new( guint rows,guint columns,

gboolean homogeneous );

O primeiro argumento é o número de linhas que terá a tabela, e o segundo o número de colunas.

O argumento homogeneous tem a ver com o modo que as caixas dentro da tabela são dimensionados. Se homogeneous for TRUE, as caixas irão ser redimencionadas para o tamanho do maior widget dentro da tabela. Se for configurado com FALSE, o tamanho das caixas na tabela vão ficar do tamanho do widget mais alto da mesma linha, e o mais largo da mesma coluna.

As linhas e colunas são numeradas do 0 ao n, onde n é um número especificado na chamada da função gtk_table_new(). Logo, se você especificar número de linhas 2 e colunas também 2, teremos uma tabela da seguinte forma:

Page 19: ProgramaÇÃo Gtk

Note que as coordenadas iniciam no canto esquerdo superior. Para se colocar um widget dentro de alguma dessas células, use a função a seguir:

void gtk_table_attach( GtkTable *table,GtkWidget *child,guint left_attach,

guint right_attach,guint top_attach,

guint bottom_attach,GtkAttachOptions xoptions,GtkAttachOptions yoptions,

guint xpadding,guint ypadding );

Calma! Irei exemplicar cada um destes argumentos.

*table - A tabela que você criou anteriomente!*child - O widget que você quer colocar nesta tabela.

left_attach, right_attach, top_attach, bottom_attach - Especificam onde colocar o widget e quantas caixas usar.

Por exemplo: Se você quiser um botão no canto direito inferior da tabela 2x2, e também queira que o widget preencha a célula toda, left_attach deverá ser 1 e right_attach 2, top_attach 1 e bottom_attach 2.

Se você quiser um botão preencha toda linha superior da tabela 2x2, você deverá usar left_attach 0, right_attach 2, top_attach 0 e bottom_attach 1

Qual a lógica disto? Isso seriam os limites nas quais o widget irá ficar. Observe a foto anterior que você irá entender.

xoptions e yoptions - São usadas para especificar opções relacionados ao empacotamento podem ser utilizados juntos para permitir múltiplas opções.

Estas opções são:

GTK_FILLSe a tabela for maior que o widget, e GTK_FILL está sendo usado como opção, o widget irá se expandir até usar todo o tamanho

de espaço disponível daquela célula.

GTK_SHRINKSe a tabela ficar menor do que o espaço alocado para ela (geralmente quando um usuário redimensiona a janela), os widgets

normalmente irão ser empurrados para a parte de baixo da janela e desaparece visivelmente. Se GTK_SHRINK for especificado, os widgets irão encolher junto com a tabela.

GTK_EXPANDIsto fará com que a tabela se expanda para ocupar todo o espaço disponível da janela.

Padding - É semelhante a caixas, cria uma área vazia ao redor do widget, especificado em pixels.

A função gtk_table_attach() é desanimadora devido ao seu grande número de opções. Vamos então criar duma forma mais "resumida" para a função anterior:

void gtk_table_attach_defaults( GtkTable *table,GtkWidget *widget,guint left_attach,

guint right_attach,guint top_attach,

guint bottom_attach );

Os parâmetros xoptions e yoptions possuem como padrão GTK_FILL | GTK_EXPAND, e os parâmetros de padding são configurados com 0. O resto dos parâmetros são idênticos a função anterior.

Page 20: ProgramaÇÃo Gtk

Temos também as funções gtk_table_set_row_spacing() e gtk_table_set_col_spacing(). Estas criam um espaçamento entre as linhas de uma coluna ou linha específica. São elas:

void gtk_table_set_row_spacing( GtkTable *table,guint row,

guint spacing );

void gtk_table_set_col_spacing ( GtkTable *table,guint column,

guint spacing );

Note que as últimas linhas e colunas não possuem nenhum espaçamento.Código fonte exemplo mostrando empacotamento usando o widget gtk table:

Não esqueça como que compila. Use a seguinte forma:gcc example4.c -o example4 `pkg-config --cflags --libs gtk+-2.0`

#include <gtk/gtk.h>

static gboolean delete_event(GtkWidget widget,GdkEvent *event,

gpointer data) {

g_printf("nada mais pra fazer, você me matou! :'(\n");gtk_main_quit();

return TRUE;}

static gboolean clicked_event(GtkWidget *widget,GdkEvent *event,

gpointer data){

g_print("OLA! %s\n",(gchar *)data);return TRUE;

}

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

GtkWidget *window;GtkWidget *box;

GtkWidget *button1;GtkWidget *button2;

GtkWidget *table;

gtk_init(&argc,&argv);

Page 21: ProgramaÇÃo Gtk

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

g_signal_connect(G_OBJECT(window), "destroy",G_CALLBACK(delete_event),NULL);

button1 = gtk_button_new_with_label("Button1");

g_signal_connect(G_OBJECT(button1), "clicked",G_CALLBACK(clicked_event), (gpointer)"button1");

button2 = gtk_button_new_with_label("Button2");

g_signal_connect(G_OBJECT(button2), "clicked",G_CALLBACK(clicked_event), (gpointer)"button2");

table = gtk_table_new(2,2,TRUE);

gtk_table_attach_defaults(GTK_TABLE(table),button1,0,1,0,1);gtk_table_attach_defaults(GTK_TABLE(table),button2,1,2,0,1);

gtk_container_add(GTK_CONTAINER(window),table);

gtk_widget_show(window);gtk_widget_show(table);

gtk_widget_show(button1);gtk_widget_show(button2);

gtk_main();

g_printf("bye bye...!");

return 0;}

Escolha a alternativa correta

Sabe-se que para criar um container em forma de tabela, usa-se o comando gtk_table_new. Suponha que você criou uma tabela 3x3 da seguinte forma:

table = gtk_table_new(3,3,TRUE);

E que você irá adicionar 2 widgets button1 e button 2 em posições da tabela usando o código a seguir:

gtk_table_attach_defaults(GTK_TABLE(table),button1,1,2,1,2);gtk_table_attach_defaults(GTK_TABLE(table),button2,2,3,0,1);

Page 22: ProgramaÇÃo Gtk

Quais são as posições dos button1 e button2 respectivamente?

letras e,c

Lição 4 - Mais Sobre Widgets

Já foi apresentado o conceito de Widget em lições anteriores, e com as liçõespudemos observar de modo mais claro como que é o processo de sua criação:

gtk_*_new() - uma das várias funções para criar um widget novo. Isso será detalhado nesta seção; conecte todos os sinais e eventos nas quais queremos que funções as manipulem;

Configure os atributos do Widget; Empacote o widget em um container usando uma chamada apropriada como gtk_container_add() ou

gtk_box_pack_start(); Mostre o widget na tela.

gtk_widget_show() permite que GTK saiba em qual momento as configurações de atributos de um widget já estaria pronto para ser mostrado. Você pode usar o comando gtk_widget_hide para fazer desaparecer o widget. A ordem que você começa a mostrar os widgets não é importante, mas é aconselhável mostrar a janela principal por último, pois desta maneira a janela com todos seus componentes saltam para a tela de uma só vez. Se não for desta forma, você irá ver os componentes individuais da janela aparecendo na tela a medida que vão se formando. Os filhos de um widget (uma janela é também um widget) não irão ser mostrados até que esta própria janela seja mostrada pela função gtk_widget_show().Você irá notar que GTK possui um sistema de tipagem. Isto é sempre feito usando macros que testa antes se é possível fazer a tipagem e caso possível fazer esta propriamente dita. Algumas macros que são mais comuns são:

G_OBJECT (object) GTK_WIDGET (widget) GTK_OBJECT (object)

GTK_SIGNAL_FUNC (function) GTK_CONTAINER (container)

GTK_WINDOW (window) GTK_BOX (box)

Essas macros são usadas para tipagem de argumentos dentro de funções. Você irá deparar sempre nos exemplos e irá encontrá-los apenas olhando para as declarações das funções.

GtkWidgets são derivados de uma classe base GObject. Isto quer dizer que você pode usar um widget em qualquer lugar onde uma função peça um objeto - apenas precisa usar a macro G_OBJECT().

Por exemplo:g_signal_connect( G_OBJECT (button), "clicked",G_CALLBACK (callback_function), callback_data);

Page 23: ProgramaÇÃo Gtk

Aqui está fazendo uma tipagem de um botão para um tipo object. Muitos widgets também são containers. Se você observar a hierarquia das classes abaixo, irá notar que muitos widgets são derivados de uma classe Container. Muitos destes widgets podem ser usados com a macro GTK_CONTAINER como argumentos de funções que pedem parâmetros container. Infelizmente aqui não irei mostrar todas as macros possíveis e recomendo que olhar arquivos cabeçalhos GTK (.h), ou então, um manual de referência GTK.Para a sua referência, aqui contém uma hierarquia de classes usado para

implementar widgets:

GObject|

GtkObject+GtkWidget| +GtkMisc

| | +GtkLabel| | | ‘GtkAccelLabel

| | +GtkArrow| | ‘GtkImage

| +GtkContainer| | +GtkBin

| | | +GtkAlignment| | | +GtkFrame

| | | | ‘GtkAspectFrame| | | +GtkButton

| | | | +GtkToggleButton| | | | | ‘GtkCheckButton

||||| ‘GtkRadioButton| | | | ‘GtkOptionMenu

| | | +GtkItem| | | | +GtkMenuItem

|||| +GtkCheckMenuItem|||| | ‘GtkRadioMenuItem|||| +GtkImageMenuItem

|||| +GtkSeparatorMenuItem|||| ‘GtkTearoffMenuItem

| | | +GtkWindow| | | | +GtkDialog

| | | | | +GtkColorSelectionDialog| | | | | +GtkFileSelection

| | | | | +GtkFontSelectionDialog| | | | | +GtkInputDialog

| | | | | ‘GtkMessageDialog| | | | ‘GtkPlug

| | | +GtkEventBox| | | +GtkHandleBox

| | | +GtkScrolledWindow| | | ‘GtkViewport

| | +GtkBox| | | +GtkButtonBox

| | | | +GtkHButtonBox| | | | ‘GtkVButtonBox

| | | +GtkVBox| | | | +GtkColorSelection| | | | +GtkFontSelection| | | | ‘GtkGammaCurve

| | | ‘GtkHBox||| +GtkCombo

||| ‘GtkStatusbar| | +GtkFixed| | +GtkPaned

| | | +GtkHPaned| | | ‘GtkVPaned| | +GtkLayout

| | +GtkMenuShell| | | +GtkMenuBar

| | | ‘GtkMenu| | +GtkNotebook

| | +GtkSocket| | +GtkTable

| | +GtkTextView| | +GtkToolbar| | ‘GtkTreeView| +GtkCalendar

| +GtkDrawingArea| | ‘GtkCurve

| +GtkEditable| | +GtkEntry

|| ‘GtkSpinButton| +GtkRuler

Page 24: ProgramaÇÃo Gtk

| | +GtkHRuler| | ‘GtkVRuler| +GtkRange| | +GtkScale

| | | +GtkHScale| | | ‘GtkVScale| | ‘GtkScrollbar

|| +GtkHScrollbar|| ‘GtkVScrollbar| +GtkSeparator

| | +GtkHSeparator| | ‘GtkVSeparator

| +GtkInvisible| +GtkPreview

| ‘GtkProgressBar+GtkAdjustment

+GtkCellRenderer| +GtkCellRendererPixbuf| +GtkCellRendererText

| +GtkCellRendererToggle+GtkItemFactory

+GtkTooltips‘GtkTreeViewColumn

Julgue: É aconselhável usar gtk_widget_show() para mostrar a janela, antes de qualquer outros widgets que estiverem internos a ela. Desta forma, todos os widgets aparecerão de uma vez só.

Falso!

Lição 5 - Botões WidgetsBotões Normais

Nós vimos anteriormente alguns exemplos com botões, mas não detalhamos suas características, logo, vamos nessa lição apresentar atributos específicos e tipos novos.

Botões são simples. Existem várias maneiras de criar um botão. Você pode usar a função gtk_button_new_width_label() ou, então, gtk_button_new_with_mnemonic() para criar botões com rótulos. Usando gtk_button_new_from_stock() você pode criar um botão contendo uma imagem e um texto de um item "stock". Caso você queira ter controle mais específico, é possível, usando a função gtk_button_new(), criar um botão vazio e futuramente criar manualmente um rótulo (label) ou imagem, e empacotar ele no botão (lembre que muitos widgets também podem ser containers).

Os passos para a criação manual é: criar um box, empacotar os objetos neste box usando gtk_box_pack_start() e depois disso, usar gtk_container_add() para empacotar a caixa no botão.

Temos abaixo um exemplo de como usar gtk_button_new() para criar um botão com uma imagem e um rótulo dentor dele.

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

/* Cria um hbox novo com uma imagem e um rótulo empacotado dentro dele,* e retorna a caixa (hbox) */

static GtkWidget *xpm_label_box( gchar *xpm_filename,gchar *label_text )

{GtkWidget *box;

GtkWidget *label;GtkWidget *image;

/* Cria uma caixa para a imagem e rótulo */box = gtk_hbox_new (FALSE, 0);

gtk_container_set_border_width (GTK_CONTAINER (box), 2);/* Agora fazendo a parte da imagem */

image = gtk_image_new_from_file (xpm_filename);

/* Cria um novo rótulo para o botão */label = gtk_label_new (label_text);

/* Empacota a imagem e o rótulo na caixa */gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 3);gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 3);

/* Mostre-me tudo! */gtk_widget_show (image);gtk_widget_show (label);

return box;

Page 25: ProgramaÇÃo Gtk

}

/* Nosso callback de sempre... */static void callback( GtkWidget *widget,

gpointer data ){

g_print ("Ola de novo! - %s foi pressionado!\n", (char *) data);}

int main( int argc,char *argv[] )

{/* GtkWidget é o tipo de estrutura para widgts */

GtkWidget *window;GtkWidget *button;

GtkWidget *box;gtk_init (&argc, &argv);

/* Cria nova janela */window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

gtk_window_set_title (GTK_WINDOW (window), "Botoões!!");

/* Sinais para as janelas! */g_signal_connect (G_OBJECT (window), "destroy",

G_CALLBACK (gtk_main_quit), NULL);g_signal_connect (G_OBJECT (window), "delete_event",

G_CALLBACK (gtk_main_quit), NULL);

/* Configura a borda da janela. */gtk_container_set_border_width (GTK_CONTAINER (window), 10);

/* Cria um novo botão */button = gtk_button_new ();

/* Conecta o sinal "clicked" do botão com um callback */g_signal_connect (G_OBJECT (button), "clicked",

G_CALLBACK (callback), (gpointer) "cool button");

/* Agora estamos chamando nossa função para criar caixas */box = xpm_label_box ("info.xpm", "botão legal!");

/* Empacote e mostre os nossos widgets! */gtk_widget_show (box);

gtk_container_add (GTK_CONTAINER (button), box);gtk_widget_show (button);

gtk_container_add (GTK_CONTAINER (window), button);gtk_widget_show (window);i

/* Descanse na função principal gtk_main e espere para começar a * diversão! */ gtk_main (); return 0;

}

A função xpm_label_box() pode ser usada para empacotar imagens e rótulos dentro de qualquer widget que seja container também.

Um widget Botão tem os seguinte sinais:

pressed - Emitido quando o botão do mouse é pressionado dentro de um widget Button; released - Emitido quando o botão do mouse é soltado dentro de um widget Button;

clicked - Emitido quando o botão do mouse é pressionado e soltado dentro de um widget Button; enter - Emitido quando o ponteiro do mouse entra na área de um widget Button; leave - Emitido quando o ponteiro do mouse sai da região de um widget Button.

Botões Toggle

O objeto em estudo nessa seção (botões toggle) são derivados dos botões normais. Eles são muito semelhantes e a única parte diferente é que eles geralmente possuem dois estados, as quais se alternam por um clique de mouse. A idéia de Toogle é similar aqueles botões de rádio antiga que afundam ao ser apertado.

Botões deste tipo são base para botões como checkbox e radio, assim como muitas das chamadas usadas por um botão toogle são herdadas pelos botões radio e checkbox. Irei ilustrar estes quando for a hora.

Código para criação de botões toggle:

GtkWidget *gtk_toggle_button_new(void);GtkWidget *gtk_toggle_button_new_with_label( const gchar *label );

GtkWidget *gtk_toggle_button_new_with_mnemonic( const gchar *label );

Page 26: ProgramaÇÃo Gtk

Assim como você pode imaginar, estes funcionam de forma idêntica a chamadas de botoões widget. A primeira função cria um botão toggle em branco e os dois últimos botões já com rótulo. A parte com _mnemonic() cria coisas adicionais para o rótulo contendo caracteres de prefixos de '_' para mnemônicos (observe alguns botões que existem nos programas, alguns contêm uma letra sublinhada no rótulo para que você pode ativá-los usando alt-<letra>, estes são os mnemônicos).

Para conseguir adquirir o estado do widget toogle, incluindo os botões radio e checkbox, nós usamos a construção da forma ilustrada abaixo. Isso testa o estado de um botão tootle, acessando o campo ativado dentro da estrutura widget do toogle. Após usar a macro GTK_TOGGLE_BUTTON para tipar o ponteiro do widget para um ponteiro widget do toogle. O sinal que nos interessa é emitido pelos botões toggle (toggle, check e radio) e é o sinal "toggled". Para verificar o estado de um destes botões, configure

um manipulador de sinais para pegar o sinal toggled e logo em seguida acessar a estrutura para determinar seu estado. A função callback deverá parecer com o código a seguir:

void toggle_button_callback (GtkWidget *widget, gpointer data){

if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))){

/* Se o controle chegar aqui, o botão toggle está afundado */} else

/* Se o controle chegar aqui, o botão toggle está levantado */}}

Para forçar que um botão, seus filhos, o botão radio e botões checkbox mudem de estado, use a função a seguir:

void gtk_toggle_button_set_active( GtkToggleButton *toggle_button,gboolean is_active );

A chamada acima pode ser usada para setar um estado de um botão toggle e os tipos relacionados a ele. Passando o botão no primeiro argumento da função acima, e o estado TRUE ou FALSE no segundo argumento, você poderá configurar a situação que você deseje que o botão fique. O padrão é deixar o botão levantado, ou FALSO. Quando ocorre a mudança do estado do botão devido ao uso da função gtk_toggle_button_set_active(), os sinais "clicked" e "toggled" são automaticamente emitidos pelo botão.

gboolean gtk_toggle_button_get_active (GtkToggleButton *toggle_button);

Isto retorna o estado do botão toggled como uma variável boolean TRUE/FALSE (Verdadeiro ou falso).Botões Checkbox herdam muitas propriedades e funções dos botões toggle, mostrados acima, mas eles são diferentes. Ao invés de serem botões com um rótulo dentro, estes são pequenos quadrados com um texto ao seu lado direito.Geralmente são usados como botões de ligar e desligar de alguma funcionalidade do aplicativo. A função para criar o botão checkbox é da forma a seguir:

GtkWidget *gtk_check_button_new( void );

GtkWidget *gtk_check_button_new_with_label ( const gchar *label );

GtkWidget *gtk_check_button_new_with_mnemonic ( const gchar *label );

A função gtk_check_button_new_with_label() Cria um botão checkbox com um rõtulo do lado dele. A checkagem do estado que se encontra o botão é da mesma forma que o toggle button.Botões Radio são semelhantes a botões checkbox, exceto que eles são agrupados de um modo que apenas um poderá ser selecionado por vez. Isto é bom em locais do seu programa que você necessita selecionar alguma coisa de uma lista de opções.

É possível criar os botões com as formas a seguir:

GtkWidget *gtk_radio_button_new( GSList *group ); GtkWidget *gtk_radio_button_new_from_widget( GtkRadioButton *group );

GtkWidget *gtk_radio_button_new_with_label( GSList *group, const gchar *label );

GtkWidget* gtk_radio_button_new_with_label_from_widget( GtkRadioButton *group, const gchar *label );

GtkWidget *gtk_radio_button_new_with_mnemonic( GSList *group, const gchar *label );

GtkWidget *gtk_radio_button_new_with_mnemonic_from_widget( GtkRadioButton *group, const gchar *label );

Observe que existem argumentos extras nestas chamadas. Eles necessitam de um "grupo" para fazer seu papel de forma correta. A primeira chamada do gtk_radio_button_new() ou gtk_radio_button_new_with_label(), deve passar NULL como primeiro argumento. Para criar um grupo, use a forma a seguir:

GSList *gtk_radio_button_get_group( GtkRadioButton *radio_button );

Page 27: ProgramaÇÃo Gtk

O mais importante para lembrar é que um gtk_radio_button_get_group() deve ser usado a cada novo botão adicionado ao grupo, com o botão anterior passado como argumento. O resultado então é passado para a próxima chamada do gtk_radio_button_new() ou gtk_radio_button_new_with_label(). Isto permite estabelecer um encadeamento de botões. O exemplo abaixo clareia as idéias:

button2 = gtk_radio_button_new_with_label(gtk_radio_button_get_group (GTK_RADIO_BUTTON (button1)),

"button2");

Você pode diminuir um pouco a quantidade de linhas colocando diretamente a função gtk_radio_button_get_group() como parâmetro da função acima.

A parte _from_widget() permite que você diminua mais ainda a função, omitindo a chamada do gtk_radio_button_get_group(). Observe o exemplo a seguir:

button2 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(button1),"button2");

Uma outra boa idéia é explicitar configurar qual dos botões será a opção padrão.

void gtk_toggle_button_set_active( GtkToggleButton *toggle_button,gboolean state );

O código a seguir cria um grupo de três botões radio:

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

static gboolean close_application( GtkWidget *widget,GdkEvent *event,

gpointer data ){

gtk_main_quit ();return FALSE;

}

int main( int argc,char *argv[] )

{GtkWidget *window = NULL;

GtkWidget *box1;GtkWidget *box2;

GtkWidget *button;GtkWidget *separator;

GSList *group;

gtk_init (&argc, &argv);window = gtk_window_new (GTK_WINDOW_TOPLEVEL);g_signal_connect (G_OBJECT (window), "delete_event",

G_CALLBACK (close_application),NULL);

gtk_window_set_title (GTK_WINDOW (window), "radio buttons");gtk_container_set_border_width (GTK_CONTAINER (window), 0);

Page 28: ProgramaÇÃo Gtk

box1 = gtk_vbox_new (FALSE, 0);gtk_container_add (GTK_CONTAINER (window), box1);

gtk_widget_show (box1);box2 = gtk_vbox_new (FALSE, 10);

gtk_container_set_border_width (GTK_CONTAINER (box2), 10);gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);

gtk_widget_show (box2);

button = gtk_radio_button_new_with_label (NULL, "button1");gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);

gtk_widget_show (button);

group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));button = gtk_radio_button_new_with_label (group, "button2");

gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);

gtk_widget_show (button);

button = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (button),"button3");

gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);gtk_widget_show (button);

separator = gtk_hseparator_new ();gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);

gtk_widget_show (separator);

box2 = gtk_vbox_new (FALSE, 10);gtk_container_set_border_width (GTK_CONTAINER (box2), 10);gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);

gtk_widget_show (box2);

button = gtk_button_new_with_label ("close");g_signal_connect_swapped (G_OBJECT (button), "clicked",

G_CALLBACK (close_application),G_OBJECT (window));

gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);

gtk_widget_grab_default (button);gtk_widget_show (button);gtk_widget_show (window)

gtk_main ();

return 0;}

Escolha a alternativa correta:

button2 = gtk_radio_button(gtk_radio_button_get_group (GTK_RADIO_BUTTON (button1)), "button2");

A função acima está correta?

A função está incorreta pois a declaração certa é: gtk_button_new_with_label(GTK_RADIO_BUTTON (button1)), "button2");

Lição 6 - Vários outros Widgets

Rótulos são usados com bastante freqüência no GTK e são relativamente simples. Rótulos não emitem sinais, pois eles não possui nenhuma janela associada a ele. Se você quiser que o rótulo emita sinais, coloque-o dentro de um widget EventBox ou um Botão.

Para criar um rótulo use:

GtkWidget *gtk_label_new( const char *str );

GtkWidget *gtk_label_new_with_mnemonic( const char *str );

Onde o parâmetro único destas funções é o texto que você quer usar.

Page 29: ProgramaÇÃo Gtk

Após a criação de um rótulo, você pode modificar o seu texto usando o comando a seguir:

void gtk_label_set_text( GtkLabel *label,const char *str );

O primeiro argumento é o rótulo que você quer modificar e o segundo o novo texto que irá substituir o antigo rótulo.

Para obter o texto de um rótulo, você deve usar o seguinte comando:

const gchar* gtk_label_get_text( GtkLabel *label );

Você pode justificar o texto usando a função a seguir:

void gtk_label_set_justify( GtkLabel *label,GtkJustification jtype );

a qual os valores possíveis do parâmetro jtype são:GTK_JUSTIFY_LEFT

GTK_JUSTIFY_RIGHTGTK_JUSTIFY_CENTER (valor padrão)

GTK_JUSTIFY_FILL

Também é possível fazer quebras automaticamente de linha. Para ativar essa função no rótulo use:

void gtk_label_set_line_wrap (GtkLabel *label,gboolean wrap);

A qual os parâmetros possíveis são TRUE e FALSE.

Além disso, é possível criar rótulos sublinhados usando a função abaixo:

void gtk_label_set_pattern (GtkLabel *label,const gchar *pattern);

Onde *pattern é o padrão de sublinhado que você quiser seguir. Por exemplo, o padrão "___ ____" significa que você terá uma palavra com 3 letras e outra com 4 letras, separadas por um espaço, que serão sublinhados.

Não confunda este sublinhado com uma letra só sublinhado usado para teclas de atalho (alt-a), por exemplo. Este apenas sublinha sem função, a outra, você ativa usando gtk_label_new_with_mnemonic() or gtk_label_set_text_with_mnemonic().

Existe outras variações para o rótulo por exemplo, tipo de fontes, cores e possíveis de serem selecionadas, copiadas e coladas. Estas características avançadas não serão abordados neste curso.

Julgue a afirmação a seguir: é impossível alterar diretamente um texto de um rótulo após a sua criação. Um modo de fazer isso é deletar o rótulo existente e substituir por outro novo criado.Verdadeiro!

Page 30: ProgramaÇÃo Gtk

#include <gtk/gtk.h>

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

static GtkWidget *window = NULL;GtkWidget *hbox;GtkWidget *vbox;GtkWidget *frame;GtkWidget *label;

GtkWidget *button;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);g_signal_connect(G_OBJECT(window), "destroy",

G_CALLBACK(gtk_main_quit), NULL);

gtk_window_set_title(GTK_WINDOW(window), "Labeling");vbox = gtk_vbox_new(FALSE,5);hbox = gtk_hbox_new(FALSE,5);

gtk_container_add(GTK_CONTAINER(window),hbox);gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE,FALSE,0);

gtk_container_set_border_width(GTK_CONTAINER(window),5);

frame = gtk_frame_new("Rótulo Normal");label = gtk_label_new("Isto é um Rótulo Normal");gtk_container_add(GTK_CONTAINER(frame),label);

gtk_box_pack_start(GTK_BOX(vbox),frame,FALSE,FALSE,0);

frame = gtk_frame_new("Rótulo com várias linhas");label = gtk_label_new("Este é um rótulo com várias linhas.\nSegunda Linha\n\

Terceira Linha" );gtk_container_add(GTK_CONTAINER(frame),label);

gtk_box_pack_start(GTK_BOX(vbox),frame,FALSE,FALSE,0);

frame = gtk_frame_new("Rótulo Justificado a Esquerda");label = gtk_label_new("Aqui justificando a Esquerda\n Várias linhas\n"\

"Rótulo");gtk_label_set_justify(GTK_LABEL(label),GTK_JUSTIFY_LEFT);

gtk_container_add(GTK_CONTAINER(frame),label);gtk_box_pack_start(GTK_BOX(vbox),frame,FALSE,FALSE,0);

frame = gtk_frame_new("Rótulo Justificado a Direita");label = gtk_label_new("qui justificando a Direita\n Várias Linhas\n" \

"Quarta Linha");gtk_label_set_justify(GTK_LABEL(label),GTK_JUSTIFY_RIGHT);

gtk_container_add(GTK_CONTAINER(frame),label);gtk_box_pack_start(GTK_BOX(vbox),frame,FALSE,FALSE,0);

vbox = gtk_vbox_new(FALSE,5);

Page 31: ProgramaÇÃo Gtk

gtk_box_pack_start(GTK_BOX(hbox),vbox,FALSE,FALSE,0);frame = gtk_frame_new("Rótulo com Quebra de Linha");

label = gtk_label_new("Este é um exemplo de quebra de linha dentro de textos do rótulo"\"Teste espaço alow! " /* Espaços grandes em branco */

"Quebra de Linha bla bla. "\"Demonstração e agora?" \

"Sigam me os bons!. "\" Será que suporta paragrafo dentro de um rótulo?" \

"Podemos criar vários espaços extras em qualquer lugar?"\"Vamos Ver Espaços... ... ");

gtk_label_set_line_wrap(GTK_LABEL(label),TRUE);gtk_container_add(GTK_CONTAINER(frame),label);

gtk_box_pack_start(GTK_BOX(vbox),frame,FALSE,FALSE,0);

frame = gtk_frame_new("Rótulo Sublinhado");label = gtk_label_new("TESTE testando testandosublinhado\n");

gtk_label_set_justify(GTK_LABEL(label),GTK_JUSTIFY_LEFT);gtk_label_set_pattern(GTK_LABEL(label),

"_____ ________ __________________");gtk_container_add(GTK_CONTAINER(frame),label);

gtk_box_pack_start(GTK_BOX(vbox),frame,FALSE,FALSE,0);

frame = gtk_frame_new("Frame com botão de saída do programa");label = gtk_label_new("SAIA!");

gtk_label_set_pattern(GTK_LABEL(label), "_");button = gtk_button_new();

gtk_container_add(GTK_CONTAINER(button),label);gtk_container_add(GTK_CONTAINER(frame),button);

gtk_box_pack_start(GTK_BOX(vbox),frame, FALSE,FALSE,0);g_signal_connect(G_OBJECT(button),"clicked",

G_CALLBACK(gtk_main_quit),NULL);gtk_widget_show_all(window);

gtk_main();

return 0;}

Widget de Cursores desenham uma seta, mostrando quais as possíveis direções de movimentação e tendo um número de estilos possíveis. É importante em aplicações mudar o estilo, para que possa saber que mudou o comportamento do cursor naquela região. Assim como os rótulos, eles não emitem sinal.

Existem apenas duas funções para manipular cursores, são elas:

GtkWidget *gtk_arrow_new( GtkArrowType arrow_type,GtkShadowType shadow_type );

void gtk_arrow_set( GtkArrow *arrow,GtkArrowType arrow_type,

GtkShadowType shadow_type );

A primeira função cria um novo widget cursor, indicando tipo e aparência. Nesta função, o parâmetro shadow_type possui alguns valores possíveis:

GTK_ARROW_UP GTK_ARROW_DOWN GTK_ARROW_LEFT GTK_ARROW_RIGHT

Intuitivamente estes tipos de seta indicam a direção que o cursor irá apontar.

A segunda função permite você fazer alterações após já existir um estilo diferente de cursor.

O último parâmetro indica a aparência dos cursores, os possíveis valores são:

GTK_SHADOW_IN GTK_SHADOW_OUT (Valor Padrão)

GTK_SHADOW_ETCHED_IN GTK_SHADOW_ETCHED_OUT

Page 32: ProgramaÇÃo Gtk

#include <gtk/gtk.h>

static GtkWidget *create_arrow_button(GtkArrowType arrow_type,GtkShadowType shadow_type){

GtkWidget *button;GtkWidget *arrow;

button = gtk_button_new();arrow = gtk_arrow_new(arrow_type,shadow_type);

gtk_container_add(GTK_CONTAINER(button),arrow);

gtk_widget_show(button);gtk_widget_show(arrow);

return button;}

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

GtkWidget *window;GtkWidget *button;

GtkWidget *box;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);gtk_window_set_title(GTK_WINDOW(window),"Cursores e botões");

g_signal_connect(G_OBJECT(window),"destroy",G_CALLBACK(gtk_main_quit),NULL);

gtk_container_set_border_width(GTK_CONTAINER(window),10);

box = gtk_hbox_new(FALSE,0);gtk_container_set_border_width(GTK_CONTAINER(box),2);

gtk_container_add(GTK_CONTAINER(window),box);

gtk_widget_show(box);

button = create_arrow_button(GTK_ARROW_UP,GTK_SHADOW_IN);gtk_box_pack_start(GTK_BOX(box),button,FALSE,FALSE,3);

button = create_arrow_button(GTK_ARROW_DOWN,GTK_SHADOW_OUT);gtk_box_pack_start(GTK_BOX(box),button,FALSE,FALSE,3);

button = create_arrow_button(GTK_ARROW_LEFT,GTK_SHADOW_ETCHED_IN);gtk_box_pack_start(GTK_BOX(box),button,FALSE,FALSE,3);

button = create_arrow_button(GTK_ARROW_RIGHT,GTK_SHADOW_ETCHED_OUT);gtk_box_pack_start(GTK_BOX(box),button,FALSE,FALSE,3);

gtk_widget_show(window);

gtk_main();

return 0;}

O widget de Caixa de Diálogo é simples também e ele nada mais é do que uma janela com algumas coisas empacotadas dentro dele. A estrutura de uma caixa de Diálogo é a seguinte:

struct GtkDialog{

GtkWindow window;GtkWidget *vbox;

GtkWidget *action_area;};

Page 33: ProgramaÇÃo Gtk

Podemos notar que ele possui uma janela , uma caixa vertical empacotado na janela e uma caixa horizontal que é o parâmetro action_area.

Geralmente estas caixas são usadas para pular janelas de notícias. Existem dois modos de criar uma caixa de Díálogo:

GtkWidget *gtk_dialog_new( void );GtkWidget *gtk_dialog_new_with_buttons( const gchar *title,

GtkWindow *parent,GtkDialogFlags flags,

const gchar *first_button_text,... );

A primeira função cria uma caixa de diálogo vazia e é com você mesmo seu modo de uso. Você pode empacotar um botão na caixa action_area fazendo da seguinte maneira:

button = ...gtk_box_pack_start GTK_BOX (GTK_DIALOG (window)->action_area),

button, TRUE, TRUE, 0)(;gtk_widget_show (button);

E poderá adicionar uma caixa vertical vbox com um rótulo dentro dele.

label = gtk_label_new ("Caixinhas Felizes");gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox),

label, TRUE, TRUE, 0);gtk_widget_show (label);

Como exemplo do uso da caixa de diálogo, você pode botar dois botões na action_area: um de cancelar e um de ok, e além disso, usar um rótulo na caixa vertical vbox, perguntando ao usuário alguma confirmação ou uma mensagem indicando erro. Logo depois poderá adiconar diferentes sinais para cada botão operar de acordo com a seleção do usuário.