Página principal

Mantendo A Sanidade Com O Glade

From CInLUG

Imagem:Helmet.png
Este artigo é somente um esboço.
Mas estamos trabalhando duro!
"Release early, release often."

Conteúdo

Introdução

No RPG Call of Cthulhu, os personagens dos jogadores perdem pontos de sanidade sempre que entram em contato com os "horrores que o homem não deveria saber que existem". Na vida real, programadores perdem sanidade em vários momentos (na verdade, é raro algum atingir idade avançada e continuar bom da cabeça), e um desses contatos macabros é com o desenvolvimento de interfaces com o usuários. Um outro é com processos de desenvolvimento como o RUP, mas vocês não estão aqui pra ouvir isso. Vamos às interfaces com o usuário.

As GUIs não são essencialmente um problema, construí-las pode ser divertido para alguns: desenhar as telas que o usuário deve ver, a disposição dos componentes, aparência geral, etecetera. O que não é nada divertido é passar o design pra código: escrever dezenas de comandos do tipo "criar janela", "criar botão", "colocar botão dentro da janela", e por aí vai. Uma interface grande e complexa, como um processador de texto, pode ser um pesadelo. E esta é sua primeira perda de sanidade! A solução para isso é utilizar algum Interface Designer, onde você pode realmente desenhar ou, melhor dizendo, montar sua interface com os componentes (chamados widgets) disponíveis, como botões, caixas de texto, e coisas do tipo. Depois de criada sua interface é gerado código com aqueles comandos simiescos "criar janela", "colocar botão". A loucura começa quando você altera o código gerado, resolve que precisa mudar a interface, daí gera mais código. E as mudanças que você fez? Não pode simplesmente copiar e colar, vai ter de escrever tudo de novo. E esta será sua segunda perda de sanidade!

Interfaces em XML

Uma prática comum em programação é dividir a aplicação a ser criada em camadas, podemos ter uma camada de dados, que cuida do acesso às informações em disco ou banco de dados, camada de negócio (ou camada lógica) que contém os algoritmos aplicados aos dados provenientes do usuário, e este normalmente interage com o sistema via camada de apresentação - em nosso caso a interface gráfica. Podemos "terceirizar" o trabalho de criação da interface gráfica para fora do código, guardando-a na forma de uma descrição em XML, a ser carregada dinamicamente pela camada de apresentação de nossa aplicação, que deve conectar as ações do usuário sobre essa interface à funções na camada de negócio.

Imagem:Interfaceloading.png

Dessa forma, a descrição de nossa interface é mantida numa linguagem descritiva (XML), e deixamos a linguagem de programação para nossos whiles e ifs. Uma solução bastante sã.

GTK+ & Glade

GTK+ é uma biblioteca para construção de interfaces gráficas com usuário, originária do projeto GIMP, escrita em C e licenciada sob a LGPL, que permite seu uso para criação de softwares GPL e não-GPL. Ela também é multi-plataforma (você pode programar para Windows e Linux numa tacada só) e, mesmo sendo escrita em C, é multi-linguagem (são muuuuitas linguagens). Muito mais pode ser dito sobre GTK+, mas fica para outra oportunidade.

O XML usado para descrever a interface é o GladeXML, gerado através do construtor de interfaces Glade. Ele é carregado em tempo de execução por nosso programa utilizando os serviços da libglade. Duas coisas muito boas resultarão dessa prática:

  • podemos alterar a interface sem alterar ou recompilar o código
  • podemos carregar a interface descrita pelo arquivo GladeXML de qualquer linguagem que possua bindings para biblioteca GTK+ e libglade: C, C++, Java, Perl, Python, C#, Pike, Ruby, Haskell, Objective Caml, Scheme, Ada, Erlang, PHP, Pascal (alou, povo do Delphi, vocês podem ser livres!), e várias outras.

E lembrem-se: não confundam "Glade", a aplicação de desenho de interface, com "libglade", a biblioteca que carrega as descrições de interface de arquivos GladeXML, gerados pelo Glade. Entenderam?

Glade 3

Por muito tempo a versão estável do construtor Glade foi a 2, que possuía diversas limitações, até que em 11 de agosto, há quase duas semanas (e estamos, eu e esse texto, em 2006, caso interesse), foi liberado o Glade 3.0, com as características que faltavam ao Glade 2. Por ser uma aplicação lançada recentemente, o pacote .deb para o Ubuntu, que usarei como base para os exemplos, não existe neste ponto do continuum espaço-temporal. Será então necessário sujar as mãos baixando o código fonte e compilando, mas ora, esse é um artigo sobre programação! Mesmo assim, caso não dê certo você pode acompanhar o artigo utilizando o Glade 2 mesmo, que no Debian/Ubuntu está nos pacotes glade e glade-gnome. O segundo pacote se trata de uma versão do Glade 2 compilada com suporte a componentes de interface do GNOME. O que me leva a falar de uma importante melhoria do Glade 3, que é a carga dinâmica de componentes para o construtor de interfaces. Basta disponibilizar um arquivo catalog que descreve o novo widget, para ele ser adicionado ao Glade 3.

Obtendo e Instalando

A versão mais recente do Glade pode ser encontrada em http://ftp.gnome.org/pub/GNOME/sources/glade3/3.0/ Baixe o arquivo com a extensão .tar.gz (ou .tar.bz2).

Para uma compilação bem sucedida vamos precisar de alguns pacotes, por isso repitam comigo:

$ sudo apt-get install build-essential libxml-parser-perl libgtk2.0-dev libxml2-dev

Então podemos começar. Visitem http://ftp.gnome.org/pub/GNOME/sources/glade3/3.0/ e troquem o X.Y da primeira linha abaixo para os valores da versão mais recente.

$ wget http://ftp.gnome.org/pub/GNOME/sources/glade3/3.0/glade3-3.X.Y.tar.gz
$ tar -xvzf glade3-3.X.Y.tar.gz
$ cd glade3-3.X.Y
$ ./configure
$ make
$ sudo make install

Se tudo correr bem você encontrará uma entrada para o Glade no menu Aplicações->Programação.

Componentes da Interface do Glade 3

Imagem:Glade.screenshot.png

Na imagem acima, da esquerda para direita: Paleta, Janela Principal (o espaço vazio é, ou será, a Árvore de Widgets) e Editor de Propriedades. Veremos como usar cada um depois, agora não daremos nem um passo sem um pouco de teoria, se não gosta, bem... você já compilou o Glade 3.0 mesmo, então pule para Usando o Glade 3. E cuidado com o Lado Negro!

Programação Orientada a Eventos

Esse não é exatamente mais um paradigma de programação, e sim uma necessidade técnica quando se está programando para um ambiente onde a ação pode acontecer a qualquer momento vinda de qualquer lugar. Para ficar mais claro, lembra daquele programa que você fez quando estava aprendendo sua primeira linguagem, aquele que perguntava a idade do sujeito e dizia alguma gracinha de acordo com a resposta? Naturalmente era um programa que rodava na linha de comando, onde ele apresentava uma mensagem "Digite sua idade:" e essa pergunta bloqueava o fluxo do computador - nada mais poderia ser feito até que ela fosse respondida ou o programa fosse interrompido. Por outro lado, o mesmo programa num ambientes gráfico teria de lidar com questões bem mais complexas do que as do exemplo anterior, especialmente porque teria de dividir o espaço com vários outros programas e não podia simplesmente bloquear todo sistema a espera de estímulos do usuário. Então usamos a técnica do Main Loop, um laço "infinito" (ou while true, como dizem por aí) onde o programa fica inquirindo o sistema se tem algum evento para ele. Tem? Tem? Tem? Tem?... Sem parar. Bem, não exatamente. O programador associa ações (funções, métodos) a determinados eventos, como o clique do mouse sobre um botão, quando ocorre tal evendo, o fluxo do programa é desviado do Main Loop para a tal ação. E depois volta ao loop ou finaliza o programa.

Eventos, Sinais e Callbacks

Como foi mencionado anteriormente, o usuário através dos dispositivos de entrada gera eventos, esperando com isso disparar algum comportamento na aplicação. A figura abaixo mostra o caminho percorrido pelo estímulo do usuário até a consecução da função.

Imagem:Signalsandcallbacks.png

Sinais

Quando os eventos (passando pelo GDK, que está abstraído na figura anterior) chegam ao Main Loop, este serve como uma espécie de comutador e os repassa para os widgets relevantes. O widget é o ponto final da viagem do evento, que começou no dispositivo do usuário, e de acordo como o widget foi programado, ele decidirá que sinais irá emitir. Cada widget possui uma lista de sinais' que pode emitir, como "clicked", "pressed", "released", "change", etc, e é nesses sinais que as funções que o usuário deseja realizar são conectados. Chamamos estas funções conectadas à sinais de widgets de callbacks.

Callbacks

Quando o evento chega do Main Loop para o widget, este procura os sinais relevantes a serem disparados e chama os callbacks conectados a estes pelo programador. Seria algo como dizer ao widget: '"ei botão, se lhe acontecer de te clicarem, ligue para este número que eu tomo uma atitude". Como ilustrado na figura anterior, os sinais não conectados não têm funções para chamar, fazendo com que o evento que o disparou "se perca".

Explodindo Coisas

A figura abaixo mostra a janela de uma aplicação num ambiente gráfico. Nada poderia ser mais simples...

Imagem:WindowPlain.png


...até explodirmos tudo.

Imagem:WindowExploded.png

Vamos catar os pedaços:

  1. Servidor X - responsável pelo controle do hardware gráfico, oferece abstração para aplicações gráficas. Recebe input de dispositivos do usuários, como mouse e teclado, gerando eventos que são enviados para as aplicações.
  2. Borda da Janela - colocada pelo gerenciador de janelas, que recebe os eventos de clique do mouse sobre o X e os repassa para a aplicação que esta sendo emoldurada por ele.
  3. Janela GDK - aqui entramos nas especificidades do toolkit gráfico GTK+, GDK (GIMP Drawning Kit) é uma camada de abstração sobre as funções de desenho do Servidor X, que facilita a portabilidade de GTK+ para plataformas não-Linux, como Win32 ou MacOSX. A janela GDK é do tamanho da área controlada por sua aplicação, ela recebe os eventos do Servidor X (XEvents), os transforma em GDKEvents e os repassa dessa forma para GTK+.
  4. Window Widget - componente de interface de alto nível, dentro da janela são dispostos os demais componentes. Como vimos até agora, a "Janela" não inclui as bordas, que são provideciadas pelo Gerenciador de Janelas.
  5. Demais Widgets - botões, entradas de texto, mais qualquer coisa que se possa empacotar numa janela.

O que vai nos interessar neste artigo está nos itens 4 e 5.

Usando o Glade 3

Agora vamos trabalhar, então abra o Glade. Você verá algo como o primeiro screenshot, mas para facilitar nossa comunicação vamos mudar algo: vá na janela principal do Glade e clique no menu View->Palette Appearance->Text beside icons. Sua paleta de widgets vai ficar como na figura ao lado.

Agora podemos começar.




Criando a Interface

Clique no botão "Window" na paleta, será adicionada uma janela ao seu projeto. Observe como as janelas principal e de propriedades ficaram mais interessantes:

Imagem:Glade.screenshot.01.png

  1. A janela recém criada onde colocaremos os outros widgets.
  2. A entrada correspondente na árvore de widgets.
  3. Propriedades dos widgets, onde iremos alterar suas características.

Propriedades

As propriedades sempre se referem ao widget em destaque, que pode ser escolhido clicando-se sobre ele na janela de edição, ou sobre seu nome na árvore de widgets. Vamos ajustar as características da nossa janela mudando os valores na aba Geral, conforme a tabela abaixo:

Propriedade Novo Valor
Nome janelaPrincipal
Título da Janela Exemplo

E na aba Comum:

Propriedade Novo Valor
Pedido de Largura 300
Pedido de Altura 120

Neste ponto salve sua interface como simple.glade.

Container Widgets

O fundo de malha cinza em 1 na figura anterior significa um espaço vazio onde podemos colocar um widget, fazendo do widget que possui o espaço vazio um container. Para colocar vários precisamos utilizar widgets containers, que disponibilizam vários espaços vazios onde podemos dispor os vários widgets da interface. Na paleta, selecione o widget Vertical Box na seção Containers, o cursor deve tomar a forma do ícone associado com uma Vertical Box, leve-o até a área cinza na janela que estamos editando e clique, aparecerá um diálogo perguntando seu tamanho, escolha 2.

A linha horizontal no meio da janela é invisível e serve para indicar que são duas áreas distintas, onde podemos incluir 2 widgets.

Agora adicionamos os dois últimos widgets do nosso exemplo: na seção Control and Display escolha Combo Box para o espaço superior, e Label para inferior.

"etiqueta" e "combo" são filhos de "vbox1", que por sua vez é filho de "aplicação"
"etiqueta" e "combo" são filhos de "vbox1", que por sua vez é filho de "aplicação"



Mude suas propriedades na aba Geral:

Combo Box
Propriedade Novo Valor
Nome combo
Items Pequeno
Normal
Grande


Label
Propriedade Novo Valor
Nome etiqueta
Etiqueta <small>Texto de exemplo.</small>
Utilizar Markup Sim


Empacotamento

Quem já usou coisas como Delphi ou VisualBasic, mesmo depois dos danos ao poder de raciocínio, deve ter notado que ao construir interfaces os widgets permaneciam exatamente onde foram colocados, e havia algo semelhante a uma "grade" para facilitar o posicionamento ordenado. Além disso, uma vez pronta a aplicação, quando o usuário aumentava a janela, nada se movia e um vasto e anti-estético espaço não aproveitado surgia nas extremidades. Ou então o usuário é proibido de redimensionar a janela, o que é uma truculência por parte do programador (a não ser que haja uma boa razão). Toolkits gráficos modernos como GTK+ usam gerenciadores de layout para posicionar e dimensionar de forma inteligente os widgets numa janela. As duas propriedades mais importantes para isso são

  • Expandir: se o widget container (pai) aumentar, este widget aumenta junto, ocupando o máximo de espaço possível, mas sem aumentar o tamanho desenhado do widget.
  • Preencher: faz com que o widget ocupe visualmente todo espaço disponível.

Você pode imaginar que a combinação das duas propriedades de empacotamento acima podem resultar em widgets enormes. No glade temos acesso às maravilhas do layout moderno através da aba Empacotamento da janela de Propriedades. Vamos alterar os nossos:

combo
Propriedade Novo Valor
Expandir Não
Preencher Sim


etiqueta
Propriedade Novo Valor
Expandir Sim
Preencher Sim

Experimente redimensionar e maximizar a janela e veja o que acontece com os widgets.

Callbacks

Agora vamos definir que queremos que o sinal "changed", na seção GtkComboBox da nossa Combo Box chamada combo, que é disparado toda vez que o usuário escolher um item diferente em sua lista, seja conectado a um callback que chamaremos de "mudaTamanho"; e o sinal "delete-event" (seção GtkWidget) da janelaPrincipal vamos dizer que deve ser conectado a uma callback chamado "sair".

Imagem:Glade.screenshot.06.png

Arquivo .glade

Ao final teremos isto:

  1. <?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2. <!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
  3. <!-- Generated with glade3
  4. Version: 3.0.0
  5. Date: Tue Aug 29 00:19:15 2006
  6. User: setanta
  7. Host: poe
  8. -->
  9. <glade-interface>
  10. <widget class="GtkWindow" id="janelaPrincipal">
  11. <property name="width_request">300</property>
  12. <property name="height_request">120</property>
  13. <property name="title">Exemplo</property>
  14. <signal name="delete_event" handler="sair"/>
  15. <child>
  16. <widget class="GtkVBox" id="vbox1">
  17. <property name="visible">True</property>
  18. <child>
  19. <widget class="GtkComboBox" id="combo">
  20. <property name="visible">True</property>
  21. <property name="items" translatable="yes">Pequeno
  22. Normal
  23. Grande</property>
  24. <signal name="changed" handler="mudaTamanho"/>
  25. </widget>
  26. <packing>
  27. <property name="expand">False</property>
  28. </packing>
  29. </child>
  30. <child>
  31. <widget class="GtkLabel" id="etiqueta">
  32. <property name="visible">True</property>
  33. <property name="label" translatable="yes">&lt;small&gt;Texto de exemplo.&lt;/small&gt;</property>
  34. <property name="use_markup">True</property>
  35. </widget>
  36. <packing>
  37. <property name="position">1</property>
  38. </packing>
  39. </child>
  40. </widget>
  41. </child>
  42. </widget>
  43. </glade-interface>
  44.  

Referências

Informações valiosas sobre a aplicação Glade em:

Libglade

A libglade é reponsável por parsear o arquivo XML com a descrição da interface e criar os widgets correspondentes em tempo de execução e conecta os sinais que definidos no glade aos callbacks implementados no código. A libglade está disponível nas linguagens em que GTK+ está disponível, e no futuro será integrada no núcleo de GTK+, embora a libglade externa não seja eliminada, para manter a compatibilidade. A seguir a interface definida no arquivo simple.glade será usada por diversas linguagens: Python, C, C++, Java, C# e Ruby. O código define que quando um item for selecionado no combo box (valores "Pequeno", "Médio" e "Grande"), o tamanho do texto no label deve mudar de acordo. Tenha em mente que é necessário obter referências, ou ponteiros, para os widgets se quisermos manipulá-los, e isso é obtido através da propriedade Name que definimos para eles no Glade. Se tivermos o cuidado de não mudar o nome dos widgets e dos sinais, podemos mudar a interface a vontade sem precisar mexer no código. As explicações estarão na forma de comentários no próprio código, e os exemplos em todas as linguagens deve ser o mais semelhante possível entre si. Compare o procedimento de operação entre as diversas linguagens, você notará que as diferenças são mais devido à linguagem do que a lógica de GTK+, embora isso fique mais claro em programas maiores do que o nosso exemplo.

Daqui para frente, um zoológico de linguagens. Escolha sua preferida, se cansar dela, escolha outra, a lógica de GTK+ será a mesma.

Python

Em minha humilde opinião, Python é uma das linguagens mais legais para se programar, boa para aprender coisas novas e com bastante suporte de vários projetos de software livre, então começaremos por ela.

Veja mais em:

Código

  1. #!/usr/bin/python
  2. #Arquivo: simple.py
  3.  
  4. import pygtk
  5. pygtk.require("2.0")
  6. import gtk, gtk.glade
  7.  
  8. class Aplicacao:
  9. def __init__(self):
  10. #Carrega a interface a partir do arquivo glade
  11. arvoreDeWidgets = gtk.glade.XML('simple.glade')
  12.  
  13. #Associa os widgets a variaveis;
  14. #Os widgets que sao obtidos a partir de pedidos ao objeto 'arvoreDeWidgets'
  15. self.janela = arvoreDeWidgets.get_widget('janelaPrincipal')
  16. self.etiqueta = arvoreDeWidgets.get_widget('etiqueta')
  17. self.combo = arvoreDeWidgets.get_widget('combo')
  18. #Conecta Sinais aos Callbacks
  19. arvoreDeWidgets.signal_autoconnect(self)
  20.  
  21. #Define o valor padrao dentre os itens da lista combo box
  22. self.combo.set_active(0)
  23.  
  24. #Exibe toda interface
  25. self.janela.show_all()
  26.  
  27. #Inicia o loop principal de eventos (GTK MainLoop)
  28. gtk.main()
  29.  
  30. #Callbacks
  31.  
  32. def mudaTamanho(self, widget):
  33. if widget.get_active_text() == 'Pequeno':
  34. self.etiqueta.set_markup('<small>Texto de exemplo.</small>')
  35. elif widget.get_active_text() == 'Normal':
  36. self.etiqueta.set_markup('Texto de exemplo.')
  37. elif widget.get_active_text() == 'Grande':
  38. self.etiqueta.set_markup('<big>Texto de exemplo.</big>')
  39. def sair(self, widget, data):
  40. #Sai do loop principal de eventos, finalizando o programa
  41. gtk.main_quit()
  42.  
  43. #Inicia a aplicacao
  44. if __name__ == "__main__":
  45. Aplicacao()
  46.  

GladeXML

O método gtk.glade.XML (e seus correlatos nas outras linguagens) retorna um objeto do tipo GladeXML, que guarda uma árvore de widgets semelhante aquela mostrada na janela principal da aplicação Glade. A partir deste objeto podemos obter referências para todos diversos widgets, e também podemos conectar os sinais às nossas funções callback com o método signal_connect. Em programas maiores utiliza-se o método signal_autoconnect, mas isso fica como lição de casa. ;)

Executando

Imagem:Package.png

Pacotes Necessários

Python é um dos componentes mais importantes no desktop Linux, logo numa instalação padrão do Ubuntu e outras distribuições que usam o GNOME como desktop, não será necessário instalar mais nada.

Para rodar o programa basta conceder permissão de execução para o arquivo simple.py (via linha de comando com chmod ou clicando com o botão direito no arquivo, em seguida clicando em Propriedades, daí vá na aba Permissões e habilite a opção Execução), então basta clicar duas vezes no executável.

C

Imagem:Package.png

Pacotes Necessários

  • libgtk2.0-dev
  • libglade2-dev

C foi escolhida como linguagem de desenvolvimento de GTK+ pela padronização da linguagem, portabilidade e uso extensivo em projetos de software livre. Por estranho que pareça, a biblioteca GTK+ é totalmente orientada a objetos, num modelo que lembra linguagens OO modernas como Java. Sim, dá um trabalhão, mas compensa, principalmente pela facilidade de fazer bindings para diversas linguagens de mais alto nível. Normalmente as aplicações são escritas nestas linguagens. Veja mais em:

Código

  1. //Arquivo: simple.c
  2. #include <gtk/gtk.h>
  3. #include <glade/glade.h>
  4.  
  5. //Widgets como variaveis globais
  6. GtkWidget *janelaPrincipal, *combo, *etiqueta;
  7.  
  8. //Callbacks
  9. void
  10. sair(GtkWidget *widget, GdkEventKey *e)
  11. {
  12. //Sai do loop principal de eventos, finalizando o programa
  13. gtk_main_quit();
  14. }
  15.  
  16. void
  17. mudaTamanho(GtkWidget *widget)
  18. {
  19. gchar* texto = gtk_combo_box_get_active_text(GTK_COMBO_BOX(widget));
  20.  
  21. if (!g_ascii_strcasecmp(texto, "Pequeno")) {
  22. gtk_label_set_markup(GTK_LABEL(etiqueta),
  23. "<small>Texto de exemplo.</small>");
  24. } else if (!g_ascii_strcasecmp(texto, "Normal")) {
  25. gtk_label_set_markup(GTK_LABEL(etiqueta),
  26. "Texto de exemplo.");
  27. } else if (!g_ascii_strcasecmp(texto, "Grande")) {
  28. gtk_label_set_markup(GTK_LABEL(etiqueta),
  29. "<big>Texto de exemplo.</big>");
  30. }
  31. }
  32.  
  33. //Inicia a aplicacao
  34. int
  35. main(int argc, char *argv[])
  36. {
  37. GladeXML *arvoreDeWidgets;
  38.  
  39. gtk_init( &argc, &argv );
  40.  
  41. //Carrega a interface a partir do arquivo glade
  42. arvoreDeWidgets = glade_xml_new("simple.glade", NULL, NULL);
  43. //Associa os widgets a variaveis;
  44. //Os widgets que sao obtidos a partir de pedidos ao objeto 'arvoreDeWidgets'
  45. janelaPrincipal = glade_xml_get_widget(arvoreDeWidgets, "janelaPrincipal");
  46. etiqueta = glade_xml_get_widget(arvoreDeWidgets, "etiqueta");
  47. combo = glade_xml_get_widget(arvoreDeWidgets, "combo");
  48.  
  49. //Conecta Sinais aos Callbacks
  50. glade_xml_signal_autoconnect(arvoreDeWidgets);
  51.  
  52. //Define o valor padrao dentre os itens da lista combo box
  53. gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
  54.  
  55. //Exibe toda interface
  56. gtk_widget_show_all(janelaPrincipal);
  57.  
  58. //Inicia o loop principal de eventos (GTK MainLoop)
  59. gtk_main ();
  60.  
  61. return 0;
  62. }
  63.  

Compilando & Executando

$ gcc -o simplec simple.c `pkg-config --cflags --libs libglade-2.0 gtk+-2.0` -rdynamic
$ ./simplec

O flag -rdynamic do gcc usado no exemplo acima é necessário para o correto funcionamento da função glade_xml_signal_autoconnect, que verifica a tabela de símbolos da aplicação em busca de funções com nomes idênticos aos callbacks definidos na aplicação Glade.

C++

Imagem:Package.png

Pacotes Necessários

  • libgtkmm-2.4-dev
  • libglademm-2.4-dev

Algumas características da natureza da linguagem C++, como a falta de mecanismos de introspecção, nos impede de usar a função signal_autoconnect da libglade. Assim, o código para C++ será um pouco diferente, o que pode ser convertido numa vantagem, pois poderemos observar uma outra forma de conectar sinais e callbacks. Também há uma diferença na implementação do callback mudaTamanho: a opção escolhida na ComboBox é verificada numericamente, em lugar de comparar Strings. Eu estava cansado e tive preguiça de manipular strings em C++, que não é minha primeira linguagem de programação.

Imagem:Helmet.png
Este artigo é somente um esboço.
Mas estamos trabalhando duro!
"Release early, release often."

...... escreva algo sobre Gtk::Main::run(janela....);

Veja mais em:

Código

  1. //Arquivo: simple.h
  2. #ifndef SIMPLE_H
  3. #define SIMPLE_H
  4.  
  5. #include <gtkmm.h>
  6. #include <libglademm.h>
  7.  
  8. class Aplicacao
  9. {
  10.  
  11. //Widgets
  12. Gtk::Window *janelaPrincipal;
  13. Gtk::ComboBox *combo;
  14. Gtk::Label *etiqueta;
  15.  
  16. public:
  17. Aplicacao();
  18.  
  19. //Callbacks
  20. virtual void mudaTamanho();
  21.  
  22. };
  23.  
  24. #endif // SIMPLE_H
  25.  
  1. //Arquivo: simple.cpp
  2. #include "simple.h"
  3.  
  4. Aplicacao::Aplicacao()
  5. {
  6. //Carrega a interface a partir do arquivo glade
  7. Glib::RefPtr<Gnome::Glade::Xml> arvoreDeWidgets =
  8. Gnome::Glade::Xml::create("simple.glade");
  9.  
  10. //Associa os widgets a variaveis;
  11. //Os widgets que sao obtidos a partir de pedidos ao objeto 'arvoreDeWidgets'
  12. arvoreDeWidgets->get_widget("janelaPrincipal", Aplicacao::janelaPrincipal);
  13. arvoreDeWidgets->get_widget("etiqueta", Aplicacao::etiqueta);
  14. arvoreDeWidgets->get_widget("combo", Aplicacao::combo);
  15.  
  16. //Conecta Sinais aos Callbacks
  17. Aplicacao::combo->signal_changed().connect(
  18. sigc::mem_fun(*this, &Aplicacao::mudaTamanho));
  19.  
  20. //Define o valor padrao dentre os itens da lista combo box
  21. Aplicacao::combo->set_active(0);
  22.  
  23. //Exibe toda interface
  24. Aplicacao::janelaPrincipal->show_all();
  25.  
  26. Gtk::Main::run(*janelaPrincipal);
  27. }
  28.  
  29. //Callbacks
  30. void
  31. Aplicacao::mudaTamanho()
  32. {
  33. switch (this->combo->get_active_row_number())
  34. {
  35. case 0:
  36. this->etiqueta->set_markup("<small>Texto de exemplo.</small>");
  37. break;
  38. case 1:
  39. this->etiqueta->set_markup("Texto de exemplo.");
  40. break;
  41. case 2:
  42. this->etiqueta->set_markup("<big>Texto de exemplo.</big>");
  43. break;
  44. default:
  45. // Tem algo errado aqui
  46. break;
  47. }
  48. }
  49.  
  50. //Inicia a aplicacao
  51. int
  52. main(int argc, char* argv[])
  53. {
  54. Gtk::Main kit(argc, argv);
  55. Aplicacao aplicacao;
  56. return 0;
  57. }
  58.  

Compilando & Executando

$ g++ simple.cpp -o simplecpp `pkg-config gtkmm-2.4 libglademm-2.4 --cflags --libs`
$ ./simplecpp


Java

Imagem:Package.png

Pacotes Necessários

  • libglib-java
  • libgtk-java
  • libglade-java

O binding GTK+ para Java se chama Java-GNOME e no site do projeto pode ser encontrada uma documentação de excelente qualidade, incluindo como compilar programas Java-GNOME para código nativo, usar com o Eclipse e mais ainda. Este código difere um pouco dos demais, pois acrescentei alguns lançamentos de exceções, que me foram pedidas pelo compilador, além de ter do nome do arquivo ser obrigatoriamente o mesmo da classe pública, em nosso caso Aplicacao.java. Perceba também que não é necessário conectar os sinais, basta criar métodos com os nomes dos callbacks definidos no Glade, que Java-GNOME faz o trabalho do Autoconnect autoconectmaticamente. Se gosta da idéia de programar com Java e GTK, dê olhada nesse Hello World, e em:

Código

  1. //Arquivo: Aplicacao.java
  2. import java.io.FileNotFoundException;
  3. import java.io.IOException;
  4.  
  5. import org.gnu.glade.LibGlade;
  6. import org.gnu.glade.GladeXMLException;
  7. import org.gnu.gtk.Gtk;
  8. import org.gnu.gtk.Widget;
  9. import org.gnu.gtk.Window;
  10. import org.gnu.gtk.ComboBox;
  11. import org.gnu.gtk.Label;
  12.  
  13. public class Aplicacao
  14. {
  15. private Window janelaPrincipal;
  16. private Label etiqueta;
  17. private ComboBox combo;
  18. public Aplicacao() throws FileNotFoundException, GladeXMLException,
  19. {
  20. //Carrega a interface a partir do arquivo glade
  21. LibGlade arvoreDeWidgets = new LibGlade("simple.glade", this);
  22.  
  23. //Carrega os Widgets em variaveis
  24. janelaPrincipal = (Window) arvoreDeWidgets.getWidget("janelaPrincipal");
  25. etiqueta = (Label) arvoreDeWidgets.getWidget("etiqueta");
  26. combo = (ComboBox) arvoreDeWidgets.getWidget("combo");
  27. //Define o valor padrao dentre os itens da lista combo box
  28. combo.setActive(0);
  29.  
  30. //Exibe toda interface
  31. janelaPrincipal.showAll();
  32.  
  33. //Inicia o loop principal de eventos
  34. Gtk.main();
  35. }
  36.  
  37. //Callbacks
  38.  
  39. public void mudaTamanho()
  40. {
  41. String text = combo.getActiveText();
  42.  
  43. if (text.equals("Pequeno")) {
  44. etiqueta.setMarkup("<small>Texto de exemplo</small>");
  45. } else if (text.equals("Normal")) {
  46. etiqueta.setMarkup("Texto de exemplo.");
  47. } else if (text.equals("Grande")) {
  48. etiqueta.setMarkup("<big>Texto de exemplo.</big>");
  49. }
  50. }
  51.  
  52. public void sair()
  53. {
  54. //Sai do loop principal de eventos
  55. Gtk.mainQuit();
  56. //Finaliza o programa
  57. System.exit(0);
  58. }
  59.  
  60. //Inicia a aplicacao
  61. public static void main(String[] args)
  62. {
  63. try {
  64. Gtk.init(args);
  65. new Aplicacao();
  66. Gtk.main();
  67. } catch(Exception e) {
  68. e.printStackTrace();
  69. }
  70. }
  71. }
  72.  

Compilando & Executando

$ javac -classpath /usr/share/java/glade2.12.jar:/usr/share/java/gtk2.8.jar:/usr/share/java/glib0.2.jar Aplicacao.java
$ java -classpath .:/usr/share/java/glade2.12.jar:/usr/share/java/gtk2.8.jar:/usr/share/java/glib0.2.jar Aplicacao


C#

Imagem:Package.png

Pacotes Necessários

  • mono-mcs
  • libgtk2.0-cil
  • libglade2.0-cil

Agora uma seção que deixará as mocinhas que tudo que vem da Micro$oft é do mal: GtkSharp! <Meu Disclaimer> Crianças vocês podem chamar de "do mal" todas as coisas que vierem deles que possam te prender, limitar sua escolha ou colocar seus traseiros e a sociedade nas mãos deles. O resto não tem problema. Vocês não adoram RTF? Foram eles que inventaram. </Meu Disclaimer> Voltando aos negócios, não tenho muita experiência com C#, mas como disse um professor lá do CIn "quem sabe Java, num fim de semana aprende C#". Daí extrapolando, quem sabe Java e GTK+, em menos de 1 hora faz um programinha em GtkSharp, ou melhor, GladeSharp. Uma das coisas legais que notei é que o Autoconnect da libglade não só conecta os sinais, mas também os widgets à atributos definidos como WidgetProperties. Ah, syntatical suggar! Vejam o código pra saber do que falo. Se quiser também pode experimentar o MonoDevelop.

Mais informações em:

Código

  1. //Arquivo: simple.cs
  2. using System;
  3. using Gtk;
  4. using Glade;
  5.  
  6. public class Aplicacao
  7. {
  8. //A linha a seguir faz com que o widget abaixo dela seja automaticamente
  9. //conectado ao widget de mesmo nome no arquivo glade
  10. [Glade.WidgetAttribute]
  11. Gtk.Window janelaPrincipal;
  12. [Glade.WidgetAttribute]
  13. Gtk.Label etiqueta;
  14. [Glade.WidgetAttribute]
  15. Gtk.ComboBox combo;
  16. public Aplicacao(string[] args)
  17. {
  18. Application.Init();
  19.  
  20. //Carrega a interface a partir do arquivo glade
  21. Glade.XML arvoreDeWidgets = new Glade.XML(null, "simple.glade", null, null);
  22.  
  23. //Associa os widgets a variaveis e conecta os Sinais aos Callbacks
  24. arvoreDeWidgets.Autoconnect(this);
  25.  
  26. //Define o valor padrao dentre os itens da lista combo box
  27. combo.Active = 0;
  28.  
  29. //Exibe toda interface
  30. janelaPrincipal.ShowAll();
  31.  
  32. //Inicia o loop principal de eventos (GTK MainLoop)
  33. Application.Run();
  34. }
  35.  
  36. //Callbacks
  37.  
  38. public void mudaTamanho(object obj, EventArgs args) {
  39. String text = ((Gtk.ComboBox) obj).ActiveText;
  40. if (text == "Pequeno") {
  41. etiqueta.Markup = "<small>Texto de exemplo.</small>";
  42. } else if (text == "Normal") {
  43. etiqueta.Markup = "Texto de exemplo.";
  44. } else if (text == "Grande") {
  45. etiqueta.Markup = "<big>Texto de exemplo.</big>";
  46. }
  47. }
  48.  
  49. public void sair(object obj, DeleteEventArgs args) {
  50. //Sai do loop principal de eventos, finalizando o programa
  51. Application.Quit();
  52. }
  53.  
  54. //Inicia a aplicacao
  55. public static void Main (string[] args)
  56. {
  57. new Aplicacao(args);
  58. }
  59. }
  60.  

Compilando & Executando

$ mcs -pkg:gtk-sharp-2.0 -pkg:glade-sharp-2.0 -resource:simple.glade simple.cs
$ ./simple.exe

Você também pode executar assim:

$ mono simple.exe

Ruby

Imagem:Package.png

Pacotes Necessários

  • ruby-gnome2

Lauro contribuiu com esse código em Ruby para o artigo.

Veja mais em:

Código

  1. #!/usr/bin/ruby
  2. # Arquivo: aplicacao.rb
  3.  
  4. require 'libglade2'
  5.  
  6. class Aplicacao
  7. # "Construtor"
  8. def initialize(path)
  9. # Cria o objeto GladeXML e associa os sinais aos
  10. # metodos com o mesmo nome atraves do bloco passado
  11. @glade = GladeXML.new(path){ |handler| method(handler) }
  12. #define os "widgets" e valores padroes
  13. @combo = @glade.get_widget("combo")
  14. @etiqueta = @glade.get_widget("etiqueta")
  15. @janela = @glade.get_widget("janelaPrincipal")
  16. @combo.active = 1 # Valor padrao = Tamanho normal
  17. @janela.show_all
  18. Gtk.main #Iniciando loop de espera por eventos
  19. end
  20.  
  21. # Signal handler: Responde a mudancas no combo box.
  22. def mudaTamanho
  23. case @combo.active
  24. when 0
  25. @etiqueta.set_markup("<small>Texto de Exemplo.</small>")
  26. when 1
  27. @etiqueta.set_markup("Texto de Exemplo.")
  28. when 2
  29. @etiqueta.set_markup("<big>Texto de Exemplo.</big>")
  30. end
  31. end
  32. # Signal handler: Sai do programa
  33. def sair
  34. Gtk.main_quit #Saindo do loop
  35. end
  36. end
  37.  
  38. Aplicacao.new("./simple.glade")
  39.  

Executando

$ ruby ./aplicacao.rb
Imagem:Important.png

Lauro: terminar parte de haskell

Haskell

Imagem:Package.png

Pacotes Necessários

  • gtk2hs (versão usada: 0.9.10)

Bem, para quem disse que Haskell é uma linguagem apenas acadêmica, sem bibliotecas, existe o Gtk2Hs, que contém bindings para a Gtk, libglade e Cairo.

De maneira parecida a C, as funções são chamadas com o formato "função widget argumentos". Por exemplo, a função onDestroy recebe um widget (window, no caso) e uma função (mainQuit) que será chamada quando o sinal Destroy for enviado para window.

Muitas funções retornam o tipo Maybe, usado em casos que a funções pode retornar um valor ou não. Mais informações podem ser vistas neste link [1].

Mais informações em:

Código

  1. -- Arquivo: aplicacao.hs
  2. import Graphics.UI.Gtk
  3. import Graphics.UI.Gtk.Glade
  4.  
  5. main::IO()
  6. main = do
  7. initGUI
  8. xmlMaybe <- xmlNew "example.glade"
  9. let xml = case xmlMaybe of
  10. (Just xml) -> xml
  11. Nothing -> error "File not found"
  12. window <- xmlGetWidget xml castToWindow "janelaPrincipal"
  13. label <- xmlGetWidget xml castToLabel "etiqueta"
  14. combo <- xmlGetWidget xml castToComboBox "combo"
  15. comboBoxSetActive combo 0
  16. onDestroy window mainQuit
  17. onChanged combo $ do
  18. indexM <- comboBoxGetActive combo
  19. let index = case indexM of
  20. (Just index) -> index
  21. Nothing -> 0
  22. let string = case index of
  23. 0 -> "<small>Texto de exemplo</small>"
  24. 1 -> "Texto de exemplo"
  25. 2 -> "<big>Texto de exemplo</big>"
  26. labelSetMarkup label string
  27. widgetShowAll window
  28. mainGUI

Compilando & Executando

$ ghc --make aplicacao.hs -o aplicacao
$ aplicacao

DevHelp

Poucas coisas são tão úteis para programar quanto documentação (com exceção talvez do café). O DevHelp é um navegador de ajuda, documentação e APIs do GNOME. Seu pacote no Debian/Ubuntu chama-se devhelp, mas esse sozinho serve tanto quanto café sem cafeína. O recheio do DevHelp está em pacotes com a terminação -doc (embora nem todo -doc vá parar no DevHelp), por exemplo: libgtk2.0-doc, glade-doc, libgtkmm-2.4-doc, python-gtk2-doc.

Imagem:DevHelp.png

Recomendo fortemente sua instalação. E é o fim por enquanto.

Autor: Marcelo Lira

Imagem:Cc-small.png : Imagem:Cc-by.png Imagem:Cc-sa.png
Atribuição-Compatilhamento pela mesma licença 2.5

Ferramentas pessoais
Vistas