Skip to content

HenryXCC/FlowTimer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🍅 FlowTimer

Python Tkinter pygame Platform License i18n


🍅 FlowTimer — English

A full-featured, modular desktop productivity timer built with Python and Tkinter. No internet connection required to run — music and sounds are stored locally after the first import.


✨ Features

  • Work/break timer with short break, long break, and full cycle modes
  • Drift-free countdown using time.monotonic() — stays accurate under any CPU load or sleep/wake cycle
  • Pause & resume — resumes exactly where you left off
  • Compact mode — non-essential controls hide automatically when the window is narrowed
  • Always on top — optional setting to keep the timer above other windows
  • Language switcher — toggle between 🇪🇸 Spanish and 🇬🇧 English at any time; preference is saved between sessions
  • Session history — every completed session is stored locally as JSON
  • Weekly statistics with a bar chart, daily breakdown, and task list
  • Streak tracking — counts consecutive days of use, resets automatically if a day is missed
  • CSV export — export your full history for analysis in Excel or similar tools
  • Configurable durations and cycles through a clean settings tab
  • Autostart for breaks and next Pomodoros
  • Tick-tock sound — optional metronome click during work and/or break sessions, toggled independently
  • Custom alarm sound — supports MP3, WAV, OGG, FLAC, M4A, OPUS, AAC; files are copied to a dedicated local folder
  • Music player — built-in background music player with persistent library, YouTube import, progress bar, and volume control; music pauses automatically when the alarm fires and resumes from the same position once the dialog is dismissed

📸 Screenshots

Timer tab

Statistics tab

Settings tab


🚀 Getting Started

Prerequisites

  • Python 3.10 or higher
  • pip (included with Python)
  • ffmpeg — required for YouTube audio import (download, must be available in your system PATH)

Installation

# 1. Clone the repository
git clone https://github.com/HenryXCC/FlowTimer.git
cd flowtimer

# 2. (Recommended) Create a virtual environment
python -m venv .venv
source .venv/bin/activate      # macOS / Linux
.venv\Scripts\activate         # Windows

# 3. Install dependencies
pip install -r requirements.txt

Run

python main.py

No configuration needed — the app creates its data file (~/.pomodoro_data.json) automatically on first run.


📦 Executable (no Python required)

A pre-built standalone executable for Windows is available on the Releases page — no Python or pip installation needed.

Platform File Status
Windows FlowTimer.exe ✅ Available
macOS FlowTimer 🔧 Build from source
Linux FlowTimer 🔧 Build from source

macOS and Linux users can build their own executable from source using the instructions below.

Building the executable from source

pip install pyinstaller

# Windows
pyinstaller --onedir --windowed --clean --noconfirm --name "FlowTimer" --icon="assets/icon.ico" --add-data "assets/icon.ico;assets" --collect-all pygame main.py

# macOS / Linux
pyinstaller --onedir --windowed --clean --noconfirm --name "FlowTimer" --icon="assets/icon.ico" --add-data "assets/icon.ico:assets" --collect-all pygame main.py

The output is a dist/FlowTimer/ folder containing FlowTimer.exe (Windows) or FlowTimer (macOS/Linux) alongside a _internal/ folder with all dependencies. Distribute the entire dist/FlowTimer/ folder — the executable will not run without _internal/.


🧪 Running the Tests

The core/ package is fully covered by a pytest test suite (no GUI required).

# Install pytest if you haven't already
pip install pytest

# Run all tests from the project root
pytest tests/

All tests run without a display or audio device — UI code and pygame are fully mocked.


🗂 Project Structure

pomodoro-timer/
│
├── main.py                  # Entry point — run this
│
├── core/                    # Pure-Python business logic (no Tkinter)
│   ├── __init__.py
│   ├── constants.py         # Colours, paths, default configuration values
│   ├── i18n.py              # Internationalisation — STRINGS dict + t() helper
│   ├── audio.py             # GestorAudio — alarm, tick-tock, and music playback
│   ├── storage.py           # JSON persistence (cargar_datos / guardar_datos)
│   └── stats.py             # estadisticas_semana(), exportar_csv()
│
├── ui/                      # Tkinter views (depend on core, not on each other)
│   ├── __init__.py
│   ├── app.py               # App(tk.Tk) — root window, timer state, coordinator
│   ├── tab_timer.py         # TabTimer — clock ring, controls, daily summary
│   ├── tab_stats.py         # TabStats — chart, history table, CSV export
│   └── tab_settings.py      # TabSettings — durations, options, alarm, music player
│
├── tests/                   # pytest test suite
│   ├── test_audio.py        # GestorAudio state, position tracking, alarm/music interaction
│   ├── test_chart_language_bug.py  # Regression: chart day-key / language consistency
│   ├── test_i18n.py         # Language switching, key parity, formatting
│   ├── test_stats.py        # Weekly statistics and CSV export
│   └── test_storage.py      # JSON persistence and forward-compatibility
│
├── assets/                  # Icons and screenshots
├── requirements.txt
├── .gitignore
├── LICENSE
└── README.md

The app also creates these paths at runtime (outside the project folder):

Path Purpose
~/.pomodoro_data.json Session history, streak, configuration, language, and music library
~/.pomodoro_sounds/ Copied alarm audio files
~/.pomodoro_music/ Copied and downloaded music files (MP3, thumbnails)

📦 Dependencies

Package Purpose Required for
tkinter GUI framework Everything — ships with Python
pygame Audio playback Alarm sounds, tick-tock, music player
yt-dlp YouTube audio download Music player — YouTube import
Pillow Image loading Music player — YouTube thumbnails
ffmpeg Audio conversion Music player — YouTube import (system tool, not pip)

All pip packages are listed in requirements.txt and installed in one step. tkinter ships with Python. ffmpeg must be installed separately and available in your system PATH.


🎵 Music Player

The built-in music player lives in the Settings tab and supports two ways to add music:

Local files

Click ➕ File to import one or more audio files (MP3, WAV, OGG, FLAC, M4A, OPUS, AAC), or use the 📁 button to import an entire folder at once (up to 50 files). Files are copied to ~/.pomodoro_music/ so they remain available even if the originals are moved or deleted.

YouTube

Click ▶ YouTube, paste up to 3 URLs (one per field), and click Import. The app downloads the audio as MP3 via yt-dlp and ffmpeg, fetches the video thumbnail, and adds the track to the library — all in the background while you keep using the timer.

ffmpeg must be installed and in your PATH for YouTube import to work.

Behaviour during alarms

When a Pomodoro or break ends, music pauses immediately before the alarm sounds. Once you dismiss the dialog, music resumes automatically from the exact position where it stopped — no manual action needed.


🌐 Internationalisation / i18n

The app ships with full Spanish and English support. The active language can be changed at any time with the 🌐 EN / 🌐 ES button in the top-right corner. The choice persists between sessions.

How it works

All user-visible strings live in core/i18n.py as a nested dictionary:

STRINGS = {
    "es": { "btn_start": "▶  Iniciar", ... },
    "en": { "btn_start": "▶  Start",   ... },
}

The t() helper translates any key in the active language with optional format arguments:

from core.i18n import t, set_language

set_language("en")
t("btn_start")           # "▶  Start"
t("lbl_streak", n=5)     # "🔥 Streak: 5 days"

Adding a new language

  1. Open core/i18n.py.
  2. Add a new entry to STRINGS using an existing language as a template.
  3. Call set_language("xx") where "xx" is your new code.

No other file needs to change.


🏗 Architecture & Technical Notes

Separation of concerns

core/ is completely free of Tkinter — all modules in that package are independently testable. The UI layer (ui/) imports from core/, but the three tab frames never import each other; they communicate exclusively through the App coordinator:

App.data          — shared session/config/music dict
App.cfg           — shortcut to App.data["config"]
App.audio         — GestorAudio instance
App.cambiar_modo()
App.toggle_start() / reset() / saltar()
App.on_config_saved()
App.on_historial_borrado()

Timer accuracy

The countdown uses time.monotonic() as its reference clock. The UI refreshes every 100 ms, but remaining time is recomputed from the wall clock on every tick — the timer never drifts regardless of CPU load.

Audio subsystem

GestorAudio manages three independent audio concerns:

  • Alarm — plays a custom sound file via pygame.mixer.Sound or mixer.music, auto-stopped after 20 seconds; falls back to a system beep if pygame is unavailable.
  • Tick-tock — a soft click generated in pure Python (no audio file needed) looped on a daemon thread, one click per second.
  • Music — streams via pygame.mixer.music with volume control, loop-forever playback, and position tracking via an offset accumulator (_music_pos_offset_ms) so the displayed progress and seek position remain accurate across pause/resume and alarm cycles.

When an alarm fires, the current playback position is saved, music is paused, and the alarm plays. After the user dismisses the dialog, music_restaurar() reloads the file and resumes from the saved position in a single synchronous call — avoiding race conditions between the alarm thread and the UI thread.

Persistence

Session data is stored in a single JSON file. The schema is forward-compatible: new keys are back-filled via setdefault, so old data files are never broken. The music library (music_tracks) is stored in the same file as a list of track objects:

{
  "name": "LoFi Beats",
  "path": "/home/user/.pomodoro_music/abc123.mp3",
  "source": "youtube",
  "youtube_id": "abc123",
  "channel": "Example Channel",
  "duration_s": 3600,
  "thumb_path": "/home/user/.pomodoro_music/abc123.png"
}

Streak logic

The streak counter increments only if the app was last used yesterday. If more than one day has passed, the streak resets to 1 on the new session — consistent with apps like Duolingo.


🎛 Configuration Reference

Setting Default Range
Pomodoro duration 25 min 1–120 min
Short break 5 min 1–30 min
Long break 15 min 1–60 min
Pomodoros per cycle 4 1–10
Sound on finish
Auto-start break
Auto-start next Pomodoro
Always on top
Tick-tock during work
Tick-tock during breaks
Custom alarm sound None MP3 / WAV / OGG / FLAC / M4A / OPUS / AAC
Music volume 70% 0–100%

Duration changes apply from the next session. All other changes apply immediately on save.


📄 License

Distributed under the MIT License. See LICENSE for details.






🍅 FlowTimer — Español

Un temporizador de escritorio completo y modular para productividad, construido con Python y Tkinter. No requiere conexión a internet para funcionar — la música y los sonidos se almacenan localmente después de la primera importación.


✨ Características

  • Temporizador con modos de descanso corto, descanso largo y ciclo completo
  • Cuenta regresiva sin drift usando time.monotonic() — preciso bajo cualquier carga de CPU o ciclos de suspensión
  • Pausar y reanudar — continúa exactamente donde lo dejaste
  • Modo compacto — los controles no esenciales se ocultan automáticamente al reducir el tamaño de la ventana
  • Siempre visible — opción para mantener el temporizador por encima de otras ventanas
  • Selector de idioma — cambia entre 🇪🇸 Español y 🇬🇧 Inglés en cualquier momento; la preferencia se guarda entre sesiones
  • Historial de sesiones — cada sesión completada se guarda localmente en JSON
  • Estadísticas semanales con gráfico de barras, desglose diario y lista de tareas
  • Racha de días — cuenta días consecutivos de uso y se reinicia automáticamente si se pierde un día
  • Exportación CSV — exporta tu historial completo para análisis en Excel o herramientas similares
  • Duraciones y ciclos configurables desde una pestaña de ajustes limpia
  • Inicio automático de descansos y siguientes Pomodoros
  • Sonido de tictac — clic de metrónomo opcional durante sesiones de trabajo y/o descanso, con activación independiente
  • Sonido de alarma personalizado — soporta MP3, WAV, OGG, FLAC, M4A, OPUS, AAC; los archivos se copian a una carpeta local dedicada
  • Reproductor de música — reproductor integrado con biblioteca persistente, importación desde YouTube, barra de progreso y control de volumen; la música se pausa automáticamente al sonar la alarma y se reanuda desde la misma posición al cerrar el diálogo

📸 Capturas de pantalla

Pestaña temporizador

Pestaña estadísticas

Pestaña configuración


🚀 Cómo empezar

Requisitos previos

  • Python 3.10 o superior
  • pip (incluido con Python)
  • ffmpeg — requerido para importar audio desde YouTube (descargar, debe estar disponible en el PATH del sistema)

Instalación

# 1. Clonar el repositorio
git clone https://github.com/HenryXCC/FlowTimer.git
cd flowtimer

# 2. (Recomendado) Crear un entorno virtual
python -m venv .venv
source .venv/bin/activate      # macOS / Linux
.venv\Scripts\activate         # Windows

# 3. Instalar dependencias
pip install -r requirements.txt

Ejecutar

python main.py

No se necesita configuración previa — la app crea su archivo de datos (~/.pomodoro_data.json) automáticamente en el primer uso.


📦 Ejecutable (sin necesidad de instalar Python)

En la página de Releases se encuentra el ejecutable listo para usar en Windows — sin necesidad de instalar Python ni pip.

Plataforma Archivo Estado
Windows FlowTimer.exe ✅ Disponible
macOS FlowTimer 🔧 Compilar desde el código
Linux FlowTimer 🔧 Compilar desde el código

Compilar el ejecutable desde el código fuente

pip install pyinstaller

# Windows
pyinstaller --onedir --windowed --clean --noconfirm --name "FlowTimer" --icon="assets/icon.ico" --add-data "assets/icon.ico;assets" --collect-all pygame main.py

# macOS / Linux
pyinstaller --onedir --windowed --clean --noconfirm --name "FlowTimer" --icon="assets/icon.ico" --add-data "assets/icon.ico:assets" --collect-all pygame main.py

El resultado es una carpeta dist/FlowTimer/ que contiene FlowTimer.exe (Windows) o FlowTimer (macOS/Linux) junto con una carpeta _internal/ con todas las dependencias. Distribuí la carpeta dist/FlowTimer/ completa — el ejecutable no funciona sin _internal/.


🧪 Ejecutar los tests

El paquete core/ tiene cobertura completa con una suite de pytest (no requiere GUI).

# Instalar pytest si aún no lo tenés
pip install pytest

# Ejecutar todos los tests desde la raíz del proyecto
pytest tests/

Todos los tests corren sin pantalla ni dispositivo de audio — el código de UI y pygame están completamente mockeados.


🗂 Estructura del proyecto

pomodoro-timer/
│
├── main.py                  # Punto de entrada — ejecutar este archivo
│
├── core/                    # Lógica de negocio en Python puro (sin Tkinter)
│   ├── __init__.py
│   ├── constants.py         # Colores, rutas, valores de configuración por defecto
│   ├── i18n.py              # Internacionalización — diccionario STRINGS + helper t()
│   ├── audio.py             # GestorAudio — alarma, tictac y reproducción de música
│   ├── storage.py           # Persistencia JSON (cargar_datos / guardar_datos)
│   └── stats.py             # estadisticas_semana(), exportar_csv()
│
├── ui/                      # Vistas Tkinter (dependen de core, no entre sí)
│   ├── __init__.py
│   ├── app.py               # App(tk.Tk) — ventana raíz, estado del timer, coordinador
│   ├── tab_timer.py         # TabTimer — anillo del reloj, controles, resumen diario
│   ├── tab_stats.py         # TabStats — gráfico, tabla de historial, exportar CSV
│   └── tab_settings.py      # TabSettings — duraciones, opciones, alarma, reproductor
│
├── tests/                   # Suite de tests con pytest
│   ├── test_audio.py        # Estado de GestorAudio, posición, interacción alarma/música
│   ├── test_chart_language_bug.py  # Regresión: consistencia claves de días / idioma en gráfico
│   ├── test_i18n.py         # Cambio de idioma, paridad de claves, formateo
│   ├── test_stats.py        # Estadísticas semanales y exportación CSV
│   └── test_storage.py      # Persistencia JSON y compatibilidad hacia adelante
│
├── assets/                  # Íconos y capturas de pantalla
├── requirements.txt
├── .gitignore
├── LICENSE
└── README.md

La app también crea estas rutas en tiempo de ejecución (fuera de la carpeta del proyecto):

Ruta Propósito
~/.pomodoro_data.json Historial, racha, configuración, idioma y biblioteca de música
~/.pomodoro_sounds/ Archivos de alarma copiados
~/.pomodoro_music/ Archivos de música copiados y descargados (MP3, miniaturas)

📦 Dependencias

Paquete Propósito Necesario para
tkinter Framework de GUI Todo — viene con Python
pygame Reproducción de audio Alarmas, tictac, reproductor de música
yt-dlp Descarga de audio de YouTube Reproductor — importación desde YouTube
Pillow Carga de imágenes Reproductor — miniaturas de YouTube
ffmpeg Conversión de audio Reproductor — importación desde YouTube (herramienta del sistema, no pip)

Todos los paquetes pip están en requirements.txt y se instalan en un solo paso. tkinter viene con Python. ffmpeg debe instalarse por separado y estar disponible en el PATH del sistema.


🎵 Reproductor de música

El reproductor integrado vive en la pestaña Configuración y admite dos formas de agregar música:

Archivos locales

Hacé clic en ➕ Archivo para importar uno o más archivos de audio (MP3, WAV, OGG, FLAC, M4A, OPUS, AAC), o usá el botón 📁 para importar una carpeta completa de una vez (hasta 50 archivos). Los archivos se copian a ~/.pomodoro_music/ y quedan disponibles aunque los originales se muevan o eliminen.

YouTube

Hacé clic en ▶ YouTube, pegá hasta 3 URLs (una por campo) y hacé clic en Importar. La app descarga el audio como MP3 mediante yt-dlp y ffmpeg, obtiene la miniatura del video y agrega la pista a la biblioteca — todo en segundo plano mientras seguís usando el timer.

ffmpeg debe estar instalado y en el PATH para que la importación desde YouTube funcione.

Comportamiento durante las alarmas

Cuando termina un Pomodoro o descanso, la música se pausa inmediatamente antes de que suene la alarma. Al cerrar el diálogo, la música se reanuda automáticamente desde la posición exacta donde se detuvo — sin acción manual.


🌐 Internacionalización / i18n

La app incluye soporte completo para Español e Inglés. El idioma activo se puede cambiar en cualquier momento con el botón 🌐 EN / 🌐 ES en la esquina superior derecha. La elección persiste entre sesiones.

Cómo funciona

Todos los textos visibles al usuario viven en core/i18n.py como un diccionario anidado:

STRINGS = {
    "es": { "btn_start": "▶  Iniciar", ... },
    "en": { "btn_start": "▶  Start",   ... },
}

El helper t() traduce cualquier clave en el idioma activo con argumentos opcionales:

from core.i18n import t, set_language

set_language("es")
t("btn_start")           # "▶  Iniciar"
t("lbl_streak", n=5)     # "🔥 Racha: 5 días"

Agregar un nuevo idioma

  1. Abrí core/i18n.py.
  2. Agregá una nueva entrada en STRINGS usando un idioma existente como plantilla.
  3. Llamá a set_language("xx") donde "xx" es tu nuevo código.

Ningún otro archivo necesita cambiar.


🏗 Arquitectura y notas técnicas

Separación de responsabilidades

core/ no tiene dependencia alguna de Tkinter — todos los módulos son testeables de forma independiente. La capa de UI (ui/) importa desde core/, pero las pestañas nunca se importan entre sí; se comunican exclusivamente a través del coordinador App:

App.data          — diccionario compartido de sesiones, config y música
App.cfg           — atajo a App.data["config"]
App.audio         — instancia de GestorAudio
App.cambiar_modo()
App.toggle_start() / reset() / saltar()
App.on_config_saved()
App.on_historial_borrado()

Precisión del temporizador

La cuenta regresiva usa time.monotonic() como reloj de referencia. La UI se refresca cada 100 ms, pero el tiempo restante se recalcula desde el reloj de pared en cada tick — el timer nunca se desvía sin importar la carga de CPU.

Subsistema de audio

GestorAudio gestiona tres funciones de audio independientes:

  • Alarma — reproduce un archivo de sonido personalizado via pygame.mixer, detenida automáticamente a los 20 segundos; cae a un beep del sistema si pygame no está disponible.
  • Tictac — un clic suave generado en Python puro (sin archivo de audio) repetido en un hilo daemon, un clic por segundo.
  • Música — transmite via pygame.mixer.music con control de volumen, reproducción en bucle y seguimiento de posición mediante un acumulador de offset (_music_pos_offset_ms) para que el progreso mostrado y la posición de reanudación sean exactos a través de ciclos de pausa/alarma.

Al sonar la alarma, se guarda la posición actual, la música se pausa y la alarma suena. Al cerrar el diálogo, music_restaurar() recarga el archivo y reanuda desde la posición guardada en una llamada síncrona — evitando condiciones de carrera entre el hilo de la alarma y el hilo de la UI.

Persistencia

Los datos se guardan en un único archivo JSON compatible hacia adelante. La biblioteca de música (music_tracks) se almacena en el mismo archivo:

{
  "name": "LoFi Beats",
  "path": "/home/user/.pomodoro_music/abc123.mp3",
  "source": "youtube",
  "youtube_id": "abc123",
  "channel": "Example Channel",
  "duration_s": 3600,
  "thumb_path": "/home/user/.pomodoro_music/abc123.png"
}

Lógica de racha

El contador se incrementa solo si la app se usó ayer. Si pasó más de un día, la racha se reinicia a 1 — igual que Duolingo.


🎛 Referencia de configuración

Ajuste Por defecto Rango
Duración del Pomodoro 25 min 1–120 min
Descanso corto 5 min 1–30 min
Descanso largo 15 min 1–60 min
Pomodoros por ciclo 4 1–10
Sonido al terminar
Iniciar descanso automáticamente
Iniciar siguiente Pomodoro automáticamente
Siempre visible
Tictac durante el trabajo
Tictac durante los descansos
Sonido de alarma personalizado Ninguno MP3 / WAV / OGG / FLAC / M4A / OPUS / AAC
Volumen de música 70% 0–100%

Los cambios de duración aplican desde la próxima sesión. Los demás cambios aplican de inmediato al guardar.


📄 Licencia

Distribuido bajo la Licencia MIT. Ver LICENSE para más detalles.

About

Full-featured Pomodoro timer with stats, streak tracking and built-in music player.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages