A tiny desktop app that lets you set a focus time and, when time ends, rewards you with a programming joke. Then you can fetch more jokes or change the joke category. It’s a simple focus → reward loop.
- Focus timer using Tkinter’s
after()(non-blocking UI) - Programming jokes via
pyjokes - Categories:
neutral,chuck,all - Simple UI: Start, Next Joke, Reset
- Clean state management: Focus Mode → Break Mode
- You focus for X minutes
- App waits quietly in the background (UI stays responsive)
- When time ends → Break Mode
- A programming joke appears (reward)
- You can get another joke, change category, or reset and start again
Not a full Pomodoro, not a tracker — just a small timed-break utility.
- Python 3.8+ (tested with 3.14 in venv)
# 1) Create and activate a virtual environment (optional but recommended)
python -m venv .venv
.venv\Scripts\activate
# 2) Upgrade pip and install dependencies
python -m pip install --upgrade pip
pip install pyjokes
# 3) Run the app
python main.pyIf you already have a .venv created, just activate and run main.py.
- Enter focus time in minutes (decimals allowed, e.g.,
0.1= 6 seconds) - Click Start (or press Enter while in the minutes box)
- When time ends, you enter Break Mode and a joke is displayed
- Use Next Joke to fetch another joke
- Change category to switch joke types
- Click Reset to go back to Ready state
-
Focus Mode (timer running)
- Inputs locked; Start disabled
- Category and Next Joke disabled
- No jokes shown
-
Break Mode (timer finished)
- Joke displayed
- Next Joke and Category enabled
- Reset available
Everything revolves around switching between these two states.
-
jokes.py— Joke engine- Wraps
pyjokes.get_joke(category=...) - Safe fallback message if a category fails
- Wraps
-
timer.py— Timer enginestart_timer(root, minutes, callback)- Uses
root.after(ms, callback)(milliseconds) to schedule a callback without freezing the UI - Returns a timer ID if you want to cancel (via
root.after_cancel(id))
-
ui.py— UI controller- Builds the window using Tkinter + ttk
- Wires buttons and key bindings (Enter starts timer)
- Manages states (locks/unlocks controls, shows/hides/updates labels)
-
main.py— App entry point- Creates the root window, instantiates
FocusTimerApp, and starts the Tk event loop
- Creates the root window, instantiates
Tkinter’s after() schedules a function to run later without blocking the main thread. That means the app stays responsive during the wait. When the timer completes, Tkinter calls your callback — in this app, that callback switches to Break Mode and displays a joke.
time_joke_app/
├─ jokes.py # Joke engine wrapper around pyjokes
├─ timer.py # Timer helper using Tkinter after()
├─ ui.py # UI controller: layout, state, event handlers
├─ main.py # App entry point
└─ README.md # This file
-
“Enter a positive number” shows under Start
- You entered 0, negative, or a non-number. Use a positive number (decimals OK).
-
UI freezes while waiting
- Make sure you didn’t use
time.sleep(...). The app usesafter()specifically to stay responsive.
- Make sure you didn’t use
-
Next Joke button disabled
- That’s expected during Focus Mode. It becomes enabled in Break Mode (after the timer finishes) or after Reset.
-
Changing category typed text doesn’t work
- The category dropdown is
readonly(by design). Select a value from the list instead of typing.
- The category dropdown is
- Minimal logic by design, focusing on event-driven UI and state transitions
- Decimals supported for quick testing (e.g.,
0.05≈ 3 seconds) start_timerreturns the timer ID for potential future cancel support
- Cancel current timer (Stop button) using the stored timer ID
- Basic theming / layout improvements
- Persist last used category and last input time
- Optional sound notification when time ends
Small project intended for learning. PRs that keep the scope small and the code simple are welcome.
- pyjokes for the programming jokes