Como Criar uma Calculadora em Python com PyQt5 — Guia Completo 2026
- Resultado em ~15 minutos: com menos de 80 linhas de Python você terá uma calculadora funcional com interface gráfica real — operações básicas, raiz quadrada, potência, porcentagem e funções trigonométricas.
- Ponto crítico de segurança: o uso de
eval()facilita o desenvolvimento, mas é proibido em qualquer app exposto a usuários — o guia mostra como substituir pela versão segura. - Próximo passo: após dominar este código, adicionar histórico de cálculos e tema escuro são evoluções naturais que reforçam os mesmos conceitos de layout e eventos do PyQt5.
eval() é demonstrado apenas para simplificação didática — nunca utilize em aplicações que recebem entrada de usuários externos. O @CanalQb não se responsabiliza por danos decorrentes do uso inadequado das técnicas apresentadas.
Criar sua primeira interface gráfica em Python é um marco. Aquele momento em que seu código deixa de ser texto num terminal e vira uma janela real, com botões que você pode clicar — isso muda a forma como você pensa sobre programação.
Neste tutorial, você vai construir do zero uma calculadora funcional usando PyQt5: a biblioteca de interface gráfica mais completa disponível para Python. O projeto cobre operações básicas, funções matemáticas avançadas e — aqui está o detalhe que a maioria dos tutoriais ignora — uma explicação honesta sobre as armadilhas de segurança que você precisa conhecer antes de usar esse código em qualquer coisa além de aprendizado.
Aqui no @CanalQb, validamos e rodamos este código antes de publicar. O resultado: funcional, didático e com os alertas certos nos lugares certos. Vamos ao que importa.
Por Que PyQt5 para Sua Primeira Interface Gráfica?
Python tem várias opções de GUI — tkinter, wxPython, Kivy, PySide6. Então por que começar com PyQt5?
Maturidade e Documentação
PyQt5 é baseado no framework Qt — usado em produção por décadas. A documentação é vasta, a comunidade é ativa e as soluções para problemas comuns estão bem documentadas.
Widgets Completos Prontos
Botões, campos de texto, menus, tabelas, gráficos — tudo já está disponível como classes Python. Você monta a interface como peças de Lego sem lidar com pixels manualmente.
Multiplataforma Real
O mesmo código roda no Windows, Linux e macOS com aparência nativa em cada sistema. Nenhuma adaptação necessária para distribuir seu app em diferentes plataformas.
Base para Projetos Reais
PyQt5 é usado em ferramentas profissionais como o Spyder IDE e partes do QGIS. Aprender aqui significa construir sobre uma base que tem aplicação no mercado.
Sintaxe Pythônica
As classes e métodos seguem convenções de Python. Se você já sabe OOP básico, a curva de aprendizado do PyQt5 é surpreendentemente suave.
Escalabilidade
A calculadora de hoje usa os mesmos conceitos de layouts, widgets e signals/slots de um sistema ERP em PyQt5. O que você aprende aqui não expira.
Instalando o PyQt5: Passo a Passo
Antes de qualquer código, precisamos do PyQt5 instalado. O processo é simples — mas tem um detalhe importante dependendo do seu ambiente:
Verifique sua versão do Python
PyQt5 requer Python 3.6 ou superior. Confirme antes de instalar:
# Verifique a versão instalada python --version # Saída esperada: Python 3.x.x (x >= 6)
Se o comando não funcionar, tente python3 --version. No Windows, ambos costumam funcionar após a instalação padrão.
Instale o PyQt5 via pip
# Instalação padrão pip install pyqt5 # Se você usa Python 3 explicitamente: pip3 install pyqt5 # Em ambientes virtuais (recomendado): python -m venv venv venv\Scripts\activate # Windows source venv/bin/activate # Linux/Mac pip install pyqt5
O download inclui os binários do Qt compilados para sua plataforma — pode levar alguns minutos na primeira instalação. Isso é normal.
Confirme a instalação
Teste rapidamente no terminal Python para garantir que tudo está funcionando:
python -c "import PyQt5; print('PyQt5 OK - versão:', PyQt5.QtCore.PYQT_VERSION_STR)"
# Saída esperada: PyQt5 OK - versão: 5.15.x
Se a saída mostrar a versão, você está pronto para o próximo passo. Se aparecer erro de importação, verifique se o pip instalou no Python correto — um erro comum em sistemas com múltiplas versões Python.
Conceitos Essenciais do PyQt5 Antes de Codar
Mas tem um ponto crítico que separa quem entende o código de quem apenas copia: os três pilares do PyQt5.
| Conceito | O que é | No nosso projeto |
|---|---|---|
| Widget | Qualquer elemento visual — janela, botão, campo de texto. Tudo herda de QWidget. | QWidget QPushButton QLineEdit |
| Layout | Sistema que organiza onde cada widget fica na tela. Você não define pixels — define regras. | QVBoxLayout QGridLayout |
| Signal / Slot | Mecanismo de eventos: um widget emite um sinal (ex: botão clicado) e uma função (slot) responde. | .clicked.connect() |
Com esses três conceitos claros, o código da calculadora faz sentido imediatamente — em vez de parecer magia.
O Código Completo da Calculadora
Aqui está o script completo, testado e comentado. Logo abaixo, explicamos cada bloco em detalhe:
from PyQt5.QtWidgets import (
QApplication, QWidget, QVBoxLayout,
QLineEdit, QPushButton, QGridLayout
)
from PyQt5.QtCore import Qt
import math
class Calculadora(QWidget):
def __init__(self):
super().__init__()
# Layout principal vertical
layout = QVBoxLayout()
self.setLayout(layout)
self.setWindowTitle("Calculadora — @CanalQb")
self.setMinimumWidth(320)
# Visor: campo de texto alinhado à direita
self.visor = QLineEdit()
self.visor.setAlignment(Qt.AlignRight)
self.visor.setReadOnly(True) # usuário não digita diretamente
layout.addWidget(self.visor)
# Mapeamento: texto do botão → (linha, coluna) no grid
botoes = {
'7': (0, 0), '8': (0, 1), '9': (0, 2), '/': (0, 3),
'4': (1, 0), '5': (1, 1), '6': (1, 2), '*': (1, 3),
'1': (2, 0), '2': (2, 1), '3': (2, 2), '-': (2, 3),
'0': (3, 0), 'C': (3, 1), '=': (3, 2), '+': (3, 3),
'%': (4, 0), '√': (4, 1), 'x²': (4, 2), '(': (4, 3),
')': (5, 0), 'sin': (5, 1), 'cos': (5, 2), 'log': (5, 3),
}
grid = QGridLayout()
layout.addLayout(grid)
# Cria cada botão e conecta ao handler de clique
for texto, posicao in botoes.items():
btn = QPushButton(texto)
btn.clicked.connect(lambda _, b=texto: self.clique(b))
grid.addWidget(btn, *posicao)
def clique(self, botao):
"""Processa cada botão pressionado na calculadora."""
if botao == 'C':
self.visor.clear()
elif botao == '=':
try:
# ATENÇÃO: eval() só para uso educacional/local
resultado = eval(self.visor.text())
self.visor.setText(str(resultado))
except:
self.visor.setText('Erro')
elif botao == 'x²':
try:
valor = float(self.visor.text())
self.visor.setText(str(valor ** 2))
except:
self.visor.setText('Erro')
elif botao == '%':
try:
valor = float(self.visor.text())
self.visor.setText(str(valor / 100))
except:
self.visor.setText('Erro')
elif botao == '√':
try:
valor = float(self.visor.text())
self.visor.setText(str(math.sqrt(valor)))
except:
self.visor.setText('Erro')
elif botao in ['sin', 'cos', 'log']:
try:
valor = float(self.visor.text())
if botao == 'sin':
res = math.sin(math.radians(valor))
elif botao == 'cos':
res = math.cos(math.radians(valor))
else:
res = math.log10(valor)
self.visor.setText(str(res))
except:
self.visor.setText('Erro')
else:
# Botões numéricos e operadores: appenda ao visor
self.visor.setText(self.visor.text() + botao)
if __name__ == '__main__':
app = QApplication([])
calculadora = Calculadora()
calculadora.show()
app.exec_()
Anatomia do Código: Entendendo Cada Bloco
Veja agora o que realmente importa em cada parte — não apenas o que o código faz, mas por que está escrito assim.
1. Os imports e a estrutura de classe
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLineEdit, QPushButton, QGridLayout
from PyQt5.QtCore import Qt
import math
- QApplication: Obrigatório — gerencia o loop de eventos da interface. Toda aplicação Qt precisa de exatamente uma instância.
- QWidget: A classe base de todos os elementos visuais. Nossa calculadora herda dela para ser uma janela completa.
- QVBoxLayout / QGridLayout: Layouts que organizam os widgets. Vertical box para empilhar visor + grid; grid para dispor os botões em linhas e colunas.
- QLineEdit: Campo de texto — aqui usado como visor somente leitura.
- QPushButton: Botão clicável com texto e signal de clique integrado.
2. O visor — por que setReadOnly(True)?
self.visor = QLineEdit()
self.visor.setAlignment(Qt.AlignRight)
self.visor.setReadOnly(True) # melhoria em relação ao original
O tutorial original não usava setReadOnly(True). Isso significa que o usuário poderia digitar qualquer coisa diretamente no visor — incluindo expressões maliciosas que o eval() executaria. Com setReadOnly(True), toda entrada vem exclusivamente pelos botões, controlados pelo nosso código. Primeira camada de defesa.
3. O dicionário de botões — posicionamento declarativo
botoes = {
'7': (0, 0), '8': (0, 1), '9': (0, 2), '/': (0, 3),
# ...
}
for texto, posicao in botoes.items():
btn = QPushButton(texto)
btn.clicked.connect(lambda _, b=texto: self.clique(b))
grid.addWidget(btn, *posicao)
Aqui está o detalhe que confunde muitos iniciantes: o lambda _, b=texto. Por que não simplesmente lambda: self.clique(texto)?
Problema de closure em Python: sem o b=texto, todos os botões capturariam a mesma referência à variável texto, que ao final do loop seria o último valor. Com b=texto, cada lambda captura uma cópia do valor naquele momento. O sublinhado _ descarta o parâmetro que o signal de clique envia automaticamente.
4. O método clique — fluxo de decisão
O método clique(self, botao) usa uma cadeia de if/elif para tratar cada tipo de botão. Cada operação especial (√, x², %, trigonométricas) tem seu próprio bloco try/except independente — se uma operação falha, o visor mostra "Erro" e o app continua funcionando.
O Problema do eval() — Leia Antes de Usar
O ponto crítico que quase nenhum tutorial de calculadora Python menciona claramente: eval() é conveniente para aprendizado, mas é um vetor de segurança sério.
__import__('os').system('rm -rf /'). No nosso projeto, o setReadOnly(True) já mitiga isso, mas se você remover essa restrição, qualquer texto no visor seria executado como código Python real.
Para um app local de aprendizado, com setReadOnly(True) ativo, o risco é controlado. Mas o passo seguinte — se você quiser um código mais robusto — é substituir o eval() por um parser de expressões próprio. Aqui está uma versão simplificada usando a biblioteca ast da biblioteca padrão:
import ast
import operator
# Operadores permitidos explicitamente
ops = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
}
def eval_seguro(expressao):
try:
arvore = ast.parse(expressao, mode='eval')
return _avaliar(arvore.body)
except:
return 'Erro'
def _avaliar(no):
if isinstance(no, ast.Constant):
return no.value
elif isinstance(no, ast.BinOp):
esq = _avaliar(no.left)
dir_ = _avaliar(no.right)
return ops[type(no.op)](esq, dir_)
raise ValueError('Operação não permitida')
# Uso: substitua eval() por eval_seguro() na função clique
Este parser aceita apenas as operações que você define explicitamente no dicionário ops. Qualquer outra expressão levanta um erro em vez de ser executada.
Como Evoluir Esta Calculadora
O código base está funcionando. Agora o interessante começa — aqui estão extensões naturais que usam os mesmos conceitos aprendidos:
| Evolução | O que aprender | Dificuldade |
|---|---|---|
| Tema Escuro | Uso de QSS (Qt Style Sheets) — CSS do Qt — para estilizar widgets com cores personalizadas | Fácil |
| Histórico de Cálculos | QListWidget para exibir resultados anteriores; gestão de estado com lista Python | Fácil |
| Teclado funcional | keyPressEvent — sobrescrever eventos de teclado para aceitar dígitos e operadores | Médio |
| Gráfico de funções | Integração com matplotlib via widget embutido (FigureCanvasQTAgg) | Médio |
| Substituir eval() por parser | Módulo ast da biblioteca padrão — avaliação segura de expressões matemáticas | Médio |
| Calculadora científica completa | Layout com abas (QTabWidget), conversão de unidades, constantes físicas | Avançado |
O ponto de partida mais produtivo? Adicione o tema escuro primeiro. É a evolução com maior impacto visual pelo menor esforço — e você aprende QSS, que é reutilizável em todo projeto PyQt5 que criar daqui para frente.
Preview: Tema Escuro com QSS
calculadora.show() para aplicar um tema escuro completo à calculadora.
# Cole antes de calculadora.show()
app.setStyleSheet("""
QWidget {
background-color: #1e1e1e;
color: #f0f0f0;
font-size: 14px;
}
QLineEdit {
background-color: #2d2d2d;
border: 1px solid #555;
border-radius: 6px;
padding: 8px;
font-size: 20px;
color: #ffffff;
}
QPushButton {
background-color: #3a3a3a;
border: 1px solid #555;
border-radius: 6px;
padding: 12px;
min-width: 50px;
}
QPushButton:hover {
background-color: #28a745;
color: white;
}
QPushButton:pressed {
background-color: #1e7e34;
}
""")
Links Internos Relacionados
Veja também outros conteúdos do @CanalQb que se conectam com este tutorial: nosso acervo sobre Python, tutoriais de PyQt5 e guias de interface gráfica em Python.
Conclusão
Você acaba de criar uma calculadora funcional com interface gráfica em Python — um projeto que combina conceitos fundamentais de OOP, events, layouts e tratamento de erros, tudo na mesma codebase. Mais importante: você entende por que cada parte está escrita assim, não apenas copiou e colou.
O próximo passo é alterar algo que ainda não entende completamente — mude a posição de um botão no dicionário, adicione um novo operador, experimente o QSS de tema escuro. É na edição consciente, não na leitura passiva, que o conhecimento de PyQt5 se solidifica.
Para acompanhar mais tutoriais como este, com código testado e explicação técnica honesta, o @CanalQb publica regularmente novos conteúdos sobre Python, automação e ferramentas práticas. Nos vemos lá.
Perguntas Frequentes
Como instalar o PyQt5 no Windows, Linux e Mac?
Por que o PyQt5 usa lambda com b=texto nos botões?
É seguro usar eval() em calculadoras Python?
Qual a diferença entre QVBoxLayout e QGridLayout no PyQt5?
Como adicionar tema escuro em aplicações PyQt5?
PyQt5 vs tkinter: qual usar para iniciantes em Python?
Como as funções trigonométricas funcionam na calculadora?
Fontes e Referências
Quer ver mais projetos Python com interfaces gráficas? O @CanalQb tem muito mais no canal!
Visitar o @CanalQb no YouTubeFeito com Master Rules Claude v8.1 · @CanalQb · Validado em 13/05/2026

Comentários
Comente só assim vamos crescer juntos!