projeto

Projeto Guiado: Calculadora Simples

Aprenda sobre projeto guiado: calculadora simples

60 min
Aula 1 de 5

🚀 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 Entry para 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:

  1. Display: Onde os números e resultados são exibidos. Usaremos um widget Entry para isso.
  2. Botões Numéricos: De 0 a 9.
  3. Botões de Operação: +, -, *, /.
  4. Botões de Controle: C (Limpar), = (Calcular).
  5. 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 widgets
  • tk.StringVar(): É uma classe especial do Tkinter que permite que o conteúdo de um widget (como Entry ou Label) 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 vertical
  • justify="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 do Entry para 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. lambda cria uma função anônima que "captura" o valor atual de text (o nome do botão) no momento em que o botão é criado. Sem t=text, todas as funções lambda apontariam para o último valor de text no 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) e grid_columnconfigure(i, weight=1): Essencial para que os botões se redimensionem corretamente quando a janela é redimensionada. weight=1 distribui 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 char for 'C', limpa o display.
    • Se char for '=', chama _calcular().
    • Caso contrário, adiciona o caractere ao display. Adicionei uma pequena lógica para evitar 007 e permitir 0.7.
  • _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) e ZeroDivisionError.
    • Atualiza o display com o resultado ou a mensagem de erro.

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 classe CalculadoraApp, 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), ou sqrt (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 para eval().
    • Dica para sqrt: Use math.sqrt() do módulo math.
  • 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 Label ou Text widget 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: Entry para exibição e Button para interação.
  • Gerenciamento de Layout: grid() para posicionamento preciso e responsivo.
  • Manipulação de Eventos: command e 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! 🚀

© 2025 Escola All Dev. Todos os direitos reservados.

Projeto Guiado: Calculadora Simples - Curso de Python com Tkinter para Criação de Interfaces | escola.all.dev.br