Skip to content

Commit 464cf09

Browse files
authoredMar 17, 2025··
Merge pull request #2564 from IAMSMR007/master
"Added Pomodoro Timer made with Tkinter"
2 parents 0224096 + 3fe9ea9 commit 464cf09

File tree

1 file changed

+208
-0
lines changed

1 file changed

+208
-0
lines changed
 

‎Pomodoro (tkinter).py

+208
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
from tkinter import *
2+
3+
# ---------------------------- CONSTANTS & GLOBALS ------------------------------- #
4+
PINK = "#e2979c"
5+
GREEN = "#9bdeac"
6+
FONT_NAME = "Courier"
7+
DEFAULT_WORK_MIN = 25
8+
DEFAULT_BREAK_MIN = 5
9+
10+
# Background color options
11+
bg_colors = {
12+
"Pink": "#e2979c",
13+
"Green": "#9bdeac",
14+
"Blue": "#1f75fe",
15+
"Yellow": "#ffcc00",
16+
"Purple": "#b19cd9"
17+
}
18+
19+
# Global variables
20+
ROUND = 1
21+
timer_mec = None
22+
total_time = 0 # Total seconds for the current session
23+
is_paused = False # Timer pause flag
24+
remaining_time = 0 # Remaining time (in seconds) when paused
25+
custom_work_min = DEFAULT_WORK_MIN
26+
custom_break_min = DEFAULT_BREAK_MIN
27+
28+
# ---------------------------- BACKGROUND COLOR CHANGE FUNCTION ------------------------------- #
29+
def change_background(*args):
30+
selected = bg_color_var.get()
31+
new_color = bg_colors.get(selected, PINK)
32+
window.config(bg=new_color)
33+
canvas.config(bg=new_color)
34+
label.config(bg=new_color)
35+
tick_label.config(bg=new_color)
36+
work_label.config(bg=new_color)
37+
break_label.config(bg=new_color)
38+
39+
# ---------------------------- NOTIFICATION FUNCTION ------------------------------- #
40+
def show_notification(message):
41+
notif = Toplevel(window)
42+
notif.overrideredirect(True)
43+
notif.config(bg=PINK)
44+
45+
msg_label = Label(notif, text=message, font=(FONT_NAME, 12, "bold"),
46+
bg=GREEN, fg="white", padx=10, pady=5)
47+
msg_label.pack()
48+
49+
window.update_idletasks()
50+
wx = window.winfo_rootx()
51+
wy = window.winfo_rooty()
52+
wwidth = window.winfo_width()
53+
wheight = window.winfo_height()
54+
55+
notif.update_idletasks()
56+
nwidth = notif.winfo_width()
57+
nheight = notif.winfo_height()
58+
59+
x = wx + (wwidth - nwidth) // 2
60+
y = wy + wheight - nheight - 10
61+
notif.geometry(f"+{x}+{y}")
62+
63+
notif.after(3000, notif.destroy)
64+
65+
# ---------------------------- TIMER FUNCTIONS ------------------------------- #
66+
def reset_timer():
67+
global ROUND, timer_mec, total_time, is_paused, remaining_time
68+
ROUND = 1
69+
is_paused = False
70+
remaining_time = 0
71+
if timer_mec is not None:
72+
window.after_cancel(timer_mec)
73+
canvas.itemconfig(timer_text, text="00:00")
74+
label.config(text="Timer")
75+
tick_label.config(text="")
76+
total_time = 0
77+
canvas.itemconfig(progress_arc, extent=0)
78+
start_button.config(state=NORMAL)
79+
pause_button.config(state=DISABLED)
80+
play_button.config(state=DISABLED)
81+
82+
def start_timer():
83+
global ROUND, total_time, is_paused
84+
canvas.itemconfig(progress_arc, extent=0)
85+
86+
if ROUND % 2 == 1: # Work session
87+
total_time = custom_work_min * 60
88+
label.config(text="Work", fg=GREEN)
89+
else: # Break session
90+
total_time = custom_break_min * 60
91+
label.config(text="Break", fg=PINK)
92+
93+
count_down(total_time)
94+
start_button.config(state=DISABLED)
95+
pause_button.config(state=NORMAL)
96+
play_button.config(state=DISABLED)
97+
is_paused = False
98+
99+
def count_down(count):
100+
global timer_mec, remaining_time
101+
remaining_time = count
102+
minutes = count // 60
103+
seconds = count % 60
104+
if seconds < 10:
105+
seconds = f"0{seconds}"
106+
canvas.itemconfig(timer_text, text=f"{minutes}:{seconds}")
107+
108+
if total_time > 0:
109+
progress = (total_time - count) / total_time
110+
canvas.itemconfig(progress_arc, extent=progress * 360)
111+
112+
if count > 0 and not is_paused:
113+
timer_mec = window.after(1000, count_down, count - 1)
114+
elif count == 0:
115+
if ROUND % 2 == 1:
116+
show_notification("Work session complete! Time for a break.")
117+
else:
118+
show_notification("Break over! Back to work.")
119+
if ROUND % 2 == 0:
120+
tick_label.config(text=tick_label.cget("text") + "#")
121+
ROUND += 1
122+
start_timer()
123+
124+
def pause_timer():
125+
global is_paused, timer_mec
126+
if not is_paused:
127+
is_paused = True
128+
if timer_mec is not None:
129+
window.after_cancel(timer_mec)
130+
pause_button.config(state=DISABLED)
131+
play_button.config(state=NORMAL)
132+
133+
def resume_timer():
134+
global is_paused
135+
if is_paused:
136+
is_paused = False
137+
count_down(remaining_time)
138+
play_button.config(state=DISABLED)
139+
pause_button.config(state=NORMAL)
140+
141+
def set_custom_durations():
142+
global custom_work_min, custom_break_min
143+
try:
144+
work_val = int(entry_work.get())
145+
break_val = int(entry_break.get())
146+
custom_work_min = work_val
147+
custom_break_min = break_val
148+
canvas.itemconfig(left_custom, text=f"{custom_work_min}m")
149+
canvas.itemconfig(right_custom, text=f"{custom_break_min}m")
150+
except ValueError:
151+
pass
152+
153+
# ---------------------------- UI SETUP ------------------------------- #
154+
window = Tk()
155+
window.title("Pomodoro")
156+
window.config(padx=100, pady=50, bg=PINK)
157+
158+
# Canvas setup with increased width for spacing
159+
canvas = Canvas(window, width=240, height=224, bg=PINK, highlightthickness=0)
160+
timer_text = canvas.create_text(120, 112, text="00:00", font=(FONT_NAME, 35, "bold"), fill="white")
161+
background_circle = canvas.create_arc(40, 32, 200, 192, start=0, extent=359.9,
162+
style="arc", outline="white", width=5)
163+
progress_arc = canvas.create_arc(40, 32, 200, 192, start=270, extent=0,
164+
style="arc", outline="green", width=5)
165+
# Updated positions for work and break time labels
166+
left_custom = canvas.create_text(20, 112, text=f"{custom_work_min}m", font=(FONT_NAME, 12, "bold"), fill="white")
167+
right_custom = canvas.create_text(220, 112, text=f"{custom_break_min}m", font=(FONT_NAME, 12, "bold"), fill="white")
168+
169+
canvas.grid(column=1, row=1)
170+
171+
label = Label(text="Timer", font=(FONT_NAME, 35, "bold"), bg=PINK, fg="green")
172+
label.grid(column=1, row=0)
173+
174+
start_button = Button(text="Start", command=start_timer, highlightthickness=0)
175+
start_button.grid(column=0, row=2)
176+
177+
reset_button = Button(text="Reset", command=reset_timer, highlightthickness=0)
178+
reset_button.grid(column=2, row=2)
179+
180+
pause_button = Button(text="Pause", command=pause_timer, highlightthickness=0, state=DISABLED)
181+
pause_button.grid(column=0, row=3)
182+
183+
play_button = Button(text="Play", command=resume_timer, highlightthickness=0, state=DISABLED)
184+
play_button.grid(column=2, row=3)
185+
186+
tick_label = Label(text="", font=(FONT_NAME, 15, "bold"), bg=PINK, fg="green")
187+
tick_label.grid(column=1, row=4)
188+
189+
# Custom durations (stacked vertically)
190+
work_label = Label(text="Work (min):", font=(FONT_NAME, 12, "bold"), bg=PINK, fg="white")
191+
work_label.grid(column=1, row=5, pady=(20, 0))
192+
entry_work = Entry(width=5, font=(FONT_NAME, 12))
193+
entry_work.grid(column=1, row=6, pady=(5, 10))
194+
break_label = Label(text="Break (min):", font=(FONT_NAME, 12, "bold"), bg=PINK, fg="white")
195+
break_label.grid(column=1, row=7, pady=(5, 0))
196+
entry_break = Entry(width=5, font=(FONT_NAME, 12))
197+
entry_break.grid(column=1, row=8, pady=(5, 10))
198+
set_button = Button(text="Set Durations", command=set_custom_durations, font=(FONT_NAME, 12))
199+
set_button.grid(column=1, row=9, pady=(10, 20))
200+
201+
# OptionMenu for changing background color
202+
bg_color_var = StringVar(window)
203+
bg_color_var.set("Pink")
204+
bg_option = OptionMenu(window, bg_color_var, *bg_colors.keys(), command=change_background)
205+
bg_option.config(font=(FONT_NAME, 12))
206+
bg_option.grid(column=1, row=10, pady=(10, 20))
207+
208+
window.mainloop()

0 commit comments

Comments
 (0)
Please sign in to comment.