Introdução ao GTK para Iniciantes
Introdução ao GTK para Iniciantes
1. Introdução
1. O que é o GTK/GTK+
2. O que é um widget
3. Questões práticas
2. Primeiros passos
1. O primeiro programa
2. Como compilar
3. Sinais e "callbacks"
4. Entendendo os widgets
5. Packing Widgets
Introdução
A interface visual é sem dúvida um dos principais aspectos de um programa. Para determinados
usuários, uma interface gráfica pode ser indispensável. Para outros, no mínimo desejável. Por isso,
os programadores precisam conhecer uma ferramenta de programação capaz de gerar interfaces
amigáveis, como o GTK. Este foi originalmente concebido em C, para ser usado na plataforma
Linux, e é muito fácil de ser usado. Para a plena compreensão do conteúdo deste tutorial, supomos
que o leitor possua conhecimentos básicos de C.
O que é o GTK ?
GTK é uma abreviação para GIMP Tool Kit. O GIMP (GNU Image Manipulation Program) é um
poderoso editor gráfico, ao estilo do Adobe Photoshop, com licença GPL, o que significa que ele é
gratuito, e pode ser distribuído livremente. O GTK tem esse nome pois foi originalmente concebido
para servir de ferramenta no desenvolvimento do GIMP. Devido a versatilidade das funções do
GTK, hoje este é utilizado na produção de diversos outros programas além do GNU Image
Manipulation Program, que variam desde pequenos utilitários, como o GTK-ICQ, até grandes
projetos, como o gerenciador de Desktop GNOME.
O GTK é na verdade um conjunto de widgets (você verá o significado desta palavra mais adiante),
que usa funções de outra biblioteca chamada GDK (GIMP Drawing Kit), que por sua vez é um
conjunto de funções que chamam outras funções de baixo nível do ambiente gráfico em que o
programa é compilado. Todo o conjunto do GTK ainda depende de uma biblioteca chamada GLib
(GNU Library) de funções úteis, comuns a vários programas GNU, e que aumentam portabilidade.
Existem interfaces de GTK para várias linguagens, embora este documento trate apenas da versão
para C. Programar em GTK exige o entendimento de conceitos de orientação a objetos, que não são
tão complicados para quem não conhece nada deste estilo de programação, e ainda servem como
uma boa introdução para o assunto.
Links interessantes:
- [Link]
- [Link]
- [Link]
O que é um widget ?
Um widget (literalmente, buginganga) é um objeto importante ao layout da aplicação, e é a estrutura
fundamental dos programas em GTK. A maioria dos widgets são objetos gráficos como janelas,
botões, listas e figuras. Outros são invisíveis, mas permitem controlar coisas como o alinhamento de
outros widgets na tela. A grande maioria das funções no GTK serve para manipular os widgets de
alguma forma.
Questões práticas
Onde conseguir o GTK. Como instalá-lo e verificar se está funcionando adequadamente ?
A principal e mais confiável fonte para se obter o GTK é com certeza o site do próprio
([Link]) na seção de downloads. De qualquer maneira, o GTK está incluído em todas as
principais distribuições de Linux.
Se você deseja apenas saber se o GTK está instalado no seu sistema, experimente digitar "gtk-
config --version". Se tudo estiver correto, você verá a versão do GTK instalada no seu sistema. Se
não, você deve procurá-lo e instalá-lo.
Para usuários da RedHat, o GTK esta disponível nos seguintes pacotes RPM:
- gtk+-(versão do pacote).rpm - contém as bibliotecas usadas pelo programa
- gtk+-devel(versão do pacote).rpm - contém os arquivos necessários para desenvolver seus
programas com GTK.
O programa gtk-config, que é distribuído junto com o GTK, tem muitas utilidades além de mostrar
a sua versão da biblioteca. Digitando apenas gtk-config, você pode ver opções deste comando. Duas
dessas opções serão realmente úteis mais adiante quando começarmos a por a mão no código.
Primeiros passos
O primeiro programa
Nesta seção, vamos ver um primeiro exemplo da programação com GTK, entendê-lo, e estende-lo
até que tudo esteja pronto para entendermos algo mais complicado. Este primeiro, mas importante
passo é nada mais nada menos que o popular 'Alô Mundo' em sua versão para GTK. O código a
seguir não faz muito além de mostrar uma janela vazia com o título 'Alo Mundo'.
Compilando...
Para compilar o programa acima usando o gcc, podemos usar uma linha de código como esta:
$ gcc -o alo alo.c -Wall -g `gtk-config --cflags --libs`
Novamente vemos o programa gtk-config sendo usado para algo extremamente útil. Colocar este
comando entre crases (atenção, use crases, e não o acento agudo) faz com que aquilo que o
programa imprimiria na tela seja adicionado à linha do compilador. Para entender melhor digite:
$ gtk-config --cflags
$ gtk-config --libs
A primeira linha retorna os diretórios onde estão cabeçalhos de funções necessários, enquanto a
outra mostra a localização das bibliotecas e quais são usadas pelo GTK. Desta maneira você não
precisa se preocupar com todos estes pequenos detalhes na hora de compilar o seu programa.
Agora é possível compilar nosso primeiro exemplo e ver o que acontece.
Ao executar o programa, repare que o único jeito de encerrá-lo é matando o processo no console,
digitando CTRL + C ou algo similar.
Para saber mais sobre o compilador gcc, consulte a seção 1.4.1.
Entendendo o exemplo...
* #include <gtk/gtk.h>
Isto vai incluir o cabeçalho principal do GTK, que está em um subdiretório chamado gtk no local
padrão dos includes (normalmente /usr/include ou /usr/local/include).
* GtkWidget *janela;
Precisamos declarar um ponteiro para um widget que guardará uma referência para o único widget
do nosso programa: a janela.
* gtk_init(&argc, &argv);
Esta função é essencial em todos os programas feitos com GTK e o exemplo não irá rodar sem que
ela tenha sido chamada no início da execução. Seu objetivo é preparar variáveis importantes ao
GTK e avaliar os argumentos passados na linha de comando para procurar por algumas opções pré-
definidas.
* janela = gtk_window_new(GTK_WINDOW_TOPLEVEL);
A função gtk_window_new() cria uma nova janela na memória e retorna um ponteiro para esta
janela, para que ela possa ser manipulada por outras funções. O parâmetro passado é o tipo de
janela a ser mostrada. Existem três tipos de janela enumeradas no arquivo gtkenums.h. O tipo
TOPLEVEL é o mais importante, e é onde a aplicação deve ser desenvolvida. Mais tarde iremos
voltar a este assunto e veremos os dois outros tipos de janelas.
* gtk_widget_show(janela);
gtk_widget_show() é uma função que espera como único parâmetro, um ponteiro para um widget,
neste caso, a janela, que deve ser mostrado na tela.
* gtk_main();
Com esta função o programa entra em um loop e espera por sinais do usuário como teclas
pressionadas no teclado ou movimentos do mouse. É necessário avisar ao programa o que fazer
quando receber um destes sinais, e este é justamente o próximo assunto.
Observação:
É bem fácil perceber que o GTK possui um número enorme de funções e que nem sempre é
possível guardar todos os seus nomes e os parâmetros necessários para cada uma. Uma boa noticia é
que os nomes das funções do GTK são muito regulares. Como regra, todas as funções que
manipulam um tipo de widget ou objeto do GTK seguem o padrão abaixo:
gtk_{nome do objeto ou widget}_{ação a ser realizada}(parâmetros)
Além disso, muitos widgets compartilham ações entre si. Por exemplo:
gtk_window_new()
gtk_button_new()
gtk_label_new()
Estas funções criam uma janela, um botão, e uma etiqueta, respectivamente, retornando ponteiros
para esses widgets.
As macros de conversão de tipo do GTK também seguem um padrão:
GTK_{TIPO} (objeto)
Estas macros checam se o objeto pode ser convertido para o tipo especificado e em seguida fazem a
conversão.
Sinais e funções de "callbacks"
Como mencionamos antes, durante o loop gtk_main() os widgets começam a receber sinais que são
enviados pelo usuário, e estes podem vir de várias fontes. Para tornar o programa mais interativo, é
necessário escrever funções que respondam a estes sinais e acrescentar ao código algo que diga ao
programa como relacionar um sinal a aquela função. Isto pode ser feito com a função
gtk_signal_connect(), e mostraremos o seu uso no próximo exemplo.
gtk_main();
return 0;
}
/* Fim do Segundo Exemplo */
Vamos agora entender as modificações feitas no programa.
* void sair(GtkWidget *w, gpointer p);
Criamos esta função para responder ao sinal de destruição da janela. Este sinal normalmente ocorre
quando pressionamos o botão do mouse sobre algum botão na barra de título do programa que fecha
a janela. O código desta função simplesmente chama a função do GTK gtk_main_quit(), que sai do
loop gtk_main() e continua com o código do programa. No nosso caso, após a chamada de
gtk_main() o programa retorna o controle ao sistema operacional.
Precisamos, no entanto, conectar esta função ao sinal de destruição da nossa janela. É então que
entra a função gtk_signal_connect(), que recebe 4 parâmetros simples:
gtk_signal_connect(GtkObject *object, gchar *name, GtkSignalFunc func, gpointer func_data);
object - O objeto que recebe o sinal. Como o parâmetro esperado é do tipo GtkObject, a macro
GTK_OBJECT é usada, para fazer a conversão do widget janela.
name - Uma string com o nome do sinal esperado. Neste caso, "destroy" é um sinal recebido pela
janela quando a mesma está para ser fechada. Existem vários sinais definidos no GTK para cada
tipo de widget. Veremos outros tipos de sinais ao longo do texto.
func - Um ponteiro para a função que vai responder ao sinal em questão. Lembre-se que em C o
nome da função é um ponteiro para ela. Esta função deve ter um protótipo do tipo:
void callback_func(GtkWidget *widget, gpointer callback_data);
GTK_SIGNAL_FUNC é uma macro que faz a conversão da função para este padrão.
func_data - Este é um parâmetro passado para a função de callback, cujo propósito depende
unicamente do programador.
Observação:
* gtk_widget_show(botao);
* gtk_widget_show(janela);
Perceba a ordem em que os widgets são exibidos. Isso não é meramente por acaso, mas para
garantir que quando a janela (que contém todos os outros widgets) apareça, os outros objetos já
estejam preparados e sejam mostrados juntos na tela. Assim, evitamos que uma máquina que não
seja rápida o suficiente mostre os widgets aparecendo um por um na janela.
Entendendo os Widgets
Como você já deve ter percebido, programar com GTK exige lidar com um número enorme de
novas funções. Talvez até mais funções do que você esteja normalmente acostumado a usar em seus
programas. Entretanto, também é fácil perceber que as funções do GTK são extremamente
regulares. Inicialmente, todas as funções do pacote GTK começam com 'gtk_'. Em seguida, o nome
da estrutura a ser manipulada ('widget', 'button', 'box', etc), e por último, a ação a ser realizada
('new', 'set_title', 'show', etc). Assim podemos admitir de agora em diante que para criar um novo
widget, não importando o seu tipo, basta chamar a função:
* gtk_{tipo de widget}_new ();
Alguns widgets podem ter mais de uma função para criá-los. Um exemplo já conhecido é o botão.
Podemos criar um botão vazio com gtk_button_new() ou um que já possua uma etiqueta com
gtk_button_new_with_label();
Outro tipo de funções bem recorrentes são aquelas que lidam com propriedades dos widgets, como
gtk_window_set_title() para designar um título a uma janela, ou gtk_widget_set_usize() para
redimensionar um widget. Estas funções levam como parâmetro o widget sendo manipulado e os
novos atributos (uma string no caso do título, ou dois inteiros no caso das dimensões).
Macros de Conversão de Tipo
Nas chamadas a funções do GTK que recebem widgets como parâmetros, muitas vezes estes devem
ser de um tipo específico, como container, ou box, ou window. De uma forma geral, o nome da
função já avisa qual será o tipo de parâmetro necessário. Por exemplo, gtk_table_attach() precisa de
um parâmetro do tipo GtkTable, mas como declaramos todos os nossos widgets como GtkWidget,
precisamos convertê-los para o tipo correto com a macro GTK_TABLE().
Ao longo deste texto já vimos outros exemplos de macros de casting como GTK_BOX(),
GTK_CONTAINER() e GTK_OBJECT(). O que estas macros realmente fazem é verificar se é
possível fazer o casting da variável para o tipo desejado, e se for, fazê-lo.
Os widgets no GTK estão todos em uma hierarquia, como objetos. Existem tipos mais genéricos e
outros mais específicos. O tipo GtkObject está no topo da hierarquia, e GtkWidget está logo abaixo.
Em orientação à objetos dizemos que GtkWidget herda de GtkObject, porque todo GtkWidget é um
GtkObject, mas nem todo GtkObject é um GtkWidget. Isto significa que a estrutura GtkWidget
possui todos os campos da estrutura GtkObject, e é possível ver um widget como um object com
alguns detalhes a mais. Por isso é possível o casting de GtkWidget para GtkObject.
Mais adiante, veremos novos widgets, e entenderemos a sua posição na hierarquia dos widgets.
Packing Widgets
Como você deve ter percebido pelo exemplo anterior, quando um widget é colocado em um
container (neste caso, o botão posicionado na janela), este geralmente tenta ocupar todo o espaço
disponível. Para conseguir colocar mais de um widget em um container e ter um controle maior
sobre o seu posicionamento é preciso entender o conceito de empacotamento de widgets.
Existe mais de uma maneira de se empacotar um widget, entretanto elas são bem parecidas, e
intuitivas de forma geral. Existem alguns tipos especiais de widget chamados caixas. Um widget
caixa é invisível até que algo seja colocado no seu interior. A caixa possui subdivisões para que
você possa colocar mais de um widget dentro dela.
Widgets Selecionados
Neste capítulo iremos apresentar uma visão geral de como usar alguns dos widgets mais comuns
fornecidos no pacote do GIMP Toolkit. Procure tirar o máximo proveito dos exemplos, de modo a
familiarizar-se com a linguagem.
- Buttons - Range Widgets - Labels - Tooltips
Buttons
Como já vimos em nossos exemplos anteriores, podemos criar um botão com as seguintes funções:
* GtkWidget *gtk_button_new(void);
* GtkWidget *gtk_button_new_with_label( gchar *label );
A primeira cria um botão vazio, enquanto a segunda automaticamente insere uma etiqueta de texto
no seu interior. Um detalhe interessante é que um botão é considerado um container, portanto é
possível inserir outros widgets em seu interior. Um método eficiente é criar uma caixa, inserir todos
os widgets desejados nesta e depois inseri-la no botão com gtk_container_add(). Desta maneira é
possível criar um botão com um ícone, usando o widget pixmap.
Quanto aos sinais, já vimos que o widget botão suporta "clicked", que ocorre quando um clique do
mouse (pressionar e soltar), acontece sobre o botão. Existem ainda os sinais "pressed", quando um
botão é pressionado, e "released" quando este é solto. Outros dois sinais, "enter" e "leave", ocorrem
respectivamente quando o ponteiro do mouse entra, ou sai de cima do botão.
Toggle Button
Um toggle button funciona como um botão comum exceto pelo fato de que ele conserva o seu
estado pressionado. Para criar um novo toggle button, existem as funções:
* GtkWidget *gtk_toggle_button_new(void);
* GtkWidget *gtk_toggle_button_new_with_label( gchar *label );
O toggle button possui um sinal especial chamado "toggled" que é chamado toda vez que o botão é
ativado ou desativado. A estrutura do widget possui um campo chamado "active" que diz se este
está ativado ou não. Podemos combinar o sinal "toggled" com este campo, para determinar as ações
do programa quando o estado do botão muda, como exemplo abaixo.
void toggled_callback( GtkWidget *widget, gpointer data ) {
if( GTK_TOGGLE_BUTTON (widget)->active ) {
/* Botão foi ativado */
} else {
/* Botão foi desativado */
}
}
O estado de um toggle button também pode ser forcado através da função:
* void gtk_toggle_button_set_active( GtkToggleButton *toggle_button, gint state );
Onde toggle_button é o botão em questão e state é um booleano que determina o estado do botão
(TRUE = ativado, FALSE = desativado). Sempre que esta função é chamada e o estado do botão é
modificado, o sinal "clicked" é emitido pelo widget.
Outra função usada com toggle buttons é:
* void gtk_toggle_button_toggled( GtkToggleButton *toggle_button );
Esta função muda o estado atual de um toggle button.
Check Button
Um check button funciona como um toggle button, mas é menor e possui o texto à sua direita. Para
criar check buttons use as seguintes funções:
* heck_button_new( void );
* gtk_check_button_new_with_label( gchar *label );
O widget check button herda a maioria das funções, campos e sinais dos toggle button, portanto, os
métodos citados anteriormente para checar e configurar o estado de um botão, também se aplicam
aqui.
Radio Button
Radio Buttons são usados especialmente em grupo para formar um conjunto de opções ao usuário.
Assim como check buttons, esses widgets funcionam como toggle buttons mas, quando agrupados,
possuem a característica de serem exclusivos. Isto significa que ao selecionar um radio button,
todos os outros do grupo são desativados.
Podemos criar radio buttons com as seguintes funções:
* GtkWidget *gtk_radio_button_new( GSList *group );
* GtkWidget *gtk_radio_button_new_with_label( GSList *group, gchar *label );
O parâmetro group em ambas as funções é um ponteiro para o grupo onde o botão deve ser
adicionado. Para o primeiro botão de um grupo este parâmetro deve ser NULL, já que nenhum
grupo foi formado ainda. Em seguida podemos chamar a função:
* GSList *gtk_radio_button_group( GtkRadioButton *radio_button );
Para criar um novo grupo contendo o radio button passado como parâmetro. O grupo retornado por
esta função pode ser passado como parâmetro na criação de novos radio buttons.
Em geral é desejado designar um botão para aparecer selecionado por padrão, quando o programa é
iniciado. Para isso usamos a mesma função que ativa toggle buttons já vista neste texto,
gtk_toggle_button_set_active(). Uma vez que o radio button herda propriedades do toggle button,
não há problema em fazer o casting com a macro GTK_TOGGLE_BUTTON().
Veja o exemplo demonstrando as principais características dos 4 tipos de botões discutidos nesta
seção. A partir de agora, você já possui experiência suficiente com o código para precisar de
comentários para cada linha.
Range Widgets
As barras de rolagem (scrollbars widgets) e os widgets de escala (scale widgets) compõem o grupo
dos range widgets. Os range widgets, do inglês widgets de extensão, são formados por uma barra
("trough") dentro da qual o usuário pode movimentar um botão ("slider"). Observe:
Os scrollbars permitem ao usuário navegar por um texto, por uma lista de valores ou qualquer outro
tipo de informação disposta numa janela. Já os scale widgets são usados para selecionar e manipular
um valor numa faixa específica. Para todos os fins, a única diferença entre os scale widgets e os
scrollbar widgets é que os primeiros permitem ao usuário visualizar, ao lado da barra, o valor
corrente assumido pelo controle. Veja as figuras a seguir, e compare um scale widget (acima) com
um scrollbar:
Pelo fato de funcionarem de forma quase idêntica, iremos tratar aqui apenas dos scrollbars widgets.
Para entendermos como funciona um scrollbar, é necessário antes compreender como funciona a
interação entre os range widgets e os widgets aos quais estes estão associados. Tomemos como
exemplo um widget do tipo caixa de texto (text widget) associado uma barra de rolagem vertical
(vertical scrollbar). Observe:
Gostaríamos que quando o usuário manipulasse a scrollbar, deslizando o "slider", o texto exibido na
caixa refletisse essa operação, rolando para cima ou para baixo. Por outro lado, quando o usuário
adicionasse texto à caixa, gostaríamos que o visual da barra de rolagem (scrollbar) se modelasse de
acordo com o novo conteúdo. Tudo isso pode ser feito de forma semi automática usando-se os
objetos do tipo ajuste (adjustments). Os adjustments guardam informações que podem ser
compartilhadas pelos objetos a ele associados.
Para criar um novo adjustment, use a função:
GtkObject *gtk_adjustment_new ( gfloat value, gfloat lower, gfloat upper, gfloat step_increment,
gfloat page_increment, gfloat page_size);
onde value é o valor assumido pelo controle. Lower é o valor mínimo que o ajuste pode assumir,
associado a posição mais a esquerda (no caso de barras de rolagem horizontais) ou superior (barras
de rolagem verticais) da barra. Upper é a coordenada associada a posição mais a direita ou inferior
da barra. Step_increment indica o menor dos dois incrementos através dos quais o usuário pode
alterar value, o que ocorre quando o usuário clicar sobre uma das setas na scrollbar, por exemplo. Já
page_increment indica o maior. Page_size indica o tamanho do "slider". Uma observação
interessante é que o campo upper não especifica o valor máximo que o adjustment pode assumir,
mas sim a coordenada associada a posição mais a direita (barras horizontais) ou inferior (barras
verticais). Observe com cuidado o primero dos três exemplos abaixo, no qual o valor máximo que o
adjustment pode assumir é 80, e não 100, como poderíamos imaginar.
Para criar uma nova barra de rolagem use uma das seguintes funções:
GtkWidget *gtk_hscrollbar_new( GtkAdjustment *adjustment );
GtkWidget *gtk_vscrollbar_new( GtkAdjustment *adjustment );
Eis a seguir alguns exemplos:
Na prática, entretanto, você raramente precisará criar os adjustments usando a função acima
apresentada. No nosso caso particular do text widget associado à scrollbar, podemos deixar esta
tarefa a cargo da função de geração da caixa texto (consulte a explicação sobre o text widget para
mais detalhes):
/* a caixa texto cria seus próprios ajustes */
text = gtk_text_new ( NULL, NULL )
/* use o recém criado ajuste para controlar a scrollbar */
vscrollbar = gtk_vscrollbar_new ( GTK_TEXT(text) -> vadj );
A interação entre a scrollbar e a caixa texto dá-se através dos ajustes (adjustments). A caixa texto
apenas consulta o campo "value" da estrutura "adjustment", a fim de exibir a porção adequada do
texto na janela. Para determinados procedimentos do usuário, como inserir mais texto na caixa, esta
última altera os outros campos do "adjustment". Já a barra de rolagem vertical altera apenas o valor
do campo "value", quando o usuário movimento o "slider", consultando todos os outros campos a
fim de modelar a sua forma. Observe:
Labels
Um label é uma etiqueta com um texto associado a ela. É usada amplamente no layout de um
programa para dar explicações sobre os campos de um formulário. Labels não possuem sinais,
portanto, para capturar eventos é necessário inseri-la em outros widgets. Existe apenas uma função
para criar labels:
* GtkWidget *gtk_label_new( const gchar *str );
Onde str é o texto contido na etiqueta. É possível mudar este texto durante o código do programa,
isto é, após a criação da label, com a função:
* void gtk_label_set_text( GtkLabel *label, const gchar *str );
O argumento label é o widget a ser modificado e str é o novo conteúdo da etiqueta.
Para ler o texto de uma label, pode ser usada a função:
* void gtk_label_get( GtkLabel *label, gchar **str );
O parâmetro str é um ponteiro para uma string e é onde o texto da etiqueta será retornado. Repare
que pelo retorno ser um ponteiro para a variável que guarda a string na etiqueta, não é aconselhável
manipular este valor.
É possível configurar o alinhamento do texto na etiqueta com a seguinte função:
* void gtk_label_set_justify( GtkLabel *label, GtkJustification jtype );
O parâmetro jtype é o tipo de justificação desejado, e GtkJustification é uma enumeração definida
em gtkenums.h. Os valores possíveis são:
GTK_JUSTIFY_LEFT
GTK_JUSTIFY_RIGHT
GTK_JUSTIFY_CENTER
GTK_JUSTIFY_FILL
Se nada for especificado, o texto de um label fica centralizado por padrão. Para usar quebra de linha
quando o texto fica maior que a etiqueta, use a função:
* void gtk_label_set_line_wrap( GtkLabel *label, gboolean wrap );
Onde wrap pode ser TRUE (ativa o retorno de linha) ou FALSE (desativa o retorno de linha).
Para sublinhar o texto de um label o GTK usa o esquema de strings de padrões. Uma string deste
tipo pode conter caracteres '_' (underscore) ou ' ' (espaço em branco). O padrão é sobreposto ao
texto na etiqueta, e os caracteres sob os símbolos de underscore são sublinhados. A função usada
para definir o padrão de um label é:
* void gtk_label_set_pattern( GtkLabel *label, const gchar *pattern );
Portanto, para criar um label com o texto "Aviso Importante!" e sublinhar a palavra importante,
faríamos o seguinte:
etiqueta = gtk_label_new( "Aviso Importante!" );
gtk_label_set_pattern( etiqueta, " __________" );
Consulte o exemplo para familiarizar-se com o label widget.
Tooltips
Uma tooltip é uma pequena etiqueta de texto que aparece sobre um widget, quando o ponteiro do
mouse fica parado por algum tempo sobre ele. Estes são widgets muito simples e de rápida
implementação. Para criar um tooltip widget, use a função:
* GtkTooltips *gtk_tooltips_new( void );
Após criar um tooltip é necessário associá-lo ao widget a que ele se refere, e determinar o texto a
ser exibido.
* void gtk_tooltips_set_tip( GtkTooltips *tooltips, GtkWidget *widget, const gchar *tip_text, const
gchar *tip_private );
O primeiro e segundo parâmetros são respectivamente o tooltip criado e o widget que vai ser
associado a ele. O parâmetro tip_text é o texto a ser exibido pelo tooltip, enquanto 'tip_private' esta'
relacionado a outro tipo de widget - GtkTipsQuery - e pode ser dispensado passando NULL em seu
lugar.
Para ativar ou desativar um tooltip existem duas funções simples:
* void gtk_tooltips_enable( GtkTooltips *tooltips );
* void gtk_tooltips_disable( GtkTooltips *tooltips );
Outras funções para configurar o funcionamento de um tooltip:
* void gtk_tooltips_set_delay( GtkTooltips *tooltips, gint delay);
* void gtk_tooltips_set_colors( GtkTooltips *tooltips, GdkColor *background, GdkColor
*foreground );
A primeira determina o tempo em milissegundos entre o evento de entrada do mouse no widget e o
aparecimento do tooltip. O padrão para este valor é 500. A outra função configura as cores de fundo
e do texto, respectivamente passadas como os parâmetros background e foreground.
Recomendamos agora que você faça download do exemplo e examine-o com atenção.
Dialogs
Dialogs são simples janelas com o objetivo de passar alguma mensagem importante ao usuário. Elas
normalmente aparecem avisando que alguma coisa deu errado ou para pedir confirmação de alguma
ação do usuário. Para criar um dialog, use a função:
* GtkWidget *gtk_dialog_new( void );
No código do GTK, um dialog não passa de um widget window com uma caixa vertical dividida em
duas partes por um separador horizontal. A área superior serve para empacotar a mensagem a que se
refere o dialog. A parte de baixo da janela contém uma outra caixa, desta vez horizontal, chamada
'action_area', onde podem ser colocados um ou mais botões que representam as possíveis opções do
usuário.
No arquivo gtkdialog.h a estrutura GtkDialog esta descrita da seguinte maneira:
struct GtkDialog {
GtkWindow window;
GtkWidget *vbox;
GtkWidget *action_area;
};
O campo window é a janela de dialogo em si, enquanto vbox e action_area são as caixas vertical e
horizontal já mencionadas. Para adicionar widgets as caixas de dialogo usamos as mesmas funções
para caixas. Podemos adicionar textos facilmente a parte superior da janela:
dialogo = gtk_dialog_new();
texto = gtk_label_new( "Tem certeza que quer sair do programa?" );
gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialogo )->vbox ), texto, TRUE,
TRUE, 0 );
gtk_widget_show(texto);
Da mesma maneira, acrescentamos os botões a caixa horizontal:
botao = gtk_button_new_with_label("Sim");
gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialogo )->action_area ), botao,
TRUE, TRUE, 0 );
gtk_widget_show(botao);
botao = gtk_button_new_with_label("Não");
gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialogo )->action_area ), botao,
TRUE, TRUE, 0 );
gtk_widget_show(botao);
Note como, em ambos os casos, fizemos o casting da variável 'dialogo' para GtkDialog antes de
poder acessar os campos 'vbox' e 'action_area'. Lembre-se que o tipo GtkWidget (como a variável
'dialogo' foi declarada) não possui estes campos. Outro detalhe importante é que os botões
adicionados ao dialog ainda precisam de funções de callback para realizar alguma ação, quando
clicados.
Também é possível fazer construções mais complicadas com caixas de dialogo, adicionando outros
tipos de widgets de empacotamento nas áreas da janela, como outras caixas ou mesmo tabelas.
Text entries
Estes widgets são pequenas caixas de texto de apenas uma linha, para ler entrada no teclado do
usuário. Eles podem ser criador com duas funções:
* GtkWidget *gtk_entry_new( void );
* GtkWidget *gtk_entry_new_with_max_length( guint16 max );
A segunda função permite criar uma caixa de texto com um numero máximo de caracteres, passado
no parâmetro max.
O conteúdo de uma destas caixas de texto pode ser lido com a função:
* gchar *gtk_entry_get_text( GtkEntry *entry );
Para modificar o texto em uma entry, existem três funções:
* void gtk_entry_set_text( GtkEntry *entry, const gchar *text );
* void gtk_entry_append_text( GtkEntry *entry, const gchar *text );
* void gtk_entry_prepend_text( GtkEntry *entry, const gchar *text );
A primeira função simplesmente altera o conteúdo da caixa de texto para a string no parâmetro text.
A segunda e terceira funções inserem texto no final e no inicio da caixa respectivamente, sem
remover o conteúdo anterior.
A posição do cursor em uma caixa de texto também pode ser configurada com a seguinte função:
* void gtk_entry_set_position( GtkEntry *entry, gint position );
Uma text entry também pode ficar desabilitada por algum tempo, para impedir que o usuário
modifique o seu conteúdo enquanto isto não é desejado. Para isto use:
* void gtk_entry_set_editable( GtkEntry *entry, gboolean editable );
O segundo parâmetro indica se a text entry pode ser editada (editable = TRUE), ou não (editable =
FALSE).
Em algumas situações também é útil esconder o texto que está sendo digitado, como no caso de
uma senha.
* void gtk_entry_set_visibility( GtkEntry *entry, gboolean visible );
Onde visible pode ser TRUE (texto visível), ou FALSE (texto escondido).
Para fazer seleções no texto contido na text entry, use a função:
* void gtk_entry_select_region( GtkEntry *entry, gint start, gint end );
O parâmetro 'start' é o primeiro caractere da seleção enquanto 'end' é o ultimo.
O widget entry possui dois sinais muito úteis para saber quando o texto foi modificado: "changed" e
"activate". O primeiro ocorre quando o conteúdo da caixa é modificado por qualquer motivo,
enquanto "activate" acontece apenas quando a tecla ENTER é pressionada no texto.
Combo Box
A Combo Box consiste-se de uma caixa de entrada de texto e de um menu a partir do qual o usuário
pode selecionar uma dentre diversas alternativas predefinidas. É possível ainda digitar na caixa de
texto qualquer conteúdo, diretamente.
Note que a Combo Box, do inglês Combination Box, tem esse nome pois é, na realidade, uma
combinação de outros widgets. Mais precisamente, uma Combo Box é formada por uma entrada de
texto, um botão, um menu do tipo "pop up" e uma lista com as alternativas predefinidas, além de
outros widgets secundários. Veja a seguir parte da estrutura que define uma Combo Box:
struct _GtkCombo {
GtkHBox hbox;
GtkWidget *entry;
GtkWidget *button;
GtkWidget *popup;
GtkWidget *popwin;
GtkWidget *list;
...
}
Entry e list são os widgets que por hora interessam-nos.
Definindo valores
Para definir o valor padrão exibido na caixa texto deve-se manipular o campo entry:
gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), "Valor padrão");
Para definir os campos a serem incluídos na lista "pop down" use a função:
void gtk_combo_set_popdown_string( GtkCombo *combo, GList *strings );
Note que é necessário passar como parâmetro para esta função uma lista (que nesse caso será do
tipo encadeada) com as strings de alternativas predefinidas. É muito simples definir essa lista. Basta
seguir o seguinte modelo:
GList *glist = NULL;
glist = g_list_append(glist, "String 1");
glist = g_list_append(glist, "String 2");
glist = g_list_append(glist, "String 3");
glist = g_list_append(glist, "String 4");
gtk_combo_set_popdown_string( GTK_COMBO(combo), glist);
É bom ressaltar que após chamar esta função, pode-se liberar a memória que está sendo ocupada
pelo ponteiro previamente declarado, usando algo como:
g_list_free (glist);
File Selection
O widget de seleção de arquivos permite que se crie facilmente janelas com caixas de diálogo para
acessar arquivo. A janela vem completa, inclusive com os botões Ok, Cancel e Help.
Criando um widget File Selectoin
Para criar uma nova caixa de seleção de arquivos use a função:
GtkWidget *gtk_file_selection_new( gchar *title );
Modos de operação
Pode-se permitir que o usuário selecione apenas uma linha da lista de cada vez, ou que este possa
selecionar várias linhas ao mesmo tempo, o que é definido através da função:
void gtk_clist_set_selection_mode ( GtkCList *clist, GtkSelectionMode mode );
Entre os modos de operação podemos citar:
- GTK_SELECTION_SINGLE - admite a seleção de um ou nenhum item por parte do usuário. A
seleção será sempre ou do tipo NULL, caso nada esteja selecionado, ou um ponteiro de uma GList
apontando para um único item selecionado.
- GTK_SELECTION_BROWSE - admite um item selecionado. Quando criada, a CList não
possuirá nenhum item selecionado (a seleção será do tipo NULL). Entretanto, a partir do momento
em que o usuário selecione um item, este terá sempre um, e exatamente um, item selecionado.
- GTK_SELECTION_MULTIPLE - admite seleção de múltiplos items. A seleção será NULL ou um
ponteiro de uma GList apontando para o primeiro item selecionado, que por sua vez apontará para o
segundo item selecionado, e assim por diante (os itens selecionados ficam armazenados numa lista
encadeada).
- GTK_SELECTION_EXTENDED - não admite a seleção de itens. O usuário não poderá
selecionar nenhum item, e a seleção será sempre do tipo NULL.
Outra função interessante é a que define a borda da CList:
void gtk_clist_set_shadow_type( GtkCList *clist, GtkShadowType border );
Onde alguns dos possíveis valores do tipo de borda são:
GTK_SHADOW_NONE
GTK_SHADOW_IN
GTK_SHADOW_OUT
Manipulando uma CList
Para definir o tamanho da largura das colunas, e a altura das linhas, use respectivamente as
seguintes funções:
void gtk_clist_set_column_width( GtkCList *clist, gint column, gint width );
void gtk_clist_set_row_height( GtkCList *clist, gint height );
Adicionando linhas
Para adicionar uma linha à lista use uma das seguintes funções:
gint gtk_clist_prepend( GtkCList *clist, gchar *text[] );
gint gtk_clist_append( GtkCList *clist, gchar *text[] );
void gtk_clist_insert( GtkCList *clist, gint row, gchar *text[] );
O valor retornado pelas duas primeiras funções indica a linha na qual o texto foi adicionado. A
quantidade de strings armazenadas no ponteiro text deve ser igual ao número de colunas na CList.
Se o argumento text[] for nulo, será criada uma linha em branco.
Para obter o conteúdo de uma célula, use:
gint gtk_clist_get_text ( GtkCList *clist, gint row, gint column, gchar **text );
O valor retornado em text será o texto contido na coluna e linha solicitadas.
Tree Widget
O objetivo do widget do tipo árvore é exibir informações organizadas hierarquicamente. O widget
do tipo Tree (árvore) é na realidade um container para widgets do tipo TreeItem.
Observações iniciais
É importante entender alguns detalhes sobre o seguinte esquema. Os comentários aqui descritos
sobre árvores seguem a nomenclatura usada pelo GTK, que pode ser um pouco distinta da usual na
ciência da computação.
A árvore 1 possui dois itens, A e B. Sob o item A foi adicionada uma árvore 2, que por sua vez
possui três itens, C, D e E. Atenção! Os itens C, D e E possuem um pai em comum, que é a árvore
1. Ou seja, tecnicamente, no GTK, o pai de uma sub-árvore é uma árvore. Além disso, a árvore 1 é a
árvore raiz (root tree) pois ela engloba todos os itens apresentados. Finalmente, é bom frisar que
toda sub-árvore é uma árvore.
Adicionando sub-árvores
Para adicionar uma sub-árvore sob um item de uma árvore utilize a função
void gtk_tree_item_set_subtree ( GtkTreeItem *tree_item, GtkWidget *subtree );
onde tree_item é o item sob o qual será adicionada a nova sub-árvore.
Atenção! O tree_item em questão deverá ter sido adicionado a uma árvore antes de se chamar a
função acima. Isto deve ser feito pois o pai da nova sub-árvore será esta árvore que guarda o
tree_item.
A lista de seleções
O widget do tipo árvore permite que selecione-se itens nele contidos . É a árvore raiz (root tree) que
guarda a lista de todos os itens que estão selecionados em qualquer uma de suas sub-árvores.
Quando um item de uma árvore é selecionado, este aciona o evento "select" enquanto que a árvore
que o contém emite o sinal "select_child". A árvore raiz, por sua vez, irá emitir o sinal de
"selection_changed" sempre que qualquer item desta ou de uma de suas sub-árvores for marcado ou
desmarcado.
Clique aqui para fazer o download do exemplo.
Menu Widget
O menu widget permite que se crie menus personalizados para suas aplicações de maneira bem
simples. No GTK existem basicamente dois tipos de menu: menus de barra (menu bar) e menus de
opções (options menu). Iremos tratar aqui dos menus do primeiro tipo.
Existem duas formas de criar-se menus no GTK. Uma mais complicada, que permite inclusive
colocar imagens nas opções apresentadas, e outra mais simples, porém um pouco mais limitada, a
qual iremos aqui abordar. Inicialmente, é interssante entender a nomenclatura utilizada:
O processo basicamente divide-se em duas etapas. Primeiro deve-se definir a estrutura que delineará
o menu, e em seguida este deverá ser ativado.
A estrutura usada para gerar os itens do menu é a GtkItemFactoryEntry, que compõem-se pelos
cinco itens a seguir:
item 1: a etiqueta do item do menu. Letras precedidas por um '_' serão consideradas como teclas de
atalho apenas quando o menu for aberto.
item 2: tecla de atalho para a entrada. Este item não será abordado neste tutorial (iremos passar o
valor NULL como parâmetro).
item 3: função de callback a ser chamada quando o usuário selecionar determinada opção.
item 4: o tipo do item. Alguns dos possíveis valores são:
NULL ou "" ou "<Item>", item simples
"<Title>", item de título
"<CheckItem>"
"<ToggleItem>"
"<RadioItem>"
"<Separator>", separador
"<Branch>", cria um item que guarda sub itens
"<LastBranch>", alinha o item a direita
Uma vez definido o menu, determinadas funções deverão ser chamadas para que o mesmo seja
efetivamente criado. Observe o exemplo.
Text Widget
O widget de texto permite que múltiplas linhas de texto sejas mostradas e editadas na tela.
Criando um text widget
Para criar um widget de caixa texto, use:
GtkWidget *gtk_text_new ( GtkAdjustment *hadj, GtkAdjustment *vadj );
Passar o valor NULL para os parâmetros fará com que a função crie seus próprios ajustes.
O widget de texto não cria automaticamente a barra de rolagem vertical. Para criá-la, deve-se fazer
algo como:
vscrollbar = gtk_vscrollbar_new ( GTK_TEXT(text) -> vadj );
gtk_box_pack_start(GTK_BOX(hbox), vscrollbar, FALSE, FALSE, 0);
gtk_widget_show( vscrollbar );
A barra de rolagem horizontal não é suportada pelo widget de texto. É possível, entretanto, quebrar
as linhas longas usando a função:
void gtk_text_set_word_wrap ( GtkText *text, gint word_wrap );
onde word_wrap assume valor TRUE ou FALSE.
/**************************************************
**************************************************/
#include <stdio.h>
#include <gtk/gtk.h>
gtk_text_set_editable(GTK_TEXT(text),
GTK_TOGGLE_BUTTON(checkbutton)->active);
GtkWidget *text)
gtk_text_set_word_wrap(GTK_TEXT(text),
GTK_TOGGLE_BUTTON(checkbutton)->active);
gtk_main_quit();
GtkWidget *window;
GtkWidget *table;
GtkWidget *vscrollbar;
GtkWidget *text;
GdkColormap *cmap;
GdkColor colour;
GdkFont *fixed_font;
FILE *infile;
GTK_SIGNAL_FUNC(close_application),
NULL);
gtk_widget_show (box1);
gtk_widget_show (table);
gtk_widget_show (text);
gtk_widget_show (vscrollbar);
/* Pega-se o mapa de cores do sistema, e aloca-se a cor vermelha */
cmap = gdk_colormap_get_system();
[Link] = 0xffff;
[Link] = 0;
[Link] = 0;
if (!gdk_color_alloc(cmap, &colour)) {
gtk_widget_realize (text);
no mesmo */
char buffer[1024];
int nchars;
while (1)
break;
fclose (infile);
gtk_widget_show (hbox);
check = gtk_check_button_new_with_label("Editavel");
GTK_SIGNAL_FUNC(text_toggle_editable), text);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE);
gtk_widget_show (check);
check = gtk_check_button_new_with_label("Quebra linhas (Wrap Words)");
GTK_SIGNAL_FUNC(text_toggle_word_wrap), text);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), FALSE);
gtk_widget_show (check);
gtk_widget_show (window);
gtk_main ();
return(0);