¿Qué es Tkinter?
Tkinter es la biblioteca estándar de Python para crear interfaces gráficas (GUI). Viene incluida con Python, no requiere instalación adicional y es perfecta para aplicaciones de escritorio simples.
Configuración Inicial
import tkinter as tk
from tkinter import ttk
# Crear ventana principal
ventana = tk.Tk()
ventana.title("Mi Primera Ventana")
ventana.geometry("400x300") # ancho x alto
# Iniciar loop de eventos
ventana.mainloop()Calculadora Básica - Versión 1
import tkinter as tk
class Calculadora:
def __init__(self, ventana):
self.ventana = ventana
self.ventana.title("Calculadora")
self.ventana.geometry("300x400")
self.ventana.resizable(False, False)
# Variable para el display
self.expresion = ""
# Crear interfaz
self.crear_display()
self.crear_botones()
def crear_display(self):
# Frame para el display
display_frame = tk.Frame(self.ventana, bg="black")
display_frame.pack(expand=True, fill="both")
# Label para mostrar números
self.display = tk.Label(
display_frame,
text="0",
font=("Arial", 24, "bold"),
bg="black",
fg="white",
anchor="e", # alinear a la derecha
padx=10
)
self.display.pack(expand=True, fill="both")
def crear_botones(self):
# Frame para botones
botones_frame = tk.Frame(self.ventana)
botones_frame.pack(expand=True, fill="both")
# Definir botones
botones = [
['7', '8', '9', '/'],
['4', '5', '6', '*'],
['1', '2', '3', '-'],
['C', '0', '=', '+']
]
# Crear botones
for fila, botones_fila in enumerate(botones):
for columna, boton in enumerate(botones_fila):
self.crear_boton(
botones_frame,
boton,
fila,
columna
)
def crear_boton(self, frame, texto, fila, columna):
boton = tk.Button(
frame,
text=texto,
font=("Arial", 18),
command=lambda: self.click_boton(texto)
)
boton.grid(row=fila, column=columna, sticky="nsew", padx=1, pady=1)
# Hacer que los botones se expandan
frame.grid_rowconfigure(fila, weight=1)
frame.grid_columnconfigure(columna, weight=1)
def click_boton(self, texto):
if texto == 'C':
self.expresion = ""
self.display.config(text="0")
elif texto == '=':
try:
resultado = eval(self.expresion)
self.display.config(text=str(resultado))
self.expresion = str(resultado)
except:
self.display.config(text="Error")
self.expresion = ""
else:
self.expresion += texto
self.display.config(text=self.expresion)
# Ejecutar
if __name__ == "__main__":
ventana = tk.Tk()
calculadora = Calculadora(ventana)
ventana.mainloop()Calculadora Avanzada - Versión 2
import tkinter as tk
from tkinter import ttk
import math
class CalculadoraAvanzada:
def __init__(self, ventana):
self.ventana = ventana
self.ventana.title("Calculadora Científica")
self.ventana.geometry("400x600")
self.ventana.configure(bg="#2c3e50")
self.expresion = ""
self.resultado_anterior = ""
self.crear_interfaz()
def crear_interfaz(self):
# Display superior (historial)
self.historial = tk.Label(
self.ventana,
text="",
font=("Arial", 12),
bg="#34495e",
fg="#95a5a6",
anchor="e",
height=2,
padx=10
)
self.historial.pack(fill="x")
# Display principal
self.display = tk.Label(
self.ventana,
text="0",
font=("Arial", 32, "bold"),
bg="#2c3e50",
fg="white",
anchor="e",
height=2,
padx=10
)
self.display.pack(fill="x")
# Frame de botones
botones_frame = tk.Frame(self.ventana, bg="#2c3e50")
botones_frame.pack(expand=True, fill="both", padx=5, pady=5)
# Botones científicos
botones_cientificos = [
['sin', 'cos', 'tan', 'π'],
['√', 'x²', 'xʸ', 'log'],
['(', ')', '%', 'C']
]
for fila, botones_fila in enumerate(botones_cientificos):
for columna, boton in enumerate(botones_fila):
self.crear_boton_cientifico(
botones_frame,
boton,
fila,
columna
)
# Botones numéricos
botones_numericos = [
['7', '8', '9', '/'],
['4', '5', '6', '*'],
['1', '2', '3', '-'],
['0', '.', '=', '+']
]
fila_offset = len(botones_cientificos)
for fila, botones_fila in enumerate(botones_numericos):
for columna, boton in enumerate(botones_fila):
self.crear_boton_numerico(
botones_frame,
boton,
fila + fila_offset,
columna
)
# Configurar grid
for i in range(len(botones_cientificos) + len(botones_numericos)):
botones_frame.grid_rowconfigure(i, weight=1)
for j in range(4):
botones_frame.grid_columnconfigure(j, weight=1)
def crear_boton_cientifico(self, frame, texto, fila, columna):
boton = tk.Button(
frame,
text=texto,
font=("Arial", 14),
bg="#34495e",
fg="white",
activebackground="#5d6d7e",
command=lambda: self.click_boton(texto)
)
boton.grid(row=fila, column=columna, sticky="nsew", padx=2, pady=2)
def crear_boton_numerico(self, frame, texto, fila, columna):
color = "#e67e22" if texto in ['/', '*', '-', '+', '='] else "#3498db"
boton = tk.Button(
frame,
text=texto,
font=("Arial", 18, "bold"),
bg=color,
fg="white",
activebackground="#5d6d7e",
command=lambda: self.click_boton(texto)
)
boton.grid(row=fila, column=columna, sticky="nsew", padx=2, pady=2)
def click_boton(self, texto):
if texto == 'C':
self.expresion = ""
self.resultado_anterior = ""
self.actualizar_display()
elif texto == '=':
self.calcular()
elif texto == '√':
self.expresion += 'math.sqrt('
self.actualizar_display()
elif texto == 'sin':
self.expresion += 'math.sin(math.radians('
self.actualizar_display()
elif texto == 'cos':
self.expresion += 'math.cos(math.radians('
self.actualizar_display()
elif texto == 'tan':
self.expresion += 'math.tan(math.radians('
self.actualizar_display()
elif texto == 'log':
self.expresion += 'math.log10('
self.actualizar_display()
elif texto == 'π':
self.expresion += str(math.pi)
self.actualizar_display()
elif texto == 'x²':
self.expresion += '**2'
self.actualizar_display()
elif texto == 'xʸ':
self.expresion += '**'
self.actualizar_display()
else:
self.expresion += texto
self.actualizar_display()
def calcular(self):
try:
resultado = eval(self.expresion)
self.resultado_anterior = f"{self.expresion} ="
self.expresion = str(resultado)
self.actualizar_display()
except Exception as e:
self.display.config(text="Error")
self.expresion = ""
def actualizar_display(self):
texto = self.expresion if self.expresion else "0"
self.display.config(text=texto)
self.historial.config(text=self.resultado_anterior)
# Ejecutar
if __name__ == "__main__":
ventana = tk.Tk()
calculadora = CalculadoraAvanzada(ventana)
ventana.mainloop()Funcionalidades Adicionales
Teclado
def __init__(self, ventana):
# ... código anterior ...
# Vincular teclas del teclado
self.ventana.bind('<Key>', self.tecla_presionada)
def tecla_presionada(self, evento):
tecla = evento.char
if tecla.isdigit() or tecla in ['+', '-', '*', '/', '.', '(', ')']:
self.expresion += tecla
self.actualizar_display()
elif tecla == '\r': # Enter
self.calcular()
elif tecla == '\x08': # Backspace
self.expresion = self.expresion[:-1]
self.actualizar_display()Botón de Borrar (Backspace)
def crear_interfaz(self):
# ... código anterior ...
# Agregar botón backspace
boton_back = tk.Button(
botones_frame,
text="⌫",
font=("Arial", 18),
bg="#c0392b",
fg="white",
command=self.borrar_ultimo
)
boton_back.grid(row=2, column=4, sticky="nsew", padx=2, pady=2)
def borrar_ultimo(self):
self.expresion = self.expresion[:-1]
self.actualizar_display()Historial de Cálculos
class CalculadoraConHistorial:
def __init__(self, ventana):
# ... código anterior ...
self.historial_calculos = []
self.crear_menu_historial()
def crear_menu_historial(self):
# Crear menú
menubar = tk.Menu(self.ventana)
self.ventana.config(menu=menubar)
# Menú Historial
historial_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="Historial", menu=historial_menu)
historial_menu.add_command(label="Ver Historial", command=self.mostrar_historial)
historial_menu.add_command(label="Limpiar Historial", command=self.limpiar_historial)
def calcular(self):
try:
resultado = eval(self.expresion)
calculo = f"{self.expresion} = {resultado}"
self.historial_calculos.append(calculo)
self.resultado_anterior = calculo
self.expresion = str(resultado)
self.actualizar_display()
except:
self.display.config(text="Error")
self.expresion = ""
def mostrar_historial(self):
ventana_historial = tk.Toplevel(self.ventana)
ventana_historial.title("Historial de Cálculos")
ventana_historial.geometry("400x300")
# Scrollbar
scrollbar = tk.Scrollbar(ventana_historial)
scrollbar.pack(side="right", fill="y")
# Listbox
listbox = tk.Listbox(
ventana_historial,
font=("Arial", 12),
yscrollcommand=scrollbar.set
)
listbox.pack(expand=True, fill="both")
scrollbar.config(command=listbox.yview)
# Agregar cálculos
for calculo in reversed(self.historial_calculos):
listbox.insert(tk.END, calculo)
def limpiar_historial(self):
self.historial_calculos = []
tk.messagebox.showinfo("Historial", "Historial limpiado")Temas y Estilos
def aplicar_tema_oscuro(self):
colores = {
'bg': '#1e1e1e',
'fg': '#ffffff',
'display_bg': '#2d2d2d',
'boton_bg': '#3c3c3c',
'boton_operador': '#ff9500'
}
self.ventana.configure(bg=colores['bg'])
self.display.configure(bg=colores['display_bg'], fg=colores['fg'])
# ... aplicar a más componentes
def aplicar_tema_claro(self):
colores = {
'bg': '#f0f0f0',
'fg': '#000000',
'display_bg': '#ffffff',
'boton_bg': '#e0e0e0',
'boton_operador': '#ff9500'
}
self.ventana.configure(bg=colores['bg'])
self.display.configure(bg=colores['display_bg'], fg=colores['fg'])
# ... aplicar a más componentesGuardar y Cargar Configuración
import json
def guardar_configuracion(self):
config = {
'historial': self.historial_calculos,
'tema': self.tema_actual,
'posicion': f"{self.ventana.winfo_x()}x{self.ventana.winfo_y()}"
}
with open('calculadora_config.json', 'w') as f:
json.dump(config, f)
def cargar_configuracion(self):
try:
with open('calculadora_config.json', 'r') as f:
config = json.load(f)
self.historial_calculos = config.get('historial', [])
# Aplicar más configuraciones...
except FileNotFoundError:
passProyecto Completo Integrado
# calculadora_completa.py
import tkinter as tk
from tkinter import messagebox
import math
import json
class CalculadoraCompleta:
def __init__(self, ventana):
self.ventana = ventana
self.ventana.title("Calculadora Profesional")
self.ventana.geometry("400x650")
self.ventana.configure(bg="#2c3e50")
self.expresion = ""
self.resultado_anterior = ""
self.historial_calculos = []
self.cargar_configuracion()
self.crear_menu()
self.crear_interfaz()
self.vincular_teclas()
# Guardar al cerrar
self.ventana.protocol("WM_DELETE_WINDOW", self.al_cerrar)
def crear_menu(self):
menubar = tk.Menu(self.ventana)
self.ventana.config(menu=menubar)
# Menú Ver
ver_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="Ver", menu=ver_menu)
ver_menu.add_command(label="Historial", command=self.mostrar_historial)
ver_menu.add_separator()
ver_menu.add_command(label="Tema Oscuro", command=self.aplicar_tema_oscuro)
ver_menu.add_command(label="Tema Claro", command=self.aplicar_tema_claro)
# Menú Ayuda
ayuda_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="Ayuda", menu=ayuda_menu)
ayuda_menu.add_command(label="Atajos de Teclado", command=self.mostrar_atajos)
ayuda_menu.add_command(label="Acerca de", command=self.mostrar_acerca_de)
def crear_interfaz(self):
# ... código de interfaz completo ...
pass
def vincular_teclas(self):
self.ventana.bind('<Key>', self.tecla_presionada)
def al_cerrar(self):
self.guardar_configuracion()
self.ventana.destroy()
# ... resto de métodos ...
if __name__ == "__main__":
ventana = tk.Tk()
calculadora = CalculadoraCompleta(ventana)
ventana.mainloop()Buenas Prácticas
- Organiza tu código: Usa clases y métodos bien definidos
- Maneja excepciones: Prevé errores de cálculo
- Interfaz intuitiva: Diseño claro y fácil de usar
- Atajos de teclado: Mejora la experiencia del usuario
- Guarda configuración: Persistencia de preferencias
- Comenta tu código: Especialmente la lógica compleja
Conclusión
Crear una calculadora con Tkinter es un excelente proyecto para aprender desarrollo de GUI en Python. Este proyecto combina lógica de programación, diseño de interfaces y manejo de eventos. Puedes extenderlo agregando más funciones matemáticas, gráficos o conversiones de unidades.