# Guía práctica de CRUD: Crea un gestor de tareas con Python y Tkinter
CRUD (Crear, Leer, Actualizar, Eliminar) es un patrón fundamental para operaciones con datos. En esta guía, lo pondremos en práctica construyendo un gestor de tareas de escritorio con Python, Tkinter y JSON para el almacenamiento de datos. Ideal para desarrolladores que quieran reforzar conceptos básicos de POO, manejo de archivos y desarrollo de interfaces gráficas.
Arquitectura de la app y configuración
Nuestra app vivirá en un único archivo app.py. Usaremos JSON para el almacenamiento de datos: simple y directo para este tipo de proyecto. Cada tarea es un diccionario con id (identificador único), text (descripción) y completed (estado). Todas las tareas se guardan en una lista, serializada en tasks.json.
Asegúrate de tener Python 3.6 o superior instalado. Tkinter viene incluido, así que no necesitas instalaciones extras. Crea una carpeta para el proyecto y ábrela en tu IDE.
Modelo de datos y manejo de JSON
Primero, implementaremos funciones para cargar y guardar datos: la base para las operaciones Leer y Actualizar de CRUD.
import json
import os
TASKS_FILE = "tasks.json"
def load_tasks():
"""Carga las tareas desde el archivo JSON."""
if not os.path.exists(TASKS_FILE):
return []
with open(TASKS_FILE, "r", encoding="utf-8") as f:
try:
return json.load(f)
except json.JSONDecodeError:
return []
def save_tasks(tasks):
"""Guarda la lista de tareas en el archivo JSON."""
with open(TASKS_FILE, "w", encoding="utf-8") as f:
json.dump(tasks, f, ensure_ascii=False, indent=2)
def get_next_id(tasks):
"""Genera el siguiente ID único para una nueva tarea."""
if not tasks:
return 1
return max(task["id"] for task in tasks) + 1
Características clave:
load_tasksmaneja archivos inexistentes o corruptos con gracia.ensure_ascii=Falseenjson.dumpasegura soporte adecuado para Unicode.indent=2mantiene el JSON legible.
Construyendo la interfaz con Tkinter
Crearemos una clase TodoApp para gestionar la ventana principal y los widgets. La interfaz incluye:
- Campo de entrada para nuevas tareas.
- Listbox para mostrar todas las tareas.
- Botones de control: agregar, marcar como hecho, eliminar.
import tkinter as tk
from tkinter import messagebox
class TodoApp:
def __init__(self, root):
self.root = root
self.root.title("Gestor de Tareas")
self.root.geometry("500x400")
self.root.resizable(False, False)
self.tasks = load_tasks()
self.create_widgets()
self.refresh_task_list()
def create_widgets(self):
"""Configura todos los elementos de la UI."""
# Panel superior: entrada y botón agregar
top_frame = tk.Frame(self.root)
top_frame.pack(pady=10, padx=10, fill=tk.X)
self.entry = tk.Entry(top_frame, font=("Arial", 12))
self.entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))
self.entry.bind("<Return>", lambda event: self.add_task())
self.add_button = tk.Button(top_frame, text="➕ Agregar", command=self.add_task)
self.add_button.pack(side=tk.RIGHT)
# Lista de tareas con scrollbar
list_frame = tk.Frame(self.root)
list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
scrollbar = tk.Scrollbar(list_frame)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.task_listbox = tk.Listbox(
list_frame,
font=("Arial", 11),
yscrollcommand=scrollbar.set,
selectmode=tk.SINGLE,
height=15
)
self.task_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.config(command=self.task_listbox.yview)
# Panel inferior: botones de acción
bottom_frame = tk.Frame(self.root)
bottom_frame.pack(pady=10, padx=10, fill=tk.X)
self.done_button = tk.Button(bottom_frame, text="✅ Hecho", command=self.mark_done)
self.done_button.pack(side=tk.LEFT, padx=5)
self.delete_button = tk.Button(bottom_frame, text="🗑 Eliminar", command=self.delete_task)
self.delete_button.pack(side=tk.LEFT, padx=5)
self.exit_button = tk.Button(bottom_frame, text="Salir", command=self.root.quit)
self.exit_button.pack(side=tk.RIGHT, padx=5)
def refresh_task_list(self):
"""Actualiza la visualización de la lista de tareas."""
self.task_listbox.delete(0, tk.END)
for task in self.tasks:
status = "✓" if task["completed"] else "○"
display_text = f"{status} {task['id']}. {task['text']}"
self.task_listbox.insert(tk.END, display_text)
refresh_task_list maneja la operación Leer, mostrando el estado actual de los datos. Las marcas (✓/○) visualizan el estado de completado.
Implementando operaciones CRUD
A continuación, agrega métodos a la clase para las acciones del usuario, cada uno ligado a una operación CRUD.
Crear: Agregar una nueva tarea
def add_task(self):
"""Agrega una nueva tarea (Crear)."""
text = self.entry.get().strip()
if not text:
messagebox.showwarning("Advertencia", "Introduce el texto de la tarea")
return
next_id = get_next_id(self.tasks)
new_task = {
"id": next_id,
"text": text,
"completed": False
}
self.tasks.append(new_task)
save_tasks(self.tasks)
self.entry.delete(0, tk.END)
self.refresh_task_list()
messagebox.showinfo("Éxito", f"Tarea agregada (ID: {next_id})")
Actualizar: Marcar tarea como hecha
def mark_done(self):
"""Marca la tarea seleccionada como hecha (Actualizar)."""
selection = self.task_listbox.curselection()
if not selection:
messagebox.showwarning("Advertencia", "Selecciona una tarea")
return
index = selection[0]
task_id = self.tasks[index]["id"]
for task in self.tasks:
if task["id"] == task_id:
if task["completed"]:
messagebox.showinfo("Info", "La tarea ya está hecha")
else:
task["completed"] = True
save_tasks(self.tasks)
self.refresh_task_list()
messagebox.showinfo("Éxito", f"Tarea #{task_id} marcada como hecha")
return
Eliminar: Quitar una tarea
def delete_task(self):
"""Elimina la tarea seleccionada (Eliminar)."""
selection = self.task_listbox.curselection()
if not selection:
messagebox.showwarning("Advertencia", "Selecciona una tarea")
return
index = selection[0]
task_id = self.tasks[index]["id"]
task_text = self.tasks[index]["text"]
if messagebox.askyesno("Confirmar", f"¿Eliminar tarea #{task_id} \"{task_text}\"?"):
del self.tasks[index]
save_tasks(self.tasks)
self.refresh_task_list()
messagebox.showinfo("Éxito", f"Tarea #{task_id} eliminada")
Cada método valida la entrada, actualiza los datos en memoria, guarda en archivo y refresca la UI. Los messagebox proporcionan retroalimentación clara al usuario.
Ensamblando y ejecutando la app
Une todo en un archivo con un punto de entrada principal.
if __name__ == "__main__":
root = tk.Tk()
app = TodoApp(root)
root.mainloop()
Ejecuta con python app.py. Prueba todas las operaciones CRUD: la app guarda los datos en tasks.json entre sesiones.
Lecciones clave
- Fundamentos de CRUD: Crear, Leer, Actualizar, Eliminar impulsa la mayoría de apps con datos.
- Almacenamiento JSON: Ideal para proyectos pequeños, sin necesidad de base de datos.
- Interfaz Tkinter: UIs de escritorio rápidas, multiplataforma y sin dependencias.
- Estructura de código limpia: Separa modelo de datos (JSON), vista (Tkinter) y lógica (TodoApp) para fácil mantenimiento.
- Manejo de errores: Valida entradas y captura problemas como JSON defectuoso.
Pasos siguientes
Expande tu gestor de tareas:
- Categorías de tareas con filtros.
- Fechas límite usando
datetimey codificación por colores. - Exportar a CSV o Excel.
- Integración con base de datos (SQLite, PostgreSQL).
- Versión web con Flask o Django.
Este proyecto muestra cómo los conceptos básicos de programación se aplican a apps del mundo real, construyendo una base sólida para temas avanzados.
— Editorial Team
Aún no hay comentarios.