GUI Programming with Tkinter

Neste tópico, iremos olhar para como programas com GUI (Graphic User Interface) funcionam, iremos abordar este tema de uma forma geral, depois iremos ver como isso é feito no Python usando a ferramenta nativa do Python o Tkinter. Este cápitulo não será de forma alguma um tutorial completo acerca do Tkinter, mesmo porque já existe um tutorial muito completo acerca deste tema, para isso basta ir a homepage do Python, onde poderás encontrar todos esses links. Sendo assim, podemos definir os objectivos deste cápitulo como mostrar so basicos da programação com GUI, introduzindo-te aos componentes mais básicos e a lforma como usa-los. Iremos também ver como a programação orientada a objectos pode ajudar a organizar as aplicações com GUI..

Os princípios que regem a programação com GUI

Antes de mais quero desde já deixar bem claro que não irás aprender nada de novo acerca de programação neste cápitulo. A programação com GUI é exactamente como qualquer outro tipo de programação que estudamos até aqui, podes usar sequências, loops e módulos da mesma forma como os tens usado até aqui. O que é certamente novo e diferente quando programamos GUI é que temos que usar um conjunto de ferramentas (toolkit) e temos que seguir um conjunto de regras e padrões que regem esse conjunto de ferrramentas. Cada set de ferramentas tem o seu próprio API e um conjunto de regras de design, e tu como programador tens que aprender como usa-los. Isto é certamente um dos grandes motivos pelo os quais muitos programadores tentam aperfeicoar-se em usar somente um pequeno grupo de ferramentas, desde que essas mesmas ferramentas estejam disponiveis em várias outras linguagens de programação - é por vezes aprender a usar essas ferramentas para criar GUI é muitas vezes mais dificil que aprender a própria linguagem!

A ferramenta (toolkit) que iremos usar dá-se pelo nome de Tk e é usada em linguagens como o Perl, Tcl e o Python. Os principios que regem o Tk são ligeiramente diferentes dos principios que regem outras toolkits, por isso para terminar este cápitulo eu irei falar (mesmo que de uma forma muito breve) sobre uma outra toolkit, também ela muito popular entre os programadores de Python (e programadores de C/C++), sendo os principios que regem esta toolkit muito mais convencionais. Mas antes de mais vamos ver umas noções gerais em programação de GUI.:

Como já foi dito, várias vezes, as aplicações com GUI são quase sempre programas guiados por eventos. Em caso que não te lembres lá muito bem o que isso significa, então é melhor dar uma vista de olhos no capitulo que fala de programas dirigidos por evntos

Vou partir do principio que como utilizador de computadores, tu já estejas habituado a GUI, pelo menos da optica do utilizador, por isso vou usar a tua experiência como utilizador para te explicar como os GUI funcionam, mas da optica do programador. De forma alguma, vou entrar em detalhes, de como criar programas com complexos GUI de várias janelas e com interfaces MDI, vou ficar pelo basico de criar uma aplicação com uma única janela com umas etiquetas (labels, e vai ser assim que me vou referir a elas durante o resto do texto), caixas de texto (textbox) e botões (buttons).

Mas como dizem os antigos, devemos sempre começar pelo principio, por isso vamos rever o nosso vocabulário. Programar GUI normalmente costuma nos obrigar a saber um novo conjunto de vocabulos. Os termos mais comuns desse novo vocábulo estão na tabela em baixo:

Termo Descrição
Janela

(Window)

Definido como sendo uma area do ecrã controlada por uma aplicação. As janelas são geralmente rectangulares, mas com certas ferramentas para criar GUI, iutras formas são permitidas. As janelas podem conter outras janelas e geralmente cada elemento de controlo de um GUI é tratado como sendo uma janela.
Controladores

(Control)

Um controlador é um objecto pertencente a um GUI que é usado para controlar uma aplicação (hmm, muito donde será que vem o nome?). Os controladores geralmente têm propriedades e geralmente também servem para criar eventos. Os controladores correspodem a objectos da aplicação e normalmente têm sempre metodos ligados directamente a eles. Sendo assim quando ocorre um evento, temos de imediato um dos metodos para lidar com esse evento.
Widget Nem todos os controladores são elementos visuais. Alguns controladores como temporizadores podem ser associados a uma determinada janela e não serem visiveis.
Os widgets são o sub-conjunto de controladores que são visiveis, e que podem ser manipulados tanto pelo programador como pelo utlizador. Os widgets que vamos cobrir são os seguintes:
  • Frame
  • Label (etiquetas)
  • Botões
  • Caixa de texto
  • Caixa de dialogos

Aqueles que não vamos usar, mas que podemos encontrar aqui neste tutor:

  • Text box (caixa de texto)
  • Radio Button (botões de múltipla)

E finalmente, aqueles que não mencionados de todo:

  • Canvas - para desenhar
  • Check button - para multiplas selecções
  • Image - para dispôr imagens BMP, GIF, JPEG e PNG
  • Listbox - para listas!
  • Menu/MenuButton - para a construção de menus
  • Scale/Scrollbar - elementos que nos ajudam a posicionar-mos
Frame Um tipo de widget, utilizado para agrupar outros widgets. Geralmente umas das frames é utilizada para representar a janela da toda a aplicação, depois vão-se adicionando mais frames a essa a medida que se forem adicionando capacidades a aplicação.
Layout Os controladores são colocados dentro das frames de acordo com uma forma especifica de layout. Os layouts podem ser especificados de várias formas, desde de usando as coordenadas do ecrã especificada através dos pixels, a usando coordenadas relativas as posicionamento de outros componementes (esquerda, direita, topo, etc...) ou então usando uma grelha ou tabela para colocar os componentes. Um sistema de coordenadas é em regra geral fácil de compreender mas dificil de gerir quando as janelas são redimensionadas. Os iniciantes são aconselhados a não-usar janelas redimensionavéis em caso que estejam a trabalhar com layouts que funcionem com um sistema de coordenadas
Child As GUI tendem em consistir numa Hierarquia de de widgets/controladores. Temos as frames de topo, que comprimem toda a aplicação, mas essas frames irão conter outras sub-frames que por sua vez poderão conter outras sub-frames, seguindo assim uma hierarquia. Estes controlos poderão ser vistos como uma arvore de estrutural, com cada controlo tendo um único "progenitor", e podendo ter vários "descendentes. Por acaso é até normal serem os proprios widgets a guardarem esta estrutura por forma a que o programador, ou mais comumente o GUI, possa executar certos comandos de controlo sobre um progenitor e todos os seus descendentes.

Uma tour por alguns dos mais comuns widgets

Nesta secção iremos usar a prompt do Python para criar alguns dos mais comuns. Há que fazer notar aqui que, uma vez que o IDLE em si uma aplicação feita em Tkinter não poderas fazer correr as tuas aplicações com grande segurança, na medida em que podes obter resultados inesperados. Mas é claro qeu podes utilizar o IDLE como um editor para criar ficheiros .py, mas depois terás que usar a linha de comandos do teu sistema operativo para correr a tua aplicação.
Os utilizadores do Pythonwin poderão correr as suas aplicações sem problemas, dentro do Pythonwin, visto que este foi criado utlizando as sua propria caixa de ferramentas GUI, o MFC. Mas contudo tqambém não posso deixar de dizer qeu uma vez por outra podes ter experiências desagradaveis com aplicações de Tkinter dentro do Pythonwin. Tudo isto dito a melhor solução será mesmo utilizar a prompt interactiva da linha de comandos para criar as tuas aplicações de TKinter.

>>> from Tkinter import *

Isto é o primeiro requerimento de qualquer aplicação com o Tkinter - "importa" os nomes de todos os widgets. É claro que também poderias importar somente o modulo, mas também é mais que claro que rapidasmente te cansarias de preceder todas as linhas de código com Tkinter.

>>> top = Tk()

Esta linha cria um widget de alto nivel (top level) na tua hierarquia de widgets. Todos os outros widgets serão criados como sendo descendentes (child) deste. Repara como uma janela vazia apreceu no teu ecrã, completa com uma barra de titulo e o usual conjunto de controlo (icon, minimizar, maximizar, etc..). O que iremos fazer agoara é adicionar componentes a widget a medida que vamos criando a nossa aplicação.

>>> dir(top)

['_tclCommands', 'children', 'master', 'tk']

A função dir mostra-nos quais os nomes válidos para ser usados como argumentos. Normalmente é usado em módulos, mas neste caso serve apra olharmos para o "interior" do objecto top, un "instance" da class Tk. Estes são os atributos do top, nota, para os atributos children e para o parent que são links da hierarquia dos widgets, nota também no atributo _tclcommands, isto acontece porque como, podes estar lembrado, O Tkinter foi feito com com uma ferramenta do Tcl chamado Tk..

>>> F = Frame(top)

Crias uma widget frame que por sua vez irá ter descendentes (child) que irão ser os widgets de controlo que são utilizados por nós. A Frame especifica top como sendo o seu primeiro parametro ( e neste caso o único), significando que F é um descendente de top. Exprimenta um bocado com o comando dir() a volta de F.

>>>F.pack()

Repara agora em como a janela encolheu, é qeu ela agora encolheu para o tamanho da Frame, que nós acabamos de adicionar (como a Frame de momento ainda não tem nada!!). O metodo pack() chama o Layout manager, este Layout manager é muito simples de usar para aplicações simples, ou seja com poucos widgets, mas assim que começares a acrescentar demasiadas capacidades a tua aplicação ela torna-se um bocado vagarosa. Mas é no entanto o que vamos usar até porque as aplicações que vamos criar são muito simples e este é um Layout manager fácil de compreender. Devo dizer que nenhum widget fica visivel até utilizarmos o método do Layout manager escolhido para o tornar visivél (neste caso, enquanto não utilizarmos o metodo pack()).

>>>lHello = Label(F, text="Hello world")

Aqui criamos um novo objecto o lHello, um instance da class Label (etiqueta), esta novo widget tem como progenitor o widget F e como atributo de text o famoso "Hello World". Uma vez que os constructors dos objectos do Tkinter normalmente constumam ter muitos parametros, também é normal usar-se a técnica de directamente passar-se argumentos para os objectos. Repara também como a label ainda não esta visivél, isto porque ainda não aplicamos o metodo pack().
Como ainda nota de rodapé são as convenções para dar nomes aos objectos. No meu caso e coloco uma letra minuscula ,l, de Label precendendo o nome Hello, o que me serve para me lembrar a sua origem como variavel, mas como a maioria das convenções de nome, é tudo uma questão de gosto pessoal. Eu cá por mim penso que é útil.

>>>lHello.pack()

Pronto, agora já esta visivél. Esperançosamente o teu terá mais ou menos este aspecto:

Janela com uma etiqueta

Podemos especificar outras propriedades mais as Label, propriedades como o tipo de letra e a cor, tudo isto usando os parametros do constructor. Mas também podemos aceder a essas mesmas propriedades usando o metodo configure para widgets do Tkinter, como se segue:

>>> lHello.configure(text="Goodbye")

E assim mudamos a mensagem, e viram como foi fácil. configure torna-se numa técnica muito útil quando queremos mudar várias das propriedades de uma só vez, isto porque todas as propriedades podem ser usadas como argumentos. Contudo, se apenas quiseres mudar uma das propriedades, como foi o caso acima, podemos tratar o objecto como se de um dicionário se tratasse:

>>> lHello['text'] = "Hello again"

O que é mais curto e (discutivelmente) mais fácil de compreender.

As labels são os widgets mais chatos por assim dizer, porque as suas únicas capacidades são a de dispôr texto, mesmo que em vários tipos de letra e em várias cores (também podem ser usados para dispôr pequenas imagens, mas disso falaremos mais tarde).

Antes de falarmos de outros objectos, ainda tenho mais uma coisa a acrescentar, o titulo da janela. Isso é feito usando um metodo no widget de alto nivel o top:

>>> F.master.title("Hello")

Poderiamos ter usado o top directamente, mas como mais tarde verás, aceder das propriedades do Frame master é uma técnica muito mais coerente.

>>> bQuit = Button(F, text="Quit", command=F.quit)

Aqui criamos um novo widget, um botão, O botão que acabamos de criar tem uma label "Terminar" e eta associado ao comando F.quit. Repara que apenas passamos o nome do método como argumento, não chamamos o método, isso só aconteceria se adicionassemos parentesis no final. Isto quer dizer que passamos um objecto função do Python, esta função tanto pode ser um metodo já existente no Tkinter, como neste caso, como pode ser um metodo por nós criado. A função ou método, não pode ter argumentos. O metodo quit tal como o metodo pack é definida na classe progenitora e é herdada por todos os outros widgets.

>>>bQuit.pack()

Mais uma vez o metodo pack torna as coisas visiveis. Agora ao pressionarmos com o rato no botão, a aplicação será fechada. Se estiveres a correr este exemplo no Pythonwin ou no IDLE poderás obter resultados diferentes. Mas de qualquer das maneiras a forma como vais criar e correr os teus programas GUI, vai ser criando um python script e depois correr o programa a partir da linha de comandos. Vais usar exactamente os mesmo comando que usaste até aqui, apenas vais adicionar mais uma linha no final.

from Tkinter import *

#criar a janela propriamente dita
top = Tk()
F = Frame(top)
F.pack()

#adicionar os widgets
lHello = Label(F, text="Hello")
lHello.pack()
bQuit = Button(F, text="Quit", command=F.quit)
bQuit.pack()

#pôr o loop a correr
F.mainloop()

A chamada ao metodo F.mainloop, da inicio ao loop gerador de eventos. Neste caso o único evento a ser capturado há-de ser o o carregar no botão Quit, que por sua vez esta ligado ao metod Fquit, esse metodo quando chamado, termina com a aplicação. O aspecto final da nossa pequena aplicação é este:

Etiqueta e um botão

Explorando os Layout

Nota: Os exemplos fornecidos a partir de agora, já não são mais para para a >>> prompt do Python, mas sim para serem inseridos dentro de ficheiros, para mais tarde serem executados.

Nesta secção vou querer dar uma vista de olhos na forma como o Tkinter posiciona os widgets dentro de uma janela. Os widgets Frame, botão e label foram os que nós já estudamos até aqui, e vão ser exactamente aqueles que vamos precisar para podermos entender esta secção. Nos exemplos anteriores nós usamos o metodo pack para colocarmos e tornarmos um widget visivel e dentro do seu widget progenitor. Tecnicamente falando o que nós fizemos foi invocar o Layout manager do Tk o packer. O trabalho Layout manager é encontrar a melhor forma de posicionar os widgets, tendo em conta certas instruções que podem ser dadas pelo programador, ou acções do utilizador, como redimensionar as janelas. Alguns Layout managers usam localizações exactas dentro da janela, localizações especificadas em pixels, isto é normal em ambientes da Microsoft Windows como por exemplo o Visual Basic. O Tkinter tem um Layout managerchamado Placer que funciona de uma forma semelhante, mas não vou falar disso neste tutorial, porque o que na minha definição de existem Layout managers muito mais inteligentes, uma vez que tomam por sua conta a tarefa de preocuparem com tarefas como por exemplo, quais as acções a seguir caso a janela seja redimensionada, deixando nós os programadores preocuparmos com coisas mais agradavéis.

O mais simples dos Layout manager do Tkinter é o packer, que é o que temos usado até agora. O packer por defeito só amontoa widgets uns em cima de outros o que muito raramente há-de ser o nosso objectivo (mas também é tudo uma questão de gosto e estetica), a não ser que a nossa aplicação tenha sido construida em várias Frames e depois é só coloca-los uns em cima dos outros. Como foi o que aconteceu no nosso Case Study .

Mesmo simples de usar, packer, ainda fornece multiplas opções, por exemplo podemos organizar os nossos widgets horizontalmente, em vez de verticalmente, para isso basta fornecer o argumento side:

lHello.pack(side="left")
bQuit.pack(side="left")

Isto irá forçar que os widgets a aparecerem do lado esquerdo do widget progenitor. Sendo assim se modificares a linha de código como acima no teu script poderás ver que a label aperece do extremo lado esquerdo, seguido logo do botão, tal como na ilustração abaixo:

Tudo a esquerda

E se em vez de "left" (esquerda) colocares "right" (direita), verás aí que a label há-de aparecer do lado direito com o botão a sua direita. Assim:

Tudo a direita

Talvez tenhas notado que este aspecto talvez não seja dos mais apelativos, isto porque os widgets estão comprimidos num minimo de espaço necessário. O packer também fornece um outro parametro para lidar com esse problema. O mais fácil das soluções apresentadas pelo packer é o Padding que nos permite especificar as distancias horizontais (padx) e as distancias verticais (pady). Este valores do Padding são especificados em pixels. Vamos ver como funcionam :

lHello.pack(side="left", padx=10)
bQuit.pack(side='left', padx=10)

O resultado final deve ser algo deste genero:

O padding na Horizontal

Em caso que tenhas tentado redimensionar as janelas podes ter visto que os widgets mantem a sua distancia relativamete um ou outro mas contudo mantem-se centralizados em relação a janela. Então mas porque é que isso acontece se nós especificamos que os queriamos a esquerda? A resposta é que nós as posicionamos dentro da frame, mas a frame não tem nenhuma especificação relativa ao seu posicionamento. Assumindo assim os valores default do metodo o top (top) centre (centro). Se queres que os widgets se mantenham do lado correcto de onde os posicionaste, terás que especificar um posicionamento para a Frame onde os colocaste:

F.pack(side='left')

Repara também em como os widgets se mantem centrados se redimensionares as janelas verticalmente - mais uma vez isso acontece porque assumen os valores default do packer.

Vou deixar-te brincar um pouco com o padx e o pady, para que vejas por ti mesmo quais são os efeitos que podes conseguir usando estas duas ferramentas com diferentes valores e combinações. Usando o side e o padx/pady já obtens uma grande versatilidade relativamente ao posicionamento dos widgets enquanto usando o metodo packer. existem ainda muitas outras opções que podes usar como argumento, cada uma delas adicionado mais uma subtilidade a forma de controlo, para mais informações da uma vista de olhos na página do Tkinter.

Existem ainda um par de outros Layout managers no Tkinter o grid (grelha) e o placer (que numa tradução livre seria o colocador). Para usares o grid basta colocares grid() em vez de pack() e paara o placer basta colocares place() em vez de pack(). Cada um deles há-de ter os seus proprios argumentos e conjuntos de opções, mas desde o inicio que sempre disse qeu isto seria uma introdução muito basica ao Tkinter, se quiseres obter mais informações acerca destes dois metodos, terás que fazer umas investigações por ti mesmo. De qualquer das maneiras manten em mente que o grid faz o posicionamento usando uma grelha (hmm, de onde terá vindo essa ideia), isto por vezes pode ser muito útil, por exemplo com caixas de dialogo que tenham caixas de texto e estas têm qeu estar alinhadas . O placer usa ou coordenadas absolutas em pixels, ou coordenadads relativas dentro da janela, tendo esta última a vantagem de permitir que o componente se redimensione juntamente com a janela, ocupando sempre 75% da parte superior da janela, um exemplo. Isto é muito útil e muito bonito, mas requer um grande pre-planeamento, porque quando não bem utilizado pode se tornar numa catastrofre.

Controlando a aparência usando Frames e o packer

A frame tem muitas propriedades que nos podem ser muito úteis. É muito bom termos todos os nossos componentes agrupados num só local, mas porque não explorar mais as capacidades desse elemento. As frames que usamos até agora, não são visiveis, mas por vezes é util elas estarem a vista, como nocaso de querermos agrupar botões de selecção múltipla, os Radio Buttons e os Check Buttons. A frame resolve este problema de visibilidade com a propriedade relief. Relief pode ter vários valores, sunken, raised, groove, ridge ou flat . Vamos usar o sunken na nossa caixa de dialogo, para isso basta fazres esta alteração na linha de código onde se cria a frame:

 F = Frame(top,relief="sunken", border=1) 

Nota 1:Precisas de fornecer um valor para border (limites). Caso falhes em fornecer esse valor, não irás notar nenhuma diferença an aplicação!

Nota 2:Como já deves ter reparado, não colocaste o valor da border entre aspas. Este é um dos aspectos que pode causar confusão ao inicio, o quando usar ou não usar aspas. Regra geral é que quando é apenas um caracter ou um valor númerico, então podemos deixar as aspas de fora. Se for uma mistura dos dois ou uma string, então vais precisar de aspas. Um outro aspecto são as maiusculas e as minusculas, porque existem certas keywords que tem que ser em maiusculas e outras em minusculas. Infelizmente não existe uma regra geral para este casos e terás que aprender baseando-te na experiência - O Python muitas vezes fornece uma lista de nomes válidos nas suas mensagens de erro!

Uma outra coisa que possas ter notado é que a frame não preenche a janela, esse "problema" pode ser resolvido com uma opção do packer que é o fill, assim:

F.pack(fill="x")

O exemplo acima é para um preenchimento horizontal, caso queiras um preenchimento vertical podes usar o fill="y".

O resultado final há-de ser este:

Frame

Adicionando mais widgets

Vamos agora dar uma vista de olhos por um widget com entrada de texto. Este widget que vamos ver é para as entradas de texto com apenas uma linha. tem muitas similaridades com o widget Text, sendo este último muito mais sofisticado, mas não o iremos estudar aqui. Mas esperançosamente com as bases que te fornecemos aqui no estudo do Entry widget poderás mais tarde exprimentar com muito mais a-vontade com o Text widget.

Voltando ao nosso programa "Olá Mundo", vamos adicionar um widget de texto com uma frame propria e um botão para limpar o texto que foi lá introduzido. Isto não só te irá permitir ver como se cria um Caixa de texto mas também ensina-te como criar as tuas proprias funções para lidar com os eventos e como ligar essas funções aos seus respectivos widgets.

from Tkinter import *

# cria o event handler promeiro
def evClear():
  eHello.delete(0,END)

# cria a janela/frame de alto nivel
top = Tk()
F = Frame(top)
F.pack(expand="true")

# agora a frame com a caixa de texto
fEntry = Frame(F, border="1")
eHello = Entry(fEntry)
fEntry.pack(side="top", expand="true")
eHello.pack(side="left", expand="true")

# e finalmente uma frame com os botões. 
# vamos usar o sunken neste só para dar um gosto diferente
fButtons = Frame(F, relief="sunken", border=1)
bClear = Button(fButtons, text="Clear Text", command=evClear)
bClear.pack(side="left", padx=5, pady=2)
bQuit = Button(fButtons, text="Quit", command=F.quit)
bQuit.pack(side="left", padx=5, pady=2)
fButtons.pack(side="top", expand="true")

# agora cria o loop que vai fazer correr o programa
F.mainloop()

Repara que mais uma vez passamos o nome responsável por eventos(evClear) como o ,comando do Botão bClear. E repara também como se tenta manter a convenção de nomes ao chamar o metodo de evXXX.

Correndo o programa, obtem-se isto como resultado:

Entry and button controls

E se escreveres qualquer coisa na entrada de testo e depois presionares o botão limpar, o texto é removido.

Ligando eventos - dos widgets para o código

Até agora apenas utilizamos os propriedade command dos botões para associar as funções do Python com os eventos do GUI. Mas há-de haver vezes em que querer um controlo mais explicito das acções. por exemplo como reagir a uma combinação de teclas. Quanto tal situação surgir podemos usar a função bind (ligar) para explicitamente ligar um certo evento a uma função do Python.

O que que vamos fazer agora é definir uma hotkey - por exemplo o Ctrl-c - para eliminar o texto escrito anteriormente. Para fazer isso temos que ligar a combinação de teclas a mesma função que o botão Clear. Infelizmente existe um um pequeno problema com isto. É que quando definimos uma função para ser utilizada com a propriedade command, essa função não pode ter argumentos, mas quando usamos a função bind para executar o mesmo trabalho a função a que se liga tem que ter pelo menos um argumento. Sendo assim temos então que criar uma nova função com um único parametro e que depois chama a função evLimpar. Para ver isso mais concretamente adiciona as seguinte linhas ao teu código:

def evHotKey(event):
    evClear()

E adiciona a seguinte linha a definição do widget Entry:

eHello.bind("",evHotKey) # atenção que esta definição é case sensitive 

Ao correres o programa, vês que a partir de agora já podes limpar a caixa de texto tanto quando presionas no botão Clear ou com a combinação de teclas Ctrl-c. Também poderiamos utilizar o bind para outras acções como capturar eventos vindos do rato ou da janela, mas se queres mais informações acerca disso é melhor consultares a documentação do Tkinter!

Uma curta mensagem

Tu podes criar curtas mensagens para comunicar com o utilizador, através da caixas de dialogo. Isto é muito facilmente conseguido, bastando para isso usar as funções do módulo tkMessageBox, como nos exemplos que se seguem:

import tkMessageBox
tkMessageBox.showinfo(("Window Title", "A short message") 

Existem também mensagens de erro, aviso, Sim/Não e Ok/Cancelar, estão todas disponiveis via função showXXX. Estas caixas de dialogo distiguem-se entre si pelos diferentes icons e botões. Os dois últimos usam a função askXXX em vez de showXXX, e retorman um valor para indicar qual dos botões o utilizador presionou. Exemplo:

res = tMessageBox.askokcancel("Which?", "Ready to stop?")
print res

Aqui estão mais alguns exemplos de caixas de dialogo:

Mensagem informação   Mensagem erro   Mensagem Sim/Não

A vista a partir do Tcl

Uma vez que no inicio deste tutorial estivemos a comparar o Python ao Tcl, parece-me mais do que justo mostrar como é os primeiros exemplos do label e dos botões seriam em Tcl:

Label .lHello -text "Hello World"
Button .bHello -text Quit -command "exit"
wm title . Hello
pack .lHello .bHello

Como podes ver é muito conciso. A hierarquia é formada atráves de uma convenção de nomes com o '.' sendo o widget de alto nivel. Como costume os widgets do Tcl são comandos com os propriedades passadas como argumentos. Mas penso que a tradução de Tcl para Python seja mais ou menos obvia, isto significa que sempre podes consultar muita da documentaçao do Tcl/Tk para resolver muitos dos problemas que possas ter quando programando com o Tkinter.

E pronto isto é tudo, pelo menos no que diz rspeito ao Tk, mas anter de terminar deixem-me mostrar uma técnica muito usada para construir aplicações de Tk como objectos.

Aplicações como objectos.

É uma practica comum enquanto programando GUI, ter a toda a aplicação como uma classe. Isto é claro que traz a questão de como é colocamos os widgets dentro da estrutura da class? Bom, temos duas escolhas possiveis ou decidimos por tornar a aplicação numa subclasse do Tkinter Frame ou então ter um dos campos a referenciar a janela de alto nivel. Esta última solução é mais comum, mesmo quando se esta falando de outras toolkits, por isso é a nossa escolha. Em caso que queiras ver como o outra forma funciona, então dá um pulinho ao Programas guiados por eventos. (O exemplo que lá irás encontrar também mostra o como a caixa de texto do Tkinter pode ser versatil).

O que proponho a fazer é converter o exemplo acima, usando uma caixa texto, um botão para limpar e um botão terminar, pegar em tudo isso e transformar numa estrutura OO. Então primeiro criamos uma Aplicação class e dentro do constructor juntar as partes visuais do GUI.

Atribuimos a Frame resultante a self.mainWindow, permitindo assim que os outros metodos da class aceder a Frame de alto nivel. Os outros widgets que possivelmente havemos de querer aceder (tal como a caixa de texto, por exemplo), atribuimos variaveis menbros da class. Usando esta técnica os event handlers tornam-se metodos da classe (que é no fundo a aplicação em si) aplicação, e todos ficam a poder aceder a qualquer outro tipo de dados, pertencentes a qualquer parte da aplicação (embora neste caso não haja nenhum),usando a referência self. Isto tras uma perfeita interação do GUI com os objectos da class:

from Tkinter import *
     
class ClearApp:
   def __init__(self, parent=0):
      self.mainWindow = Frame(parent)
      # cria um widget caixa de texto
      self.entry = Entry(self.mainWindow)
      self.entry.insert(0,"Hello world")
      self.entry.pack(fill=X)
      
      # agora adiciona 2 botões
      fOuter = Frame(self.mainWindow, border=1, relief="sunken")
      fButtons = Frame(fOuter, border=1, relief="raised")
      bClear = Button(fButtons, text="Clear", 
                      width=8, height=1, command=self.clearText)
      bQuit = Button(fButtons, text="Quit", 
                      width=8, height=1, command=self.mainWindow.quit)
      bClear.pack(side="left", padx=15, pady=1)
      bQuit.pack(side="right", padx=15, pady=1)
      fButtons.pack(fill=X)
      fOuter.pack(fill=X)
      self.mainWindow.pack()

      # coloca um titulo
      self.mainWindow.master.title("Clear")
      
   def clearText(self):
      self.entry.delete(0,END)
      
app = ClearApp()
app.mainWindow.mainloop()

E aqui esta o resultado:

Versão OOP

O resultado parece ter umas semelhanças incrivéis com a sua anterior encarnação, apesar de termos alterado um pouco a frame de baixo pra lhe dar um aspecto mais groovy e eu dei uma largura idêntica aos botões para dar um aspecto mais parecidos, com os que vamos ver no exemplo do wxPython abaixo..

É claro que não é só a aplicação principal que podemos transformar em OO. Podiamos criar class baseada na numa Frame contendo um conjunto botões standarde depois reusar a class na criação de caixas de dialogo, isto é apenas um exemplo. Se quisemos podiamos até criar dialogos inteiros e usa-los depois em vários projectos diferentes.Ou então podes podes extender as capacidades do widgets base, criando subclasses das mesmas - e então criar um botão que muda de cor dependo da sua estado. Como podes ver o mundo esta cheio de possibilidades. Aliás isto é o que tem sido feito com o Python Mega Widgets (PMW), que é uma extensão do Tkinter e do qual podes fazer um download.

As alternativas - wxPython

Existem muitas outras alternativas disponiveis para a criação de GUI, mas uma das mais populares é o wxPython que por sua vez é uma implementação feita em volta da toolkit do C++ para Windows O wxWindows. o wxPython tem uma aparência muito mais tipica do que o Tkinter. E também tem mais funcionalidades standarts que o Tk - coisas como barra de status com tips, que em Tk teriam que ser criadas e trabalhadas a unha no Tk. Vamos usar o wxWindows para recriar o exemplo do "Olá Mundo" com uma label e um botão.

Deixem-me dizer que não vou entrar muito em detalhe com isto, se quiserem saber mais informações sobre o wxPython e o seu funcionamento, podem ir ao proprio wxPython website.

Em termos gerais as toolkkits definem uma framework, que depois nos permite a criação de janelas e popular essas mesmas com controladores que estão ligados a metodos dentro da aplicação. Isto é um conceito muito proprio da OOP portanto aconselho o uso de metodos em vez de funções. agora vamos ao exemplo:

from wxPython.wx import *

# --- define um frame para ser a frame principal ---
class HelloFrame(wxFrame):
   def __init__(self, parent, ID, title, pos, size):
        wxFrame.__init__(self, parent, ID, title, pos, size)
	# vamos precisar de um painel para o fundo
        panel = wxPanel(self, -1)

	# agora é criar os widgets de botões e caixas de texto
	self.tHello = wxTextCtrl(panel, -1, "Hello world", (3,3), (185,22))
        button = wxButton(panel, 10, "Clear", (15, 32))
        button = wxButton(panel, 20, "Quit", (100, 32))

	# agora liga o botão ao sua função
        EVT_BUTTON(self, 10, self.OnClear)
        EVT_BUTTON(self, 20, self.OnQuit)
	
   # estes são os metodos que lidam com as acções ocoridas
   def OnClear(self, event):
       self.tHello.Clear()
       
   def OnQuit(self, event):
       self.Destroy()

# --- define o objecto aplicação ---
# repara que todos os programas de wxPython têm qeu definir 
# uma classe que devire de wxApp
class HelloApp(wxApp):
   def OnInit(self):
       frame = HelloFrame(NULL, -1, "Hello", (200,50),(200,90) )
       frame.Show(true)
       # self.setTopWindow(frame)
       return true

# cria uma instancia e inicia o loop
HelloApp().MainLoop()

E eis o resultado:

 Ola na versão wxPython

Pontos a notar são, o uso da convenções relativamente aos nomes dos metodos que são chamados pela framework - onXXX. Repara também no funções EVT_XXX para ligar os eventos aos widgets - existe uma vasta familia destas funções. wxPython tem uma vasta tabela de widgets, muito mais que o Tkinter, e podes criar GUI muito sofisticados.
Infelizmente tende para o uso de coordenadas para posicionar os controles o que torna a tarefa de programar um bocado entediante. Existe um programa para construir GUI, mas só para venda, mas esperançosamente alguém há-de aparecer com uma coisa semelhante mas só que de graça..

Isto é apenas uma curiosidade, mas repara como os dois exemplos (que apesar de usarem dois toolkits diferentes), têm exactamnte o mesmo número de linhas de código executavél - 21.

Em conclusão, se quiseres um GUI construido rapidamente, e sabes de antemão que vai ser uma aplicação baseada em texto, então o Tkinter há-de ser a escolha acertada, poruqe não exige esforço quase nenhum da tua parte. Mas em caso que queiras construir algo muito mais sofisticado que funcione várias plataformas então aí recomendo uma atenção especial ao wxPython.

Outras ferramentas para a criação de GUI são o MFC, o pyGtk, pyQt, sendo estes dois últimos especificos do Linux, mas potencialmente podem ser importados para o Windows e finalmente existe o curses, que é a base de texto. Muitas destas lições aprendidas com o Tkinter podem ser aplicadasas outras ferramentas, mas casda qual com as suas pequenas particularidades e caracteristicas. Então escolhe e seja bem-vindo ao maravilhoso mundo dos GUI.

E por afgora chega, isto não teve a intenção de ser uma página de referência do Tkinter, mas sim uma pequena iniciação. Podes ver a secção Tkinter na homepage do Python para obteres mais recursos.

Existem também vários livros acerca do assunto, Tcl/Tk e pelo menos um acerca do Tkinter. Eu contudo irei voltar a esta temática no case study, onde irei mostrar uma maneira de encapsular um batch mode program dentro de um GUI, para lhe tornar mais user-friendly e mais usavél.


Anterior Próxima Índice
 

Em caso que tenhas alguma dúvida ou queiras comentar esta página envia-me um e-mail para: babyboy@oninet.pt

Programas dirigidos por eventos