Escolar Documentos
Profissional Documentos
Cultura Documentos
STEVEN FERG
PENSANDO EM TKINTER
ndice
3
3
4
4
4
5
6
7
9
9
11
11
12
13
14
14
15
16
19
22
24
24
25
26
26
27
27
29
29
31
35
35
36
38
40
40
43
46
49
50
STEVEN FERG
PENSANDO EM TKINTER
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.
No decorrer do programa de ensino, a discusso dedicada exclusivamente ao gerenciador
de geometria pack. No falaremos sobre os gerenciadores grid ou place.
As quatro questes bsicas na programao de GUIs.
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.
Alguns jarges da programao de GUIs.
A programao GUI (Graphic User Interface Interface Grfica com o Usurio) tem alguns
jarges especiais associados s suas questes bsicas.
1) Ns especificamos como queremos que um GUI se parea descrevendo os widgets
que queremos exibir, e suas relaes especiais (por exemplo, o quanto um widgets
est abaixo ou acima, ou direita ou esquerda de outros widgets). A palavra
widget um termo sem traduo que designa componentes de interface grfica
com o usurio de um modo geral. Widgets inclui elementos como janelas, botes,
menus e itens de menus, cones, listas rolantes, barras de rolagem, etc.
2) Os procedimentos que executam as tarefas dos GUIs so chamados event handler.
Events so formas de entrada de dados como cliques de mouse ou digitao no
teclado. Esses procedimentos so chamados handlers (alimentadores) porque eles
alimentam (isto , respondem a) estes eventos.
3) A associao de um event handler a um widget chamada binding. De modo
geral o processo de binding envolve a associao entre trs coisas diferentes:
STEVEN FERG
PENSANDO EM TKINTER
def handle_C():
print "Wrong! Try again!"
#Questo 1: Define a aparncia da tela
print "\n"*100
# clear the screen
print "
VERY CHALLENGING GUESSING GAME"
print "========================================================"
STEVEN FERG
PENSANDO EM TKINTER
print
print
print
print
print
print
print
print
print
print
A.
B.
C.
Animal"
Vegetable"
Mineral"
"
X.
"========================================================"
"What kind of thing is 'Trillium'?"
quaisquer
outros
eventos
no
interessam,
por
isso
so
Entenda por de alto nvel a aplicao que tem mais relao com o usurio que com o cdigo (N do T).
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.
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.
Ento, neste programa (linha 1),
myContainer1 = Frame(myParent)
Comportamento do programa
Quando voc roda este programa, ele se parecer muito com seu antecessor, exceto pelo
tamanho. Isso porque...
STEVEN FERG
PENSANDO EM TKINTER
Frames so elsticos
Um frame basicamente um container. O interior de um container o espao existente
dentro dele chamado cavidade, um termo tcnico que Tkinter herdou de Tk. Essa
cavidade extensvel (ou elstica) como uma borracha. A menos que voc especifique um
tamanho mximo ou mnimo para o frame, a cavidade ser esticada ou comprimida at
acomodar qualquer coisa que o frame contiver.
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.
Nos prximos programas, colocaremos widgets e outros containeres dentro do Container1,
e voc ver como ele se arranja para acomod-los.
from Tkinter import *
root = Tk()
myContainer1 = Frame(root)
myContainer1.pack()
###(1)
###(2)
root.mainloop()
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
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.
button1["text"] = "Hello, World!
button1["background"] = "green"
"
(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.
from Tkinter import *
root = Tk()
myContainer1 = Frame(root)
myContainer1.pack()
button1 = Button(myContainer1)
button1["text"]= "Hello, World!"
button1["background"] = "green"
button1.pack()
### (1)
### (2)
### (3)
### (4)
root.mainloop()
10
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)
11
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.
from Tkinter import *
class MyApp:
### (1)
def __init__(self, myParent):
### (1a)
self.myContainer1 = Frame(myParent)
self.myContainer1.pack()
self.button1 = Button(self.myContainer1)
self.button1["text"]= "Hello, World!"
self.button1["background"] = "green"
self.button1.pack()
root = Tk()
myapp = MyApp(root)
root.mainloop()
### (2)
### (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
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.
from Tkinter import *
class MyApp:
def __init__(self, parent):
self.myContainer1 = Frame(parent)
self.myContainer1.pack()
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()
self.button4 = Button(self.myContainer1, text="Goodbye!",
background="red"
) ###(4)
self.button4.pack()
root = Tk()
myapp = MyApp(root)
root.mainloop()
13
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.
(1) (2) (3) (4)
Fazer pack um das maneiras de controlar a relao visual entre os componentes da GUI. O
que faremos agora usar a opo side como argumento de pack para colocar os botes
lado a lado, desse jeito:
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..
Porque os botes apareceram verticalmente no ltimo programa
Como voc se lembra, no ltimo programa ns simplesmente empacotamos os botes sem
especificar a opo side, e os botes ficaram daquela forma, empilhados. Isso aconteceu
porque a opo pr-designada da opo side TOP.
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.
Alguns termos tcnicos Orientao
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
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.
from Tkinter import *
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)
self.button4 = Button(self.myContainer1, text="Goodbye!",
background="red"
)
self.button4.pack(side=LEFT)
### (4)
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
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.
16
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.
Finalmente, observamos que os botes tm tamanhos diferentes porque os textos que
contm tm tamanhos diferentes. S por causa disso. No fica muito elegante assim, por
isso consertaremos esse problema no prximo programa.
from Tkinter import *
class MyApp:
def __init__(self, parent):
self.myParent = parent ### (7) lembra seu mestre, a raiz
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.bind("<Button-1>", self.button1Click) ### (1)
self.button2 = Button(self.myContainer1)
self.button2.configure(text="Cancel", background="red")
17
STEVEN FERG
PENSANDO EM TKINTER
self.button2.pack(side=RIGHT)
self.button2.bind("<Button-1>", self.button2Click) ### (2)
def button1Click(self, event):
### (3)
if self.button1["background"] == "green": ### (4)
self.button1["background"] = "yellow"
else:
self.button1["background"] = "green"
def button2Click(self, event):
self.myParent.destroy()
### (5)
### (6)
root = Tk()
myapp = MyApp(root)
root.mainloop()
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
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.
No ltimo programa, nossos botes respondiam ao evento de teclado pressionar a tecla
TAB, que movia o foco entre os botes, mas ao pressionar ENTER, nada acontecia. Isso
porque s foram ligados cliques de mouse, no eventos de teclado, aos botes.
Neste programa ns iremos ligar tambm alguns eventos de teclado aos botes.
19
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
Quando voc roda o programa, v dois botes. Clicando no boto da esquerda, ou
pressionando ENTER quando o foco estiver neste boto, a sua cor ser mudada. Clicando
no boto direito, ou pressionando ENTER quando o foco estiver neste boto, a aplicao
ser fechada. Para qualquer um desses eventos de mouse ou teclado, voc dever ver uma
mensagem impressa na tela informando a hora e descrevendo o evento.
from Tkinter import *
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
STEVEN FERG
PENSANDO EM TKINTER
self.button2.bind("<Return>", self.button2Click)
### (2)
21
STEVEN FERG
PENSANDO EM TKINTER
22
STEVEN FERG
PENSANDO EM TKINTER
23
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.
Infelizmente, a nica fonte confivel dessa informao o cdigo Tk em si. Para
informaes mais acessveis, d uma olhada nos livros sobre Tk (Practical Programming
in Tcl and Tk, de Brent Welch, especialmente bom) ou sobre Tkinter. A documentao
sobre Tk difusa, mas disponvel on-line.
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.
Usando Event Binding e Command Binding juntos.
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).
O problema que o command binding no transformar um objeto evento num argumento,
mas o event binding sim. Ento como deveremos escrever nosso alimentador de eventos?
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
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.
from Tkinter import *
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)
def button1Click(self): ### (2)
print "button1Click event handler"
if self.button1["background"] == "green":
self.button1["background"] = "yellow"
else:
self.button1["background"] = "green"
def button2Click(self): ### (2)
print "button2Click event handler"
self.myParent.destroy()
def button1Click_a(self, event): ### (3)
print "button1Click_a event handler (a wrapper)"
self.button1Click()
def button2Click_a(self, event): ### (3)
print "button2Click_a event handler (a wrapper)"
self.button2Click()
root = Tk()
myapp = MyApp(root)
root.mainloop()
25
STEVEN FERG
PENSANDO EM TKINTER
26
STEVEN FERG
PENSANDO EM TKINTER
27
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
print "\n"*100 # um jeito simples de limpar a tela
print "Starting..."
root = Tk()
myapp = MyApp(root)
root.mainloop()
print "... Done!"
28
STEVEN FERG
PENSANDO EM TKINTER
Em aplicaes srias, a rotina buttonHandler iria, claro, trabalhar seriamente, mas neste
programa ela meramente imprime os argumentos que recebeu.
Event binding
Chega de command binding. O que diremos sobre 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!"
)
)
29
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.
Veja que a nica maneira de fechar o programa clicando no cone FECHAR (o X na
caixa) no lado direito da barra de ttulos.
Ento rode o programa agora, e veja o que acontece. No prximo programa veremos porque
isso acontece.
from Tkinter import *
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)
def buttonHandler(self, arg1, arg2, arg3):
print "buttonHandler routine received arguments:", arg1.ljust
(8), arg2, arg3
def buttonHandler_a(self, event, arg1, arg2, arg3):
print "buttonHandler_a received event", event
self.buttonHandler(arg1, arg2, arg3)
30
STEVEN FERG
PENSANDO EM TKINTER
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
STEVEN FERG
PENSANDO EM TKINTER
H uma soluo?
Ento... qual a soluo? H algum meio de parametrizar, e reutilizar, uma funo
alimentadora de eventos?
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
H pouco tempo, achvamos que a seguinte linha devesse funcionar:
self.button1 = Button(self.myContainer1,
command = self.buttonHandler(button_name,
1, "Good stuff!"
)
)
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
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.
Eis o que fizemos com o segundo boto:
# event binding sem transmitir o evento como um argumento
self.button2.bind("<Return>",
lambda
event, arg1=button_name, arg2=2, arg3="Bad stuff!" :
self.buttonHandler(arg1, arg2, arg3)
)
Comportamento do programa
Rodando o programa, ele se comportar exatamente como queremos.
Veja que voc pode mudar o foco de teclado entre os botes OK e CANCEL pressionando
a tecla TAB.
Particularmente, tente ativar OK pressionando ENTER. Fazendo isso voc estar
colocando-o para trabalhar pela funo buttonHandler_a, e tambm receber uma
mensagem impressa, informando sobre qual evento foi recebido por ele.
Em qualquer caso, tanto clicando em um dos botes com o mouse, ou solicitando um
widget via ENTER, o programa imprimir direitinho os argumentos que foram transmitidos
pela funo buttonHandler.
from Tkinter import *
class MyApp:
def __init__(self, parent):
33
STEVEN FERG
PENSANDO EM TKINTER
self.myParent = parent
self.myContainer1 = Frame(parent)
self.myContainer1.pack()
#------------------ BOTO N. 1 ----------------------button_name = "OK"
# command binding
self.button1 = Button(self.myContainer1,
command = lambda
arg1=button_name, arg2=1, arg3="Good stuff!" :
self.buttonHandler(arg1, arg2, arg3)
)
# event binding transmitindo o evento como um argumento
self.button1.bind("<Return>",
lambda
event, arg1=button_name, arg2=1, arg3="Good stuff!" :
self.buttonHandler_a(event, 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
#------------------ BOTO N. 2 ----------------------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)
)
# event binding sem passer o evento como um argumento
self.button2.bind("<Return>",
lambda
event, 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)
def buttonHandler(self, argument1, argument2, argument3):
print "buttonHandler routine received arguments:" \
, argument1.ljust(8), argument2, argument3
def buttonHandler_a(self, event, argument1, argument2, argument3):
print "buttonHandler_a received event", event
self.buttonHandler(argument1, argument2, argument3)
print "\n"*100 # limpa a tela
print "Starting program tt078."
root = Tk()
myapp = MyApp(root)
34
STEVEN FERG
PENSANDO EM TKINTER
Como foi discutido sobre lambda, no meu objetivo explicar como funciona currying:
trataremos isso como uma caixa-preta.
Curry como us-lo
A maneira de usar curry (a tcnica, no o tempero!) incluir a classe curry em seu
programa, ou import-la de seu prprio arquivo Python. Neste programa, iremos incluir o
cdigo curry diretamente no programa.
Inicialmente, pensamos que a seguinte linha liga self.buttonHandler opo command de
self.button1, mas descobrimos que isso no funciona da maneira que pensvamos.
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
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.
# ---------- cdigo
def event_lambda(f,
"""Escrevendo
return lambda
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.
Para button2, usamos um processo em dois passos. Primeiro solicitamos a funo
event_lambda:
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).
lambda event, f=f, args=args, kwds=kwds : f( *args, **kwds )
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.
Quando event_lambda retorna a funo annima, damos a ela o nome de event_handler.
4
A utilizao desta tcnica exige verso de Python igual ou superior a Python 2.0
36
STEVEN FERG
PENSANDO EM TKINTER
event_handler=event_lambda(self.buttonHandler,
button_name,2,"Bad stuff!")
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.
Ufa! J torrei um monte de neurnios!
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.
Lambda versus Curry e event_lambda Qual devo usar?
Bem
Escolha o que lhe convier. Como diz o ditado, o cliente sempre tem razo. Use o que for
cmodo para suas tarefas.
A REAL moral dessa histria que...
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
STEVEN FERG
PENSANDO EM TKINTER
38
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.
GRUPO PYTHON UNESP ILHA SOLTEIRA
39
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.
Obs: no especificamos o espaamento do frame como um atributo do frame, e sim como
uma opo que fornecemos ao gerenciador de geometria (neste caso, pack).
(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
STEVEN FERG
PENSANDO EM TKINTER
### (2)
### (2)
buttons_frame_padx = "3m"
### (3)
buttons_frame_pady = "2m"
### (3)
buttons_frame_ipadx = "3m"
### (3)
buttons_frame_ipady = "1m"
### (3)
# -------------- fim das constantes ---------------self.myParent = parent
self.buttons_frame = Frame(parent)
self.buttons_frame.pack(
ipadx=buttons_frame_ipadx,
ipady=buttons_frame_ipady,
padx=buttons_frame_padx,
pady=buttons_frame_pady,
)
### (4)
### (3)
### (3)
### (3)
### (3)
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()
def button1Click_a(self, event):
self.button1Click()
41
STEVEN FERG
PENSANDO EM TKINTER
42
STEVEN FERG
PENSANDO EM TKINTER
self.myParent = parent
### Nosso frame mais importante chama-se myContainer1
self.myContainer1 = Frame(parent)
self.myContainer1.pack()
###
###
###
###
###
# 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
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)
# agora adicionamos os botes a buttons_frame
#------ constantes para controle do layout -----button_width = 6
### (1)
button_padx = "2m"
button_pady = "1m"
### (2)
### (2)
buttons_frame_padx = "3m"
### (3)
buttons_frame_pady = "2m"
### (3)
buttons_frame_ipadx = "3m"
### (3)
buttons_frame_ipady = "1m"
### (3)
# -------------- fim das constantes ---------------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()
def button1Click_a(self, event):
self.button1Click()
def button2Click_a(self, event):
self.button2Click()
root = Tk()
myapp = MyApp(root)
root.mainloop()
44
STEVEN FERG
PENSANDO EM TKINTER
45
STEVEN FERG
PENSANDO EM TKINTER
Side;
Fill;
Expand e
Anchor.
46
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.
O propsito do programa mostrar a voc os resultados das opes de pack. Rodar o
programa vai permitir a voc escolher diferentes opes e observar os efeitos das diferentes
combinaes de opes.
Os conceitos subjacentes da opo pack
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.
Em termos de posicionamento e exibio dos componentes num container, til entender
trs conceitos:
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.
Quando um widget acomodado ao longo de um lado do container, solicitado o lado
inteiro, mesmo que ele no ocupe todo o espao solicitado. Suponha que acomodemos um
pequeno boto chamado X ao longo do lado esquerdo de uma grande cavidade, como na
figura da prxima pgina.
A cavidade (rea no solicitada) agora ao lado direito do widget. O widget X solicitou o
lado esquerdo inteiro, numa tira que larga somente o suficiente para acomod-lo. Por seu
tamanho reduzido, o widget X usa somente uma pequena parte da rea total que foi
solicitada, somente o necessrio para que ele aparea.
47
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.
O frame do boto A d uma cavidade horizontal para o boto se locomover o frame no
muito mais alto que o boto; o frame do boto B d a ele uma cavidade vertical para se
locomover o frame no to mais largo que o boto, e o frame do boto C tem uma
enorme cavidade muito mais alta e larga que o boto para ele se divertir l dentro.
Se a aparncia de qualquer dos botes sob certas configuraes surpreende voc, tente
descobrir por que ele se parece assim.
E finalmente
Um jeito prtico de encontrar erros
Veja que empacotar widgets em containeres um negcio complicado porque o
posicionamento de um widget em relao a outros widgets prximos depende em parte de
como os widgets prximos foram acomodados. Isto , se os outros widgets foram
acomodados esquerda, ento a cavidade dentro da qual o prximo widget ser
empacotado ser direita. Mas se eles foram acomodados no topo da cavidade, ento a
cavidade dentro da qual o prximo widget ser empacotado ser abaixo deles. Tudo isso
uma bela salada!
48
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.
... ou amarelo, ou azul, ou vermelho, o que voc quiser!
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