Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python Alert!

Alert running two seconds earlier

Hello,

I been trying to figure out what is wrong but I couldn't find a proper solution. The alert is displayed two seconds before it reaches the 0

import tkinter
from tkinter import messagebox
DEFAULT_GAP=6
class Pomodoro:
    def __init__(self, master):
        self.master = master
        self.mainframe = tkinter.Frame(self.master, bg = "White")
        self.mainframe.pack(fill=tkinter.BOTH, expand=True)

        self.timer_text = tkinter.StringVar()
        self.timer_text.trace('w', self.build_timer)
        self.time_left = tkinter.IntVar()
        self.time_left.set(DEFAULT_GAP)
        self.running = False


        self.build_gruid()
        self.build_banner()
        self.build_buttons()
        self.build_timer()
        self.update()
        self.time_left.trace('w',self.alert)

    def build_gruid(self):
        self.mainframe.columnconfigure(0, weight=1)
        self.mainframe.rowconfigure(0, weight=0)
        self.mainframe.rowconfigure(1, weight=1)
        self.mainframe.rowconfigure(2, weight=0)


    def build_banner(self):
        banner = tkinter.Label(
            self.mainframe,
            bg = "tomato",
            text= "Pomodoro",
            fg="white",
            font = ("Helvetica", 24))
        banner.grid(
            row=0, column=0,
            sticky='ew',
            padx=10, pady=10,
            )

    def build_buttons(self):
        buttons_frame = tkinter.Frame(self.mainframe)
        buttons_frame.grid(row=2,column=0,sticky='nsew',padx=10,pady=10)
        buttons_frame.columnconfigure(0,weight=1)
        buttons_frame.columnconfigure(1,weight=1)

        self.start_button = tkinter.Button(
            buttons_frame,
            text = 'Start',
            command=self.start_timer
            )
        self.stop_button = tkinter.Button(
            buttons_frame,
            text = 'Stop',
            command=self.stop_timer
            )

        self.start_button.grid(row=0,column=1,sticky="ew")
        self.stop_button.grid(row=0,column=0,sticky="ew")
        self.stop_button.config(state=tkinter.DISABLED)

    def build_timer(self, *args):
        timer =tkinter.Label(
            self.mainframe,
            text=self.timer_text.get(),
            font=("Helvetica",36)
            )
        timer.grid(row=1,column=0,sticky="nsew")

    def start_timer(self):
        self.time_left.set(DEFAULT_GAP)
        self.running = True
        self.stop_button.config(state=tkinter.NORMAL)
        self.start_button.config(state=tkinter.DISABLED)

    def stop_timer(self):
        self.running = False
        self.start_button.config(state=tkinter.NORMAL)
        self.stop_button.config(state=tkinter.DISABLED)

    def minutes_seconds(self, seconds):
        return int(seconds/60), int(seconds%60)

    def update(self):
        time_left = self.time_left.get()

        if self.running and time_left:
            minutes, seconds = self.minutes_seconds(time_left)
            self.timer_text.set(
                '{:0>2}:{:0>2}'.format(minutes, seconds)
                )
            self.time_left.set(time_left-1)
        else:
            minutes, seconds = self.minutes_seconds(DEFAULT_GAP)
            self.timer_text.set(
                '{:0>2}:{:0>2}'.format(minutes, seconds)
                )
            self.stop_timer()

         self.master.after(1000, self.update)

     def alert(self, *args):
         if not self.time_left.get():
             messagebox.showinfo('Timer done!','Time ran out')
 if __name__ == '__main__':
     root = tkinter.Tk()
     Pomodoro(root)
     root.mainloop()

4 Answers

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 68,423 Points

I am seeing two delay factors. The first one is cosmetic. Since the update loop runs at most once per second, there can be a delay between 0 and 1 second between the Start button and the countdown starting.

The second delay is real. After the time is displayed, self.time_left.set() with the decremented value. As "1" is displayed, the time_left is decremented and set 0. This causes alert() to be called. Since time_left is already 0, "Timer done!" is displayed before the last second had a chance to pass. Here is a print statements I added:

get time left: 10 at 2015-12-29 20:06:46.454930
set text to 0:10
stopped....
2015-12-29 20:06:46.456526 wait a sec....
called alert()
started....

get time left: 10 at 2015-12-29 20:06:47.458007  # Actual Start Time
set text to 0:10
set time_left to 9
called alert()
2015-12-29 20:06:47.459633 wait a sec....

get time left: 9 at 2015-12-29 20:06:48.461199   # 1 sec
set text to 0:9
set time_left to 8
called alert()
2015-12-29 20:06:48.463181 wait a sec....

get time left: 8 at 2015-12-29 20:06:49.464566   # 2 sec
set text to 0:8
set time_left to 7
called alert()
2015-12-29 20:06:49.466762 wait a sec....

get time left: 7 at 2015-12-29 20:06:50.468267   # 3 sec
set text to 0:7
set time_left to 6
called alert()
2015-12-29 20:06:50.470962 wait a sec....

get time left: 6 at 2015-12-29 20:06:51.472751   # 4 sec
set text to 0:6
set time_left to 5
called alert()
2015-12-29 20:06:51.474804 wait a sec....

get time left: 5 at 2015-12-29 20:06:52.476558   #  5 sec
set text to 0:5
set time_left to 4
called alert()
2015-12-29 20:06:52.478566 wait a sec....

get time left: 4 at 2015-12-29 20:06:53.480272   #  6 secc
set text to 0:4
set time_left to 3
called alert()
2015-12-29 20:06:53.481910 wait a sec....

get time left: 3 at 2015-12-29 20:06:54.482756   # 7 sec
set text to 0:3
set time_left to 2
called alert()
2015-12-29 20:06:54.485123 wait a sec....

get time left: 2 at 2015-12-29 20:06:55.488098   # 8 sec
set text to 0:2
set time_left to 1
called alert()
2015-12-29 20:06:55.490145 wait a sec....

get time left: 1 at 2015-12-29 20:06:56.491080   # 9 sec
set text to 0:1
set time_left to 0
called alert()
POP UP!
2015-12-29 20:07:01.915116 wait a sec....

get time left: 0 at 2015-12-29 20:07:02.917143   # time after waiting at Pop-Up.
set text to 0:10
stopped....
2015-12-29 20:07:02.918746 wait a sec....

get time left: 0 at 2015-12-29 20:07:03.919775
set text to 0:10
stopped....
2015-12-29 20:07:03.922946 wait a sec....

# Program killed

Notice the timer text is never set to "0:0".

You may want to rearchitect the flow of the update() method. One quick changed I tried is below:

    def update(self):
        time_left = self.time_left.get()
        print("\nget time left:", time_left, "at", datetime.datetime.now())

        # if self.running and time_left:
        if self.running and self.timer_text.get() != "00:00":  # <-- Changed timer running condtion

# ...

    def alert(self, *args):
        print("called alert()")
        # if not self.time_left.get():
        if self.timer_text.get() == "00:00":  # <-- Changed pop-up condition
            print("POP UP!")
            messagebox.showinfo('Timer done!', 'Time ran out')

Dude you are like the Yoda of python! Many thanks!

Josh Keenan
Josh Keenan
19,652 Points

Can you attach your code?

Chris Freeman
Chris Freeman
Treehouse Moderator 68,423 Points

Hi Josh, Might I suggest to ask for clarifications and code attachments as a comment under the original post. When using an Answer it hides posts that still need attention.

yeah sure, its basically the same that kenneth did on the course of Tkinter, ALL THe funtions are inside the class for some reason the display is all messed up, the if boolean its outside the class

import tkinter
from tkinter import messagebox
DEFAULT_GAP=6
class Pomodoro:
    def __init__(self, master):
        self.master = master
        self.mainframe = tkinter.Frame(self.master, bg = "White")
        self.mainframe.pack(fill=tkinter.BOTH, expand=True)

        self.timer_text = tkinter.StringVar()
        self.timer_text.trace('w', self.build_timer)
        self.time_left = tkinter.IntVar()
        self.time_left.set(DEFAULT_GAP)
        self.running = False


        self.build_gruid()
        self.build_banner()
        self.build_buttons()
        self.build_timer()
        self.update()
        self.time_left.trace('w',self.alert)

    def build_gruid(self):
        self.mainframe.columnconfigure(0, weight=1)
        self.mainframe.rowconfigure(0, weight=0)
        self.mainframe.rowconfigure(1, weight=1)
        self.mainframe.rowconfigure(2, weight=0)


    def build_banner(self):
        banner = tkinter.Label(
            self.mainframe,
            bg = "tomato",
            text= "Pomodoro",
            fg="white",
            font = ("Helvetica", 24))
        banner.grid(
            row=0, column=0,
            sticky='ew',
            padx=10, pady=10,
            )

    def build_buttons(self):
        buttons_frame = tkinter.Frame(self.mainframe)
        buttons_frame.grid(row=2,column=0,sticky='nsew',padx=10,pady=10)
        buttons_frame.columnconfigure(0,weight=1)
        buttons_frame.columnconfigure(1,weight=1)

        self.start_button = tkinter.Button(
            buttons_frame,
            text = 'Start',
            command=self.start_timer
            )
        self.stop_button = tkinter.Button(
            buttons_frame,
            text = 'Stop',
            command=self.stop_timer
            )

        self.start_button.grid(row=0,column=1,sticky="ew")
        self.stop_button.grid(row=0,column=0,sticky="ew")
        self.stop_button.config(state=tkinter.DISABLED)

    def build_timer(self, *args):
        timer =tkinter.Label(
            self.mainframe,
            text=self.timer_text.get(),
            font=("Helvetica",36)
            )
        timer.grid(row=1,column=0,sticky="nsew")

    def start_timer(self):
        self.time_left.set(DEFAULT_GAP)
        self.running = True
        self.stop_button.config(state=tkinter.NORMAL)
        self.start_button.config(state=tkinter.DISABLED)

    def stop_timer(self):
        self.running = False
        self.start_button.config(state=tkinter.NORMAL)
        self.stop_button.config(state=tkinter.DISABLED)

    def minutes_seconds(self, seconds):
        return int(seconds/60), int(seconds%60)

    def update(self):
        time_left = self.time_left.get()

        if self.running and time_left:
            minutes, seconds = self.minutes_seconds(time_left)
            self.timer_text.set(
                '{:0>2}:{:0>2}'.format(minutes, seconds)
                )
            self.time_left.set(time_left-1)
        else:
            minutes, seconds = self.minutes_seconds(DEFAULT_GAP)
            self.timer_text.set(
                '{:0>2}:{:0>2}'.format(minutes, seconds)
                )
            self.stop_timer()

         self.master.after(1000, self.update)

     def alert(self, *args):
         if not self.time_left.get():
             messagebox.showinfo('Timer done!','Time ran out')
 if __name__ == '__main__':
     root = tkinter.Tk()
     Pomodoro(root)
     root.mainloop()
Chris Freeman
Chris Freeman
Treehouse Moderator 68,423 Points

Corrected markdown, but indentation on original code is off.

Thats why I specified that due to the mark down the indentation was not correct, but it was in the original code. It doesn't matter now as I just corrected that answer. Still, the problem remains any ideas guys?