Escolar Documentos
Profissional Documentos
Cultura Documentos
PensandoTkinter PDF
PensandoTkinter PDF
ndice
2
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
Tenho tentado aprender sozinho Tkinter por vrios livros, e tenho encontrado mais
dificuldade do que poderia pensar.
O problema que os autores desses livros j comeam dizendo tudo sobre os widgets
disponveis em Tkinter, mas nunca do uma parada para explicar os conceitos bsicos. Eles
no explicam como pensar em Tkinter. Ento achei que deveria tentar escrever um livro
do tipo que eu mesmo estou procurando. Ou ao menos rascunhar esse tipo de livro.
Acima de tudo, quero enfatizar que Practical Programming in Tcl and Tk de Brend
Welch absolutamente essencial para trabalhar com Tk e Tkinter. Arranje esse livro!
Note que voc no deve rodar estes programar usando o IDLE. O IDLE por si s uma
aplicao de Tkinter; possui seu prprio mainloop que entrar em conflito com o
mainloop includo nestes programas. Se voc insiste em ver e rodar estes programas usando
IDLE, ento para cada programa dever apagar a linha correspondente ao mainloop
antes de rod-lo.
Este material tem sido substancialmente melhorado pelo feedback dos entusiastas e
comunidade pythoniana. Meu grande Obrigado! a Alan Colburn, Jeff Epler, Greg Ewing,
Tom Good, Steve Holden, Joseph Knapka, Gerrit Mller, Russel Owen, e Chad Netzer.
Obrigado a Terry Carroll por recolher e organizar esse material.
Finalmente, se voc novo na programao orientada a eventos (que Tkinter requer), deve
dar uma olhada neste assunto antes.
Os Programas
Voc deve baixar o arquivo zip que contm todos os programas. Pode ser atravs do
seguinte endereo:
http://geocities.yahoo.com.br/grupopython/pensando_em_tkinter.zip
3
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
TT000 Introduo.
Eis alguns programas que comearo a explicar como pensar em Tkinter. Neles, como j
disse, no atento para todos os tipos de widgets, atributos e mtodos disponveis em
Tkinter, tampouco tento dar uma introduo detalhada sobre Tkinter, somente tento iniciar
voc no caminho para entender os seus conceitos bsicos.
Quando voc desenvolve uma interface com o usurio (IU) h um conjunto padro de
questes que voc deve satisfazer.
1) Voc deve especificar como voc quer que a IU se parea. Isto , voc precisa
escrever um cdigo que determina o que o usurio ver na tela do computador;
2) Voc deve decidir o que quer que a IU faa. Isto , voc deve escrever
procedimentos que satisfaam as necessidades do programa;
3) Voc precisa associar o parecer com o fazer. Isto , voc deve escrever um cdigo
que associa as coisas que o usurio v na tela com os procedimentos que voc
escreveu para desempenhar os papis requeridos pelo programa;
4) Finalmente, voc deve escrever um cdigo que senta e espera pela entrada do
usurio.
A programao GUI (Graphic User Interface Interface Grfica com o Usurio) tem alguns
jarges especiais associados s suas questes bsicas.
4
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
4) O cdigo que senta e espera pela entrada de dados chamada de event loop.
Se voc tem visto filmes, sabe que toda cidadezinha tem uma vov que perde todo seu
tempo debruada na janela, s olhando. Ela v tudo que acontece na vizinhana. Uma parte
disso no tem graa, claro s pessoas subindo e descendo a rua. Mas a outra
interessante como uma bela briga entre os pombinhos recm-casados do outro lado da
rua. Quando algo interessante acontece, essa vovozinha co-de-guarda imediatamente corre
para o telefone contar tudo polcia da vizinhana.
O Event Loop algo como essa vovozinha: gasta todo seu tempo esperando que eventos
aconteam. Muitos dos eventos no tm importncia (como clicar num ponto neutro da
janela), e quando os v, no faz nada. Mas quando v alguma coisa interessante um
evento que ele sabe, por meio de um binding de event handler, que interessante (como um
clique num dos botes da janela) ento imediatamente contata os event handler e faz com
que saibam que o evento aconteceu.
Comportamento do Programa
def handle_B():
print "Absolutely right! Trillium is a kind of flower!"
def handle_C():
print "Wrong! Try again!"
5
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
print "Press the letter of your answer, then the ENTER key."
print
print " A. Animal"
print " B. Vegetable"
print " C. Mineral"
print
print " X. Exit from this program"
print
print "========================================================"
print "What kind of thing is 'Trillium'?"
(1)
A primeira linha importa o mdulo Tkinter e deixa-o disponvel para uso. Perceba que a
forma de importar (from Tkinter import *) significa que ns no queremos ter que
usar a forma Tkinter. para especificar nada que quisermos utilizar.
(2)
A segunda linha cria uma janela toplevel (que pode ou no se tornar visvel). Tecnicamente,
o que esta linha faz criar uma instncia da classe Tkinter.Tk.
Esta janela toplevel o componente GUI de mais alto nvel1 de qualquer aplicao de
Tkinter. Por conveno, esta janela normalmente chamada de raiz.
(3)
A terceira linha executa o mtodo mainloop (isso , o event loop) deste objeto raiz. Assim
que roda, o mainloop espera que eventos aconteam no objeto raiz. Se um evento ocorre,
ento ele alimentado (o event handler executado) e o loop continua rodando, esperando
1
Entenda por de alto nvel a aplicao que tem mais relao com o usurio que com o cdigo (N do T).
6
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
pelo prximo evento, ou at que acontea um evento que destrua a raiz. Um evento
destruidor pode ser o fechamento da janela pelo boto X de fechamento. Quando a raiz
destruda, a janela fechada e o event loop cancelado.
Comportamento do programa
Ao rodar este programa, voc ver (graas ao Tk) a janela toplevel automaticamente com os
widgets para minimizar, maximizar e fechar a janela. Tente us-los voc ver que eles
realmente funcionam.
Clicando no widget fechar (o X em uma caixa, do lado direito da barra de ttulo) ser
gerado um evento destruidor terminando o event loop principal, que no caso deste programa
mainloop. E desde que no haja mais nada depois da linha root.mainloop(), como
neste caso, o programa no faz mais nada e se encerra.
Frames so oferecidos pelo Tkinter como uma classe chamada Frame. Uma expresso
como:
Frame(meuMestre)
7
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
cria uma instncia da classe Frame (isto , cria um frame), e associa a instncia ao seu
mestre, meuMestre. Outra maneira de ver isso como uma expresso que adiciona um
frame escravo ao componente meuMestre.
myContainer1 = Frame(myParent)
Perceba que a relao mestre/escravo aqui somente lgica, no tem nada de visual. Esta
relao existe para otimizar eventos do tipo destrutivo isso porque quando um
componente mestre (como root) destrudo, o mestre sabe quem so seus escravos, e pode
destru-los antes de se destruir.
(2)
A prxima linha define o gerenciador de geometria pack para administrar myContainer1.
myContainer1.pack()
O esquema padro bsico da programao em Tkinter, que veremos ainda diversas vezes,
funciona mais ou menos assim:
Comportamento do programa
Quando voc roda este programa, ele se parecer muito com seu antecessor, exceto pelo
tamanho. Isso porque...
8
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
Frames so elsticos
No programa anterior, por no termos colocado nada dentro dela, a raiz mostrou a si mesma
na tela com seu tamanho padro, mas neste programa, ns preenchemos sua cavidade com o
myContainer1. Agora, a raiz se estica para acomodar o tamanho de myContainer1, mas
como no colocamos nenhum widget neste frame, nem especificamos um tamanho mnimo
para ele, a cavidade da root se encolhe at o limite. Por isso no h nada para ser visto
abaixo da barra de ttulo desse programa.
root = Tk()
root.mainloop()
(1)
O Widget ser um boto isto , ele ser uma instncia da classe Button de Tkinter. A
linha:
Button1=Button(myContainer1)
cria o boto, dando-o o nome de button1, e associa-o ao seu mestre, o objeto container
chamado myContainer1.
(2)(3)
Os widgets tem muitos atributos, todos disponveis no dicionrio do namespace local. O
widget Button tem atributos para controlar seu tamanho, sua cor de fundo e de fonte, seu
texto, como suas bordas se parecero, etc. Neste exemplo, ns iremos ajustar somente dois
9
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
atributos de button: a cor de fundo e o texto. Faremos isso mudando os valores do seu
dicionrio referentes s chaves text e background.
(4)
E, claro, ns fazemos pack no boto button1.
button1.pack()
Comportamento do programa
Quando voc rodar este programa, dever ver que o Container1 agora contm um boto
verde com o texto Hello, World!. Quando voc clica nele no acontece nada, porque ns
ainda no especificamos o que queremos que acontea quando o boto for clicado, se bem
que o faremos mais tarde.
Por ora, voc dever clicar no boto X da barra de ttulo para fech-lo, como antes.
root = Tk()
myContainer1 = Frame(root)
myContainer1.pack()
root.mainloop()
Nele, criamos uma classe chamada MyApp e transcrevemos alguns cdigos dos programas
anteriores para dentro de seu mtodo construtor (__init__). Nesta verso reestruturada do
programa, fazemos 3 coisas diferentes:
(1)
Em nosso cdigo, designamos uma classe (MyApp) que define como queremos que a GUI
se parea e que tipo de coisa queremos fazer com isso. Todo este cdigo inserido no
mtodo construtor da classe.
10
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
(2)
Quando o programa executado, a primeira coisa que ele faz criar uma instncia da
classe. A linha que cria a instncia
myapp=MyApp(root)
Perceba tambm que essa linha faz de root, a raiz, um argumento dentro do mtodo
construtor de MyApp. O mtodo construtor reconhece root sob o nome myParent.
(3)
Finalmente, deixamos a raiz em mainloop.
Uma das razes para usar uma estrutura de classes em seu programa simplesmente
control-lo melhor. Um programa estruturado em classes provavelmente especialmente
se seu programa for muito grande muito mais fcil de ser entendido.
Uma considerao muito importante que estruturar sua aplicao como uma classe
ajudar voc a evitar o uso de variveis globais. Eventualmente, conforme seu programa for
crescendo, voc provavelmente ir querer que alguns de seus event handler consigam
compartilhar informaes entre si. Uma maneira usar variveis globais, mas uma tcnica
muito maante. Um caminho muito melhor usar instncias (isto , usar self. nas
variveis), e para isso voc precisa estruturar sua aplicao como classes. Exploraremos
esse tpico em outros programas desta srie.
Temos introduzido a noo de estrutura de classes para programas de Tkinter logo cedo
para podermos explic-la e partir para outros assuntos. Porm, no andar da carruagem, voc
poder escolher proceder de outra forma.
Em muitos casos, um programa em Tkinter comea com um script simples. Todo o cdigo
acontece numa nica linha, como nossos programas anteriores. Ento, conforme a aplicao
toma corpo, o programa cresce, de forma que em pouco tempo voc se ver envolvo por
uma porrada de cdigo. Voc pode comear a usar variveis globais... talvez uma porrada
de variveis globais. O programa comea a ficar difcil de entender e editar. Quando isso
acontece, hora de voltar prancheta e refazer tudo, desta vez usando classes.
Por outro lado, se voc se sentir bem com classes, e tem uma boa idia da forma final do
seu programa, comece estruturando-o em classes desde o comeo.
Por outro lado (voltamos assim ao lado anterior?), logo no comeo do processo de
desenvolvimento (como observou Gerrit Muller) freqentemente voc ainda no sabe a
11
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
melhor estrutura de classes a usar logo no comeo, voc simplesmente no tem uma idia
clara o suficiente do problema e soluo. Comear a usar classes cedo demais no processo
pode gerar muitas estruturas desnecessrias que s servem para bagunar o cdigo, alm de
geralmente exigir mais consertos.
Tudo depende do gosto pessoal e das circunstncias. Faa o que parecer bem a voc. E
no importa o que voc escolha no se assuste com as reestruturaes quando se tornarem
necessrias.
Comportamento do programa
Quando voc rodar este programa, ele ter uma aparncia exatamente igual anterior. Nada
funcional foi mudado somente como o cdigo foi estruturado. Agora ele est em forma de
classes.
self.button1 = Button(self.myContainer1)
self.button1["text"]= "Hello, World!"
self.button1["background"] = "green"
self.button1.pack()
root = Tk()
myapp = MyApp(root) ### (2)
root.mainloop() ### (3)
Neste programa, adicionamos mais trs botes ao Container1, usando mtodos ligeiramente
diferentes.
(2)
Para o boto button2, o processo essencialmente o mesmo que no boto button1, mas em
vez de acessar o dicionrio de Button, usamos o mtodo configure atribudo aos objetos
Button.
12
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
(3)
Para o boto button3, vemos que o mtodo configure pode ter muitos argumentos, ento
designamos vrias opes em uma nica linha.
(4)
Nos exemplos anteriores, fazer um boto era uma tarefa em dois passos: primeiro criar o
boto e ento configurar suas propriedades. Tambm possvel configurar estas
propriedades ao mesmo tempo em que criamos o boto. O widget Button, como qualquer
widget, espera como primeiro argumento o nome do seu mestre. Depois desse argumento
voc pode, se desejar, adicionar um ou mais argumentos sobre as propriedades do widget.
Comportamento do programa
Quando voc rodar este programa, dever ver que o Container1 agora contm, junto com o
antigo boto verde, mais trs botes. Veja como myContainer1 (o frame) se estica para
acomodar todos estes botes.
Perceba tambm que esses botes so empilhados um sobre o outro. No prximo programa,
veremos por que eles se arranjam dessa forma, e veremos como arranj-los de forma
diferente.
class MyApp:
self.button1 = Button(self.myContainer1)
self.button1["text"] = "Hello, World!" ###(1)
self.button1["background"] = "green" ###(1)
self.button1.pack()
self.button2 = Button(self.myContainer1)
self.button2.configure(text="Off to join the circus!") ###(2)
self.button2.configure(background="tan") ###(2)
self.button2.pack()
self.button3 = Button(self.myContainer1)
self.button3.configure(text="Join me?",background="cyan")#(3)
self.button3.pack()
root = Tk()
myapp = MyApp(root)
root.mainloop()
13
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
TT050 Packing.
No ltimo programa, ns vimos quatro botes, empilhados um sobre o outro. Entretanto,
provavelmente gostaramos de v-los lado a lado em alguma ocasio. Neste programa
fazemos isso, e comearemos a ver as possibilidades que pack() oferece.
self.button1.pack(side=LEFT)
Veja que LEFT (assim como RIGHT, TOP e BOTTOM) so constantes de nomes bem
amigveis definidas em Tkinter. Isto , LEFT deveria aparecer no cdigo como
Tkinter.LEFT, mas por causa da nossa maneira de importar o mdulo Tkinter (pgina 6),
no precisamos mais usar o prefixo Tkinter..
Ento, quando fizemos pack no boto button1, ele foi colocado no topo da cavidade do
frame myContainer1. Ao fazer pack sem argumentos sobre o boto button2, ele tambm
colocado no topo da cavidade deste frame, que neste caso fica exatamente abaixo do boto
button1, e assim por diante.
Se ns tivssemos feito pack nos botes em ordem diferente por exemplo, se tivssemos
feito pack em button2 primeiro, e depois em button1 suas posies teriam sido invertidas:
o boto button2 estaria em cima.
Ento, como voc pode ver, uma das maneiras de controlar como sua GUI vai se parecer
controlando a ordem de fazer pack em cada um dos widgets dentro do container.
Orientao vertical inclui os lados TOP (de cima) e BOTTOM (de baixo).
Orientao horizontal inclui os lados LEFT (esquerdo) e RIGHT (direito).
Quando voc est empacotando widgets e containeres, possvel mesclar dois tipos de
orientao. Por exemplo, podemos precisar posicionar um boto com orientao vertical
(como TOP) e outro com orientao horizontal (como LEFT), mas fazer isso misturando
orientaes dentro do container no uma boa idia. Se voc misturar orientaes poder
prejudicar a maneira como os objetos aparecero na tela, alm da bela surpresa que ter se
precisar redimensionar a janela depois.
14
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
Por estes motivos uma boa prtica de projeto nunca misturar orientaes, mas sem voc
precisar mesmo fazer isso, melhor usar um container dentro do outro. Exploraremos este
tpico no prximo programa.
Comportamento do programa
Quando voc rodar este programa, ver quatro botes lado a lado.
class MyApp:
def __init__(self, parent):
self.myContainer1 = Frame(parent)
self.myContainer1.pack()
self.button1 = Button(self.myContainer1)
self.button1["text"]= "Hello, World!"
self.button1["background"] = "green"
self.button1.pack(side=LEFT) ### (1)
self.button2 = Button(self.myContainer1)
self.button2.configure(text="Off to join the circus!")
self.button2.configure(background="tan")
self.button2.pack(side=LEFT) ### (2)
self.button3 = Button(self.myContainer1)
self.button3.configure(text="Join me?", background="cyan")
self.button3.pack(side=LEFT) ### (3)
root = Tk()
myapp = MyApp(root)
root.mainloop()
TT060 Binding.
Agora hora de colocar nossos botes para trabalhar. Chamamos sua ateno para as duas
ltimas (das quatro) questes bsicas da programao GUI escrever procedimentos de
alimentao de eventos para fazer os trabalhos necessrios no seu programa, e ligar estes
procedimentos a widgets e eventos.
Perceba que neste programa rejeitamos todos os botes que tnhamos criado no ltimo
programa, e retornamos a uma GUI muito simples contendo dois botes: OK e
CANCEL.
15
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
Voc deve se lembrar da discusso de nosso primeiro programa, de que uma das questes
bsicas da programao GUI o binding. Binding o processo de definir a conexo ou
relao (geralmente) entre:
um widget;
um tipo de evento e
um alimentador de eventos.
Em Tkinter, uma maneira de criar bindings pelo mtodo bind(), da seguinte forma:
widget.bind(nome_do_tipo_de_evento,nome_do_alimentador_de_eventos)
Antes de comearmos, vamos esclarecer uma coisa: a palavra boto pode ser usada para
designar duas coisas inteiramente diferentes:
(1)
Ns ligamos o evento <Button-1> (um clique com o boto esquerdo do mouse) sobre o
boto button1 ao mtodo self.button1Click. Quando button1 clicado com o boto
esquerdo do mouse, o mtodo self.button1Click() chamado para alimentar o evento.
(3)
Veja que, embora no tenhamos especificado na operao de binding, self.button1Click
() receber dois argumentos. O primeiro, claro, ser self, primeiro argumento para
qualquer mtodo de classes em Python. O segundo ser um objeto evento. Esta tcnica de
binding e evento isto , usando o mtodo bind() sempre implica na utilizao de um
objeto evento como argumento.
16
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
mtodos. Voc pode examinar o objeto evento para encontrar que tipo de evento ocorreu, o
widget onde ocorreu, e outras informaes teis.
(4)
Ento, o que acontece quando button1 clicado? Bem, neste caso ns o designamos para
fazer algo bem simples: mudar sua cor de verde para amarelo e vice-versa.
(2)
Vamos fazer o button2 (o boto do Tchau!) fazer algo mais til: fechar a janela. Para isso
ligamos o evento de clicar com boto esquerdo do mouse sobre o button2 ao mtodo
button2Click() e
(6)
Fazemos o mtodo button2Click() destruir a janela raiz, a mestra de myapp. Isso vai ter
um efeito bem devastador, porque todos os escravos tambm sero destrudos. Na verdade,
toda a GUI ser destruda.
claro que para isso, myapp tem que saber quem sua mestra (aqui, a janela raiz). Ento
adicionamos um cdigo (7) ao construtor da classe para lembr-la disso.
Comportamento do programa
Quando voc roda este programa, v dois botes. Clicar no boto OK o faz mudar de cor.
Clicar no boto Cancel fecha a aplicao.
Quando a GUI aberta, se voc apertar TAB, ver que o foco do teclado vai ficar pulando
entre os dois botes, embora apertar ENTER no teclado no faa nada. Isso porque temos
ligado somente cliques de mouse aos nossos botes, no eventos de teclado. Os prximos
tpicos falaro deste assunto.
class MyApp:
self.button1 = Button(self.myContainer1)
self.button1.configure(text="OK", background= "green")
self.button1.pack(side=LEFT)
self.button1.bind("<Button-1>", self.button1Click) ### (1)
self.button2 = Button(self.myContainer1)
self.button2.configure(text="Cancel", background="red")
17
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
self.button2.pack(side=RIGHT)
self.button2.bind("<Button-1>", self.button2Click) ### (2)
root = Tk()
myapp = MyApp(root)
root.mainloop()
Se voc tem alguma intimidade com a mitologia grega (ou se voc viu Hrcules, aquele
filme da Disney), se lembrar das Fates. As Fates eram trs velhas que controlavam o
destino dos homens. Cada humano vivo era uma linha em suas mos, e quando uma delas
cortava a linha, a vida do humano terminava.
As Fates
Um fato notvel sobre as Fates era que havia somente um olho para as trs. A que estava
com o olho tinha que dizer para as outras duas tudo o que estava vendo. O olho poderia
18
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
passar de uma Fate a outra, assim elas podiam se revezar entre ver ou no. claro, se voc
conseguisse roubar esse olho, teria algo valiosssimo para barganhar o que quisesse com
elas.
Foco o que permite aos widgets da sua GUI ver os eventos de teclado. O foco est para
os widgets da sua GUI como o olho estava para as Fates. Somente um widget por vez pode
ter o foco, e o widget que o tem o que v, e responde a, os eventos de teclado.
Neste programa, por exemplo, nossa GUI tem dois botes, OK e Cancel. Supondo que
eu pressione o boto ENTER no teclado, ser que ele ser visto pelo boto OK indicando
que o usurio est confirmando sua escolha? Ou ser que o ato de pressionar esta tecla ser
visto pelo boto Cancel, indicando que o usurio est cancelando (e destruindo) a
aplicao? Tudo isso depende de onde est o foco. Isto , depende de que boto tem foco.
Como as Fates, que passavam seu olho de uma a outra, o foco pode ser passado de um
widget na GUI para outro. H vrias formas de fazer isso. Uma delas clicando no widget
com o boto esquerdo do mouse (esse modo funciona em Windows e Macintosh, em Tk e
Tkinter. H alguns sistemas que usam a conveno foco segue o mouse, em que o widget
que est sob o mouse tem foco automaticamente no necessrio clicar)
Outra maneira de mudar o foto usando o fato de que os widgets so adicionados a uma
lista conforme so criados. Pressionando a tecla TAB, o foco se move do widget atual para
o prximo da lista. O widget seguinte ao ltimo o primeiro. Pressionando SHIFT+TAB, o
foco se move para o item anterior da lista.
Quando um boto GUI tem foco, isso indicado por uma caixinha de pontos em torno do
texto do boto. Para ver isso, rode o programa anterior. Quando o programa comea,
nenhum dos botes tem foco, por isso nenhum deles tem a caixa de pontos. Pressione TAB
e voc ver essa caixa em torno do texto do boto esquerdo, mostrando que o foco est
sobre ele. Agora pressione vrias vezes TAB e observe o foco pular de um boto a outro
repetidamente.
(0)
Neste programa, queremos que o boto OK tenha foco desde o comeo. Para isso usamos o
mtodo focus_force(), que fora o foco a comear no boto OK. Quando voc roda o
programa, ver a caixa de pontos sobre o boto OK desde que a aplicao se abrir.
Neste programa ns iremos ligar tambm alguns eventos de teclado aos botes.
19
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
(1)(2)
As linhas para ligar eventos de teclado aos botes so bem simples elas tm o mesmo
formato que aquelas que ligam eventos de mouse. A nica diferena que o nome do
evento agora o nome de um evento de teclado (neste caso, <Return2>) .
Quando queremos que a tecla ENTER ou um clique no boto esquerdo do mouse tenham o
mesmo efeito no widget, ligamos o mesmo alimentador de eventos a ambos eventos.
Este programa mostra que voc pode ligar vrios tipos de eventos a um nico widget (como
um boto), ou mesmo ligar vrias combinaes de widgets a um mesmo alimentador de
eventos.
(3)(4)
Agora que nossos botes respondem a vrios tipos de eventos, podemos demonstrar como
receber informaes de um objeto evento. O que vamos fazer passar o objeto evento para
(5) a rotina report_event que ir (6) imprimir uma informao sobre o evento, obtida a
partir dos atributos do evento.
Perceba que para vermos estas informaes impressas na tela, necessrio rodar o
programa usando python (nunca pythonw como IDLE).
Comportamento do programa
class MyApp:
def __init__(self, parent):
self.myParent = parent
self.myContainer1 = Frame(parent)
self.myContainer1.pack()
self.button1 = Button(self.myContainer1)
self.button1.configure(text="OK", background= "green")
self.button1.pack(side=LEFT)
self.button1.focus_force() ### (0)
self.button1.bind("<Return>", self.button1Click)
self.button1.bind("<Return>", self.button1Click) ### (1)
self.button2 = Button(self.myContainer1)
self.button2.configure(text="Cancel", background="red")
self.button2.pack(side=RIGHT)
self.button2.bind("<Return>", self.button2Click)
2
Os americanos chamam a tecla ENTER de RETURN, da o evento de teclado pressionar ENTER receber a
sintaxe <Return>. (Nota do Tradutor)
20
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
root = Tk()
myapp = MyApp(root)
root.mainloop()
Command Binding
Voc deve se lembrar que nos nossos programas anteriores, ns ligamos o evento de mouse
<Button-1> ao widget boto. Button outro nome para um evento de mouse
ButtonPress, que diferente do evento de mouse ButtonRelease3. O evento
ButtonPress o ato de pressionar o boto do mouse, mas sem solt-lo, e ButtonRelease
e o ato de soltar o boto, deixando-o retornar sua posio inicial.
3
Release = Liberar, soltar (N do T).
21
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
Esta uma noo mais complicada de invocao de botes do que ns temos usado at
agora, onde simplesmente ligamos o evento Button-1 ao widget boto usando event
binding.
Por sorte, h outra forma de fazer binding que suporta essa noo mais complicada de
invocao de widgets. o command binding.
Neste programa, veja as linhas com comentrios (1) e (2) para ver como o command
binding feito. Nestas linhas, usamos a opo command para ligar o button1 ao
alimentador de eventos self.button1Click, e para ligar o button2 ao alimentador de
eventos self.button2Click.
(3)(4)
D uma olhada em como os alimentadores de eventos se definem. Veja que diferente dos
alimentadores dos programas anteriores eles no esperam por um objeto evento como
argumento. Isso porque o command binding, diferente do event binding, no faz
automaticamente a passagem do objeto evento como um argumento. Isso faz sentido porque
o command binding no liga um nico evento a um alimentador, e sim mltiplos eventos.
Para um boto, por exemplo, ele liga o ButtonPress seguido de um ButtonRelease a um
alimentador.
Daremos uma olhada um pouco melhor nas diferenas entre event binding e command
binding no prximo programa, mas por ora, rodemos o programa.
Comportamento do programa
Quando voc roda o programa, o boto que aparece exatamente igual ao dos programas
anteriores... s na aparncia.
Para comparar seu comportamento com os botes anteriores, leve o ponteiro do mouse at o
boto esquerdo e clique, mas no solte o boto do mouse.
H tambm outra diferena. Compare seu comportamento quando voc pressiona a barra de
espao e ENTER. Por exemplo, use TAB para focar o boto OK e pressione espao ou
ENTER.
22
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
class MyApp:
def __init__(self, parent):
self.myParent = parent
self.myContainer1 = Frame(parent)
self.myContainer1.pack()
self.button1 = Button(self.myContainer1,
command=self.button1Click
) ### (1)
self.button1.configure(text="OK", background= "green")
self.button1.pack(side=LEFT)
self.button1.focus_force()
self.button2 = Button(self.myContainer1,
command=self.button2Click
) ### (2)
self.button2.configure(text="Cancel", background="red")
self.button2.pack(side=RIGHT)
def button1Click(self): ### (3)
print "button1Click event handler"
if self.button1["background"] == "green":
self.button1["background"] = "yellow"
else:
self.button1["background"] = "green"
root = Tk()
myapp = MyApp(root)
root.mainloop()
No programa anterior, se voc usar TAB para focar o boto OK e pressionar a barra de
espao, o boto mudar de cor, mas pressionar ENTER no tem efeito algum.
23
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
A razo para isto que a opo command d a um widget Button distino sobre eventos
de teclado, assim como eventos de mouse. Neste caso, o evento de teclado aguardado pelo
boto o acionamento da barra de espao, no da tecla ENTER. Isso quer dizer que, com
command binding, pressionar a barra de espaos faz o boto OK mudar de cor, enquanto
ENTER no tem efeito.
Este comportamento parece (ao menos para mim, usurio Windows) incomum. Parte da
moral da histria aqui que se voc est fazendo uso de command binding, uma boa idia
entender exatamente o que voc quer que seu binding faa. Isto , uma boa idia entender
exatamente que eventos de mouse e/ou teclado devero causar os comandos desejados.
Voc deve tambm saber que nem todos os widgets oferecem a opo command. Muitos
dos widgets Button o fazem (RadioButton, CheckButton, etc.) e outros oferecem opes
similares (por exemplo, scrollcommand), mas voc realmente deve investigar cada um dos
diferentes tipos de widgets para saber se suportam ou no command binding. Isso dar um
controle maior sobre o comportamento da sua GUI e tornar mais fcil sua vida de
programador.
Percebemos no ltimo programa que command binding, diferente de event binding, no faz
automaticamente a passagem evento objeto argumento. Isso pode tornar sua vida um
pouco complicada se voc deseja ligar um alimentador de eventos a um widget usando
simultaneamente event binding e command binding.
Por exemplo, neste programa realmente desejaremos que nossos botes respondam to bem
ativao da tecla ENTER quando da barra de espaos. Para isso, teremos que usar event
binding para o evento de teclado <Return>, como foi feito no ltimo programa (1).
H algumas solues para este problema, mas a mais simples escrever dois alimentadores
de eventos. O alimentador real (2) ser o nico usado pelo command binding, que no
esperar por um objeto evento.
O outro alimentador (3) vai ser s um wrapper do real. Este wrapper esperar o argumento
do objeto evento, mas o ignorar e chamar o alimentador real. Daremos ao wrapper o
mesmo nome do alimentador real, porm adicionando o sufixo _a.
24
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
Comportamento do programa
Se voc rodar este programa, o comportamento ser o mesmo do programa anterior, exceto
pelo fato de que agora os botes iro responder tanto ao ENTER quando barra de espaos.
class MyApp:
def __init__(self, parent):
self.myParent = parent
self.myContainer1 = Frame(parent)
self.myContainer1.pack()
self.button1 = Button(self.myContainer1,
command=self.button1Click
)
self.button1.bind("<Return>", self.button1Click_a) ### (1)
self.button1.configure(text="OK", background= "green")
self.button1.pack(side=LEFT)
self.button1.focus_force()
self.button2 = Button(self.myContainer1,
command=self.button2Click
)
self.button2.bind("<Return>", self.button2Click_a) ### (1)
self.button2.configure(text="Cancel", background="red")
self.button2.pack(side=RIGHT)
root = Tk()
myapp = MyApp(root)
root.mainloop()
25
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
H uma variedade de situaes nas quais voc pode querer que um alimentador de eventos
realize alguma tarefa e divida os resultados desta tarefa com outros alimentadores em seu
programa.
Um exemplo comum quando voc tem uma aplicao com dois grupos de widgets. Um
deles seleciona alguma parte da informao, e ento o outro faz alguma coisa com esta
informao.
Por exemplo, talvez voc queira ter um widget que permita a um usurio escolher um nome
de arquivo de uma lista, e outro conjunto de widgets que oferea vrias operaes sobre o
arquivo escolhido abrir, deletar, copiar, renomear, etc.
Ou voc pode ter uma srie de widgets que modifique vrias configuraes da sua
aplicao, e outra srie de widgets (oferecendo opes de SALVAR e CANCELAR, talvez)
que salve em disco estas modificaes de configurao ou as cancele sem salvar.
Quem sabe ainda um conjunto de widgets que mudem alguns parmetros de um programa
que voc deseje rodar, enquanto outro widget (provavelmente um boto com um nome
RODAR ou EXECUTAR) que rode o programa considerando os parmetros que voc
escolheu.
O problema
O problema aqui que cada um dos alimentadores uma funo separada. Cada um deles
tem suas prprias variveis locais que no fazem parte das outras funes alimentadoras,
nem mesmo de invocaes anteriores delas mesmas. Eis o problema: como pode uma
funo alimentadora de eventos partilhar dados com outros alimentadores, se ela no pode
partilhar suas variveis locais com eles?
Uma tcnica para conseguir isto fazer delas (as variveis que queremos compartilhar)
variveis globais. Por exemplo, em cada alimentador que precise modificar ou ver
minhaVariavel1 e minhaVariavel2, voc pode escrever o seguinte:
26
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
Este uma das razes pelas quais ns desenvolvemos as aplicaes deste tutorial em forma
de classes. por termos comeado neste formato logo de incio que neste ponto nossa
aplicao j temos uma infra-estrutura que nos permitir usar variveis instanciadas.
Neste programa, iremos compartilhar uma informao bem simples: o nome do ltimo
boto pressionado. Armazenaremos esta informao numa varivel instanciada chamada
self.myLastButtonInvoked (veja o comentrio ### 1).
Para mostrar que realmente estamos lembrando desta informao, toda vez que o
alimentador do boto for solicitado, ela ser impressa (veja o comentrio ### 2).
Comportamento do programa
Este programa mostra trs botes. Quando voc coloc-lo para rodar, se voc clicar em
qualquer um dos botes, ser mostrado seu prprio nome, e o nome do boto que foi
clicado anteriormente.
Perceba que nenhum dos botes fechar a aplicao, ento se voc desejar fech-la, dever
clicar no widget FECHAR (o cone com um X em uma caixa, do lado direito da barra de
ttulo).
class MyApp:
def __init__(self, parent):
self.myLastButtonInvoked = None
self.myParent = parent
self.myContainer1 = Frame(parent)
self.myContainer1.pack()
self.yellowButton=Button(self.myContainer1,
command=self.yellowButtonClick
)
self.yellowButton.configure(text="YELLOW",
background="yellow"
)
self.yellowButton.pack(side=LEFT)
27
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
self.redButton=Button(self.myContainer1,
command=self.redButtonClick
)
self.redButton.configure(text="RED", background="red")
self.redButton.pack(side=LEFT)
self.whiteButton=Button(self.myContainer1,
command=self.whiteButtonClick
)
self.whiteButton.configure(text="WHITE",background="white")
self.whiteButton.pack(side=LEFT)
def redButtonClick(self):
print "RED button clicked. Previous button invoked was",
self.myLastButtonInvoked ### 2
self.myLastButtonInvoked = "RED" ### 1
def yellowButtonClick(self):
print "YELLOW button clicked. Previous button invoked was",
self.myLastButtonInvoked ### 2
self.myLastButtonInvoked = "YELLOW" ### 1
def whiteButtonClick(self):
print "WHITE button clicked. Previous button invoked was",
self.myLastButtonInvoked ### 2
self.myLastButtonInvoked = "WHITE" ### 1
Tambm usamos event binding para ligar nossos botes ao evento de teclado <Return>.
self.button1.bind("<Return>", self.button1Click_a)
28
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
Mas suponha que a situao seja diferente. Suponha que temos diversos botes, todos eles
desenvolvendo essencialmente o mesmo tipo de ao. A melhor maneira de alimentar este
tipo de situao ligar os eventos de todos os botes a um nico alimentador de eventos.
Cada um dos botes dever invocar a mesma rotina alimentadora, mas fornecendo a ela
diferentes argumentos contando o que fazer.
isso que fazemos neste programa.
Command binding
Neste programa, como voc pode ver, temos dois botes, e usamos a opo command
para lig-los todos ao mesmo alimentador de eventos a rotina buttonHandler.
Fornecemos a esta rotina trs argumentos: o nome do boto (na varivel button_name), um
nmero e uma string.
self.button1=Button(self.myContainer1,
command=self.buttonHandler(button_name,1,
"Good stuff!"
)
)
Em aplicaes srias, a rotina buttonHandler iria, claro, trabalhar seriamente, mas neste
programa ela meramente imprime os argumentos que recebeu.
Event binding
Voc perceber que comentamos as duas linhas que fazem event binding no evento
<Return>.
self.button1.bind("<Return>",self.buttonHandler_a(event,
button_name, 1,
"Good stuff!"
)
)
Teremos que voltar a este problema mais tarde. Por ora, vamos simplesmente rodar o
programa e ver o que acontece.
29
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
Comportamento do programa
Vendo o cdigo, o programa at que no parece to mal, mas quando voc o roda, percebe
que ele no trabalha direito. A rotina buttonHandler solicitada antes mesmo que a GUI
seja exibida. Na verdade, ela solicitada duas vezes!
Se voc clicar com o boto esquerdo do mouse em qualquer boto, descobrir que nada
acontece a rotina eventHandler no est sendo solicitada.
Ento rode o programa agora, e veja o que acontece. No prximo programa veremos porque
isso acontece.
class MyApp:
def __init__(self, parent):
self.myParent = parent
self.myContainer1 = Frame(parent)
self.myContainer1.pack()
button_name = "OK"
self.button1=Button(self.myContainer1,
command=self.buttonHandler(button_name,
1,"Good stuff!"
)
)
# self.button1.bind("<Return>", self.buttonHandler_a(event,
# button_name, 1, "Good stuff!"))
self.button1.configure(text=button_name, background="green")
self.button1.pack(side=LEFT)
self.button1.focus_force() # Foca o teclado em button1
button_name = "Cancel"
self.button2=Button(self.myContainer1,
command=self.buttonHandler(button_name,
2,"Bad stuff!"
)
)
# self.button2.bind("<Return>",self.buttonHandler_a(event,
# button_name, 2, "Bad stuff!"))
self.button2.configure(text=button_name, background="red")
self.button2.pack(side=LEFT)
30
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
self.button1 = Button(self.myContainer1,
command = self.buttonHandler(button_name,
1, "Good stuff!"
)
)
foi chamada de funo buttonHandler, embora fosse melhor t-la chamado de rechamada.
Embora no seja o que pretendamos, o que realmente acontece.
Veja que:
Quando a linha
self.button1 = Button(self.myContainer1,
command = self.buttonHandler(button_name,
1, "Good stuff!"
)
)
31
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
H uma soluo?
Sim. H uma srie de tcnicas reconhecidas de fazer isso. Uma delas usa a funo Lambda,
uma funo built-in de Python, e outra usa uma tcnica chamada de currying.
Neste programa discutiremos como trabalhar com lambda, e no prximo programa daremos
uma olhada em currying.
No vou tentar explicar como lambda e currying trabalham isso muito complicado e est
bem longe do nosso objetivo principal, que ter programas Tkinter rodando. Vamos ento
simplesmente trat-las como caixas-pretas. No vou dizer como elas trabalham s como
trabalhar com elas.
Vejamos lambda.
Command binding
self.button1 = Button(self.myContainer1,
command = self.buttonHandler(button_name,
1, "Good stuff!"
)
)
self.button1 = Button(self.myContainer1,
command = lambda,
arg1=button_name,arg2=1,
arg3="Good Stuff!":self.buttonHandler(arg1,
arg2,
arg3)
)
Event binding
Felizmente, lambda tambm nos d um jeito de parametrizar event binding. Em vez de:
self.button1.bind("<Return>",
self.buttonHandler_a(event, button_name, 1, "Good stuff!")
)
32
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
(Veja que event aqui o nome de uma varivel no uma keyword de Python ou algo
que o valha. Este exemplo usa o nome event para o argumento de evento; alguns autores
usam o nome e para isso, mas tanto faz. Poderamos ter escrito event_arg, se
quisssemos.)
Uma das caractersticas mais elegantes quando se usa lambda que podemos (se
quisermos), simplesmente no transmitir o argumento de evento. Se fizermos isso,
poderemos ento chamar a funo self.buttonHandler diretamente, em vez de indiretamente
pela funo self.buttonHandler_a.
Para ilustrar esta tcnica, iremos programar o event binding do button2 diferente do
button1.
Comportamento do programa
Veja que voc pode mudar o foco de teclado entre os botes OK e CANCEL pressionando
a tecla TAB.
class MyApp:
def __init__(self, parent):
33
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
self.myParent = parent
self.myContainer1 = Frame(parent)
self.myContainer1.pack()
# command binding
self.button1 = Button(self.myContainer1,
command = lambda
arg1=button_name, arg2=1, arg3="Good stuff!" :
self.buttonHandler(arg1, arg2, arg3)
)
self.button1.configure(text=button_name, background="green")
self.button1.pack(side=LEFT)
self.button1.focus_force() # pe o foco de teclado em button1
button_name = "Cancel"
# command binding
self.button2 = Button(self.myContainer1,
command = lambda
arg1=button_name, arg2=2, arg3="Bad stuff!":
self.buttonHandler(arg1, arg2, arg3)
)
self.button2.configure(text=button_name, background="red")
self.button2.pack(side=LEFT)
root = Tk()
myapp = MyApp(root)
34
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
Sobre Curry
Em seu sentido mais simples, currying a tcnica de usar funo para construir outras
funes.
Currying uma tcnica herdada da programao funcional. Se voc quiser saber mais sobre
ela, h diversas informaes no Python Cookbook. A classe curry usada neste programa
a receita de Scott David Daniel, Curry associando parmetros com funes, disponvel
em
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52549
Como foi discutido sobre lambda, no meu objetivo explicar como funciona currying:
trataremos isso como uma caixa-preta.
self.button1 = Button(self.myContainer1,
command=self.buttonHandler(button_name,1,"Good stuff!")
)
Usando curry, o jeito de fazer o que queremos reescrever esta linha assim:
self.button1 = Button(self.myContainer1,
command=curry(self.buttonHandler,button_name,1,"Good stuff!")
)
Como voc pode ver, o cdigo bem direto. Em vez de solicitar a funo
self.buttonHandler, criamos um objeto curry (isto , uma instncia da classe curry),
transmitindo a funo self.buttonHandler em seu primeiro argumento. Basicamente, o que
35
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
acontece que o objeto curry recorda o nome da funo dada. Por isso, quando
requisitado, o objeto curry chama a funo que lhe foi dada quando foi criado.
Event binding
Chad Netzer desenvolveu uma tcnica similar ao currying, que pode ser usada para
parametrizar event binding4 que envolve uma funo event_lambda.
Para usar event_lambda, como com curry, voc deve incluir o cdigo para a funo
event_lambda em seu programa, ou import-la de seu prprio arquivo Python. Neste
programa, inclumos o cdigo da funo event_lambda diretamente no programa.
Uma vez tendo a funo event_lambda disponvel, podemos us-la para ligar
self.buttonHandler ao evento de teclado ENTER, fornecendo a ela alguns argumentos. Eis
como fazemos isso:
self.button1.bind("<Return>",
event_lambda(self.buttonHandler,button_name,
1,"Good stuff!")
)
Se voc tiver uma curiosidade insacivel sobre como event_lambda funciona, d uma
olhadinha no cdigo do boto button2.
event_handler=event_lambda(self.buttonHandler,
button_name,2,"Bad stuff!")
Quando ela solicitada, usa lambda para criar um objeto funo novo e sem nome
(annimo).
O objeto funo sem nome um envoltrio para a funo que realmente queremos solicitar
(f, que neste programa self.buttonHandler) e os argumentos ns especificamos
quando chamamos a funo event_lambda. Assim, a funo event_lambda retorna esta
nova e annima funo.
36
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
event_handler=event_lambda(self.buttonHandler,
button_name,2,"Bad stuff!")
self.button2.bind("<Return>",event_handler)
Perceba que para a funo annima, event s um argumento suporte que descartado e
no chega a ser usado. Somente os argumentos posicionais (args) e os argumentos de
teclado (kwds) so transmitidos rotina alimentadora do boto.
Este um assunto complicado. Mas voc no precisa torrar seus neurnios tentando
entender como tudo isso funciona. Voc no precisa saber como curry e event_lambda
trabalham se seu objetivo for us-los. Trate-os como caixas-pretas: use-os sem se preocupar
com o que h dentro.
Bem
Escolha o que lhe convier. Como diz o ditado, o cliente sempre tem razo. Use o que for
cmodo para suas tarefas.
Python uma linguagem poderosa, e oferece vrias ferramentas que podem ser usadas para
criar funes de rechamada para manipulao de eventos. Pensando em Tkinter uma
introduo aos conceitos bsicos, no uma enciclopdia de tcnicas, por isso exploramos s
algumas delas aqui. Mas c entre ns, voc se torna muito mais hbil com Python, e como
sua necessidade por mais flexibilidade cada vez maior, h caractersticas mais avanadas
da linguagem que estaro disponveis para voc desenvolver exatamente o tipo de funes
de rechamada que voc precisar.
Comportamento do programa
Se voc rodar este programa, ele se comportar como o anterior. No mudamos nada no
comportamento do programa, somente a maneira como foi escrito.
37
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
class MyApp:
button_name = "OK"
self.button1.configure(text=button_name, background="green")
self.button1.pack(side=LEFT)
self.button1.focus_force() # Pe o foco de teclado em button1
button_name = "Cancel"
38
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
Neste programa, veremos como controlar a aparncia mudando os atributos dos widgets e
as opes de pack().
Trabalhemos um pouco com alguns botes e com o frame que os contm. Nas ltimas
verses deste programa, chamamos o frame de myContainer1. Aqui, iremos renome-lo
como algo mais descritivo: buttons_frame.
39
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
Os nmeros das sees seguintes se referem aos comentrios numerados no cdigo fonte.
(1)
Primeiro, para nos certificarmos de que todos os botes tm a mesma largura,
especificamos um atributo largura (width) que o mesmo para todos eles. Perceba que o
atributo width especfico para o widget Button de Tkinter nem todos os widgets
possuem atributos de largura. Perceba tambm que o atributo width especificado em
unidades de caracteres (e no, por exemplo, em unidades de pixels, polegadas, milmetros).
Sabendo que nosso maior rtulo (Cancel) tem seis caracteres, vamos definir a largura dos
botes como 6.
(2)
Agora adicionamos padding aos nossos botes. Padding so espaos extra em torno do
texto, entre o texto e a borda do boto. Fazemos isso mudando os atributos padx e pady
dos botes. padx cria os espaos extras ao longo do eixo X, horizontalmente, direita e
esquerda do texto, enquanto pady faz isso ao longo do eixo Y, verticalmente, acima e
abaixo do texto.
Vamos especificar nosso espao horizontal como 3 milmetros (padx=3m) e nosso espao
vertical como 1 milmetro (pady=1m). Veja que, diferente do atributo width, que
numrico, estes atributos so escritos entre aspas. Isso porque estamos especificando as
unidades do padding por meio do sufixo m, ento temos que especificar o tamanho dos
espaos como uma string em vez de nmeros.
(3)
Finalmente, adicionamos alguns espaos ao container (buttons_frame) que organiza os
botes. Para o container, especificamos quatro atributos de padding. padx e pady
especificam o espao que deve existir em torno (do lado de fora) do frame. ipadx e
ipady (padx interno e pady interno) especificam os espaos internos. Este o
espaamento em tono de cada um dos widgets dentro do container.
(4)
Como voc pode ver, o espaamento um pouco confuso. Frames tm espaamentos
internos, mas widgets como button no. Em alguns casos, o espaamento um atributo do
widget, enquanto em outros casos temos que especific-lo com opes de pack().
Comportamento do programa
Quando voc rodar este programa, ver dois botes, mas agora eles devem ter o mesmo
tamanho. O tamanho dos botes tal que o texto no fica to espremido com antes: agora
os botes tm uma borda considervel.
40
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
class MyApp:
self.myParent = parent
self.buttons_frame = Frame(parent)
self.button1=Button(self.buttons_frame,
command=self.button1Click)
self.button1.configure(text="OK", background= "green")
self.button1.focus_force()
self.button1.configure(width=button_width, ### (1)
padx=button_padx, ### (2)
pady=button_pady ### (2)
)
self.button1.pack(side=LEFT)
self.button1.bind("<Return>", self.button1Click_a)
self.button2=Button(self.buttons_frame,
command=self.button2Click)
self.button2.configure(text="Cancel", background="red")
self.button2.configure(width=button_width, ### (1)
padx=button_padx, ### (2)
pady=button_pady ### (2)
)
self.button2.pack(side=RIGHT)
self.button2.bind("<Return>", self.button2Click_a)
def button1Click(self):
if self.button1["background"] == "green":
self.button1["background"] = "yellow"
else:
self.button1["background"] = "green"
def button2Click(self):
self.myParent.destroy()
41
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
root = Tk()
myapp = MyApp(root)
root.mainloop()
Estes frames no contero nada nenhum widget. Normalmente, por causa da elasticidade
dos frames, eles se encolheriam at no serem visveis, mas especificando atributos de
altura e largura podemos fornecer um tamanho inicial a eles.
Veja que no especificamos altura e largura para todos os frames. Para myContainer1, por
exemplo, no especificamos nenhum dos dois atributos, mas especificando estes atributos
para os widgets que este frame continha, ele se esticou at acomodar as alturas e larguras
acumuladas pelas alturas e larguras desses widgets.
Colocaremos tambm bordas em torno dos trs frames que nos interessaro mais no futuro:
bottom_frame, left_frame e right_frame. Os outros frames (top_frame e buttons_frame) no
recebero bordas.
Comportamento do programa
Quando voc roda este programa, v frames diferentes, com diferentes cores de fundo.
class MyApp:
def __init__(self, parent):
42
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
self.myParent = parent
# buttons_frame
self.buttons_frame = Frame(self.myContainer1)
self.buttons_frame.pack(
side=TOP,
ipadx=buttons_frame_ipadx,
ipady=buttons_frame_ipady,
padx=buttons_frame_padx,
pady=buttons_frame_pady,
)
# top_frame
self.top_frame = Frame(self.myContainer1)
self.top_frame.pack(side=TOP,
fill=BOTH,
expand=YES,
)
# bottom_frame
self.bottom_frame = Frame(self.myContainer1,
borderwidth=5, relief=RIDGE,
height=50,
background="white",
)
self.bottom_frame.pack(side=TOP,
fill=BOTH,
expand=YES,
)
# left_frame
self.left_frame = Frame(self.top_frame, background="red",
borderwidth=5, relief=RIDGE,
height=250,
width=50,
)
self.left_frame.pack(side=LEFT,
fill=BOTH,
expand=YES,
)
43
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
### right_frame
self.right_frame = Frame(self.top_frame, background="tan",
borderwidth=5,relief=RIDGE,
width=250)
self.right_frame.pack(side=RIGHT,fill=BOTH,expand=YES)
self.button1=Button(self.buttons_frame,
command=self.button1Click)
self.button1.configure(text="OK", background= "green")
self.button1.focus_force()
self.button1.configure(
width=button_width,
padx=button_padx,
pady=button_pady
)
self.button1.pack(side=LEFT)
self.button1.bind("<Return>", self.button1Click_a)
self.button2=Button(self.buttons_frame,
command=self.button2Click)
self.button2.configure(text="Cancel", background="red")
self.button2.configure(width=button_width,
padx=button_padx,
pady=button_pady
)
self.button2.pack(side=RIGHT)
self.button2.bind("<Return>", self.button2Click_a)
def button1Click(self):
if self.button1["background"] == "green":
self.button1["background"] = "yellow"
else:
self.button1["background"] = "green"
def button2Click(self):
self.myParent.destroy()
root = Tk()
myapp = MyApp(root)
root.mainloop()
44
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
Dimensionar janelas pode ser uma experincia frustrante quando se trabalha com Tkinter.
Imagine esta situao: voc acredita em desenvolvimento interativo, ento primeiro voc
cuidadosamente cria um frame com a especificao de altura e largura que deseja e depois
de uns testes, percebe que funcionou. Ento voc vai para o prximo passo, que de
adicionar alguns botes ao frame. Testa de novo, mas agora, para sua surpresa, o Tkinter
est agindo como se no houvesse especificao alguma quando altura e largura do frame:
o frame se encolheu ao mnimo suficiente para acomodar os botes.
O que voc pode controlar o tamanho inicial da janela raiz inteira (aquela que um
container do container possui barra de ttulo, etc.), e voc faz isso pela opo
gerenciadora de janelas geometry.
(1)
Neste programa, usamos a opo geometry para fazer uma janelona em torno de um
framezinho.
(2)
Veja que a opo title, que ns tambm usamos neste programa, tambm um mtodo
gerenciador de janelas. Title controla o texto do ttulo na barra de ttulos da janela.
Veja tambm que a opo gerenciadora de janelas pode opcionalmente ser especificada com
um prefixo wm_. Por exemplo, wm_geometry e wm_title. Neste programa, s para
mostrar que voc pode usar qualquer uma destas sintaxes, usamos geometry e
wm_title.
Comportamento do programa
Observe que voc ter que fechar cada uma delas clicando no widget CLOSE o
famigerado X em uma caixa, do lado direito da barra de ttulos.
45
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
Na terceira janela, novamente mostramos como o frame vazio se parece, s que dessa vez
usamos a opo geometry para controlar o tamanho da janela inteira. Podemos ver o fundo
azul do frame dentro de um grande campo cinza da janela.
Na ltima, novamente mostramos o frame com os trs botes dentro dele, mas desta vez
especificando o tamanho da janela com a opo geometry. O tamanho da janela o mesmo
que na janela anterior, porm (como na segunda janela) o frame se encolheu em torno dos
botes, por isso no podemos ver seu fundo azul.
class App:
def __init__(self, root, use_geometry, show_buttons):
fm = Frame(root, width=300, height=200, bg="blue")
fm.pack(side=TOP, expand=NO, fill=NONE)
if use_geometry:
root.geometry("600x400")
### (1) observe o mtodo gerenciador de janelas
if show_buttons:
Button(fm, text="Button 1", width=10).pack(side=LEFT)
Button(fm, text="Button 2", width=10).pack(side=LEFT)
Button(fm, text="Button 3", width=10).pack(side=LEFT)
case = 0
for use_geometry in (0, 1):
for show_buttons in (0,1):
case = case + 1
root = Tk()
root.wm_title("Case " + str(case))
### (2) observe o mtodo gerenciador de janelas wm_title
display = App(root, use_geometry, show_buttons)
root.mainloop()
Side;
Fill;
Expand e
Anchor.
46
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
Este programa diferente dos outros da srie. Isto , voc no ter que ler seu cdigo para
entender suas caractersticas. S precisa rodar o programa.
Para entender como podemos controlar a aparncia dos widgets dentro de um container
(como um frame), precisamos lembrar que o gerenciador de geometria pack usa o modelo
de arranjo baseado no conceito de cavidade. Isto , cada container uma cavidade, e pack
acomoda o contedo dentro dela.
Quando voc acomoda um widget, como um boto, ele sempre acomodado ao longo de
um dos quatro lados da cavidade. A opo side de pack especifica quais lados sero
usados. Por exemplo, se especificamos side=LEFT, o widget ser acomodado (isto ,
posicionado) do lado esquerdo da cavidade.
47
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
Como voc pode ver, o widget X solicitou somente o espao que necessitava para aparecer.
Se especificarmos a opo de pack chamada expand=YES, ele ir solicitar toda a rea
disponvel. Nenhum pedao do lado direito da cavidade permanecer no-solicitado. Isso
no significa que o widget usa a rea inteira; ele continua usando somente a pequena parte
de que necessita.
Em tal situao, o widget X tem um espao muito grande sem utilidade em torno dele. Em
que lugar deste espao ele dever se posicionar? isso que a opo anchor diz a ele.
Quando um widget solicitou mais espao do que realmente usa, a opo anchor refina suas
coordenadas de posicionamento. N significa Norte (isto , centrado no topo da rea
solicitada). NE significa Nordeste (isto , acima e direita da rea solicitada). E assim
por diante.
J a opo fill serve para dizer ao widget se ele deve ou no ocupar todo o espao livre, e
em que direo ele deve se expandir:
Rodando o programa
Ok, vamos rodar o programa. Voc no precisa ler o cdigo, s rodar o programa e
experimentar com as vrias opes de pack dos trs botes-demonstrao.
Se a aparncia de qualquer dos botes sob certas configuraes surpreende voc, tente
descobrir por que ele se parece assim.
E finalmente
48
GRUPO PYTHON UNESP ILHA SOLTEIRA
STEVEN FERG
PENSANDO EM TKINTER
Eis uma forma prtica de encontrar erros: se voc est desenvolvendo seu layout e se
deparar com um problema coisas que no funcionam da maneira que voc pensava que
deveriam funcionar ento d a cada um dos seus containeres (isto , cada um dos seus
frames) uma cor de fundo diferente, por exemplo
bg = red, ou
bg = cyan, ou
bg = tan.
Isso permitir a voc ver como os frames esto se arranjando. Freqentemente, o que voc
vir lhe dar um indcio de qual o problema.
49
GRUPO PYTHON UNESP ILHA SOLTEIRA