associação pythonbrasil[11] django zope/plone planet Início Logado como (Entrar)

Diferenças para "LibGlade"

Diferenças entre as versões de 3 e 25 (22 versões de distância)
Revisão 3e 2004-08-22 14:14:46
Tamanho: 15941
Editor: 201
Comentário:
Revisão 25e 2006-02-20 16:21:55
Tamanho: 16256
Comentário:
Deleções são marcadas assim. Adições são marcadas assim.
Linha 5: Linha 5:
Última atualização: 21.08.2004
Linha 9: Linha 7:
= 1. Prefácio =
== 1.1. Motivação ==
A maior motivação para escrever esse primeiro tutorial foi: eu quero
contribuir com Software Livre e quero fazer parte da comunidade, quero
aprender e produzir, quero disseminar informação. Bem, como eu já tinha isso
em mente, fui ao V FISL 2004, onde assisti uma palestra muito interessante
sobre como ser um desenvolvedor de software livre, ministrada pelo Kiko e o
Kov, dois grandes desenvolvedores e figuras da comunidade. Depois da
apresentação, procurei os dois e falei: "eu quero ajudar". Simples assim. Foi
aí que o Kiko falou que eu poderia contribuir com o projeto PyGTK e que seria
interessante começar escrevendo um tutorial sobre libglade. E cá estou
escrevendo sobre libglade. Antes disso, eu nunca havia usado o Glade, a
libglade e também as bibliotecas GTK+ e GNOME. Já programava em Python e C o
que facilitou alguma coisa. Depois de algumas semanas estudando essas
bibliotecas, me senti finalmente seguro para escrever essa documentação. Espero
que seja útil para alguém assim como foi importante para mim escrevê-la. Me
coloco a disposição para dúvidas, sugestões e correções. Por favor, me ajude a
tornar esse documento cada vez mais útil.

Quero registrar aqui o agradecimento ao Kiko e ao Kov pela oportunidade. Do
coração, valeu a força.

= 2. Introdução =
== 2.1. Do que estamos falando? ==
O assunto aqui é Libglade. Libglade é uma biblioteca que permite carregar uma
interface gráfica (GUI) a partir de um arquivo XML e conecta os ''signal''
''haldlers'' as funções de ''callback''. A libglade foi desenvolvida por JAMES HENSTRIDGE,
mesmo autor do PYGTK, o binding de _
GTK+ para PYTHON.

A libglade foi originalmente desenvolvida em C e já existem ''bindings'' para
diversas linguagens (Python, C++, Java), contudo o foco deste tutorial é
mostrar o uso da libglade com Python e PyGTK

Um bom exemplo do poder da libglade é o Glade3 que está sendo completamente
reescrito e não trará mais geração de código, a solução adotada foi o uso da
libglade interpretando o XML gerado. Outros grandes projetos como o
Gnumeric e o Evolution já usam a libglade.
= Prefácio =

== Motivação ==
A maior motivação para escrever esse primeiro tutorial foi: eu quero contribuir com Software Livre e quero fazer parte da comunidade, quero aprender e produzir, quero disseminar informação. Bem, como eu já tinha isso em mente, fui ao V FISL 2004, onde assisti uma palestra muito interessante sobre como ser um desenvolvedor de software livre, ministrada pelo Kiko e o Kov, dois grandes desenvolvedores e figuras da comunidade. Depois da apresentação, procurei os dois e falei: "eu quero ajudar". Simples assim. Foi
aí que o Kiko falou que eu poderia contribuir com o projeto PyGtk e que seria interessante começar escrevendo um tutorial sobre libglade. E cá estou escrevendo sobre libglade. Antes disso, eu nunca havia usado o Glade, a libglade e também as bibliotecas GTK+ e GNOME. Já programava em Python e C o que facilitou alguma coisa. Depois de algumas semanas estudando essas bibliotecas, me senti finalmente seguro para escrever essa documentação. Espero que seja útil para alguém assim como foi importante para mim escrevê-la. Me
coloco a disposição para dúvidas, sugestões e correções. Por favor, me ajude a tornar esse documento cada vez mais útil.

Quero registrar aqui o agradecimento ao Kiko e ao Kov pela oportunidade. Do coração, valeu a força.

= Introdução =

== Do que estamos falando? ==

O assunto aqui é Libglade. Libglade é uma biblioteca que permite carregar uma interface gráfica (GUI) a partir de um arquivo XML e conecta os ''signal'' ''haldlers'' as funções de ''callback''. A libglade foi desenvolvida por James Henstridge, mesmo autor do PyGtk, o binding de GTK+ para Python.

A libglade foi originalmente desenvolvida em C e já existem ''bindings'' para diversas linguagens (Python, C++, Java), contudo o foco deste tutorial é mostrar o uso da libglade com Python e PyGtk.

Um bom exemplo do poder da libglade é o Glade3 que está sendo completamente reescrito e não trará mais geração de código, a solução adotada foi o uso da libglade interpretando o XML gerado. Outros grandes projetos como o Gnumeric e o Evolution já usam a libglade.
Linha 56: Linha 35:
sobre gentoo linux 2004.1.

Este documento está em desenvolvimento, atualizações podem ser encontradas
na minha [http://econofisica.com.br/people/wilson/ página pessoal]
.

Para maiores detalhes a respeito de PyGTK, aconselho o
[http://www.pygtk.org/pygtk2tutorial/index.html PyGTK 2.0 Tutorial] escrito por
John Finlay, é o documento ''must read'' sobre o assuto.

= 3. Iniciando =
== 3.1. Glade e a janela vazia ==
Antes de começar qualquer coisa com libglade, precisamos de um XML que
descreva um interface. Para isso usei o Glade.

 [glade-empty-session-screenshot.png]

Essa é uma sessão bastante imediata do glade. Basicamente, criei um
projeto GTK+ e adicionei um objeto gtk.Window a ele. Ao salvar esse projeto,
ele gera o seguinte arquivo XML.
sobre Gentoo linux (2.6.7-gentoo-r11).

Para maiores detalhes a respeito de PyGtk, aconselho o [http://www.pygtk.org/pygtk2tutorial/index.html PyGTK 2.0 Tutorial] escrito por John Finlay, é o documento ''must read'' sobre o assuto.

= Iniciando =

== Glade e a janela vazia ==

Antes de começar qualquer coisa com libglade, precisamos de um XML que descreva um interface. Para isso usei o Glade.

attachment:glade-empty-session-screenshot.png

Essa é uma sessão bastante imediata do glade. Basicamente, criei um projeto GTK+ e adicionei um objeto gtk.Window a ele. Ao salvar esse projeto, ele gera o seguinte arquivo XML.
Linha 104: Linha 77:
Isso já é o suficiente para vermos como funciona a libglade. O programa abaixo
lê o arquivo XML e carrega a interface.

{{{
#!/usr/bin/python
Isso já é o suficiente para vermos como funciona a libglade. O programa abaixo lê o arquivo XML e carrega a interface.

{{{
#!python
Linha 118: Linha 89:
[empty-window.png]

Todo o trabalho sujo é feito pela classe {{{gtk.glade.XML}}}. Nela é feito o
parse do XML e os objetos são carregados e estão prontos para serem
manipulados.

Note que, ao fechar a janela, o programa ainda fica rodando, para finalizar
basta digitar <Crtl-C> no console onde foi executado o programa. Isso acontece
porque não foi feito nenhum tratamento de sinais no programa.

== 3.2. Fechando a janela ==
Para fechar a janela deve-se tratar o sinal que é disparado quando ela é
destruída. Para isso, selecione a aba de sinais no Glade, escolha qual sinal
quer tratar, nesse caso é {{{destroy}}} e informe qual é o {{{handler}}} para esse
sinal.

 [
empty-window-signals.png]

O {{{handler}}} deve ser mapeado com uma função ou método de uma classe, e esse
será executado quando o sinal for disparado. O código com o tratamento de
sinais fica:

{{{
#!/usr/bin/python
attachment:empty-window.png

Todo o trabalho sujo é feito pela classe {{{gtk.glade.XML}}}. Nela é feito o parse do XML e os objetos são carregados e estão prontos para serem manipulados.

Note que, ao fechar a janela, o programa ainda fica rodando, para finalizar basta digitar {{{Crtl-C}}} no console onde foi executado o programa. Isso acontece porque não foi feito nenhum tratamento de sinais no programa.

== Fechando a janela ==

Para fechar a janela deve-se tratar o sinal que é disparado quando ela é destruída. Para isso, selecione a aba de sinais no Glade, escolha qual sinal quer tratar, nesse caso é {{{destroy}}} e informe qual é o {{{handler}}} para esse sinal.

attachment:
empty-window-signals.png

O {{{handler}}} deve ser mapeado com uma função ou método de uma classe, e esse será executado quando o sinal for disparado. O código com o tratamento de sinais fica:

{{{
#!python
Linha 145: Linha 107:
dic = { "gtk_main_quit" : gtk.mainquit } dic = { "gtk_main_quit" : gtk.main_quit }
Linha 153: Linha 115:
Olhando novamente para a chamada {{{gtk.glade.XML("empty.glade")}}},
observa-se que um objeto é retornado. Esse objeto é o {{{GladeXML}}} que é
identificado pela variável {{{gladefile}}}. Através desse objeto, tem-se acesso a
toda estrurura montada no XML. O método {{{signal_autoconnect}}} recebe
mapeamento das funções de ''callback'' com os ''handlers''.  É exatamente na
chamada a {{{signal_autoconnect}}} que a libglade conecta o programa a interface
fazendo com que os sinais sejam tratados da maneira definida.

Execute e veja que o programa agora é encerrado normalmente, executando a
função {{{gtk_main_quit}}} quando o sinal {{{destroy}}} é disparado. Sei que não
é de nenhuma utilidade uma janela vazia que fecha, mas entenda que a interface
foi criada sem nenhuma interação com ''widgets'' e todo o trabalho de criar a
estrutura da interface gráfica fica a cargo do glade e da libglade. E
convenhamos que a criação da interface na mão é um pé saco, mesmo em Python
:). Note que com a libglade é possível alterar a interface gráfica quantas
vezes se fizer necessário, sem precisar recompilar o código para isso. É
simples, rápido e limpo. O lema aqui é: '''Quanto menos código melhor.'''

= 4. Seguindo adiante =
Ousando um pouco mais agora e com o intuito de levar a bancarrota os bancos de
investimento, vamos construir uma calculadora de investimentos chamada
InvestCalc. Ela calcula os dividendos de um investimento, para isso basta
fornecer o montante investido mensalmente ou anualmente, a taxa de juros e o
período de duração do investimento que ela informa o valor obtido no
investimento.
Segue uma amostra da interface desenvolvida no Glade.

 [investcalc-glade.png]

Para calcular o valor obtido, é preciso obter as referências para os
''widgets'' de entrada de dados ({{{gtk.Entry}}}, {{{gtk.Combo}}},
{{{gtk.SpinButton}}}). Isso é feito através do seguinte método:

{{{
Olhando novamente para a chamada {{{gtk.glade.XML("empty.glade")}}},  observa-se que um objeto é retornado. Esse objeto é o {{{GladeXML}}} que é identificado pela variável {{{gladefile}}}. Através desse objeto, tem-se acesso a toda estrurura montada no XML. O método {{{signal_autoconnect}}} recebe mapeamento das funções de ''callback'' com os ''handlers''. É exatamente na chamada a {{{signal_autoconnect}}} que a libglade conecta o programa a interface fazendo com que os sinais sejam tratados da maneira definida.

Execute e veja que o programa agora é encerrado normalmente, executando a função {{{gtk_main_quit}}} quando o sinal {{{destroy}}} é disparado. Sei que não é de nenhuma utilidade uma janela vazia que fecha, mas entenda que a interface foi criada sem nenhuma interação com ''widgets'' e todo o trabalho de criar a estrutura da interface gráfica fica a cargo do glade e da libglade. E
convenhamos que a criação da interface na mão é um pé saco, mesmo em Python :). Note que com a libglade é possível alterar a interface gráfica quantas vezes se fizer necessário, sem precisar recompilar o código para isso. É simples, rápido e limpo. O lema aqui é: '''quanto menos código, melhor'''.

= Seguindo adiante =

Ousando um pouco mais agora e com o intuito de levar a bancarrota os bancos de investimento, vamos construir uma calculadora de investimentos chamada !InvestCalc. Ela calcula os dividendos de um investimento, para isso basta fornecer o montante investido mensalmente ou anualmente, a taxa de juros e o período de duração do investimento que ela informa o valor obtido no investimento. Segue uma amostra da interface desenvolvida no Glade.

attachment:investcalc-glade.png

Para calcular o valor obtido, é preciso obter as referências para os ''widgets'' de entrada de dados ({{{gtk.Entry}}}, {{{gtk.Combo}}}, {{{gtk.SpinButton}}}). Isso é feito através do seguinte método:

{{{
#!python
...
Linha 189: Linha 134:
Onde {{{gladefile}}} é uma referência para {{{gtk.glade.XML}}} e {{{widget_name}}} é
o nome dado ao ''widget'' no Glade.

 [glade-widget-name.png]
Onde {{{gladefile}}} é uma referência para {{{gtk.glade.XML}}} e {{{widget_name}}} é o nome dado ao ''widget'' no Glade.

attachment:glade-widget-name.png
Linha 207: Linha 151:
#!python
...
Linha 216: Linha 163:
Obtidas as referências, vem o tratamento de sinais. Como foi visto
anteriormente, o sinal também é associado ao ''widget'' através do Glade, só
precisa informar qual sinal e o nome do ''handler'' para ele. Veja na figura
que o sinal {{{clicked}}} do ''widget'' {{{button_calculate}}} está associado ao
''handler'' {{{on_button_calculate_clicked}}}.

 [investcalc-button-signal.png]

Agora só precisa associar o sinal a uma função de ''callback'' no programa. Isso é
feito colocando o ''handler'' como chave em um dicionário e a função como
valor. Esse dicionário é passado como parâmetro para o método que faz a
auto-conexão dos sinais.

{{{
Obtidas as referências, vem o tratamento de sinais. Como foi visto anteriormente, o sinal também é associado ao ''widget'' através do Glade, só precisa informar qual sinal e o nome do ''handler'' para ele. Veja na figura que o sinal {{{clicked}}} do ''widget'' {{{button_calculate}}} está associado ao ''handler'' {{{on_button_calculate_clicked}}}.

attachment:investcalc-button-signal.png

Agora só precisa associar o sinal a uma função de ''callback'' no programa. Isso é feito colocando o ''handler'' como chave em um dicionário e a função como valor. Esse dicionário é passado como parâmetro para o método que faz a auto-conexão dos sinais.

{{{
#!python
Linha 232: Linha 173:
 import math
 # entry boxes
 amount = float(w_amount.get_text())
 return_rate = float(w_return_rate.get_text())
 # spin button
 duration = w_duration.get_value_as_int()
 # combo boxes
 duration_period = w_duration_period.entry.get_text()
 amount_period = w_amount_period.entry.get_text()

 if duration_period == 'Years':
  duration *= 12
 return_rate /= 100

 total = 0.0
 for i in range(duration):
  if amount_period == 'Year':
   if i%12 == 0:
    total += (total*return_rate) + amount
   else:
    total += total * return_rate
  else:
   total += (total*return_rate) + amount

 # label
 w_total_investment.set_text('$ %.2f' % total)
    import math
    # entry boxes
    amount = float(w_amount.get_text())
    return_rate = float(w_return_rate.get_text())
    # spin button
    duration = w_duration.get_value_as_int()
    # combo boxes
    duration_period = w_duration_period.entry.get_text()
    amount_period = w_amount_period.entry.get_text()

    if duration_period == 'Years':
        duration *= 12
        return_rate /= 100

    total = 0.0
    for i in range(duration):
        if amount_period == 'Year':
            if i%12 == 0:
                total += (total*return_rate) + amount
            else:
                total += total * return_rate
        else:
            total += (total*return_rate) + amount

    # label
    w_total_investment.set_text('$ %.2f' % total)
Linha 261: Linha 202:
...
        ... }
...
Linha 266: Linha 207:
Toda vez que o botão ''Calculate'' for clicado a função
{{{on_button_calculate_clicked}}} será executada e o valor obtido inpresso na
label. Veja o InvestCalc em ação:

 [screenshot-investcalc.png]

Atente para o uso dos ''widgets'' que foram obtidos anteriormente. Os valores
foram capturados das caixas de texto e ''combo boxes'', calculados e impressos em
um ''label''. Todos esse objetos, assim como toda a interface, foram instanciados
pela libglade. Pode-se mudar os botões e os demais ''widgets'' de posição e não
mexer em uma linha sequer de código. Pode-se mudar todo o ''design'' sem precisar
recompilar o programa. Lembre-se '''Quanto menos código, melhor.'''

== 4.1. Colocando uma caixa de diálogo ==
Toda aplicação que se preze deve ter uma caixa de diálogo com pelo menos o
nome do autor. Pois bem, para incluir essa caixa criei o botão {{{button_help}}}
e associei ao sinal {{{clicked}}} o ''handler'' {{{on_button_help_clicked}}}. No
Glade, criei uma caixa de diálogo chamada {{{dialog_help}}} com um botão
'''Close'''. A esse botão associei o seu sinal {{{clicked}}} ao ''handler''
{{{on_closebutton_dialog_clicked}}}. Criei as duas funções ''callback'' e e as
adicionei ao dicionário para a auto-conexão, como segue:

{{{
Toda vez que o botão ''Calculate'' for clicado a função {{{on_button_calculate_clicked}}} será executada e o valor obtido inpresso na label. Veja o !InvestCalc em ação:

attachment:screenshot-investcalc-1.png

Atente para o uso dos ''widgets'' que foram obtidos anteriormente. Os valores foram capturados das caixas de texto e ''combo boxes'', calculados e impressos em um ''label''. Todos esse objetos, assim como toda a interface, foram instanciados pela libglade. Pode-se mudar os botões e os demais ''widgets'' de posição e não mexer em uma linha sequer de código. Pode-se mudar todo o ''design'' sem precisar recompilar o programa. Lembre-se: '''quanto menos código, melhor'''.

== Colocando uma caixa de diálogo ==

Toda aplicação que se preze deve ter uma caixa de diálogo com pelo menos o nome do autor. Pois bem, para incluir essa caixa criei o botão {{{button_help}}} e associei ao sinal {{{clicked}}} o ''handler'' {{{on_button_help_clicked}}}. No Glade, criei uma caixa de diálogo chamada {{{dialog_help}}} com um botão '''Close'''. A esse botão, associei o seu sinal {{{clicked}}} ao ''handler'' {{{on_closebutton_dialog_clicked}}}. Criei as duas funções ''callback'' e e as adicionei ao dicionário para a auto-conexão, como segue:

{{{
#!python
...
Linha 303: Linha 234:
ao clicar no botão ''Help'' aparecerá:

 [screenshot-investcalc-dialog.png]
ao clicar no botão {{{Help}}} aparecerá:

attachment:screenshot-investcalc-dialog.png
Linha 310: Linha 241:
#!/usr/bin/python #!python
Linha 330: Linha 261:
 import math
 amount = float(w_amount.get_text())
 return_rate = float(w_return_rate.get_text())
 duration = w_duration.get_value_as_int()
 duration_period = w_duration_period.entry.get_text()
 amount_period = w_amount_period.entry.get_text()

 if duration_period == 'Years':
  duration *= 12
 return_rate /= 100

 total = 0.0
 for i in range(duration):
  if amount_period == 'Year':
   if i%12 == 0:
    total += (total*return_rate) + amount
   else:
    total += total * return_rate
  else:
   total += (total*return_rate) + amount

 w_total_investment.set_text('$ %.2f' % total)
    import math
    amount = float(w_amount.get_text())
    return_rate = float(w_return_rate.get_text())
    duration = w_duration.get_value_as_int()
    duration_period = w_duration_period.entry.get_text()
    amount_period = w_amount_period.entry.get_text()

    if duration_period == 'Years':
        duration *= 12
        return_rate /= 100

    total = 0.0
    for i in range(duration):
        if amount_period == 'Year':
            if i%12 == 0:
                total += (total*return_rate) + amount
            else:
                total += total * return_rate
        else:
            total += (total*return_rate) + amount

    w_total_investment.set_text('$ %.2f' % total)
Linha 354: Linha 285:
 help_dialog.show()     help_dialog.show()
Linha 357: Linha 288:
 help_dialog.hide()     help_dialog.hide()
Linha 369: Linha 300:
= 5. Para Contribuir =
Este documento, assim como muitos outros software livres, foi criado por
voluntários. Se existe algum tópico a respeito do tema que este documento não
contempla, por favor ajude a mantê-lo completo enviando essas informações ou
escrevendo você mesmo uma seção ou trecho de alguma seção.

Atente para o fato de que esse documento é livre e qualquer conteúdo
adicionado a ele também deverá ser.

Se você realmente quiser contribuir, envie um email para Wilson Freitas
<wilson freitas (a) econofisica com br>.
Todo o código produzido para esse artigo pode ser encontrado aqui [http://www.aboutwilson.net/libglade-tutorial-code.tar.gz]. Divirta-se.

= Para Contribuir =

Este documento, assim como muitos outros software livres, foi criado por voluntários. Se existe algum tópico a respeito do tema que este documento não contempla, por favor ajude a mantê-lo completo enviando essas informações ou escrevendo você mesmo uma seção ou trecho de alguma seção.

Atente para o fato de que esse documento é livre e qualquer conteúdo adicionado a ele também deverá ser.

Se você realmente quiser contribuir, envie um email para Wilson Freitas <wilson (a) econofisica com br>.
Linha 383: Linha 312:
= 6. Créditos =
Agora eu vou falar sobre os que ou o que tornou esse documento possível:

 * Christian Robottom Reis <kiko (a) async com br> e Gustavo Noronha <kov (a) debian org> pela oportunidade de contribuir.
 * Jonh Finlay, <finlay (a) moeraki com> pelo maravilhoso tutorial de PyGTK.
 * Gustavo Noronha, <kov (a) debian org> por fazer a revisão, I hope!
 * txt2tags, [http://txt2tags.sf.net] por me fazer não precisar usar as horríveis markup languages.
 * Lissandra pelo carinho.
 * Pedrinho pelo bom humor.

E todos os que eu espero que leiam e ajudem a contribuir com esse documento.

= 7. Copyright =
= Créditos =

Agora eu vou falar sobre o que tornou esse documento possível:

 * Jonh Finlay, <finlay (a) moeraki com> pelo tutorial de PyGtk.
 * txt2tags [http://txt2tags.sf.net].
 * Lissandra pelo carinho e Pedrinho pelo bom humor.
 * E todos os que eu espero que leiam e ajudem a contribuir com esse documento.

= Copyright =
Linha 398: Linha 325:
Esse documento está licenciado sob a ''GNU Free Documentation License'', publicada
pela ''Free Software Foundation''.

É permitido distribuir cópias deste manual, desde que se forneça o ''Copyright'' em
todas as cópias.

Se você pretende incluir este documento em alguma publicação, por favor
contate o responsável e nós trabalharemos para garantir que todo
documento esteja atualizada e de acordo com as informações disponíveis.

= 8. Referências =
Esse documento está licenciado sob a ''GNU Free Documentation License'', publicada pela ''Free Software Foundation''.

É permitido distribuir cópias deste manual, desde que se forneça o ''Copyright'' em todas as cópias.

Se você pretende incluir este documento em alguma publicação, por favor contate o responsável e nós trabalharemos para garantir que todo documento esteja atualizada e de acordo com as informações disponíveis.

= Referências =

libglade 2.4.0 tutorial versão 0.1

Wilson Freitas

TableOfContents

Prefácio

Motivação

A maior motivação para escrever esse primeiro tutorial foi: eu quero contribuir com Software Livre e quero fazer parte da comunidade, quero aprender e produzir, quero disseminar informação. Bem, como eu já tinha isso em mente, fui ao V FISL 2004, onde assisti uma palestra muito interessante sobre como ser um desenvolvedor de software livre, ministrada pelo Kiko e o Kov, dois grandes desenvolvedores e figuras da comunidade. Depois da apresentação, procurei os dois e falei: "eu quero ajudar". Simples assim. Foi aí que o Kiko falou que eu poderia contribuir com o projeto PyGtk e que seria interessante começar escrevendo um tutorial sobre libglade. E cá estou escrevendo sobre libglade. Antes disso, eu nunca havia usado o Glade, a libglade e também as bibliotecas GTK+ e GNOME. Já programava em Python e C o que facilitou alguma coisa. Depois de algumas semanas estudando essas bibliotecas, me senti finalmente seguro para escrever essa documentação. Espero que seja útil para alguém assim como foi importante para mim escrevê-la. Me coloco a disposição para dúvidas, sugestões e correções. Por favor, me ajude a tornar esse documento cada vez mais útil.

Quero registrar aqui o agradecimento ao Kiko e ao Kov pela oportunidade. Do coração, valeu a força.

Introdução

Do que estamos falando?

O assunto aqui é Libglade. Libglade é uma biblioteca que permite carregar uma interface gráfica (GUI) a partir de um arquivo XML e conecta os signal haldlers as funções de callback. A libglade foi desenvolvida por James Henstridge, mesmo autor do PyGtk, o binding de GTK+ para Python.

A libglade foi originalmente desenvolvida em C e já existem bindings para diversas linguagens (Python, C++, Java), contudo o foco deste tutorial é mostrar o uso da libglade com Python e PyGtk.

Um bom exemplo do poder da libglade é o Glade3 que está sendo completamente reescrito e não trará mais geração de código, a solução adotada foi o uso da libglade interpretando o XML gerado. Outros grandes projetos como o Gnumeric e o Evolution já usam a libglade.

Esse tutorial foi desenvolvido com:

  • libglade-2.4.0
  • glade-2.6.0
  • pygtk-2.2.0
  • python-2.3.3-r1
  • gtk+-2.4.1
  • gnome-2.6

sobre Gentoo linux (2.6.7-gentoo-r11).

Para maiores detalhes a respeito de PyGtk, aconselho o [http://www.pygtk.org/pygtk2tutorial/index.html PyGTK 2.0 Tutorial] escrito por John Finlay, é o documento must read sobre o assuto.

Iniciando

Glade e a janela vazia

Antes de começar qualquer coisa com libglade, precisamos de um XML que descreva um interface. Para isso usei o Glade.

attachment:glade-empty-session-screenshot.png

Essa é uma sessão bastante imediata do glade. Basicamente, criei um projeto GTK+ e adicionei um objeto gtk.Window a ele. Ao salvar esse projeto, ele gera o seguinte arquivo XML.

<?xml version="1.0" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
                                                                                                                                            
<glade-interface>
                                                                                                                                            
<widget class="GtkWindow" id="window1">
  <property name="visible">True</property>
  <property name="title" translatable="yes">window1</property>
  <property name="type">GTK_WINDOW_TOPLEVEL</property>
  <property name="window_position">GTK_WIN_POS_NONE</property>
  <property name="modal">False</property>
  <property name="resizable">True</property>
  <property name="destroy_with_parent">False</property>
  <property name="decorated">True</property>
  <property name="skip_taskbar_hint">False</property>
  <property name="skip_pager_hint">False</property>
  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
                                                                                                                                            
  <child>
    <placeholder/>
  </child>
</widget>
                                                                                                                                            
</glade-interface>

Isso já é o suficiente para vermos como funciona a libglade. O programa abaixo lê o arquivo XML e carrega a interface.

   1 import gtk, gtk.glade
   2 
   3 gtk.glade.XML("empty.glade")
   4 gtk.main()

Essa é a interface gerada por esse programa de 4 linhas.

attachment:empty-window.png

Todo o trabalho sujo é feito pela classe gtk.glade.XML. Nela é feito o parse do XML e os objetos são carregados e estão prontos para serem manipulados.

Note que, ao fechar a janela, o programa ainda fica rodando, para finalizar basta digitar Crtl-C no console onde foi executado o programa. Isso acontece porque não foi feito nenhum tratamento de sinais no programa.

Fechando a janela

Para fechar a janela deve-se tratar o sinal que é disparado quando ela é destruída. Para isso, selecione a aba de sinais no Glade, escolha qual sinal quer tratar, nesse caso é destroy e informe qual é o handler para esse sinal.

attachment:empty-window-signals.png

O handler deve ser mapeado com uma função ou método de uma classe, e esse será executado quando o sinal for disparado. O código com o tratamento de sinais fica:

   1 import gtk, gtk.glade
   2 
   3 dic = { "gtk_main_quit" : gtk.main_quit }
   4 
   5 gladefile = gtk.glade.XML("empty.glade")
   6 gladefile.signal_autoconnect(dic)
   7 
   8 gtk.main()

Olhando novamente para a chamada gtk.glade.XML("empty.glade"), observa-se que um objeto é retornado. Esse objeto é o GladeXML que é identificado pela variável gladefile. Através desse objeto, tem-se acesso a toda estrurura montada no XML. O método signal_autoconnect recebe mapeamento das funções de callback com os handlers. É exatamente na chamada a signal_autoconnect que a libglade conecta o programa a interface fazendo com que os sinais sejam tratados da maneira definida.

Execute e veja que o programa agora é encerrado normalmente, executando a função gtk_main_quit quando o sinal destroy é disparado. Sei que não é de nenhuma utilidade uma janela vazia que fecha, mas entenda que a interface foi criada sem nenhuma interação com widgets e todo o trabalho de criar a estrutura da interface gráfica fica a cargo do glade e da libglade. E convenhamos que a criação da interface na mão é um pé saco, mesmo em Python :). Note que com a libglade é possível alterar a interface gráfica quantas vezes se fizer necessário, sem precisar recompilar o código para isso. É simples, rápido e limpo. O lema aqui é: quanto menos código, melhor.

Seguindo adiante

Ousando um pouco mais agora e com o intuito de levar a bancarrota os bancos de investimento, vamos construir uma calculadora de investimentos chamada InvestCalc. Ela calcula os dividendos de um investimento, para isso basta fornecer o montante investido mensalmente ou anualmente, a taxa de juros e o período de duração do investimento que ela informa o valor obtido no investimento. Segue uma amostra da interface desenvolvida no Glade.

attachment:investcalc-glade.png

Para calcular o valor obtido, é preciso obter as referências para os widgets de entrada de dados (gtk.Entry, gtk.Combo, gtk.SpinButton). Isso é feito através do seguinte método:

   1 ...
   2 widget = gladefile.get_widget('widget_name')

Onde gladefile é uma referência para gtk.glade.XML e widget_name é o nome dado ao widget no Glade.

attachment:glade-widget-name.png

no XML fica assim:

...
      <child>
        <widget class="GtkEntry" id="entry_amount">
          <property name="visible">True</property>
...

o código para obter as referências dos widgets de entrada de dados fica:

   1 ...
   2 
   3 w_duration = gladefile.get_widget('spinbutton_duration')
   4 w_return_rate = gladefile.get_widget('entry_return')
   5 w_amount = gladefile.get_widget('entry_amount')
   6 w_total_investment = gladefile.get_widget('label_total_investment')
   7 w_duration_period = gladefile.get_widget('combo_duration_period')
   8 w_amount_period   = gladefile.get_widget('combo_amount_period')
   9 help_dialog = gladefile.get_widget('dialog_help')

Obtidas as referências, vem o tratamento de sinais. Como foi visto anteriormente, o sinal também é associado ao widget através do Glade, só precisa informar qual sinal e o nome do handler para ele. Veja na figura que o sinal clicked do widget button_calculate está associado ao handler on_button_calculate_clicked.

attachment:investcalc-button-signal.png

Agora só precisa associar o sinal a uma função de callback no programa. Isso é feito colocando o handler como chave em um dicionário e a função como valor. Esse dicionário é passado como parâmetro para o método que faz a auto-conexão dos sinais.

   1 ...
   2 def on_button_calculate_clicked(*args):
   3     import math
   4     # entry boxes
   5     amount = float(w_amount.get_text())
   6     return_rate = float(w_return_rate.get_text())
   7     # spin button
   8     duration = w_duration.get_value_as_int()
   9     # combo boxes
  10     duration_period = w_duration_period.entry.get_text()
  11     amount_period = w_amount_period.entry.get_text()
  12 
  13     if duration_period == 'Years':
  14         duration *= 12
  15         return_rate /= 100
  16 
  17     total = 0.0
  18     for i in range(duration):
  19         if amount_period == 'Year':
  20             if i%12 == 0:
  21                 total += (total*return_rate) + amount
  22             else:
  23                 total += total * return_rate
  24         else:
  25             total += (total*return_rate) + amount
  26 
  27     # label
  28     w_total_investment.set_text('$ %.2f' % total)
  29 
  30 dic = { 'on_window1_destroy' : lambda win: gtk.main_quit(),
  31         'on_button_calculate_clicked' : on_button_calculate_clicked,
  32         ... }
  33 ...
  34 gladefile.signal_autoconnect(dic)

Toda vez que o botão Calculate for clicado a função on_button_calculate_clicked será executada e o valor obtido inpresso na label. Veja o InvestCalc em ação:

attachment:screenshot-investcalc-1.png

Atente para o uso dos widgets que foram obtidos anteriormente. Os valores foram capturados das caixas de texto e combo boxes, calculados e impressos em um label. Todos esse objetos, assim como toda a interface, foram instanciados pela libglade. Pode-se mudar os botões e os demais widgets de posição e não mexer em uma linha sequer de código. Pode-se mudar todo o design sem precisar recompilar o programa. Lembre-se: quanto menos código, melhor.

Toda aplicação que se preze deve ter uma caixa de diálogo com pelo menos o nome do autor. Pois bem, para incluir essa caixa criei o botão button_help e associei ao sinal clicked o handler on_button_help_clicked. No Glade, criei uma caixa de diálogo chamada dialog_help com um botão Close. A esse botão, associei o seu sinal clicked ao handler on_closebutton_dialog_clicked. Criei as duas funções callback e e as adicionei ao dicionário para a auto-conexão, como segue:

   1 ...
   2 def on_button_help_clicked(*args):
   3         help_dialog.show()
   4 
   5 def on_closebutton_dialog_clicked(*args):
   6         help_dialog.hide()
   7 
   8 dic = { 'on_window1_destroy' : lambda win: gtk.main_quit(),
   9         'on_button_calculate_clicked' : on_button_calculate_clicked,
  10         'on_button_help_clicked' : on_button_help_clicked,
  11         'on_closebutton_dialog_clicked' : on_closebutton_dialog_clicked }
  12 
  13 gladefile.signal_autoconnect(dic)

ao clicar no botão Help aparecerá:

attachment:screenshot-investcalc-dialog.png

o código na integra:

   1 import gtk
   2 import gtk.glade
   3 
   4 gladefile = gtk.glade.XML('investcalc.glade')
   5 
   6 # widgets
   7 
   8 w_duration = gladefile.get_widget('spinbutton_duration')
   9 w_return_rate = gladefile.get_widget('entry_return')
  10 w_amount = gladefile.get_widget('entry_amount')
  11 w_total_investment = gladefile.get_widget('label_total_investment')
  12 w_duration_period = gladefile.get_widget('combo_duration_period')
  13 w_amount_period   = gladefile.get_widget('combo_amount_period')
  14 help_dialog = gladefile.get_widget('dialog_help')
  15 
  16 # callback functions
  17 
  18 def on_button_calculate_clicked(*args):
  19     import math
  20     amount = float(w_amount.get_text())
  21     return_rate = float(w_return_rate.get_text())
  22     duration = w_duration.get_value_as_int()
  23     duration_period = w_duration_period.entry.get_text()
  24     amount_period = w_amount_period.entry.get_text()
  25 
  26     if duration_period == 'Years':
  27         duration *= 12
  28         return_rate /= 100
  29 
  30     total = 0.0
  31     for i in range(duration):
  32         if amount_period == 'Year':
  33             if i%12 == 0:
  34                 total += (total*return_rate) + amount
  35             else:
  36                 total += total * return_rate
  37         else:
  38             total += (total*return_rate) + amount
  39 
  40     w_total_investment.set_text('$ %.2f' % total)
  41 
  42 def on_button_help_clicked(*args):
  43     help_dialog.show()
  44 
  45 def on_closebutton_dialog_clicked(*args):
  46     help_dialog.hide()
  47 
  48 dic = { 'on_window1_destroy' : lambda win: gtk.main_quit(),
  49         'on_button_calculate_clicked' : on_button_calculate_clicked,
  50         'on_button_help_clicked' : on_button_help_clicked,
  51         'on_closebutton_dialog_clicked' : on_closebutton_dialog_clicked }
  52 
  53 gladefile.signal_autoconnect(dic)
  54 
  55 gtk.main()

Todo o código produzido para esse artigo pode ser encontrado aqui [http://www.aboutwilson.net/libglade-tutorial-code.tar.gz]. Divirta-se.

Para Contribuir

Este documento, assim como muitos outros software livres, foi criado por voluntários. Se existe algum tópico a respeito do tema que este documento não contempla, por favor ajude a mantê-lo completo enviando essas informações ou escrevendo você mesmo uma seção ou trecho de alguma seção.

Atente para o fato de que esse documento é livre e qualquer conteúdo adicionado a ele também deverá ser.

Se você realmente quiser contribuir, envie um email para Wilson Freitas <wilson (a) econofisica com br>.

Obrigado.

Créditos

Agora eu vou falar sobre o que tornou esse documento possível:

  • Jonh Finlay, <finlay (a) moeraki com> pelo tutorial de PyGtk.

  • txt2tags [http://txt2tags.sf.net].

  • Lissandra pelo carinho e Pedrinho pelo bom humor.
  • E todos os que eu espero que leiam e ajudem a contribuir com esse documento.

Copyright

Libglade Tutorial (C) 2004 Wilson Freitas.

Esse documento está licenciado sob a GNU Free Documentation License, publicada pela Free Software Foundation.

É permitido distribuir cópias deste manual, desde que se forneça o Copyright em todas as cópias.

Se você pretende incluir este documento em alguma publicação, por favor contate o responsável e nós trabalharemos para garantir que todo documento esteja atualizada e de acordo com as informações disponíveis.

Referências