Curso de Python com Tkinter para Criação de Interfaces
Projeto Guiado: Calculadora Simples
Aprenda sobre projeto guiado: calculadora simples
🚀 Projeto Guiado: Calculadora Simples com Tkinter
Bem-vindos à nossa aula prática! 🎉 Neste módulo, vamos colocar em prática todo o conhecimento adquirido sobre Tkinter, construindo um projeto completo e funcional: uma calculadora simples. Este projeto não só solidificará sua compreensão sobre widgets e layouts, mas também introduzirá a lógica de interação e processamento de dados em uma aplicação GUI.
🎯 Objetivos da Aula
Ao final desta aula, você será capaz de:
- Criar a janela principal de uma aplicação Tkinter.
- Utilizar o widget
Entrypara exibir e manipular texto. - Criar e estilizar múltiplos botões (
Button) para diferentes funções. - Organizar widgets usando o gerenciador de layout
grid(). - Implementar funções de callback para lidar com eventos de clique dos botões.
- Desenvolver a lógica para processar expressões matemáticas simples.
- Aplicar boas práticas de estruturação de código para aplicações GUI.
Vamos mergulhar de cabeça e construir nossa primeira aplicação interativa! 💡
🛠️ Entendendo a Estrutura da Calculadora
Uma calculadora, mesmo que simples, possui alguns componentes essenciais:
- Display: Onde os números e resultados são exibidos. Usaremos um widget
Entrypara isso. - Botões Numéricos: De 0 a 9.
- Botões de Operação:
+,-,*,/. - Botões de Controle:
C(Limpar),=(Calcular). - Lógica de Cálculo: A parte que interpreta a expressão e retorna o resultado.
Nossa abordagem será modular, construindo cada parte passo a passo e integrando-as em uma classe para manter o código organizado e reutilizável.
📝 Construindo a Calculadora: Passo a Passo
1. Configuração Inicial e Janela Principal
Começaremos importando o módulo tkinter e definindo a classe CalculadoraApp. Esta classe encapsulará toda a lógica e a interface da nossa calculadora.
import tkinter as tk
from tkinter import ttk # Usaremos ttk para widgets mais modernos, se necessário
class CalculadoraApp:
def __init__(self, master):
self.master = master
master.title("Calculadora Simples")
master.geometry("300x400") # Define um tamanho inicial para a janela
master.resizable(False, False) # Impede que o usuário redimensione a janela
# Variável para armazenar o texto do display
self.expressao_atual = tk.StringVar()
self.expressao_atual.set("") # Inicializa com string vazia
self._criar_widgets() # Método para criar todos os widgetstk.StringVar(): É uma classe especial do Tkinter que permite que o conteúdo de um widget (comoEntryouLabel) seja associado a uma variável Python. Quando a variável muda, o widget é automaticamente atualizado, e vice-versa. É uma ótima prática para gerenciar o texto do display._criar_widgets(): Usaremos um método separado para organizar a criação dos componentes da interface.
2. O Display da Calculadora
O display será um widget Entry. Ele será de "somente leitura" (state='readonly') para que o usuário não possa digitar diretamente, apenas interagir via botões.
# Dentro do método _criar_widgets() da classe CalculadoraApp:
# Frame para o display (opcional, mas ajuda a organizar)
display_frame = tk.Frame(master, bd=4, relief="ridge", bg="#f0f0f0")
display_frame.pack(side="top", fill="x", pady=5, padx=5)
self.display = tk.Entry(
display_frame,
textvariable=self.expressao_atual,
font=("Arial", 24),
justify="right", # Alinha o texto à direita
state="readonly", # Impede edição direta pelo teclado
bd=0, # Remove a borda padrão do Entry
bg="#f0f0f0",
fg="#333333"
)
self.display.pack(fill="both", expand=True, ipady=10) # ipady adiciona preenchimento interno verticaljustify="right": Faz com que o texto no display seja alinhado à direita, como em calculadoras reais.state="readonly": Fundamental para que a entrada de dados seja controlada apenas pelos botões.bd=0: Remove a borda padrão doEntrypara um visual mais limpo.
3. Criando os Botões
Esta é a parte mais interativa. Vamos criar uma grade de botões para números e operações. Usaremos grid() para posicioná-los de forma organizada.
# Dentro do método _criar_widgets() da classe CalculadoraApp, após o display:
# Frame para os botões
buttons_frame = tk.Frame(master, bg="#cccccc")
buttons_frame.pack(expand=True, fill="both", padx=5, pady=5)
# Configuração de estilo para os botões
button_style = {
"font": ("Arial", 18),
"width": 4,
"height": 2,
"bd": 1,
"relief": "raised",
"bg": "#e0e0e0",
"fg": "#333333"
}
operator_style = button_style.copy()
operator_style.update({"bg": "#f0a040", "fg": "white"}) # Laranja para operadores
equals_clear_style = button_style.copy()
equals_clear_style.update({"bg": "#4CAF50", "fg": "white"}) # Verde para igual/limpar
# Layout dos botões
buttons = [
('7', 1, 0), ('8', 1, 1), ('9', 1, 2), ('/', 1, 3, operator_style),
('4', 2, 0), ('5', 2, 1), ('6', 2, 2), ('*', 2, 3, operator_style),
('1', 3, 0), ('2', 3, 1), ('3', 3, 2), ('-', 3, 3, operator_style),
('0', 4, 0), ('.', 4, 1), ('C', 4, 2, equals_clear_style), ('+', 4, 3, operator_style),
('=', 5, 0, equals_clear_style, 4) # O botão de igual ocupará 4 colunas
]
for text, row, col, *args in buttons:
style = button_style
colspan = 1
if args:
style = args[0]
if len(args) > 1:
colspan = args[1]
button = tk.Button(
buttons_frame,
text=text,
**style,
command=lambda t=text: self._on_button_click(t) # Usamos lambda para passar o texto do botão
)
button.grid(row=row, column=col, columnspan=colspan, sticky="nsew", padx=2, pady=2)
# Configura as linhas e colunas para expandir igualmente
for i in range(1, 6): # Linhas 1 a 5
buttons_frame.grid_rowconfigure(i, weight=1)
for i in range(4): # Colunas 0 a 3
buttons_frame.grid_columnconfigure(i, weight=1)lambda t=text: self._on_button_click(t): Esta é uma técnica muito comum em Tkinter.lambdacria uma função anônima que "captura" o valor atual detext(o nome do botão) no momento em que o botão é criado. Semt=text, todas as funçõeslambdaapontariam para o último valor detextno loop.sticky="nsew": Faz com que o botão preencha completamente a célula da grade, tanto na vertical quanto na horizontal.grid_rowconfigure(i, weight=1)egrid_columnconfigure(i, weight=1): Essencial para que os botões se redimensionem corretamente quando a janela é redimensionada.weight=1distribui o espaço extra igualmente entre as linhas/colunas.
4. Implementando a Lógica dos Botões
Agora, precisamos definir as funções que serão chamadas quando os botões forem clicados.
# Dentro da classe CalculadoraApp:
def _on_button_click(self, char):
current_expression = self.expressao_atual.get()
if char == 'C':
self.expressao_atual.set("")
elif char == '=':
self._calcular()
else:
# Previne múltiplos zeros iniciais (ex: 007)
if current_expression == "0" and char != ".":
self.expressao_atual.set(char)
else:
self.expressao_atual.set(current_expression + char)
def _calcular(self):
try:
expressao = self.expressao_atual.get()
# Substitui 'x' por '*' para que eval() entenda
expressao = expressao.replace('x', '*')
resultado = str(eval(expressao))
self.expressao_atual.set(resultado)
except SyntaxError:
self.expressao_atual.set("Erro de Sintaxe")
except ZeroDivisionError:
self.expressao_atual.set("Divisão por Zero")
except Exception as e:
self.expressao_atual.set("Erro")
print(f"Ocorreu um erro inesperado: {e}")_on_button_click(self, char):- Se
charfor 'C', limpa o display. - Se
charfor '=', chama_calcular(). - Caso contrário, adiciona o caractere ao display. Adicionei uma pequena lógica para evitar
007e permitir0.7.
- Se
_calcular(self):- Tenta usar
eval()para avaliar a expressão.eval()é uma função poderosa do Python que executa código Python de uma string. Cuidado: Em aplicações de produção,eval()pode ser um risco de segurança se você não controlar rigorosamente a entrada do usuário. Para uma calculadora simples e educacional, é aceitável. - Trata
SyntaxError(expressões inválidas) eZeroDivisionError. - Atualiza o display com o resultado ou a mensagem de erro.
- Tenta usar
5. Executando a Aplicação
Finalmente, precisamos do bloco principal para instanciar e rodar nossa calculadora.
# Fora da classe CalculadoraApp:
if __name__ == "__main__":
root = tk.Tk()
app = CalculadoraApp(root)
root.mainloop()if __name__ == "__main__":: Garante que o código dentro deste bloco só será executado quando o script for rodado diretamente, e não quando for importado como um módulo.root = tk.Tk(): Cria a janela principal da aplicação.app = CalculadoraApp(root): Instancia nossa classeCalculadoraApp, passando a janela principal como parâmetro.root.mainloop(): Inicia o loop de eventos do Tkinter, que fica "escutando" por interações do usuário (cliques, digitação, etc.) e atualizando a interface. A aplicação permanece aberta até que esta janela seja fechada.
💻 Código Completo da Calculadora
Aqui está o código completo da nossa calculadora simples, seguindo as melhores práticas de organização e legibilidade:
import tkinter as tk
from tkinter import ttk
class CalculadoraApp:
def __init__(self, master):
self.master = master
master.title("Calculadora Simples")
master.geometry("300x400")
master.resizable(False, False)
self.expressao_atual = tk.StringVar()
self.expressao_atual.set("0") # Inicializa com '0'
self._criar_widgets()
def _criar_widgets(self):
# Frame para o display
display_frame = tk.Frame(self.master, bd=4, relief="ridge", bg="#f0f0f0")
display_frame.pack(side="top", fill="x", pady=5, padx=5)
self.display = tk.Entry(
display_frame,
textvariable=self.expressao_atual,
font=("Arial", 24),
justify="right",
state="readonly",
bd=0,
bg="#f0f0f0",
fg="#333333"
)
self.display.pack(fill="both", expand=True, ipady=10)
# Frame para os botões
buttons_frame = tk.Frame(self.master, bg="#cccccc")
buttons_frame.pack(expand=True, fill="both", padx=5, pady=5)
# Estilos dos botões
button_style = {
"font": ("Arial", 18),
"width": 4,
"height": 2,
"bd": 1,
"relief": "raised",
"bg": "#e0e0e0",
"fg": "#333333",
"activebackground": "#d0d0d0" # Cor ao ser clicado
}
operator_style = button_style.copy()
operator_style.update({"bg": "#f0a040", "fg": "white", "activebackground": "#e09030"})
equals_clear_style = button_style.copy()
equals_clear_style.update({"bg": "#4CAF50", "fg": "white", "activebackground": "#3e8e41"})
zero_style = button_style.copy()
zero_style.update({"width": 8}) # Botão 0 maior
# Layout dos botões (texto, linha, coluna, [estilo], [colspan])
buttons_layout = [
('C', 1, 0, equals_clear_style, 2), ('/', 1, 2, operator_style), ('*', 1, 3, operator_style),
('7', 2, 0), ('8', 2, 1), ('9', 2, 2), ('-', 2, 3, operator_style),
('4', 3, 0), ('5', 3, 1), ('6', 3, 2), ('+', 3, 3, operator_style),
('1', 4, 0), ('2', 4, 1), ('3', 4, 2),
('0', 5, 0, zero_style, 2), ('.', 5, 2), ('=', 4, 3, equals_clear_style, 1, 2) # O botão de igual ocupará 2 linhas e 1 coluna
]
for item in buttons_layout:
text = item[0]
row = item[1]
col = item[2]
style = button_style
colspan = 1
rowspan = 1
if len(item) > 3: # Se estilo foi especificado
style = item[3]
if len(item) > 4: # Se colspan foi especificado
colspan = item[4]
if len(item) > 5: # Se rowspan foi especificado
rowspan = item[5]
button = tk.Button(
buttons_frame,
text=text,
**style,
command=lambda t=text: self._on_button_click(t)
)
button.grid(row=row, column=col, columnspan=colspan, rowspan=rowspan, sticky="nsew", padx=2, pady=2)
# Configura as linhas e colunas para expandir igualmente
for i in range(1, 6): # Linhas 1 a 5
buttons_frame.grid_rowconfigure(i, weight=1)
for i in range(4): # Colunas 0 a 3
buttons_frame.grid_columnconfigure(i, weight=1)
def _on_button_click(self, char):
current_expression = self.expressao_atual.get()
if char == 'C':
self.expressao_atual.set("0") # Reseta para '0'
elif char == '=':
self._calcular()
else:
# Se o display está '0' e o caractere não é um operador ou ponto, substitui '0'
if current_expression == "0" and char not in "+-*/.":
self.expressao_atual.set(char)
# Se o display está '0' e o caractere é um ponto, mantém '0.'
elif current_expression == "0" and char == ".":
self.expressao_atual.set("0.")
else:
self.expressao_atual.set(current_expression + char)
def _calcular(self):
try:
expressao = self.expressao_atual.get()
# Garante que a expressão não esteja vazia antes de avaliar
if not expressao:
self.expressao_atual.set("0")
return
# Avalia a expressão
# Nota: eval() é perigoso para entradas não confiáveis, mas ok para este projeto educacional.
resultado = str(eval(expressao))
self.expressao_atual.set(resultado)
except (SyntaxError, NameError):
self.expressao_atual.set("Erro de Sintaxe")
except ZeroDivisionError:
self.expressao_atual.set("Divisão por Zero")
except Exception as e:
self.expressao_atual.set("Erro")
print(f"Ocorreu um erro inesperado: {e}")
if __name__ == "__main__":
root = tk.Tk()
app = CalculadoraApp(root)
root.mainloop()🧑💻 Exercícios e Desafios
Parabéns! Você construiu uma calculadora funcional. Agora, para aprimorar suas habilidades, tente os seguintes desafios:
Task List de Desafios
- Adicionar Mais Operações: Inclua botões para operações como
%(porcentagem),+/-(trocar sinal), ousqrt(raiz quadrada).- Dica para
%: Você pode interpretar "50+10%" como "50 + (50 * 0.10)". Isso exigirá uma lógica de parsing mais complexa ou uma abordagem diferente paraeval(). - Dica para
sqrt: Usemath.sqrt()do módulomath.
- Dica para
- Melhorar a Estilização: Experimente diferentes cores, fontes, tamanhos e tipos de borda para os botões e o display.
- Histórico de Operações: Adicione um pequeno
LabelouTextwidget acima do display para mostrar as últimas operações realizadas. - Validação de Entrada Avançada: Implemente uma lógica para evitar que o usuário digite múltiplos pontos decimais em um número (ex:
1.2.3) ou múltiplos operadores seguidos (ex:++). - Modo Científico (Opcional): Adicione funções trigonométricas (sen, cos, tan), logaritmos, potências, etc. Isso provavelmente exigirá um layout de botões dinâmico ou um segundo "modo" da calculadora.
📝 Resumo e Próximos Passos
Nesta aula, você construiu uma calculadora simples, aplicando diversos conceitos fundamentais do Tkinter:
- Estrutura de Aplicações GUI: Usando classes para organizar seu código.
- Widgets Essenciais:
Entrypara exibição eButtonpara interação. - Gerenciamento de Layout:
grid()para posicionamento preciso e responsivo. - Manipulação de Eventos:
commande funções de callback para responder às interações do usuário. - Lógica de Negócio: Implementando o cálculo e tratamento de erros básicos.
Você deu um grande passo na criação de interfaces gráficas com Python!
No próximo módulo, exploraremos como criar aplicações com múltiplas janelas ou abas, e como lidar com dados mais complexos e persistência. Prepare-se para levar suas aplicações Tkinter para o próximo nível! 🚀