Saltar al contenido
Volver al inicio 100 Días de Código en Python — Día 86: Proyecto de portafolio — Test de velocidad de escritura

100 Días de Código en Python — Día 86: Proyecto de portafolio — Test de velocidad de escritura

Nivel: Grado décimo Área: Tecnología e Informática Modalidad: Tutorial práctico Fuente: Curso "100 Days of Code: The Complete Python Pro Bootcamp" (Angela Yu, App Brewery). Contenido del Día 86, proyecto de portafolio profesional (categoría GUI).

Este tutorial adapta pedagógicamente el Día 86. La sección 📝 Contenido original del curso proviene del único archivo de la carpeta: dia86-tarea5.md (enunciado y pregunta de reflexión, en inglés; aquí traducido). El curso no ofrece guía ni solución, y este tutorial no entrega la aplicación terminada. Todo lo que no está en ese archivo (definición de "palabra estándar", fórmulas de PPM y precisión, manejo del tiempo y de eventos de teclado en Tkinter, código de ejemplo, retos 🟢🟡🟠🔴, proyecto extra y la etapa 5 de Mi Portafolio Profesional) es creación de esta adaptación.

Nota sobre las verificaciones: el 28 de septiembre de 2026, con Python 3.14 y Tk 8.6, se ejecutaron las funciones de cálculo de la sección 3 (por ejemplo: 200 caracteres en 60 segundos dan 40 PPM; una frase de 13 caracteres escrita sin errores en 6 segundos da 26 PPM netas) y se comprobó una cuenta regresiva con ventana.after(...). La ventana completa de la sección 4 es un esqueleto para completar.


Tabla de contenidos

  1. La tarea: medir cuántas palabras por minuto escribes
  2. Analizar el problema antes de programar
  3. 📖 Medir: palabras por minuto y precisión
  4. 📖 Tiempo y teclado en Tkinter: after y bind
  5. 🚀 Proyecto del día: construcción por versiones
  6. 🌟 Proyecto extra: entrenador de vocabulario en inglés
  7. 🏗️ Mi Portafolio Profesional — Etapa 5
  8. 🧩 Comprueba lo aprendido
  9. 🤖 Programar con apoyo de IA
  10. 🛠️ Observaciones sobre el material original
  11. 📋 Matriz de trazabilidad
  12. 📦 Evidencias de aprendizaje del Día 86

1. La tarea: medir cuántas palabras por minuto escribes

(Fuente: dia86-tarea5.md)

📝 Contenido original del curso

Usando Tkinter y lo que aprendiste sobre aplicaciones gráficas con Python, construye una aplicación de escritorio que evalúe tu velocidad de escritura. Dale al usuario un texto de muestra y detecta cuántas palabras por minuto puede escribir.

La velocidad promedio de escritura es de 40 palabras por minuto; con práctica puedes llegar a 100. (El enunciado enlaza un artículo de typing.com sobre la velocidad promedio y una versión web del test: typing-speed-test.aoeu.eu.)

Si tienes más tiempo, convierte tu test en un entrenador de mecanografía, con puntajes máximos y más textos de muestra. Puedes diseñar el programa como quieras.

Pregunta de la tarea — Momento de reflexión: cómo abordaste el proyecto; qué fue difícil y qué fácil; cómo mejorarías; tu mayor aprendizaje; qué harías distinto.

Prerequisitos: Tkinter y el temporizador del Pomodoro con after (Días 27–28); manejo de archivos y JSON (Días 24 y 30); listas, cadenas y random (Días 2 y 4); funciones con retorno (Día 10).


2. Analizar el problema antes de programar

(Sección creada para esta adaptación.)

🔍 Antes de programar

Pregunta Respuesta que debes construir tú
¿Qué información tenemos? Un texto de muestra
¿Qué necesitamos obtener? Lo que escribe el usuario y cuánto tiempo tarda
¿Cuándo empieza a contar el tiempo? ¿Al abrir la app? ¿Al pulsar "Empezar"? ¿Con la primera tecla? (decide y justifica)
¿Cuándo termina? ¿A los 60 segundos? ¿Cuando termina el texto?
¿Qué resultado esperamos? Palabras por minuto y, para ser justos, precisión (errores)
¿Qué condiciones hay que tener en cuenta? Errores de escritura, borrar con Retroceso, pegar texto (¡trampa!), tiempo 0

Dos formatos de test

Formato Cómo funciona Ventaja Desventaja
Tiempo fijo (60 s) Escribe todo lo que puedas en un minuto Resultado comparable entre personas Necesitas textos largos
Texto fijo Escribe una frase completa; se mide el tiempo Más simple de programar Frases cortas dan resultados muy variables

3. 📖 Medir: palabras por minuto y precisión

(Sección creada para esta adaptación. Funciones verificadas.)

🎯 Objetivo de aprendizaje

Traducir una definición (palabras por minuto) a una fórmula y a una función que se pueda probar.

📖 Explicación conceptual

¿Qué es una "palabra"? Si contamos palabras reales, escribir "sol" no vale lo mismo que "electroencefalograma". Por eso los tests de mecanografía usan la palabra estándar = 5 caracteres (incluidos espacios y signos).

Medida Fórmula Qué mide
PPM brutas (caracteres escritos ÷ 5) ÷ minutos Velocidad, aunque haya errores
PPM netas (caracteres correctos ÷ 5) ÷ minutos Velocidad útil
Precisión caracteres correctos ÷ caracteres escritos × 100 Porcentaje de aciertos

Ejemplo a mano: escribes 200 caracteres en 60 s → 200 ÷ 5 = 40 palabras en 1 minuto → 40 PPM. Si fueron 30 s: 40 ÷ 0,5 = 80 PPM.

🔎 Ejemplo explicado

Python
def contar_correctos(modelo, escrito):
    return sum(1 for a, b in zip(modelo, escrito) if a == b)

def ppm_brutas(escrito, segundos):
    if segundos <= 0:
        return 0
    return (len(escrito) / 5) / (segundos / 60)
  • zip(modelo, escrito) recorre ambos textos a la par: primer carácter con primer carácter, etc. Se detiene en el más corto.
  • sum(1 for ... if a == b) cuenta cuántas parejas coinciden (una expresión generadora, pariente de la comprensión de listas del Día 26).
  • if segundos <= 0: return 0 evita un ZeroDivisionError si el tiempo no ha empezado.

Tu turno: escribe ppm_netas(modelo, escrito, segundos) y precision(modelo, escrito) (cuidado con el texto vacío). Comprueba: ppm_netas("el perro come", "el perro come", 6) debe dar 26.0.

⚠️ Una limitación importante (analízala)

La comparación posición a posición es muy estricta: si el usuario omite una letra, todo lo que sigue queda "corrido" y cuenta como error. Comprobado: contar_correctos("hola", "hla") da 1, aunque solo faltó una letra.

Alternativas (elige una y justifícala en tu diario):

  1. Comparar palabra por palabra (modelo.split() y escrito.split()): una palabra mal escrita no arrastra a las demás.
  2. No dejar avanzar mientras haya un error (como algunos tests en línea): el usuario debe corregir con Retroceso.
  3. Usar difflib.SequenceMatcher (biblioteca estándar) para medir el parecido: más justo, pero más difícil de explicar.

⚠️ Errores frecuentes

Error Qué ocurre Solución
Dividir por segundos en lugar de minutos Resultados 60 veces menores segundos / 60
Contar palabras con split() para las PPM Las palabras cortas "valen" igual que las largas Palabra estándar = 5 caracteres
Medir el tiempo con time.time() en saltos de 1 s Poca exactitud en textos cortos time.perf_counter() mide con más precisión
División por cero El programa se cierra al terminar "demasiado rápido" o sin empezar Comprueba segundos <= 0

4. 📖 Tiempo y teclado en Tkinter: after y bind

(Sección creada para esta adaptación. after verificado con una cuenta regresiva; la ventana completa es un esqueleto.)

Dos herramientas clave

Herramienta Para qué Ejemplo
ventana.after(ms, funcion) Ejecutar funcion dentro de ms milisegundos sin congelar la ventana Cuenta regresiva de 60 s (como el Pomodoro del Día 28)
widget.bind("<KeyRelease>", funcion) Ejecutar funcion cada vez que se suelta una tecla en ese widget Comparar lo escrito con el modelo y colorear errores

⚠️ Nunca uses time.sleep() en una app Tkinter: congela la ventana entera. after programa la tarea y deja que mainloop siga atendiendo al usuario.

🔎 Esqueleto

Python
import time
import tkinter as tk

TEXTO = "La práctica constante mejora la velocidad y la precisión."
DURACION = 60

ventana = tk.Tk()
ventana.title("Test de mecanografía")

tk.Label(ventana, text=TEXTO, wraplength=500, font=("Arial", 14)).pack(pady=10)
entrada = tk.Text(ventana, height=4, width=60, font=("Arial", 14))
entrada.pack()
reloj = tk.Label(ventana, text=f"{DURACION} s")
reloj.pack()

inicio = None

def al_teclear(evento):
    global inicio
    if inicio is None:                  # la primera tecla inicia el reloj
        inicio = time.perf_counter()
        cuenta_regresiva(DURACION)
    escrito = entrada.get("1.0", "end-1c")
    # Tu turno: comparar con TEXTO y actualizar una etiqueta de errores

def cuenta_regresiva(restante):
    reloj.config(text=f"{restante} s")
    if restante > 0:
        ventana.after(1000, cuenta_regresiva, restante - 1)
    else:
        entrada.config(state="disabled")
        # Tu turno: calcular y mostrar PPM y precisión

entrada.bind("<KeyRelease>", al_teclear)
ventana.mainloop()

Puntos clave:

  • entrada.get("1.0", "end-1c"): en un Text, "1.0" es línea 1, carácter 0; "end-1c" quita el salto de línea que Tkinter agrega siempre al final.
  • after(1000, cuenta_regresiva, restante - 1): los argumentos extra se pasan a la función. La función se reprograma a sí misma cada segundo.
  • state="disabled" impide seguir escribiendo cuando se acaba el tiempo.

Resaltar errores (idea): un Text admite etiquetas (tags) de formato. Con entrada.tag_config("error", background="#ffb3b3") y entrada.tag_add("error", "1.5", "1.6") pintas de rojo el carácter 5. Elimina las marcas anteriores con tag_remove antes de volver a pintar.

⚠️ Errores frecuentes

Error Qué ocurre Solución
Iniciar la cuenta regresiva en cada tecla El reloj va cada vez más rápido (varias cuentas en paralelo) Iníciala solo una vez (if inicio is None)
time.sleep(1) en un ciclo La ventana se congela after
entrada.get() sin índices en un Text TypeError En Text se usa get("1.0", "end-1c"); en Entry, get()
Reiniciar el test sin cancelar el after pendiente Aparecen dos relojes Guarda el identificador que devuelve after y usa after_cancel

5. 🚀 Proyecto del día: construcción por versiones

(Desarrolla la tarea dia86-tarea5.md. Las versiones son una propuesta de esta adaptación.)

Problema

Construir una aplicación de escritorio que muestre un texto, mida la velocidad de escritura del usuario y le dé su resultado.

Contexto

En la clase de tecnología se quiere medir el progreso en mecanografía durante el periodo, sin depender de páginas web con publicidad.

Objetivo

Una app Tkinter que mida PPM netas y precisión, con varios textos y un registro de mejores puntajes.

Requisitos funcionales

  1. Muestra un texto de muestra elegido al azar de una lista (o de un archivo).
  2. El tiempo empieza con la primera tecla y termina a los 60 s (o al completar el texto, según tu diseño).
  3. Muestra PPM netas y precisión al final.
  4. Permite reiniciar con otro texto.

Restricciones técnicas

  • Cálculos en funciones separadas y probadas con assert (sección 3).
  • Tiempo con after, nunca con sleep.
  • Textos de muestra en un archivo (textos.txt o textos.json), no escritos dentro del código.

Planificación

  1. Comprender cómo funciona un test real (prueba la versión web del enunciado). 2. Entradas: teclas del usuario. 3. Procesos: iniciar reloj → comparar → terminar → calcular. 4. Salidas: PPM, precisión, errores resaltados. 5. Dividir en cálculos, interfaz y datos. 6. Bocetar la ventana. 7. Implementar. 8. Probar. 9. Corregir. 10. Mejorar.

Construcción paso a paso

  • Versión 1 — Cálculos en consola: las funciones de la sección 3 con sus pruebas. Simula: ppm_netas(modelo, escrito, 45).
  • Versión 2 — Ventana: texto de muestra, área de escritura, reloj que baja al teclear la primera letra.
  • Versión 3 — Resultado: al terminar el tiempo, bloquear la escritura y mostrar PPM y precisión.
  • Versión 4 — Retroalimentación en vivo: resaltar errores mientras se escribe.
  • Versión 5 — Varios textos y reinicio: textos desde archivo, botón "Otra vez" (con after_cancel).
  • Versión final — Mejores puntajes: guardar en puntajes.json los 5 mejores (fecha, PPM, precisión); README.md y diario.

Pruebas

# Caso Resultado esperado
1 200 caracteres en 60 s 40 PPM brutas
2 Texto perfecto de 13 caracteres en 6 s 26 PPM netas, 100 % de precisión
3 Terminar sin escribir nada 0 PPM, sin errores de división
4 Pulsar teclas muy rápido al inicio Solo un reloj, que baja de 1 en 1
5 Pegar el texto con Ctrl + V Tu app lo detecta o lo impide (decide cómo)
6 Reiniciar a mitad de un test El reloj anterior se detiene
7 Borrar puntajes.json y abrir la app Funciona y crea el archivo (Día 30: FileNotFoundError)

🧪 Prueba y depuración

Python
def al_teclear(evento):
    inicio = time.time()
    cuenta_regresiva(60)
    escrito = entrada.get()
    ppm = len(escrito.split()) / (time.time() - inicio)

Encuentra cuatro problemas: inicio se reinicia en cada tecla (y es local); la cuenta regresiva se lanza en cada tecla; get() sin índices falla en un Text; y las PPM usan palabras reales, segundos en vez de minutos y un tiempo casi cero (división por un número diminuto o por cero).

⭐ Extensión del proyecto

  • 🟢 Reto 1 — Aplicación: muestra también las PPM en vivo cada segundo.
  • 🟡 Reto 2 — Ampliación: tabla de mejores puntajes por usuario (nombre o alias) en JSON.
  • 🟠 Reto 3 — Desafío: estadísticas de errores: ¿qué letras falla más el usuario? Guárdalas y muestra las 5 peores al final (pista: diccionario o collections.Counter).
  • 🔴 Reto 4 — Pensamiento computacional: con las estadísticas del reto 3, genera textos de práctica que incluyan con más frecuencia las letras que el usuario falla. Diseña la estrategia y comprueba en 3 sesiones si la precisión mejora.

6. 🌟 Proyecto extra: entrenador de vocabulario en inglés

(Proyecto creado para esta adaptación; no existe en el curso original.)

Situación

El área de inglés quiere una app para practicar la ortografía de palabras del periodo: se muestra la palabra en español (o se escucha, como extensión) y el estudiante escribe la traducción contra reloj.

Requisitos

  1. Leer las palabras desde un CSV (español, inglés) (Día 25; parecido a las tarjetas del Día 31).
  2. Límite de 10 segundos por palabra con after; si se acaba, cuenta como error y pasa a la siguiente.
  3. Al final: aciertos, errores y la lista de palabras falladas, guardada para repasarla.

Decisiones que debes tomar (y justificar)

  • ¿Aceptas "Colour" y "color"? ¿Mayúsculas? ¿Espacios al final?
  • ¿Cómo haces que las palabras falladas aparezcan con más frecuencia en la próxima sesión?

Casos de prueba

  • Responder a los 9,9 s cuenta; a los 10 s, no.
  • Una respuesta con espacios de más (" dog ") se acepta.

7. 🏗️ Mi Portafolio Profesional — Etapa 5

(Proyecto integrador creado para esta adaptación; etapas anteriores en los Días 82 a 85.)

Etapa 5 (hoy):

  1. Repositorio del test de mecanografía con calculos.py (y sus pruebas), app.py, textos.json y README.md con una captura y tus PPM de la primera y la última prueba.
  2. En el README.md, explica cómo mides las PPM y la precisión, y la limitación que elegiste resolver (sección 3).
  3. DIARIO.md del Día 86 y ficha en proyectos.json.
  4. Revisión intermedia: ya tienes 5 proyectos. Usa la rúbrica del Día 83 y elige los 3 que destacarías en tu sitio.

8. 🧩 Comprueba lo aprendido

Conceptuales

  1. ¿Por qué se usa la "palabra estándar" de 5 caracteres?
  2. ¿Qué diferencia hay entre PPM brutas y netas?
  3. ¿Por qué time.sleep congela una app Tkinter y after no?
  4. ¿Para qué sirve after_cancel?

Análisis de código

Python
sum(1 for a, b in zip("gato", "gata") if a == b)

¿Qué devuelve y por qué?

Predicción

¿Cuántas PPM brutas obtiene quien escribe 150 caracteres en 45 segundos?

Depuración

"Mi reloj baja de 2 en 2 y a veces de 3 en 3." ¿Cuál es la causa más probable?

Modificación

Cambia el formato del test a "texto fijo": el tiempo termina cuando lo escrito es igual al modelo.

Creación

Escribe comparar_palabras(modelo, escrito) que devuelva cuántas palabras están bien escritas, comparando palabra por palabra.


9. 🤖 Programar con apoyo de IA

Ciclo de trabajo: formula el problema → inténtalo tú primero → consulta a la IA sobre un punto concreto → lee y entiende → prueba → compara con la documentación oficial → modifica → explica con tus palabras.

Actividad 1 — Comprobar una definición. Pregunta a una IA cómo se calculan las palabras por minuto. ¿Menciona los 5 caracteres? Contrástalo con al menos una fuente confiable y anota si coinciden.

Actividad 2 — Revisar un cálculo. Pide a una IA una función de PPM y aplícale las pruebas 1 a 3 de la sección 5. ¿Maneja el tiempo cero?

Actividad 3 — Documentación. Pregunta cómo resaltar texto en un tkinter.Text. Compara con la documentación de Tkinter (métodos tag_config, tag_add, tag_remove) y anota cualquier diferencia.

Declara el uso: sección "Uso de IA" en el README.md.


10. 🛠️ Observaciones sobre el material original

Archivo Observación Tratamiento
dia86-tarea5.md No define qué es una "palabra" ni cómo tratar los errores Sección 3: palabra estándar, PPM brutas y netas, precisión y sus limitaciones
dia86-tarea5.md Cita 40 PPM de promedio y 100 con práctica (según un blog de typing.com) Se conserva como referencia orientativa; varía según la fuente, el idioma y el teclado
dia86-tarea5.md Enlaza versiones web externas Se conservan como referencia para comprender el problema
dia86-tarea5.md "Entrenador con puntajes máximos y más textos" como extra Versiones 5 y final, y retos 2 a 4

11. 📋 Matriz de trazabilidad

Contenido original Archivo de origen Concepto desarrollado Ejercicio original Reto adicional Proyecto relacionado
App Tkinter que evalúa la velocidad de escritura con un texto de muestra dia86-tarea5.md Secciones 2 y 4 Test de velocidad Retos 1 y 3 Secciones 5 y 7
Palabras por minuto; promedio 40, hasta 100 dia86-tarea5.md Sección 3 Test de velocidad Predicción de la sección 8 Sección 5
Extra: entrenador con puntajes máximos y más textos dia86-tarea5.md Sección 5 (versiones 5 y final) Reto opcional Retos 2 y 4 Sección 5
Momento de reflexión dia86-tarea5.md Sección 5 (versión final) Pregunta de reflexión — Sección 7
(No existe en el curso original) — after, bind, etiquetas de Text — Entrenador de vocabulario Sección 6

12. 📦 Evidencias de aprendizaje del Día 86

  • La app funcionando (captura o video corto de un test completo).
  • Las pruebas de los cálculos superadas.
  • Registro de tus PPM y precisión en varias sesiones (¿mejoraste?).
  • DIARIO.md del Día 86, repositorio y ficha en el portafolio.
  • Cuaderno de errores: segundos frente a minutos, división por cero, varios after en paralelo, sleep que congela la ventana, get de Text sin índices, comparación posición a posición demasiado estricta.