pythongtk

29
Python é uma linguagem interpretada, orientada a objetos, extensível e de fácil utilização. GTK+ (Gimp Tool Kit) é uma biblioteca para criar interfaces gráficas. Para utilizá-las em conjunto, foi criado o PyGTK, que consiste em uma série de módulos para prover uma interface ao Python para acesso ao GTK, possibilitando ao desenvolvedor a criação de sistemas com interfaces gráficas riquíssimas. Lição 1 - Apresentando PyGTK Embora o curso assuma um certo conhecimento de Python, não é necessário que o aluno tenha experiência com GTK+. Entretanto, isto não nos impede de oferecer uma visão geral de todos os componentes envolvidos. GTK+ O GIMP Toolkit , popularmente conhecido por GTK+, é um dos mais famosos toolkits voltados para a criação de interfaces gráficas (referidas GUIs daqui em diante). O que, então, justifica a parte GIMP do nome? Originalmente, foi idealizado para servir de base para o editor de imagens GIMP (GNU Image Manipulation Program), mas acabou expandindo muito além. O GTK+ é um dos pilares do famoso ambiente de janelas GNOME, portanto aplicações baseadas nesta biblioteca podem ser facilmente identificadas pelo visual em comum. Seus autores incluem Spencer Kimball, Peter Mattis e Josh MacDonald, todos os quais trabalhavam na Universidade de Berkeley à época de criação do GTK+/GIMP.

Upload: api-3838125

Post on 07-Jun-2015

618 views

Category:

Documents


22 download

TRANSCRIPT

Page 1: PythonGtk

Python é uma linguagem interpretada, orientada a objetos, extensível e de fácil utilização. GTK+ (Gimp Tool Kit) é uma

biblioteca para criar interfaces gráficas.

Para utilizá-las em conjunto, foi criado o PyGTK, que consiste em uma série de módulos para prover uma interface ao Python

para acesso ao GTK, possibilitando ao desenvolvedor a criação de sistemas com interfaces gráficas riquíssimas.

Lição 1 - Apresentando PyGTK

Embora o curso assuma um certo conhecimento de Python, não é necessário que o aluno tenha experiência com GTK+. Entretanto, isto não nos impede de oferecer uma visão geral de todos os componentes envolvidos.

GTK+

O GIMP Toolkit, popularmente conhecido por GTK+, é um dos mais famosos toolkits voltados para a criação de interfaces gráficas (referidas GUIs daqui em diante). O que, então, justifica a parte GIMP do nome? Originalmente, foi idealizado para servir de base para o editor de imagens GIMP (GNU Image Manipulation Program), mas acabou expandindo muito além.O GTK+ é um dos pilares do famoso ambiente de janelas GNOME, portanto aplicações baseadas nesta biblioteca podem ser facilmente identificadas pelo visual em comum.Seus autores incluem Spencer Kimball, Peter Mattis e Josh MacDonald, todos os quais trabalhavam na Universidade de Berkeley à época de criação do GTK+/GIMP.

Exemplo de aplicativo GTK+

Page 2: PythonGtk

Os menus, botões, formulários, dropdowns e outros elementos da janela (widgets) possuem um visual bastante padronizado, mesmo entre os vários temas disponíveis no GTK+ (acima representado está o tema padrão da distribuição Ubuntu).

Python

Pythonidae é uma família de répteis escamados da subordem Serpentes. O grupo inclui diversas espécies de cobras constritoras

Python é uma linguagem de programação de alto nível orientada a objetos. Seu criador, Guido van Rossum, a criou em 1991 com o objetivo de enfatizar o esforço do programador sobre o esforço computacional, tornando-se uma linguagem de fácil aprendizagem. Além do mais, Python se difere de outras linguagens no quesito simplicidade e legibilidade. Por exemplo, a indentação do código é de prima importância para que o código execute corretamente, o que conseqüentemente facilita bastante a sua compreensão e padroniza os programas com um look similar. O caractere ; não é necessário para indicar o término de um comando, servindo a quebra de linha (\n) para tal.Quanto ao aspecto de simplicidade, isto apenas se aplica ao núcleo da linguagem. Há uma vasta quantidade de bibliotecas disponíveis, inclusive distribuídas com o Python. Uma destas, aliás, é o...

PyGTK

Como o leitor mais atento deve ter percebido, o objetivo deste curso é aliar o Python e o GTK+ através da biblioteca PyGTK. Dispensando explicações sobre a origem de seu nome, partiremos para um overview geral.O PyGTK é um conjunto de wrappers (vulgarmente "envoltórios") para a biblioteca GTK+. Assim como o Python, o PyGTK é software livre, no entanto é distribuído sob a licença LGPL. É de autoria de um dos desenvolvedores do GNOME, James Henstridge, e hoje possui uma equipe maior, inclusive com grande participação da comunidade através de patches e bug reports.Um dos méritos do PyGTK é ter sido escolhido como o ambiente de escolha para aplicações rodando nos sistemas do projeto One Laptop Per Child. Além disso, algums outros projetos usam o PyGTK, tais como:

Instalador Anaconda (usado no Fedora) Bittorrent (cliente) GNOME Sudoku (ótimo passatempo)

Talvez a melhor forma de se passar a conhecer uma ferramenta previamente desconhecida seja instalando-a, e em seguida rodando um simples exemplo para testá-la e familiarizar-se com o que está por vir.É exatamente o que será feito.

Instação

O Linux é, decididamente, a plataforma ideal para este tipo de desenvolvimento e é a que nós recomendamos. Não obstante, usuários de Windows podem seguir com o curso, assim como os de Mac OS X. Apenas a etapa de instalação deverá diferir do que está apresentado no curso. (Devido à indisponibilidade de recursos, a tarefa de instalçao será deixada por conta do usuário.)

Linux

Talvez até excessivamente simples, para se instalar o PyGTK nas distribuições Debian e derivados (tais como Ubuntu), basta instalar o pacote python-gtk2:

apt-get install python-gtk2

Isto irá instalar as bibliotecas necessárias. Caso seja solicitado para instalar pacotes dependentes, aceite.Caso esteja usando outra distribuição, procure um pacote com este nome ou pygtk.OK, tecnicamente estamos prontos para seguir em frente e aprendermos os conceitos. Mas primeiro vamos verificar se a etapa de instalação realmente foi concluída com êxito.

Crie um arquivo com o seguinte código usando seu editor predileto:import pygtk

import gtk

# Teste inicial

class Janela:

def __init__ (self):

self.janela = gtk.Window(gtk.WINDOW_TOPLEVEL)

self.janela.show()

def main (self):

Page 3: PythonGtk

gtk.main()

if __name__ == "__main__":

janela = Janela()

janela.main()

Salve-o como "testando123.py" e execute-o (python testando123.py).

Enfrentou algum problema? Envie sua dúvida ao fórum! Não será possível prosseguir com o curso se este exemplo não funcionar.

Não se preocupe com o código, iremos detalhá-lo mais adiante.

Perceba que este exemplo apenas exibe uma janela vazia. Nem mesmo o botão de fechá-la funciona. Veremos os motivos mais adiante.

Lição 2 - Apresentando Widgets, Signals e Callbacks

Antes de prosseguirmos com os exemplos, iremos dar uma breve olhada nos conceitos que serão explorados. Estes são de crucial importãncia para o entendimento pleno da matéria. Usuários que não possuem experiência prévia com GTK+: leiam esta página atenciosamente.

Widgets

Um conceito extremamente simples; derivado de window gadets (apetrechos de janela), o termo pode representar qualquer elemento de uma janela, tais como botões, barras de rolagem, menus dropdown, campo de texto, barra de título, barra de ferramentas e qualquer outra parafernália que pode estar contida em uma janela. Em suma, tudo representado é um widget.

Sinais, callbacks e eventos

Sinais

O GTK+ é um chamado event-driven toolkit, significando que uma vez que se atinge o gtk.main(), o processo adormece até que um evento ocorra e o controle seja redirecionado para a função correspondente. Este conceito é denominado sinal (entretanto este conceito é meramente semelhante aos signals Unix, a implementação é bastante diferente). Quando ocorre um evento, como o apertar de um botão, o widget irá emitir um sinal. Os sinais variam de widget para widget. Por exemplo, o botão herda sinais de várias classes:

gobject.GObject: "activate", "clicked", "enter", "leave", "pressed" e "released". gtk.Object: "destroy" gtk.Widget: "accel-closures-changed", "button-press-event", "button-release-event", "can-activate-accel", "child-notify",

dentre muitas, muitas outras. gtk.Container: "add", "check-resize", "remove", "set-focus-child"

Para o exemplo do botão, a lista completa de sinais pode ser encontrada em: http://www.pygtk.org/docs/pygtk/class-gtkbutton.html#signal-prototypes-gtkbutton.

Percebe-se que a quantidade possível de sinais é imensa para um dado widget, e neste curso iremos ver apenas os signals mais freqüentemente utilizados.

Callback

Quando um sinal ocorre, uma função callback pode ser acionada. Esta função pode alterar a interface, abrir uma janela popup, fechar o programa, conectar a um banco de dados... enfim. É o elo entre os widgets e o resto do programa. Como, então, associar uma função callback a um signal? Para tal, os objetos em GTK possuem a função connect:

handler_id = object.connect (name, func, func_data)

Nesta linha, object é a instância do GtkWidget que estará emitindo o sinal, e o primeiro argumento (name) indica o nome do sinal que irá acionar o callback (por exemplo "clicked" ou "destroy"). O segundo argumento (func) indica a função (callback) que deverá ser acionada. Repare que o argumento é o *objeto* da função, e não o seu nome. No terceiro argumento, estão os dados

Page 4: PythonGtk

que serão repassados para a função.Portanto o callback deverá ter o seguinte cabeçalho:def callback_func (widget, callback_data)O primeiro parâmetro será um ponteiro para a widget que emitiu o sinal, e o segundo parâmetro um ponteiro para os dados definidos na chamada do connect.Naturalmente, se a função callback for definida em um método, sua forma geral será:def callback_func_method (self, widget, callback_data)Onde self representa a instância do object acionando o método. (Existem exceções quanto aos seus argumentos, mas de forma geral serão estes.)Juntos, estes dois conceitos (signals e callbacks) formam boa parte dos fundamentos por trás do GTK+.

Eventos

Além dos sinais, existem também eventos, que refletem o mecanismo de eventos do X. São muito similares a sinais, no sentido que callbacks também podem ser associados a estes. Refletem eventos tais como o requisitado fechamento de um programa, ou o clique do mouse (ou duplo-, triplo-clique). No entanto, o cabeçalho de suas funções callback é um pouco diferente:

def callback_func (widget, event, callback_data)

ou, no caso de uma função callback definida em um método:

def callback_func_method (self, widget, event, callback_data)

Como fazer para conectar uma função callback a um evento? Usando connect:

handler_id = object.connect (name, func)

Por exemplo, para um botão pode-se definir o evento da seguinte forma:

handler_id = botao.connect ("button_press_event", button_click_event)

No momento do evento, isto iria invocar a seguinte função:

button_click_event (widget, event, callback_data)

O valor que esta função retorna indica se o evento deverá ser propagado pelo mecanismo de gerenciamento de eventos do GTK+. Se retornar True, significa que o evento já foi tratado e não deverá propagar. (Isto é, se houver mais de um callback associado a um evento.) Se retornar False, então o evento será propagado.

Se isto ainda está um pouco nebuloso, não se preocupe. Exemplos tornarão os conceitos bem mais claros.

Desconectando callbacks

É claro, também é possível desassociar um callback de um sinal ou um evento. O comando a ser usado é o seguinte:

object.disconnect (handler_id)

Agora que temos um embasamento teórico podemos partir para exemplos.

Crie um arquivo com o nome botao1.py com o seguinte conteúdo:#!/usr/bin/env python# -*- coding: latin-1 -*-

import pygtkimport gtk

class Janela: def __init__ (self): """ Primeiro criamos uma janela do tipo WINDOW_TOPLEVEL. Isto é feito através da geração de uma instância da classe Window. """self.janela = gtk.Window (gtk.WINDOW_TOPLEVEL) """ Ajustamos alguns parâmetros referentes à janela: - Ajusta o espaçamento da borda para 10 (espaço entre borda e os widgets - Seta o título da janela - Altera o tamanho da janela"""self.janela.set_border_width (10) self.janela.set_title ("Pythonizando GTK+") self.janela.resize (300, 100) """ Associa o callback self.delete_event ao evento delete_event. Caso isso não fosse feito, o programa continuaria sua execução mesmo após o fechamento da janela """self.janela.connect ("delete_event", self.delete_event) """ Em seguida cria-se um botão (perceba que é a isntância de uma classe. """self.botao = gtk.Button ("Hey there") """ IMPORTANTÍSSIMO: (quase) todo elemento deve ser explicitamente exibido para que apareça na tela. """self.botao.show () """ Adiciona o botão á janela. Para adicionar mais elementos será necessário criar um container, como veremos adiante """self.janela.add (self.botao) """ Exibe a janela (e consequentemente tudo contido nela). """self.janela.show ()

Page 5: PythonGtk

def delete_event (self, widget, event): gtk.main_quit() return False def main (self):

gtk.main() if __name__ == "__main__":

janela = Janela() janela.main()

O resultado será algo assim:

A maior parte do código é bastante intuitiva; a criação da janela, atribuição de parãmetros, criação do botão. Há 2 elementos importantes neste exemplo: o callback e as chamadas a show().

O método show() causa uma widget a ser exibida assim que possível. Antes de se chamar este método a widget não será mostrada! Naturalmente, para que uma widget seja visualizada, é necessário também que todos os containers da widget também sejam explicitamente mostrados. Quando um container "toplevel" (que contém outros widgets) é mostrado, este é imediatamente mapeado e exibido na tela, assim como todos os widgets contidos dentro deste que já estejam exibidos. É por este motivo que a ordem de chamada deve começar das widgets internas até chegar nas widgets externas; caso contrário o usuário irá ver a atualização dos componentes dependendo da complexidade gráfica do GUI.

Outro aspecto importante do exemplo é o callback uasdo para o fechamento do programa. O evento "delete_event" é chamado quando o sistema operacional recebe uma soilcitação de fechamento do programa. Isto pode ocorrer quando o usuário clica no X para fechar, ou pelo gerenciador de tarefas. Em todo caso, se não tratarmos este evento a widget em questão (no caso a janela em si) será finalizada, no entanto o gtk.main() continuará sua execução. (Tente isto; remova o self.janela.connect e veja o que ocorre depois de fechada a janela.) Por esse motivo é necessário explicitamente executar gtk.main_quit(). O dado passado pelo delete_event (no quarto parâmetro da função callback) é None e portanto é ignorado pela função callback.

Você consegue ver a utilidade disto? Nem sempre que o usuário fecha uma janela ele necessariamente teve tal intenção. O browser Firefox confirma o fechamento da janela se mais de uma aba estiver aberta. O OpenOffice pergunta se deseja salvar o documento antes de fechar. É este evento o responsável por essas ações.

Lição 3 - Widgets elementares

Até o momento trabalhamos com apenas uma widget na janela principal. Como fazer para agrupá-las?

Há várias formas de fazê-lo; a mais comum delas são caixas (boxes).

Boxes

Boxes são contêineres transparentes nos quais podem ser empacotados outras widgets. Pode-se usar tanto o HBox (horizontal) quanto o VBox (vertical). Ao empacotar elementos no HBox eles podem ser inseridos da esquerda para a direita ou da direita para a esquerda, dependendo da forma como for criado. É comum usar uma combinação de boxes (boxes dentro de boxes) para criar o efeito desejado.

Como é de se esperar, para criar um HBox basta gerar uma instância de gtk.HBox(). Para adicionar elementos a um box, usa-se o método pack_start() e pack_end(), onde o primeiro insere da esquerda para a direita e o último da direita para a esquerda. Boxes podem agrupar tanto widgets quanto outros boxes.

Vale frisar que botões são contêineres por si só, mas normalmente usa-se apenas um label dentro do botão.

Como criá-los

O cabeçalho da HBox() está definido da seguinte forma:

gtk.HBox (homogeneous = False, spacing = 0)

se o primeiro parâmetro for True, isto garantirá que todos os elementos terão exatamente a mesma largura. O segundo parâmetro especifica o espaçamento entre os elementos.

Além disso, a forma como se agrupa os elementos também altera a aparência geral do layout:

box.pack_start(child, expand=True, fill=True, padding=0)

Page 6: PythonGtk

Box é o container onde o widget será empacotado e o parâmetro child deve ser o objeto do widget a ser empacotado. Os outros parâmetros controlam como os widgets inseridos se adaptarão ao box (ou vice-versa):

Expand define se o objeto se ajustará para ocupar todo o espaço disponível no widget ( True) ou se o box se reduzirá ao tamanho dos widgets (False). Se isto for False, é possível manter todos os widgets alinhados no canto direito ou esquerdo do box.

O parâmetro fill somente tem efeito se o expand for false; neste caso, ele controla se o espaço remanescente no box deverá ser alocado como espaçamento entre os widgets (False) ou se os widgets receberão o espaço remanescente (True).

Finalmente padding é o espaçamento que será adicionado a ambos os lados do widget.

Qual a diferença entre padding (fornecido no pack_start() ou pack_end()) e spacing (fornecido no HBox() ou VBox())? O spacing é o espaço adicionado entre os elementos, e o padding é o espaço adicionado a ambos os lados de um elemento.

Toda essa teoria também se aplica ao VBox, invertendo-se os eixos.

Exemplificando

Observe o exemplo a seguir, botao2.py:#!/usr/bin/env python# -*- coding: latin-1 -*-import pygtk import gtk classJanela: def__init__ (self): # Primeiro criamos uma janela do tipo WINDOW_TOPLEVEL. self.janela = gtk.Window (gtk.WINDOW_TOPLEVEL) # Ajustamos alguns parâmetros referentes à janela: self.janela.set_border_width (10) self.janela.set_title (u"Botões 2") self.janela.resize (300, 50) # Em seguida cria-se os botões self.botao1 = gtk.Button ("Hey there") self.botao2 = gtk.Button ("Au revoir", gtk.STOCK_QUIT) # Associa o callback self.delete_event ao evento# delete_event. self.janela.connect ("delete_event", self.delete_event) # Especificamos o argumento data como None self.botao1.connect ("clicked", self.button_click, None) self.botao2.connect ("clicked", self.delete_event, None) # Exibe-os self.botao1.show () self.botao2.show () # Cria-se um HBox homogêneo (todos elementos ocupam o mesmo# espaço) com espaçamento 10 entre os elementos. self.hbox = gtk.HBox (True, 10) # Empacota os botões no HBox self.hbox.pack_start (self.botao1) self.hbox.pack_start (self.botao2) # Adiciona o botão à janela. Para adicionar mais elementos# será necessário criar um container, como veremos adiante self.janela.add (self.hbox) # Exibe o hbox self.hbox.show () # Exibe a janela self.janela.show () defdelete_event (self, widget, event): gtk.main_quit() defbutton_click (self, widget, event): print"Hullo"defmain (self): gtk.main() if __name__ == "__main__": janela = Janela() janela.main() O resultado deve ser o seguinte:

Obviamente pode-se ir muito além disso, encapsulando vários boxes e criando um layout extremamente flexível.

Lição 4 - Widgets miscelâneas 1

Um elemento muito frequente em interfaces com usuários é o "slider", uma barra de rolagem através da qual o usuário pode escolher uma faixa de valores. Eis um exemplo:

O GTK+ obviamente suporta tais widgets, mas primeiro devemos entender um pouco sobre a teoria por trás de seu funcionamento.

Adjustments

Uma aplicação necessita reagir às ações de usuários nestes widgets; isto poderia ser feito através de um sinal emitido pela widget quando houvesse uma mudança no seu valor, no qual a widget transmite também o novo valor para a função responsável. No entanto, e se quiséssemos associar os ajustes de várias widgets, de tal forma que mudar o valor em uma widget altera outra? Uma aplicação prática para isto seria conectar as barras de rolagem de tal forma a selecionar o conteúdo a ser visualizado, por exemplo, de uma imagem. (Será isso um presságio?) Se cada widget de ajuste tivesse de emitir um sinal a cada mudança de valor, o programador teria de escrever gerenciadores de sinais para transmitir a mudança de valor de uma widget para outra.

O GTK+ nos auxilia através do objeto Adjustment, que não é uma widget, mas uma forma através da qual widgets podem transmitir informações sobre ajustes de uma forma abstrata e flexível. Além de guardarem parâmetros de configuração e valores de widgets (ou várias delas), Adjustments podem emitir sinais, assim como widgets convencionais, e isso pode ser usado para propagar informações entre várias widgets separadas.

Você já conhece bem esse tipo de widget; são barras de progresso, janelas com barra de rolagem, dentre outros.

Criando um Adjustment

Page 7: PythonGtk

A sintaxe é simples:

adjustment = gtk.Adjustment(value=0, lower=0, upper=0, step_incr=0, page_incr=0, page_size=0)

O parâmetro value indica o valor inicial a ser atribuído à widget. Lower e upper são o menor e maior valores que podem ser assumidos pelo widget. Step_incr define o intervalo entre os ajustes, page_incr define o maior intervalo entre estes. Finalmente o page_size é o parâmetro que contém a área visível no caso de uma widget à qual se aplique.Os widgets ajustáveis podem ser divididos naqueles que necessitam de uma unidade específica (por exemplo pixels) e aqueles que usam números arbitrários. Alguns widgets que usam números arbitrários incluem a barra de rolagem e a barra de progresso. Estas podem ser ajustadas pelo mouse ou pelo teclado, e a faixa de valores pode ser definida no ajuste. Por padrão, manipular tal widget apenas altera o valor de seu Adjustment.O outro grupo inclui o widget de texto e a janela com barra rolagem. Estes necessitam valores em pixels para seus Adjustments. Alguns widgets que podem ser ajustados indiretamente usando barras de rolagem, a exemplo do ScrolledWindow que veremos adiante.Adjustments podem também ser manipulados manualmente. Para alterar o valor de um objeto Adjustment, usamos

adjustment.set_value(value)

Como já dissemos, Adjustments são capazes de emitir sinais (por serem derivados da classe Object, assim como outros widgets) e por este motivo mudanças se propagam automaticamente quando por exemplo uma barra de rolagem conectada a outra widget é alterada; todas widgets ajustáveis se conectam através do sinal value_changed.Com um pouco de bagagem sobre Adjustments, podemos voltar ao tópico inicial da lição.Como se pode imaginar, todas as widgets de faixa de valores são associadas a um objeto Adjustment, através da qual estas calculam as dimensões da barra de rolagem.

Barra de rolagem

São os widgets mais simples desta categoria e é uma das quais mais lidamos. São apropriadas para mudar aspectos de outras widgets, como uma lista, uma caixa de texto ou um viewport. Para outras funcionalidades (como escolher um valor específico) é mais recomendável usar widgets de escala, que são mais amigáveis e possuem mais funcionalidades.

Criamo-as através do seguinte comando (horizontal ou vertical):

hscroll = gtk.HSrollbar (adjustment=None)vscroll = gtk.VSrollbar (adjustment=None)

O parâmetro pode ser um adjustment criado pela função mostrada na seção anterior. Na omissão deste será criado um adjustment automaticamente, que pode subseqüentemente ser obtido através de funções auxiliares.

Widget de escala

Idêntico ao exemplo dado no início da lição, widgets Scale permitem que o usuário manipule um valor em uma faixa específica. Pode ser usado, por exemplo, para alterar o zoom de uma página ou o contraste de uma imagem.

Estes widgets possuem 2 tipos: 1 para widgets horizontais outro para widgets verticais. (a versão horizontal é mais comum). Funcionam exatamente da mesma maneira. Para criá-los:

vertical_scale = gtk.VScale (adjustment=None)horizontal_scale = gtk.HScale (adjustment=None)

Vê o parâmetro adjustment? É através deste que se pode "linkar" widgets a um adjustment em comum. Caso seja omitido, o adjustment será criado pela função com todos os valores ajustados em 0.0, o que não é muito útil. Como o page_size pode afetar o valor máximo da widget (além do especificado pelo parâmetro upper), é recomendado que se ajuste o page_size em 0.0 para que o valor upper coincida com o maior valor que o usuário pode selecionar.

Opções

Algumas widgets de escala exibem o valor escolhido ao seu lado; isto pode ser feito através da seguinte função:

scale.set_draw_value (draw_value)

onde draw_value pode ser True ou False. O número de casas decimais exibido pode ser ajustado através de:

scale.set_digits (digits) (o limite é 13)

Para definir a posição relativa onde o valor será representado, usa-se:

scale.set_value_pos (pos)

onde o argumento pos pode ser qualquer um de POS_LEFT, POS_RIGHT, POS_TOP ou POS_BOTTOM.

Política de atualização

Como você já sabe, o sinal "value_changed" é emitido quando o valor é alterado. Mas quando exatamente é isso? A qualquer mudança? Apenas quando o usuário pára de mover a barra da widget? Há 3 políticas de atualização:

Page 8: PythonGtk

UPDATE_CONTINUOUS: emite o sinal value_changed continuamente, mesmo que a barra seja movida muito pouco. UPDATE_DISCONTINUOUS: emite o sinal value_changed apenas quando o valor da widget não está em movimento e o

usuário solta o botão do mouse. UPDATE_DELAYED: emite o sinal value_changed se o usuário soltar o botão do mouse, ou se a barra ficar parada por um

curto instante.

Para ajustar uma destas políticas faça o seguinte:

range.set_update_policy (policy)

Obtendo e usando um adjustment

Às vezes o adjustment não é criado explicitamente, sendo isto feito através da criação de uma widget que o necessita. Nestes casos podemos obter o adjustmente através de:

adjustment = range.get_adjustment()

Da mesma forma, para configurar uma widget para usar outro adjustment usamos:

range.set_adjustment(adjustment)

(Perceba que o set_adjustment() não surtirá efeito se o adjustment for o mesmo em uso pela widget, mesmo que seus valores tenham sido alterados. Para tal seria adequado emitir o sinal "changed" através de adjustment.emit("changed").)Labels (rótulos) estão entre os widgets mais simples, no entanto são um dos mais frequentemente usados. Simplesmente exibem texto que não é editável pelo usuário, como usado em um formulário para indicar os campos correspondentes. Para criá-los:

label = gtk.Label (str)

Onde str deve conter o valor inicial a ser assumido pelo label. Para alterar este texto posteriormente à sua criação:

label.set_text (str)

Da mesma forma pode-se obter o texto já contido em um label:

str = label.get_text ()

Além disso, estas widgets podem exibir texto ao longo de várias linhas quando a str os contém. É possível também fazer com que a widget automaticamente acrescente as quebras de linha:

label.set_line_wrap (wrap)

Onde o parâmetro wrap pode ser True ou False.Muitas outras opções são suportadas; como sempre recomenda-se referir à documentação original: http://www.pygtk.org/docs/pygtk/class-gtklabel.html.

Ainda no tópico de adjustments e widgets com faixa de valores, um dos widgets mais úteis que utilizam esse recurso é o ScrolledWindow. Consiste em uma área (um viewport) sendo que apenas uma parte pode ser exibida. Barras de rolagem nas laterais permitem que o usuário selecione a área a ser visualizada. (Podem ser omitidas caso a área possa ser exibida na sua íntegra, tanto no eixo horizontal quanto no eixo vertical.) O melhor de tudo é o fato de tudo isto ser uma única widget! Veja só:

scrolled_window = gtk.ScrolledWindow(hadjustment=None, vadjustment=None)

Os dois parâmetros são os adjustments a serem usados no sentido horizontal e vertical da widget. Usualmente não é necessário especificá-los.Para definir quando as barras de rolagem deverão ser exibidas, usa-se

scrolled_window.set_policy(hscrollbar_policy, vscrollbar_policy)

Onde cada um desses valores pode ser POLICY_AUTOMATIC (determina automaticamente se a barra de rolagem deverá estar disponível) ou POLICY_ALWAYS (sempre estará disponível).Para adicionar o widget que se deseja exibir ao ScrolledWindow:

scrolled_window.add_with_viewport(child)

Temos a seguir um simples exemplo (misc1.py) que usam os conceitos aprendidos nesta lição. O exemplo não é particularmente útil, mas ilustra bem os conceitos aprendidos.

#!/usr/bin/env python# -*- coding: latin-1 -*-import pygtk import gtk

classJanela:

def__init__ (self):

Page 9: PythonGtk

# Primeiro criamos uma janela do tipo WINDOW_TOPLEVEL. self.janela = gtk.Window (gtk.WINDOW_TOPLEVEL)

# Ajustamos alguns parâmetros referentes à janela: self.janela.set_border_width (10)

self.janela.set_title (u"Widgets miscelâneos")

self.janela.resize (200, 400)

# Assegura-se que a janela será fechada corretamente.# (Lembre-se de funções lambda em Python!) self.janela.connect("destroy", lambda wid: gtk.main_quit())

self.janela.connect("delete_event", lambda a1,a2:gtk.main_quit())

# Conteiner de todos os elementos vbox = gtk.VBox (False, 2)

# Widgets de faixa de valores hadjustment = gtk.Adjustment()

vadjustment = gtk.Adjustment()

# Cria os widgets de escala usando o recém-criado adjustment hscale = gtk.HScale(hadjustment)

vscale = gtk.HScale(vadjustment)

hscale.show()

vscale.show()

# Widgets label hlabel = gtk.Label("Ajuste horizontal:")

vlabel = gtk.Label("Ajuste vertical:")

hlabel.show()

vlabel.show()

# Imagem que sera encapsulada image = gtk.Image()

image.set_from_file ("fall_wallpaper_10.jpg")

image.show()

# Conteiner da imagem scrolled_window = gtk.ScrolledWindow(hadjustment, vadjustment)

scrolled_window.set_policy (gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)

scrolled_window.add_with_viewport(image)

scrolled_window.show()

# Acrescenta todos os elementos ao VBox vbox.pack_start (hlabel, False, False, 2)

vbox.pack_start (hscale, False, False, 2)

vbox.pack_start (vlabel, False, False, 2)

vbox.pack_start (vscale, False, False, 2)

vbox.pack_start (scrolled_window, True, True, 2)

# E adiciona o HBox com os conteúdos do botão self.janela.add (vbox)

# Exibe-os vbox.show()

# Exibe a janela self.janela.show ()

defmain (self):

gtk.main()

if __name__ == "__main__":

Page 10: PythonGtk

janela = Janela()

janela.main(Screenshot:

Até a próxima lição!

Lição 5 - Widgets miscelâneas 2

Iremos nesta lição continuar a exploração pelas widgets do GTK+. Todas estas são vistas com bastante freqüência.

Barras de progresso

Todo usuário as conhece; são usadas para representar o processo de uma operação. Como tudo no GTK+, são fáceis de se criar e manipular:

progressbar = gtk.ProgressBar(adjustment=None)

O parâmetro adjustment (opcional) pode receber um adjustment criado previamente. Reveja a lição anterior se necessário.

Para alterar o valor exibido pela barra de status, usa-se

progressbar.set_fraction(fraction)

onde fraction é um número entre 0 e 1, representando a percentagem da barra que deverá estar preenchida.

É também possível que o preenchimento ocorra da direita para esquerda (ou outros), em vez de ser da esquerda para a direita:

progressgar.set_orientation(orientation)

orientation pode assumir um dos seguintes valores: PROGRESS_LEFT_TO_RIGHT: da esquerda para a direita PROGRESS_RIGHT_TO_LEFT: da direita para a esquerda PROGRESS_BOTTOM_TO_TOP: de baixo para cima PROGRESS_TOP_TO_BOTTOM: de cima para baixo

Além de poder exibir uma fração, a barra pode também indicar que um processo está ocorrendo sem percisar o quanto da operação foi concluído. Para isso, usa-se pulse:

progressbar.pulse()

Page 11: PythonGtk

Isto exibe uma barra oscilante. E para configurar o tamanho desta barra, usa-se:

progressbar.set_pulse_step (fraction)

Onde fraction novamente é um valor entre 0 e 1.

Para configurar e obter um texto a ser exibido na barra, temos os seguintes comandos:

progressbar.set_text (text)text = progressbar.get_text ()

O widget Dialog é também conhecido como pop-up por alguns. Consiste em uma janela com alguns elementos empacotados (um vbox (chamado vbox) para conteúdo e um hbox (chamado action_area) para botões e ações que o usuário possa realizar). Sua criação é bastante simples:

dialog = gtk.Dialog(title=None, parent=None, flags=0, buttons=None)

Onde title é o título do Dialog, parent é a janela principal da aplicação e flags setam as várias opções de criação, dentre elas:

DIALOG_MODAL - faz com que o usuário não possa manipular a janela principal, apenas a que foi criada DIALOG_DESTROY_WITH_PARENT - força que o Dialog seja destruído quando sua janela principal for fechada DIALOG_NO_SEPARATOR - omite o separador enter vbox e action_area

Para adicionar elementos ao Dialog basta empacotar usando pack_start() ou pack_end() no vbox ou action_area do Dialog. Por exemplo:

dialog.action_area.pack_start(button, True, True, 0)dialog.vbox.pack_start(label, True, True, 0)

Podem ser manipulados como se fossem janelas (isto é, callbacks para "destroy" funcionarão normalmente, por exemplo).São widgets relativamente simples que exibem uma mensagem por vez. Servem para auxiliar o usuário na interação com um programa; por exemplo, quando sobrepor o mouse sobre um ícone a barra de status pode comunicar a funcionalidade do botão. Ou então informar sobre o progresso de um processo.

Para que permita que várias partes da aplicação utilizem a mesma barra de status, tem-se o que se chama de identificadores de contexto, que identificam os diferentes "usuários". Ela também tem uma pilha que assegura que a mensagem a ser exibida sempre será a que está no topo da pilha. (Ou seja, seu funcionamento é LIFO, last in first out).

Para criar uma barra de status:

statusbar = gtk.Statusbar()

Para requisitar-se um identificador de contexto, usa-se a seguinte função, que recebe também uma breve descrição do contexto:

context_id = statusbar.get_context_id (description)

Para inserir uma mensagem, deve-se especificar o id do contexto bem como o texto a ser exibido:

message_id = statusbar.push (context_id, message)

A remoção da última mensagem adicionada naquele contexto é feita assim:

statusbar.pop (context_id)

E a remoção de uma mensagem específica:

statusbar.remove (context_id, message_id)

Lembre-se que a mensagem a ser exibida sempre será a última que foi adicionada (através de push). Como se pode perceber, cada parte do programa pode interagir com a barra de status de forma independente, já que terá seu próprio context_id.

Como de praxe, terminaremos esta lição de widgets com um exemplo sobre o que foi aprendido.

Por motivos didáticos foi usado um timeout, tópico que não será visto neste curso, para a atualização automatizada da barra de progresso. No entanto basta saber que timer = gobject.timeout_add(interval, function, ...) irá executar a cada interval milisegundos a função function. Para que se cancele a execução de um timeout, usa-se gobject.source_remove (timer).

Ao código, misc2.py:

#!/usr/bin/env python# -*- coding: latin-1 -*-import pygtk import gtk import gobject

defprogress_timeout (pbobj):

Page 12: PythonGtk

# Cria valor (valor atual + 1%) valor = pbobj.progressbar.get_fraction() + 0.01

if valor > 1.0:

# Atingimos 100%, fechar popup pbobj.dialog.destroy()

# Ao retornar "False" garantimos que progress_timeout cessará de ser chamadoreturn False

# Atualiza valor da barra de progresso pbobj.progressbar.set_fraction(valor)

# Returna True para que o callback continue sendo executado.return True

classJanela:

defdestroy_popup (self, widget, context_id):

# Fechamento do popup self.statusbar.pop(context_id)

gobject.source_remove (self.timer)

defbotao_popup (self, widget, dialog):

# Aborta operação do popup dialog.destroy()

defpopup (self, data):

# Criação da janela pop-up (perceba a referência à janela que a criou) self.dialog = gtk.Dialog ("Processando...", self.janela, gtk.DIALOG_MODAL)

# Adiciona-se um botão ao "action_area" (area inferior do pop-up) button = gtk.Button ("Abortar!")

button.connect ("clicked", self.botao_popup, self.dialog)

button.show()

self.dialog.action_area.pack_start (button, True, True, 5)

# Adiciona-se uma barra de progresso ao "vbox" (area superior do pop-up) self.progressbar = gtk.ProgressBar ()

self.progressbar.set_text ("Por favor aguarde...")

self.progressbar.show()

self.dialog.vbox.pack_start (self.progressbar, True, True, 5)

# Cria um timeout que atuailza periodicamente o status da barra de progresso self.timer = gobject.timeout_add (100, progress_timeout, self)

# Atualiza a mensagem da barra de status na janela principal para indicar atividade context_id = self.statusbar.get_context_id ("popup")

message_id = self.statusbar.push (context_id, "Processando...")

# Associa o fechamento da janela a dsetroy_popup# (para remover a mensagem da barra de status) self.dialog.connect ("destroy", self.destroy_popup, context_id)

Page 13: PythonGtk

# Exibe o pop-up self.dialog.show()

def__init__ (self):

# Primeiro criamos uma janela do tipo WINDOW_TOPLEVEL. self.janela = gtk.Window (gtk.WINDOW_TOPLEVEL)

# Ajustamos alguns parâmetros referentes à janela: self.janela.set_border_width (10)

self.janela.set_title (u"Widgets miscelâneos")

self.janela.resize (200, 80)

# Assegura-se que a janela será fechada corretamente.# (Lembre-se de funções lambda em Python!) self.janela.connect("destroy", lambda wid: gtk.main_quit())

self.janela.connect("delete_event", lambda a1,a2:gtk.main_quit())

# Conteiner de todos os elementos vbox = gtk.VBox (False, 2)

# Cria o botão que iniciará a ação button = gtk.Button (u"Faça algo!")

button.connect ("clicked", self.popup)

button.show()

# Cria a barra de status e adiciona uma mensagem inicial self.statusbar = gtk.Statusbar()

context_id = self.statusbar.get_context_id ("main")

message_id = self.statusbar.push (context_id, "Em standby.")

self.statusbar.show()

# Insere os elementos recém-criados no vbox vbox.pack_start (button, True, True, 2)

vbox.pack_end (self.statusbar, False, False, 0)

# E adiciona o HBox com os conteúdos do botão self.janela.add (vbox)

# Exibe-os vbox.show()

# Exibe a janela self.janela.show ()

defmain (self):

gtk.main()

if __name__ == "__main__":

janela = Janela()

janela.main()

Page 14: PythonGtk

Screenshot do programa em execução:

Lição 6 - Widgets miscelâneas 3

Em GTK+ existem várias formas possíveis de se criar menus. Neste curso iremos ver a forma mais manual, por motivos didáticos. Os outros métodos abstraem parte do funcionamento o que acaba sendo prejudicial para o aprendizado.

São 3 os widgets usados para se fazer uma barra de menus e submenus:

o item de menu, que o usuário clica para realizar a ação solicitada (por exemplo "Fechar") o menu, que atua como contêiner para os itens de menu, e a barra de menus, que serve de contêiner para cada um dos menus.

Uma abstração do termo item de menu deve ser feita, já que seu widget pode ser usado tanto para criar os itens de menu que efetivamente realizam a ação solicitada pelo usuário (por exemplo "Novo", "Recortar", "Colar"), quanto para os elementos na barra de menus (por exemplo "Arquivo", "Editar").

Começando pelo topo, criaremos a barra de menus:

menu_bar = gtk.MenuBar()

Dispensando maiores explicações, esta função cria a barra de menus, que poderá subseqüentemente ser adicionada a outro contêiner.

menu = gtk.Menu()

Ests função retorna uma referência para um novo menu. Não é necessário chamar a função show(), pois serve apenas de contêiner para os itens de menu.

menu_item = gtk.MenuItem(label=None)

Finalmente, o item de menu. Este recebe um rótulo (label), que será interpretado por caracteres mnemônicos precedidos de "_". É necessário que se chame a função show() pra que seja exibido. Não confunda item de menu com menu! O primeiro é o botão associado a uma ação, e o segundo é um contêiner para itens de menu.

A adição de um item de menu ao menu é feita através do comando append:

menu.append(menu_item)

E, finalmente, a associação de ações aos itens de menu é feita de forma convencional:

menu_item.connect_object ("activate", menuitem_response, "menu_action")

Page 15: PythonGtk

OK, temos o menu pronto. Precisa-se de uma barra de menus:

menu_bar = gtk.MenuBar()widget.add (menu_bar)menu_bar.show()

No entanto, lembre-se que o menu em si não pode ser exibido; a exibição do mesmo será feita através de um item de menu, que será então adicionado à barra de menus:

menu_item_arquivo = gtk.MenuItem("Arquivo")menu_item_arquivo.show()menu_item_arquivo.set_submenu(menu)

Caso se queira que o item fique alinhado à direita, podemos usar o seguinte comando antes de adicioná-lo à barra de menus:

menu_item.set_right_justified (right_justified)

Finalmente, o adicionamos à barra de menus, finalizando a construção:

menu_bar.append (menu_item_arquivo)

Afinal, não foi tão difícil quanto possa parecer! Mas agora que você já entende os elementos de construção do menu, caso deseje automatizar este procedimento, é possívei tanto escrever funções auxiliares ou usar as formas embutidas no GTK+: ItemFactory ou UIManager. Desde PyGTK 2.4, o uso do ItemFactory está depreciado, portanto deve-se usar o UIManager, que constrói o menu a partir de uma estrutura XML

Um elemento crucial de interfaces deixado de fora até o momento é o campo onde o usuário pode entrar com conteúdo livre (texto). Em GTK+ um tal widget é o Entry. Este permite criar um campo simples, de apenas uma linha.

A sintaxe de criação é a seguinte:

entry = gtk.Entry (max=0)

onde max é o limite de caracteres permitido, e o valor 0 indica a ausência da limitação. Sua alteração é possível após a criação:

entry.set_max_length (max)

Da mesma forma é possível alterar o conteúdo (texto):

entry.set_text (text)

(Como a classe Entry é derivada da classe Editable, aquela suporta algumas funções tais como insert_text; a lista completa pode ser visualizada em http://www.pygtk.org/docs/pygtk/class-gtkeditable.html).

entry.set_editable(editable)entry.set_visibility(visible)

Estes comandos permitem, respectivamente, habilitar e desabiiltar a edição de seu conteúdo por parte do usuário (onde editable pode ser True ou False), e permitir ou não a visualização do conteúdo sendo digitado. Isto pode ser útil em campos de senhas.

Este tipo de botão pode ser usado pelo usuário para se escolher entre uma faixa de valores numéricos. Seu funcionamento rege em torno dos Adjustments; reveja a lição caso seja necessário.

A criação do spin button é feita assim:

spin_button = gtk.SpinButton(adjustment=None, climb_rate=0.0, digits=0)

O adjustment será automaticamente criado caso seja omitido, e é o que controla a faixa de valores do widget. O climb rate indica a quantidade de aceleração que o botão tem ao ser pressionado (variando entre 0.0 e 1.0), e digits é a quantidade de casas decimais que deve ser exibida.

É possível também alterar esses valores:

spin_button.configure (adjustment, climb_rate, digits)

que obedece à mesma sintaxe descrita acima.

Page 16: PythonGtk

Da mesma forma é possível alterar apenas outros parâmetros:

spin_button.set_adjustment (adjustment)spin_button.set_digits (digits)spin_button.set_value (value)

Ou obter o valor do spin button:

float_value = spin_button.get_value()int_value = spin_button.get_value_as_int()

A alteração do valor também pode ser feita de forma relativa através da função spin:

spin_button.spin (direction, increment)

onde direction pode assumir um dos seguintes valores:

SPIN_STEP_FORWARD - Aumenta o valor do spin button de acordo com o valor increment ou page_increment (do Adjustment) caso increment seja igual a 0.

SPIN_STEP_BACKWARD - Diminui o valor do spin button de acordo com o valor increment ou page_increment (do Adjustment) caso increment seja igual a 0.

SPIN_PAGE_FORWARD - Aumenta o valor do spin button de acordo com o valor increment. SPIN_PAGE_BACKWARD - Diminui o valor do spin button de acordo com o valor increment. SPIN_HOME - Ajusta o valor para o mínimo do Adjustment. SPIN_END - Ajusta o valor para o máximo do Adjustment. SPIN_USER_DEFINED - Altera o valor pela quantidade especificada.

Algumas funções podem controlar a aparência e o comportamento do botão de forma mais detalhada:

spin_button.set_numeric (numeric)

Se numeric for True, isto garantirá que o usuário pode digitar apenas números no spin button; caso contrário outros caracteres poderão ser digitados.

spin_button.set_wrap (wrap)

Já este comando faz com que, caso wrap seja True, o botão retorne ao valor mínimo quando o usuário atingir o valor máximo e vice-versa.

spin_button.set_snap_to_ticks (snap_to_ticks)

Esta função faz com que valores "quebrados" sejam arredondados para seus equivalentes mais próximos explicitados pelo Adjustment.

spin_button.set_update_policy (policy)

onde policy pode ser UPDATE_ALWAYS ou UPDATE_IF_VALID, onde o primeiro atualiza o valor mesmo que este seja inválido (após uma alteração), e o segundo ignora erros enquanto convertendo para valor numérico.

Este é um dos widgets mais visto nos programas, por isso seria extremamente entediante se cada desenvolvedor tivesse de programar tal funcionalidade, sem contar que provavelmente seriam inconsistentes.

Page 17: PythonGtk

Com isso em mente, os desenvolvedores do GTK+ incluiram isso como uma widget fácil de se usar. Para criá-la:

filesel = gtk.FileSelection(title=None)

Para alterar o endereço sendo exibido, temos:

filesel.set_filename (filename)

O que irá atualizar a janela de maneira correspondente.

Para obter o arquivo selecionado:

filename = filesel.get_filename()

Como são várias as widgets que compõem o file selection dialog, é possível manipular essas widgets "internas" normalmente. Elas seguem o seguinte padrão de nomes:

filesel.dir_listfilesel.file_listfilesel.selection_entryfilesel.selection_textfilesel.main_vboxfilesel.ok_buttonfilesel.cancel_buttonfilesel.help_buttonfilesel.history_pulldownfilesel.history_menufilesel.fileop_dialogfilesel.fileop_entryfilesel.fileop_filefilesel.fileop_c_dirfilesel.fileop_del_filefilesel.fileop_ren_filefilesel.button_areafilesel.action_area

Em particular, é útil manipular os sinais do ok_button, cancel_button e help_button para atribuir funcionalides aos mesmos.

Ficará como exercício ao leitor experimentar com os widgets apresentados nesta lição, já que o funcionamento destes é praticamente idêntico aos apresentados anteriormente.

Page 18: PythonGtk

Lição 7 - Controle avançado de layout

O widget de alinhamento (Alignment) permite que se adicione um widget em uma posição e tamanho relativo ao widget de alinhamento. Por exemplo, pode ser usado para se centralizar um botão na tela.

Existem apenas dois comandos a serem aprendidos:

alignment = gtk.Alignment (xalign=0.0, yalign=0.0, xscale=0.0, yscale=0.0)align.set (xalign, yalign, xscale, yscale)

Como é de se esperar, o gtk.Alignment cria uma widget de alinhamento com os parâmetros especificados. Os valores são em ponto-flutuante, e devem variar entre 0.0 e 1.0. O xalign e yalign ajustam a posição do widget no Alignment. Estas propriedades especificam a fração de espaço vazio que deve ser adicionado ao topo (yalign) ou à esquerda (xalign) do widget inserido.

Os outros dois parâmetros (xscale e yscale) definem a quantidade de espaço vazio que deve ser absorvido pela widget: 0.0 não absorve nenhum espaço, e 1.0 absorve todo o espaço vazio disponível. Naturalmente se ambos xscale e yscale forem iguais a 1.0, então o uso do widget de alinhamento perde seu sentido, já que todo espaço vazio será absorvido.

Para adicionar um (e apenas um) widget ao alinhamento, usa-se alignment.add(widget).

Screenshot para transmitir a idéia:

O contêiner fixo permite que se posicione widgets em uma posição, ..., fixa. Esta posição é relativa à ponta superior esquerda. Além disso, a posição dos widgets pode ser alterada dinamicamente.

Novamente são poucos os comandos que precisam ser aprendidos:

fixed = gtk.Fixed()fixed.put (widget, x, y)fixed.move (widget, x, y)

O primeiro comando cria o widget (fixed) ao qual poderão ser adicionados outros (widget). O segundo adiciona a widget desejada (widget) ao widget fixo (fixed) na posição x e y. O terceiro meramente muda a widget de posição.

É possível adicionar vários widgets a um layout do tipo fixo.

Veja o seguinte exemplo:

Page 19: PythonGtk

Estes devem ser usados com cautela, já que algumas configurações locais (como tamanho de fontes) serão ignorados, como a posição é absoluta.O contêiner Layout é bastante similar ao último visto (Fixed), com a exceção de que ele implementa uma área infinita (ou melhor dizendo, até 2^32 pixels, devido a uma restrição do X). Também se assemelha a um layout Fixed adicionado a uma ScrolledWindow, mas sem as barras de rolagem. É basicamente a mesma idéia implementada de forma um pouco diferente.

A criação é feita usando

layout = gtk.Layout (hadjustment=None, vadjustment=None)

Como se pode perceber, é possível especificar objetos do tipo Adjustment que o widget Layout irá usar para barra de rolagem. Omiti-los significa que novos adjustments serão criados.

Para respectivamente adicionar e mover widgets, usa-se

layout.put(widget, x, y)layout.put(widget, x, y)

O tamanho do contêiner Layout pode ser ajustado e obtido usando estes 2 comandos:

layout.set_size (width, height)size = layout.get_size()

Finalmente, para obter e manipular os Adjustments

hadj = layout.get_hadjustment()vadj = layout.get_vadjustment()layout.set_hadjustment(adjustment)layout.set_vadjustment(adjustment)

Perceba que este contêiner não inclui a barra de rolagem, sendo necessário adicioná-las manualmente.

Veja o seguinte exemplo:

Page 20: PythonGtk

Frames normalmente são usados para agrupar widgets de forma clara e objetiva. Ao contrário dos últimos widgets de agrupamento, este agrupa de forma visível ao usuário. Pode ser usado, por exemplo, para separar seções em um grande formulário, ou para separar opções de partes distintas de um programa.

Criá-los é simples:

frame = gtk.Frame (label=None)

O label é a descrição (visível) do agrupamento, que por padrão estará no canto superior esquerdo. Pode também ser omitido. É possível também alterá-lo, através do comando

frame.set_label(label)

Para mudar a posição do label, usa-se a função

frame.set_label_align (xalign, yalign)

onde o parâmetro xalign pode variar entre 0.0 (esquerda) e 1.0 (direita). Atualmente o yalign não é usado.

A adição de uma widget ao frame é feita usando-se frame.add (widget).

Exemplo:

Muitas vezes, por exemplo em um programa de e-mail, se deseja dividir uma área em 2 partes tal que o usuário possa controlar o tamanho de cada uma dessas áreas. O GTK+ permite que se faça isso diretamente através de 2 funções:

hpane = gtk.HPaned()vpane = gtk.VPaned()

A HPane() criará uma divisória horizontal (ou seja, uma área superior e outra inferior), o VPane() fará o seu equivalente vertical.

Para adicionar um elemento a cada uma das áreas, temos os seguintes comandos:

paned.add1(child)paned.add2(child)

O add1 adicionará o widget desejado ao painel superior (ou esquerdo), enquanto o add2 o adicionará ao painel inferior (ou direito).

Page 21: PythonGtk

Lição 8 - Progredindo com PyGTK

O que foi visto neste curso serve apenas de base para que o programador possa se aventurar nas aplicações mais avançadas do Python/GTK+. Com algumas centenas de funções, boa parte do conteúdo não foi visto porque o curso se tornaria repetitivo, mas a base para seu entendimento pleno está presente no curso.

Algumas fontes recomendadas para o contínuo aprendizado:

http://www.pygtk.org/http://www.pygtk.org/tutorial.htmlhttp://www.pygtk.org/reference.html

Com isto encerra-se a matéria do curso propriamente dito.

Vale frisar que o curso foi bastante teórico, e permite ao programador entender todos os conceitos e conhecer boa parte dos widgets e seu funcionamento. No entanto existem métodos mais fáceis de se realizar a tarefa de diagramação de interfaces usando-se ferramentas tais como Glade. Por não ser matéria do curso em si, vemos isso apenas de forma superficial.

Matéria extra: Criando interfaces gráficas usando PyGTK e Glade

O primeiro passo é a instalação. No Debian e derivados, os pacotes necessários são python-glade2 (contém as bibliotecas para o Python) e glade-2 (contém o construtor de interfaces). Portanto como usuário root execute o seguinte comando:

apt-get install python-glade2 glade-2

Em seguida execute o glade-2, para se deparar com o seguinte:

Page 22: PythonGtk

Inicialmente o Glade abre com um projeto novo e vazio. Repare que o projeto não contém janelas ainda. Clique no ícone da janela na paleta (o primeiro ícone) que isto automaticamente cria uma janela vazia. Em seguida, é possível alterar as propriedades do elemento recém-criado, no caso a janela. Em "Propriedades" altere Name para JanelaPrincipal e Title para o que quiser.

Para adicionar elementos na janela, eles são "empacotados". Tendo a noção de como funciona no nível do código fonte (ou seja, tendo feito o curso) já dá ao programador todo o conhecimento que ele precisa para criar interfaces com facilidade.

Vamos criar uma interface muito simples, com apenas 2 widgets: um label e um botão. Portanto siga os seguintes passos:1. Clique no botão VBox (Vertical Box) na palheta. 2. Em seguida clique na janela para criar o VBox. Irá parecer uma janela solicitando o número de células; reduza o valor

para 2. 3. Clique no botão Label na palheta. 4. Clique na primeira célula do VBox recém-criado para adicioná-lo lá.

Viu? Temos um VBox com um elemento empacotado. Podemos alterar o conteúdo deste rótulo para algo mais apropriado. Como a janela Properties sempre exibe as propriedades da widget selecionada, mude o campo Label para "Clique abaixo!" Da mesma forma é possível alterar muitas outras características que foram vistas ao longo do curso. Lembre-se de experimentar os ajustes disponíveis também nas outras abas, tais como Packing.

Page 23: PythonGtk

Falta agora somente o botão; clique no seu respectivo botão na palheta e clique na segunda célula do VBox para adicioná-lo. Em seguida altere o seu nome (Name) para botao1 e o rótulo (Label) para "Clique em mim."

Ao término deste processo devemos ter algo assim:

Clique em Project --> Save para salvar o projeto em uma pasta própria. O resultado disso serão 2 arquivos XML que descrevem a interface. Abra-os em um editor de texto para ter uma noção de como a interface é descrita.

Ou seja, o Glade não gera código em si, e sim uma descrição. O python-glade2 foi instalado por esse motivo: permitir com que o código faça uso de arquivos .glade.

OK, para ver a interface em funcionamento, crie um arquivo com o seguinte conteúdo:

import sysimport pygtkimport gtkimport gtk.glade

class GladeTest:def __init__ (self):

Page 24: PythonGtk

self.wTree = gtk.glade.XML("project2.glade")self.window = self.wTree.get_widget("window1")gtk.main()

if __name__ == "__main__":glade_test = GladeTest()

Execute-o e verifique seu funcionamento.

Perceba que a biblioteca gtk.glade está sendo usada; esta foi instalada no início da lição. A primeira chamada dentro do __init__ carrega a interface contida no arquivo XML (vide documentação aqui):

Este objeto respresenta uma 'instanciação' da interface descrita em XML. Quando um desses objetos é criado, o arquivo XML é lido, e a interface é criada. O objeto gtk.glade.XML provê uma interface para acessar as widgets através dos nomes associados a estas na descrição XML.

A segunda carrega o widget especificado que, no caso, é uma janela. Finalmente gtk.main() inicia a execução.

Mas e se quiséssemos usar sinais e eventos assim como fizemos até o momento? Vamos lá...

O primeiro passo é conectar o evento "destroy" da janela à função gtk.main_quit() como fizemos em todos os programas até o momento. Volte ao Glade e selecione a janela principal no projeto. Vá para a janela de propriedades e escolha a aba Signals. Em "Signal" escolha "destroy" (disponível em GtkObject).

E clique em "Add" para adicioná-lo:

Page 25: PythonGtk

Com isso criamos um handler que funciona de forma semelhante a um sinal.

Faremos o mesmo para o botão; clique no botão "Clique em mim", e na janela Propriedades (aba Signals) escolha o sinal "clicked". Em seguida clique em add.

Logo temos 2 handlers a gerenciar: on_window1_destroy e on_botao1_clicked. Esses nomes podem ser alterados, mas é recomendável que sigam um padrão.

Como, agora, fazer uso desses handlers? A biblioteca do glade para Python permite que isso seja de forma fácil, atribuindo

Page 26: PythonGtk

vários handlers a funções diferentes. Veja o código abaixo:

import sysimport pygtkimport gtkimport gtk.glade

class GladeTest:def botao_clicked (self, widget):print "botao clicado"

def __init__ (self):self.wTree = gtk.glade.XML("project2.glade")self.window = self.wTree.get_widget("window1")dic = { "on_window1_destroy" : gtk.main_quit,"on_botao1_clicked" : self.botao_clicked }

self.wTree.signal_autoconnect (dic)gtk.main()

if __name__ == "__main__":glade_test = GladeTest()

Salve este código em um arquivo na mesma pasta do projeto Glade e execute-o. O resultado é simples:

E como já deve ter sido possível perceber, o dicionário dic associa cada handler a uma função. A função signal_autoconnect faz a associação automaticamente e de uma só vez.

Falso.

Verdadeiro.

Infelizmente o curso não irá se aprofundar mais do que isso em Glade, ficando isso a cargo do leitor. Informações sobre Glade podem ser encontradas na Web; a página do próprio Glade é um bom ponto de partida: http://glade.gnome.org/

E com isso concluímos o curso. Boa parte da matéria foi vista, mas recomenda-se ao aluno que nunca pare de aprender novas tecnologias e aprofundar-se nas existentes; busque cursos, sites, leia, mas não fique parado!

Espero que tenham gostado!