Day 28 - Pomodoro Timer
Day 28
In day 28, we're making "Pomodoro timer."
The Pomodoro Technique is a time management method based on 25-minute stretches of focused work broken by five-minute breaks
First step
setting up basic UI
# ---------------------------- UI SETUP ------------------------------- #
window = Tk()
window.title("Pomodoro")
window.config(padx=100, pady=50, bg=YELLOW)
timer_label = Label(text="Timer", bg=YELLOW, fg=GREENER, font=(FONT_NAME, 42, "bold"))
timer_label.grid(column=1, row=0)
canvas = Canvas(width=200, height=224, bg=YELLOW, highlightthickness=0)
tomato_img = PhotoImage(file="tomato.png")
canvas.create_image(100, 112, image=tomato_img)
canvas.create_text(100, 130, text="00:00", fill="white", font=(FONT_NAME, 35, "bold"))
canvas.grid(column=1, row=1)
start_button = Button(text="start", bg=YELLOW, highlightthickness=0)
start_button.grid(column=0, row=2)
reset_button = Button(text="reset", bg=YELLOW, highlightthickness=0)
reset_button.grid(column=2, row=2)
check_label = Label(text=CHECK_MARK, bg=YELLOW, fg=GREENER, font=(FONT_NAME, 28, "bold"))
check_label.grid(column=1, row=3)
window.mainloop()
That green font color was actually much lighter but Imma go with little more saturated minty green color. ◡̈
# ---------------------------- COUNTDOWN MECHANISM ------------------------------- #
count = 5
while True:
count -= 1
Because we're using >> window.mainloop() << if we using while function, mainloop function won't work.
So we have to avoid using codes like above.
Here's what we're going to use instead.
window.after()
window.after(millisecond:int, function:function_name, *args_parameter)
We need a count-down loop.
Next step:
setting up the timer
def count_down(count):
canvas.itemconfig(timer_text, text=count)
if count > 0:
window.after(1000, count_down, count-1)
timer_text = canvas.create_text(100, 130, text="00:00", fill="white", font=(FONT_NAME, 35, "bold"))
count_down(5)
For now, we have set up to count down from 5 to 0.
In order to change the text of specific canvas, we had to set that specific canvas equal to a variable called "timer_text."
Then we have to use canvas.itemconfig().
changing a label = label.confi()
chaingin a canvas = canvas.itemconfig()
Next step:
start the timer when the start button gets clicked.
def start_timer():
count_down(5)
start_button = Button(text="start", bg=YELLOW, highlightthickness=0, command=start_timer)
Next step:
Setting the time in 00:00 format
Let's say that we started the timer from 300 seconds which is equivalent to 5 minutes.
300
when we reach 245, how do we display 245 seconds left in 00:00 format?
If we devided 245 by 60 and round down the number to an integer we can get minutes in 00:00 format.
245 / 60 = 4.083333333
So that's 4 minutes.
For secondes left in 00:00 format, we can use modulo remainder.
245 % 60 = 5
So, that's 5 seconds left.
Now, let write this out in a function.
import math
count_min = math.floor(count/60)
math.floor() = round down to an integer
count_sec = count % 60
So, now we can set the timer to 5 minutes and see if it works
def start_timer():
count_down(300)
def count_down(count):
count_min = math.floor(count/60)
count_sec = count % 60
canvas.itemconfig(timer_text, text=f"{count_min}:{count_sec}")
if count > 0:
window.after(1000, count_down, count-1)
It works!
But I see it starts as 5:0 not 5:00 like we want.
It happens when count_sec < 10
How do we fix it?
In order to do that, we have to learn about python dynamic typing.
Dynamic typing
a = 3 (data type:int)
a = "Hello" (data type: str)
a = 3 (data type:int)
You can actually change the data type in python.
You can actually change the data type in python.
Here's what I came up with to fix this problem.
def count_down(count):
count_min = math.floor(count/60)
count_sec = count % 60
if 0 <= count_sec < 10:
count_sec = "0"+str(count_sec)
canvas.itemconfig(timer_text, text=f"{count_min}:{count_sec}")
if count > 0:
window.after(1000, count_down, count-1)
In this course, Angela used:
if count_sec < 10:
count_sec = f"0{count_sec}"
Well, either way works fine.
We can do this because of python's dynamic typing.
This is not a feature that's available in all programming laguages.
These kinds of code won't work in Java, C, Swift, and many other programming languages.
In other langauges listed above, once you set the data type to certain variable, you can't change the data type.
Next step:
In Pomodoro,
🍅25min Work
5min Break🍅 (short break)
🍅25min Work
5min Break🍅 (short break)
🍅25min Work
5min Break🍅 (short break)
🍅25min Work
20min Break🍅 (long break)
How do we set the timer to work like that?
WORK_MIN = 25
SHORT_BREAK_MIN = 5
LONG_BREAK_MIN = 20
reps = 0
def start_timer():
global reps
work_sec = WORK_MIN * 60
short_break_sec = SHORT_BREAK_MIN * 60
long_break_sec = LONG_BREAK_MIN * 60
#If it's the 1st/3rd/5th/7th rep:
count_down(work_sec)
#If it's the 8th rep:
count_down(long_break_sec)
#If it's the 2nd/4th/6th rep:
count_down(short_break_sec)
Here's what I came up with:
def start_timer():
global reps
reps += 1
work_sec = WORK_MIN * 60
short_break_sec = SHORT_BREAK_MIN * 60
long_break_sec = LONG_BREAK_MIN * 60
if reps % 2 == 1:
count_down(work_sec)
elif reps == 8:
count_down(long_break_sec)
reps = 0
elif reps % 2 == 0:
count_down(short_break_sec)
Angela's:
if reps % 8 == 0:
count_down(long_break_sec)
elif reps % 2 == 0:
count_down(short_break_sec)
else:
count_down(work_sec)
Imma go with Angela's code it works without resetting reps which is a global variable.
Next step:
Adding check marks after finshing each work session.
def count_down(count):
count_min = math.floor(count/60)
count_sec = count % 60
if count_sec < 10:
count_sec = "0"+str(count_sec)
canvas.itemconfig(timer_text, text=f"{count_min}:{count_sec}")
if count > 0:
window.after(1000, count_down, count-1)
else:
start_timer()
marks = ""
work_session = math.floor(reps/2)
for _ in range(work_session):
marks += CHECK_MARK
check_label.config(text=marks)
I couldn't figure it out myself.
Next step:
Making the reset button work.
def reset_timer():
window.after_cancel()
In order to reset the timer, we gotta cancel this:
if count > 0:
window.after(1000, count_down, count-1)
To do that, we have to put it inside a variable
def count_down(count):
count_min = math.floor(count/60)
count_sec = count % 60
if count_sec < 10:
count_sec = "0"+str(count_sec)
canvas.itemconfig(timer_text, text=f"{count_min}:{count_sec}")
if count > 0:
timer = window.after(1000, count_down, count-1)
else:
start_timer()
marks = ""
work_session = math.floor(reps/2)
for _ in range(work_session):
marks += CHECK_MARK
check_label.config(text=marks)
But because this is going to be a local variable because it's created inside this particular function.
I actually have to bring it out. I have to make it a global variable.
# ---------------------------- CONSTANTS ------------------------------- #
PINK = "#e2979c"
RED = "#e7305b"
GREEN = "#9bdeac"
GREENER = "#75d18c"
YELLOW = "#f7f5dd"
FONT_NAME = "Courier"
CHECK_MARK = "✔"
WORK_MIN = 25
SHORT_BREAK_MIN = 5
LONG_BREAK_MIN = 20
reps = 0
timer = None
def count_down(count):
count_min = math.floor(count/60)
count_sec = count % 60
if count_sec < 10:
count_sec = "0"+str(count_sec)
canvas.itemconfig(timer_text, text=f"{count_min}:{count_sec}")
if count > 0:
global timer
timer = window.after(1000, count_down, count-1)
else:
start_timer()
marks = ""
work_session = math.floor(reps/2)
for _ in range(work_session):
marks += CHECK_MARK
check_label.config(text=marks)
Now, here's what's left to do with reset button:
- timer text = 00:00
- timer label = "Timer"
- reset check marks
def reset_timer():
window.after_cancel(timer)
canvas.itemconfig(timer_text, text="00:00")
timer_label.config(text="Timer")
check_label.config(text="")
global reps
reps = 0