Aplicación universitaria para la materia Estructuras de Datos I que implementa un gestor de tareas (To-Do List) usando una Lista Enlazada Simple como estructura de datos central.
La v2.0.0 agrega soporte para múltiples listas independientes, búsqueda global con normalización de texto, edición inline, favoritos y persistencia automática en JSON.
Cada tarea se representa como un Nodo que contiene su título, descripción, estado (Pendiente / Completada), bandera de favorito, y un puntero al siguiente nodo en la cadena.
El proyecto sigue el patrón Modelo–Vista–Controlador (MVC), con responsabilidades separadas y sin dependencias cruzadas entre capas:
| Capa | Archivo | Responsabilidad |
|---|---|---|
| Modelo | src/models/model.py |
Nodo, ListaEnlazadaTareas, GestorListas + JSON |
| Vista | src/views/view.py |
Interfaz gráfica Tkinter; no importa al controlador |
| Controlador | src/controllers/controller.py |
Intermediario: recibe callbacks de la Vista, opera el Modelo |
VISTA → usuario escribe título y pulsa ✓
CTRL → llama a gestor.listas[lista_activa].agregar_tarea(titulo, desc)
MODELO → crea Nodo y lo enlaza al final de la cadena [O(n)]
CTRL → llama a gestor.guardar_json() + vista._refrescar_vista()
VISTA → redibuja las tarjetas con la nueva tarea visible
VISTA → usuario hace click en una card (selección por índice)
CTRL → registra tarea_seleccionada y habilita botones de la toolbar
VISTA → usuario pulsa ✓ Completar o ✕
CTRL → lista.toggle_completada(indice) | lista.eliminar_tarea(indice)
MODELO → recorre la cadena hasta el nodo en posición [indice] [O(n)]
CTRL → guardar_json() + _refrescar_vista()
VISTA → redibuja con el estado actualizado
VISTA → usuario pulsa 🔍 y escribe texto
CTRL → llama a gestor.buscar_global(texto, solo_favoritas)
MODELO → recorre TODAS las listas y aplica normalización NFD + lower()
CTRL → entrega la lista de resultados a vista.renderizar_resultados_busqueda()
VISTA → muestra tarjetas con "NombreLista › Título"
envWin/
├── src/
│ ├── main.py # Punto de entrada; instancia ControladorTareas
│ ├── models/
│ │ ├── __init__.py
│ │ └── model.py # Nodo · ListaEnlazadaTareas · GestorListas
│ ├── views/
│ │ ├── __init__.py
│ │ ├── view.py # VistaTareas (widgets Tkinter)
│ │ └── styles.py # Paleta de colores, fuentes y constantes UI
│ ├── controllers/
│ │ ├── __init__.py
│ │ └── controller.py # ControladorTareas (callbacks MVC)
│ └── data/
│ └── tareas.json # Persistencia automática (generado en runtime)
├── tests/
│ └── test_model.py # 15 pruebas unitarias con unittest
├── requirements.txt # Sin dependencias externas (solo stdlib)
├── run.bat # Lanzador rápido para Windows
└── README.md # Este archivo
Unidad atómica de la lista enlazada.
| Atributo | Tipo | Descripción |
|---|---|---|
titulo |
str |
Texto principal de la tarea |
descripcion |
str |
Detalle opcional (cadena vacía por defecto) |
estado |
str |
'Pendiente' o 'Completada' |
favorito |
bool |
Marca de favorito; False por defecto |
siguiente |
Nodo | None |
Puntero al próximo nodo; None indica fin de cadena |
Implementa la lista enlazada simple. La cabeza (self.cabeza) es el único punto de acceso.
| Método | Complejidad | Descripción |
|---|---|---|
agregar_tarea |
O(n) | Recorre hasta el último nodo y enlaza el nuevo al final |
_obtener_nodo |
O(n) | Busca un nodo por índice (helper interno) |
toggle_completada |
O(n) | Alterna estado entre 'Pendiente' y 'Completada' |
editar_tarea |
O(n) | Sobrescribe titulo y descripcion del nodo en [indice] |
marcar_favorito |
O(n) | Invierte el valor booleano de favorito |
eliminar_tarea |
O(n) | Redirige el puntero del nodo anterior para saltar al eliminado |
obtener_todas |
O(n) | Recorre la cadena y retorna una lista de dicts (0-indexed) |
obtener_favoritas |
O(n) | Filtra obtener_todas() por favorito == True |
Repositorio central de múltiples ListaEnlazadaTareas con persistencia JSON.
| Método | Descripción |
|---|---|
agregar_lista |
Crea una nueva lista; retorna False si el nombre ya existe |
eliminar_lista |
Elimina la lista completa con todas sus tareas |
buscar_global |
Busca subcadena en títulos y descripciones de todas las listas |
guardar_json |
Serializa todas las listas y las escribe en data/tareas.json |
cargar_json |
Lee el JSON y reconstruye las listas enlazadas en memoria |
Modo
:memory:: si se instancia conGestorListas(':memory:'), los métodosguardar_jsonycargar_jsonson no-operativos. Se usa en los tests unitarios para evitar E/S de disco.
Todas las operaciones son O(n) en tiempo porque la Lista Enlazada Simple no tiene acceso aleatorio — es necesario recorrer la cadena nodo a nodo hasta alcanzar el índice deseado.
| Operación | Tiempo | Espacio auxiliar | Justificación |
|---|---|---|---|
agregar_tarea |
O(n) | O(1) | Recorre hasta el último nodo para enlazar el nuevo |
toggle_completada |
O(n) | O(1) | Recorre hasta el índice deseado y cambia el campo estado |
editar_tarea |
O(n) | O(1) | Recorre hasta el índice y sobreescribe titulo y descripcion |
marcar_favorito |
O(n) | O(1) | Recorre hasta el índice y niega el booleano favorito |
eliminar_tarea |
O(n) | O(1) | Necesita el nodo anterior al eliminado para redirigir el puntero |
obtener_todas |
O(n) | O(n) | Construye una lista Python con un dict por cada nodo |
buscar_global |
O(n·m) | O(k) | n = tareas totales en todas las listas, m = len(texto), k = coincidencias |
Nota: mantener un puntero
colabajaríaagregar_tareaa O(1), pero se omitió para hacer el recorrido explícito y pedagógico.
-
Python 3.8 o superior.
-
tkinterviene incluido en Python para Windows y macOS. En Linux puede requerir instalación adicional:sudo apt-get install python3-tk
# 1. Clonar el repositorio
git clone <URL-del-repositorio>
cd <carpeta-del-proyecto>
# 2. No se requiere instalar dependencias externas.
# El proyecto usa exclusivamente la biblioteca estándar de Python.run.batDesde la raíz del proyecto:
python src/main.pyLa ventana se abre con:
- Barra de tabs: una pestaña por lista, con botón ✕ para eliminar y
+ Agregar lista. - Panel de tarjetas: cada tarea muestra su estado (barra lateral verde/naranja), favorito (★/○), título, descripción y etiqueta
[Pendiente]/[Completada]. - Toolbar inferior: botones
✓ Completar,☆ Favorito,✏ Editar,✕ Eliminar(habilitados al seleccionar una tarea). - Modo búsqueda: botón 🔍 activa un campo de texto con búsqueda en tiempo real y filtro de favoritas.
- Modo edición: el panel de tarjetas se reemplaza por un formulario de edición con campos
TítuloyDescripción.
Desde la raíz del proyecto:
python -m unittest discover tests -vResultado esperado:
test_busqueda_global_multilist (test_model.TestGestorListas) ... ok
test_busqueda_ignora_acentos (test_model.TestGestorListas) ... ok
test_busqueda_ignora_mayusculas (test_model.TestGestorListas) ... ok
test_busqueda_solo_favoritas (test_model.TestGestorListas) ... ok
test_gestor_agregar_lista (test_model.TestGestorListas) ... ok
test_gestor_eliminar_lista (test_model.TestGestorListas) ... ok
test_gestor_nombre_duplicado (test_model.TestGestorListas) ... ok
test_persistencia_guardar_cargar (test_model.TestGestorListas) ... ok
test_agregar_enlaza_correctamente (test_model.TestListaEnlazadaTareas) ... ok
test_editar_tarea_cambia_campos (test_model.TestListaEnlazadaTareas) ... ok
test_eliminar_cabeza (test_model.TestListaEnlazadaTareas) ... ok
test_eliminar_reasigna_punteros (test_model.TestListaEnlazadaTareas) ... ok
test_lista_vacia_obtener_todas (test_model.TestListaEnlazadaTareas) ... ok
test_marcar_favorito_toggle (test_model.TestListaEnlazadaTareas) ... ok
test_toggle_completada (test_model.TestListaEnlazadaTareas) ... ok
----------------------------------------------------------------------
Ran 15 tests in 0.016s
OK
| Test | Qué verifica |
|---|---|
test_agregar_enlaza_correctamente |
Cadena A→B→C correcta; último nodo apunta a None |
test_eliminar_reasigna_punteros |
Eliminar B de A→B→C produce A→C; punteros redirigidos correctamente |
test_eliminar_cabeza |
Eliminar índice 0 promueve al segundo nodo como nueva cabeza |
test_toggle_completada |
Doble toggle: Pendiente→Completada→Pendiente; retorna True |
test_editar_tarea_cambia_campos |
Sobrescribe título y descripción sin crear un nodo nuevo |
test_marcar_favorito_toggle |
Doble toggle: False→True→False |
test_lista_vacia_obtener_todas |
Lista sin nodos retorna [], no None |
| Test | Qué verifica |
|---|---|
test_gestor_agregar_lista |
Lista nueva registrada como ListaEnlazadaTareas en el dict interno |
test_gestor_nombre_duplicado |
Nombre repetido retorna False y no duplica la entrada |
test_gestor_eliminar_lista |
Eliminar "A" no afecta "B"; "A" desaparece del gestor |
test_busqueda_global_multilist |
Búsqueda "comprar" encuentra resultados en L1 y L2 (2 resultados) |
test_busqueda_ignora_mayusculas |
"TAREA IMPORTANTE" encontrada con búsqueda "tarea" |
test_busqueda_ignora_acentos |
"Música relajante" encontrada con búsqueda "musica" (sin acento) |
test_busqueda_solo_favoritas |
Filtro solo_favoritas=True excluye tareas no marcadas |
test_persistencia_guardar_cargar |
Datos guardados en JSON (título, favorito) se recuperan en otro gestor |