Anda di halaman 1dari 13

Computadores e Programação

Engª Biomédica
Departamento de Física
Faculdade de Ciências e Tecnologia da Universidade de Coimbra
Ano Lectivo 2003/2004

F I CHA 1 1
INTERFACE GRÁFICA EM PYTHON

11. Objectivos
Conhecimentos que o aluno deverá obter após realização da ficha:
- Compreender a necessidade de interfaces gráficos;
- O módulo Tkinter;
- Posicionamento de widgets;
- Elementos gráficos do Tkinter.

11.1. Porquê usar Interfaces Gráficas?


O número de aplicações que o aluno desenvolveu ao longo do semestre, permite-lhe de certa forma,
formar uma opinião crítica relativamente à eficiência de um programa. Por outro lado, tornou-se
também evidente, que, por muito completo e eficiente que um algoritmo seja, se o seu controlo por
parte de um utilizador com poucos conhecimentos, se mostrar difícil, este não retirará daquele
qualquer proveito. De facto, um programa deve ser intuitivo, o seu design deve ser apelativo e
objectivo. As funcionalidades que um programa permite deverão estar bem visíveis, e a forma de as
operar deverá ser clara ao programador. É pois, necessário “vestir” o algoritmo com um interface
apelativo ao utilizador. Desta forma, uma parte importante na aprendizagem de uma linguagem de
programação consiste no estudo das bibliotecas que esta dispõe para interfaces gráficos com o
utilizador. Muitas das linguagens de programação actuais disponibilizam essas bibliotecas gráficas
para auxílio do programador no desenvolvimento de interfaces amigáveis. A biblioteca
multiplataforma de interfaces gráficas mais conhecida é o Tkinter.

Computadores e Programação – Engª Biomédica 2003/04 – Ficha 11 Página 1 de 13


11.2. O módulo Tkinter
O módulo Tkinter é um binding, isto é, uma biblioteca que disponibiliza uma interface gráfica
com o utilizador (Graphical User Interface – GUI) em Python. O Tkinter baseia-se no Tk GUI
toolkit, e apesar de não ser o único GUI em Python, é no entanto o único portável entre plataformas
Windows, Unix e Macintosh. Para o usar, é necessário importar o módulo, disponibilizando as
classes do Tkinter ao programador:

>>> from Tkinter import *

11.2.1. Um programa simples uma janela com mensagem


O exemplo seguinte permite abordar de forma simples a criação de interfaces. Suponha o seguinte
código:

from Tkinter import *

Janela = Tk()
Mensagem = Label(Janela, text = "A minha primeira janela...")
Mensagem.pack()
Janela.mainloop()

Após a sua execução deverá aparecer uma janela semelhante à seguinte:

A linha seguinte cria um objecto que é a principal janela da aplicação:

Janela = Tk()

É comum uma aplicação ter apenas uma janela, a partir da qual derivam todos os elementos gráficos
que a possam compor. Esses elementos podem ser menus, listas de elementos a seleccionar pelo
utilizador, botões, etc., e no caso são considerados filhos da janela principal (a gestão de janelas
segue na maioria dos casos um modelo hierárquico). A janela que acaba de ser criada é bastante

Computadores e Programação – Engª Biomédica 2003/04 – Ficha 11 Página 2 de 13


simples, sendo composta por um contorno e uma barra de título, ou seja, possui as decorações
básicas que o gestor de janelas exige.
O passo seguinte analisa a mensagem de texto.

Mensagem = Label(Janela, text = "A minha primeira janela...")

A instrução cria um Label, isto é, um campo de texto intitulado Mensagem pertencente à janela
mãe Janela. O texto "A minha primeira janela..." fica associado a este campo. Para
que o campo de texto Mensagem fique completamente visível, o método pack faz o seu
redimensionamento. Caso não fosse usado, uma parte da mensagem de texto poderia não estar
visível (em comum ver este tipo de erro em determinadas aplicações). A instrução que realiza esta
operação é:

Mensagem.pack()

Finalmente, é necessário desencadear todo o processo da janela. Por esse motivo se usa:

Janela.mainloop()

A maioria das interfaces gráficas funciona por eventos. Um evento pode ser o movimento do rato, o
clicar deste num botão da interface, o premir de uma tecla, etc. e espera-se que a janela esteja
sempre alerta na eventualidade de que um evento ocorra. Assim, uma aplicação que contenha uma
interface, deve estar permanentemente em vigilância num ciclo, a partir do qual executa
determinadas funções associadas aos eventos que vão surgindo. Se o utilizador clicar com o rato
num determinado botão, espera-se que essa acção desencadeie uma determinada funcionalidade. É
da responsabilidade do ciclo mainloop mapear os eventos de uma interface nas acções
correspondentes. Para tal, o método Janela.mainloop() executará enquanto a janela não for
fechada pelo utilizador.
Parabéns, acaba de criar a sua primeira interface gráfica!

Computadores e Programação – Engª Biomédica 2003/04 – Ficha 11 Página 3 de 13


11.2.2. Acrescentando funcionalidades ao exemplo anterior
Suponha agora que se pretendia acrescentar à janela um botão, ao qual se desejava associar uma
funcionalidade. Imagine que essa funcionalidade consistia em terminar a aplicação sempre que o
utilizador premisse o botão (ocorrência de um evento).
As bibliotecas GUI baseiam o tratamento dos seus elementos gráficos em objectos. Desta forma,
uma interface não é mais do que a composição de vários objectos, alguns dos quais desencadeiam
eventos. No caso do exemplo que se pretende implementar, a interface é constituída por um objecto
janela, e por um objecto botão. Analise o código que se apresenta:

def Sair():
print "Vou-me embora..."
jan.destroy()

from Tkinter import *

jan=Tk()
etiqueta=Label(jan, text="Premir botao para sair da aplicacao")
etiqueta.pack()
botao=Button(jan, text="SAIR", fg="red", command=Sair)
botao.pack()
jan.mainloop()

Há neste exemplo duas novidades relativamente ao anterior. Em primeiro lugar, criou-se o objecto
botão (instanciando um objecto da classe Button). Ao objecto foi dado o nome botao, e foi-lhe
indicada a sua janela mãe jan, bem como o texto que o botão deverá indicar "SAIR" e ainda a
cor das letras no mesmo. A segunda novidade prende-se com a associação de um evento nesse
botão a uma determinada acção. A função Sair() definida no início do script ficará associada a
esse evento. Sempre que o utilizador premir o botão, a função Sair() executa, terminando a
aplicação. Todas estas novidades são conseguidas através da introdução da linha de código:

botao=Button(jan, text="SAIR", fg="red", command=Sair)

Teste a interface e tente compreender todas as linhas de código.

Computadores e Programação – Engª Biomédica 2003/04 – Ficha 11 Página 4 de 13


Complicando um pouco mais o exemplo, vamos agora criar um interface, que permita ao utilizador
optar por três botões dentro de uma janela. Uma vez mais, a interface é composta por vários
objectos. A cada botão deverá estar associada uma funcionalidade.

Janela

Botão Soma

Botão Subtrai

Botão Sair

Neste momento, criar uma janela com três botões já não é novidade. Interessante é agora perceber
como se podem associar acções aos botões. No caso específico deste novo exemplo, um dos botões
efectuará o incremento, enquanto que outro efectuará o decremento de uma mesma variável comum
a ambos. O valor dessa variável poderá ser definido pelo programador antes de executar o script. O
terceiro botão servirá para fechar a janela e terminar a aplicação. Os nomes são os indicados na
figura. Associar um comando ou uma função a um objecto Button é, como se ilustrou no exemplo
anterior, definido no momento de criação deste tipo de elementos gráficos (widgets). Com efeito, ao
criar um objecto Button, é possível passar como parâmetro ao seu construtor o command a
executar, se o utilizador accionar um evento naquele objecto. Neste caso, o command associado a
cada objecto será um método, como se descreve de seguida. Vamos então tentar criar o esqueleto da
classe Janela, que será o nosso objecto principal, e que é por sua vez composto por três outros
objectos do tipo Button:

from Tkinter import *

class Janela:
valor=0

def __init__(self, mae, val):


................
def CarregouSoma(self):

Computadores e Programação – Engª Biomédica 2003/04 – Ficha 11 Página 5 de 13


............
def CarregouSubtrai(self):
...........
def CarregouSair(self):
..............

De seguida, criam-se os objectos que formam a classe. O local indicado para o fazer, é, como
provavelmente já deve ter compreendido, no construtor.

def __init__(self, mae, val):


self.frame=Frame(mae)
self.frame.pack()
self.valor=val

self.BotaoSoma=Button(self.frame, text="SOMA 1", fg="red",


command=self.CarregouSoma)
self.BotaoSoma.pack()

self.BotaoSubtrai=Button(self.frame, text="SUBTRAI 1", fg="blue",


command=self.CarregouSubtrai)
self.BotaoSubtrai.pack()

self.BotaoSair=Button(self.frame, text="SAIR", fg="green",


command=self.CarregouSair)
self.BotaoSair.pack()

Além do argumento self que representa o próprio objecto, o construtor recebe o argumento mae,
que indica a janela (interface) que deverá conter a instância da classe Janela. A gestão de janelas
é normalmente feita de forma hierárquica, e desta forma se define a posição de cada elemento
gráfico nessa hierarquia. Adicionalmente, o construtor recebe um terceiro parâmetro, que é o valor
inicial da variável que a incrementar ou decrementar. Finalmente são criados os botões. Para tal,
instanciam-se três objectos da classe Button, chamados BotaoSoma, BotaoSubtrai e
BotaoSair. A todos eles é indicada a janela mãe, o texto que deverão mostrar, a cor desse texto e
finalmente, o método ao qual um evento que neles ocorra é associado (a função que é chamada
quando o utilizador carregar no botão).

Computadores e Programação – Engª Biomédica 2003/04 – Ficha 11 Página 6 de 13


Falta apenas definir os callbacks dos botões. Um callback é um método ou função que não é
directamente invocado pelo utilizador. Assim sendo, pode apenas ser desencadeado pela ocorrência
de um evento exterior ao código, por exemplo, através do premir dos botões por parte do utilizador.
Os callbacks implementados neste exemplo manipulam o atributo valor da classe Janela.
Pressionando um botão incrementa-se a variável, enquanto que o outro botão lhe faz a operação
inversa. O botão SAIR termina a aplicação, fazendo para tal uso do método quit na frame1 que
contém os botões. Apresenta-se de seguida o código completo:

from Tkinter import *

class Janela:
valor=0

def __init__(self, mae, val):


self.frame=Frame(mae)
self.frame.pack()
self.valor=val

self.BotaoSoma=Button(self.frame, text="SOMA 1", fg="red",


command=self.CarregouSoma)
self.BotaoSoma.pack()

self.BotaoSubtrai=Button(self.frame, text="SUBTRAI 1", fg="blue",


command=self.CarregouSubtrai)
self.BotaoSubtrai.pack()

self.BotaoSair=Button(self.frame, text="SAIR", fg="green",


command=self.CarregouSair)
self.BotaoSair.pack()

def CarregouSoma(self):
self.valor+=1
print "Incrementou... ", self.valor

def CarregouSubtrai(self):

1
Frame é um elemento usado para agrupar outros elementos.

Computadores e Programação – Engª Biomédica 2003/04 – Ficha 11 Página 7 de 13


self.valor-=1
print "Decrementou... ", self.valor

def CarregouSair(self):
print "vou SAIR do programa... Adeus!"
self.frame.quit()

Principal = Tk()
jan=Janela(Principal, 0)
Principal.mainloop()
Principal.destroy()

O aspecto geral da aplicação é o seguinte:

11.3. Posicionamento de Widgets


Widgets são elementos gráficos que facilitam o desenvolvimento de interfaces gráficas. No exemplo
anterior verificou-se que os botões surgiam alinhados na vertical, centrados em relação à janela.
Nem sempre esse é o posicionamento desejado para os widgets de uma interface. A colocação dos
widgets de acordo com a alocação de espaço na interface, é possível através de um posicionamento
simples, indicando apenas se o objecto deve ficar colocado à esquerda, à direita, no topo ou na parte
inferior da janela, e como se deve ocupar o seu espaço (se toda a largura, se toda a altura, etc.). A

Computadores e Programação – Engª Biomédica 2003/04 – Ficha 11 Página 8 de 13


classe Pack implementa o gestor de posicionamento. Para aplicações simples e para posterior
composição em interfaces mais complicadas, é o método mais usado. Acresce que é também o mais
simples.

O método pack encaixa os elementos no widget que os contém. Para tal, tem de fazer uma gestão
do espaço ocupado dentro do widget. Dentro desse espaço colocam-se três questões: dentro do
espaço livre de que lado deve ficar o elemento (direita, esquerda, ...)?; dentro do espaço que lhe foi
atribuído, o elemento deve também ocupar o espaço circundante?; em caso afirmativo, segundo que
direcção o deve fazer? É possível esclarecer estas questões, passando três argumentos ao métodos
pack:

side(TOP, BOTTOM, LEFT, RIGHT)


expand(YES ou NO)
fill(X, Y, BOTH)

O texto entre parêntesis representa os valores possíveis que os argumentos podem assumir.
Teste e analise o seguinte programa, verá que é exemplificativo do que acaba de ser referido:

from Tkinter import *


Principal=Tk()
Button(Principal, text="esq").pack(side=LEFT)
Button(Principal, text="dir").pack(side=RIGHT)
Button(Principal, text="cima").pack(side=TOP)
Button(Principal, text="baixo").pack(side=BOTTOM)

Principal.mainloop()

Computadores e Programação – Engª Biomédica 2003/04 – Ficha 11 Página 9 de 13


11.4. Elementos Gráficos do Tkinter
O módulo Tkinter oferece um conjunto de widgets, que vão neste ponto ser descritos. Enunciam-
se as classes mais importantes (não são referidas todas as classes do Tkinter, mas apenas as mais
utilizadas):

Button: botão de pressão;


Checkbutton: designa uma check box. A selecção de um botão deste tipo comuta o seu
estado entre ligado ou desligado (assume apenas um dos dois estados);
Entry: campo para utilizador introduzir texto;
Frame: elemento usado para agrupar outros elementos;
Label: elemento que contém texto ou uma imagem;
Listbox: lista constituída de itens, na qual se pode seleccionar um ou vários dos seus
elementos;
Menu: lista de opções, usada por exemplo, na criação de menus de contexto (menu que
surge quando se usa o botão do lado direito do rato).
Menubutton: semelhante ao anterior, com diferenças na configuração da sua aparência
poder ser personalizada, poder ser posicionado individualmente, etc..
Radiobutton: comporta-se de forma semelhante ao Checkbutton. Num conjunto de
elementos Radiobutton, só está seleccionada uma opção de cada vez (selecção
exclusiva);
Scale: barra deslizante que permite definir um valor numérico;
Scrollbar: barras deslizantes normalmente usadas nos elementos Listbox e em
janelas de texto;
Text: semelhante ao Label. Permite representar vários estilos de texto e atributos
gráficos, imagens e janelas embebidas;
Toplevel: representa uma janela de topo, independente de outras que a aplicação possua.

É de seguida descrito um novo exemplo, onde se pretende ilustrar o funcionamento de dois campos
(ou widgets) Entry para introdução de dados por parte do utilizador e visualização dos resultados.
O processamento é muito simples e consiste numa multiplicação do valor de entrada por 2. Analise
o seguinte código:

Computadores e Programação – Engª Biomédica 2003/04 – Ficha 11 Página 10 de 13


from Tkinter import *
from string import *

def Duplica():
var.set(2.0*atof(entrada.get()))

Principal=Tk()
Principal.title("Mostra o Dobro")
entrada=Entry(Principal)
entrada.pack(side=TOP)
var=StringVar()
saida=Entry(Principal,textvariable=var)
saida.pack(side=BOTTOM)

botao=Button(Principal,text="Duplica Entrada",command=Duplica)
botao.pack(side=RIGHT)
Principal.mainloop()

A linha seguinte fornece à janela o título "Mostra o Dobro". É esse o texto que aparecerá na
barra superior da janela:

Principal.title("Mostra o Dobro")

De seguida usa-se um campo Entry para o utilizador introduzir texto. O objecto que o suporta
denomina-se entrada e será posicionado na parte superior da janela (TOP) por:

entrada=Entry(Principal)
entrada.pack(side=TOP)

A instrução:

var=StringVar()

cria uma instância de StringVar ao qual podemos atribuir a nossa entrada. No exemplo, usa-se o
método entrada.get() associado ao objecto var (a tal instância de StringVar) para ir

Computadores e Programação – Engª Biomédica 2003/04 – Ficha 11 Página 11 de 13


buscar o conteúdo do widget entrada (campo de texto onde o utilizador introduz um valor). A
função Duplica() encarrega-se dessa tarefa, efectuando também a multiplicação por 2.

def Duplica():
var.set(2.0*atof(entrada.get()))

A função atof() converte uma string num número de vírgula flutuante (com parte decimal).
Como o utilizador introduz um número em formato de texto e a aplicação requer um número com
parte decimal (não se multiplica uma string, mas sim um valor numérico), torna-se necessário usar a
função atof(), e por este motivo se importa o módulo string no início do código.

Para mostrar o valor do resultado, usa-se a opção textvariable do outro widget (campo
Entry), à qual fica associado o objecto var que contém neste ponto, o valor de entrada
multiplicado por 2, o que não é mais do que o resultado desejado. Este campo é posicionado na
parte inferior da janela (BOTTOM):

saida=Entry(Principal,textvariable=var)
saida.pack(side=BOTTOM)

Falta apenas descrever a instanciação de um objecto (widget) Button, que irá ser desencadear um
evento sempre que for pressionado pelo utilizador. Qual a função associada a esse evento? A
resposta está na função Duplica() descrita acima, e a forma de fazer o mapeamento do evento a
essa função é a que se descreve de seguida:

botao=Button(Principal,text="Duplica Entrada",command=Duplica)

A linha de baixo encarrega-se de posicionar o botão no lado direito da janela:

botao.pack(side=RIGHT)

Desta forma, cada alteração em var, é agora reflectida no widget saida:

def Duplica():

Computadores e Programação – Engª Biomédica 2003/04 – Ficha 11 Página 12 de 13


var.set(2.0*atof(entrada.get()))
........
.....
saida=Entry(Principal,textvariable=var)

Tal acontece, sempre que o utilizador pressionar o botão. E o resultado é o que aparece em baixo:

11.5. Exercícios sugeridos

1. Implemente e teste todos os exemplos fornecidos na ficha.

2. Desenvolva uma interface gráfica que crie uma calculadora em Python. Deverá suportar as
operações de soma, subtracção, multiplicação e divisão entre dois números introduzidos pelo
utilizador. Deverá suportar dois campos para introdução dos dois operandos, bem como um
terceiro para visualização do resultado. Recorra, se necessário, aos exemplos fornecidos na
ficha.

Computadores e Programação – Engª Biomédica 2003/04 – Ficha 11 Página 13 de 13