Trouble implementing countdown timer...

Hello–

I’m going crazy trying to figure out how to get my countdown timer working. I’ve attached my source if anyone is willing to take a look at it to see if they can figure out what’s being messed up. Here’s the scoop on what my app should do. It’s almost in working condition:

  • The user enters a task and a duration for that task (say 30 minutes), and both the task and the duration are stored in separate lists.

  • Once the user is finished entering tasks with accompanied durations, they press the start button.

  • At that point, I want the timer to look at the first stored task and accompanied duration, and begin counting down.

  • Once it hits 0, I want the timer to move on to the next task and alert the user. Right now, all the program does is go straight to the last task in the list.

  • I would like it to continue looping through the tasks until the user decides to press stop.

  • I don’t know how to get the timer to move from task A to task B, to task C, etc. Any help?

  • The main issue I’m having is found in my StartButton function. If you look at my source, you’ll see a commented-out while loop section that I was playing with to get it working.

  • I’m assuming it has something to do with the way my StartButton and Update functions talk to each other, but I’m unsure what to do.

If you’d rather not download my source code and look through it, here are some basic guts of my program…but it may make more sense to see the whole picture.

Thank you in advance, anyone who can help!

Time Manager.py (16.3 KB)

···

#Start/Stop Button
self.start_button = wx.Button( self.leftpanel, wx.ID_ANY, u"Start",
wx.DefaultPosition, wx.Size( 60,60 ), 0 )
self.start_button.SetFont( wx.Font( 16, 70, 90, 90, False, wx.EmptyString ) )
self.Bind(wx.EVT_BUTTON, self.StartButton, self.start_button)
#-------------------------------------------------------------------------
#Timer Text
self.current_task = wx.StaticText( self.leftpanel, wx.ID_ANY, u"Current Task:", wx.DefaultPosition, wx.DefaultSize, 0 )

    self.timer_text = wx.StaticText( self.leftpanel, wx.ID_ANY,
                                     u"00:00:00", wx.DefaultPosition,
                                     wx.DefaultSize, wx.ALIGN_CENTRE|wx.ST_NO_AUTORESIZE )
    #Create Timer
    self.timer = wx.Timer(self)
    self.Bind(wx.EVT_TIMER, self.Update, self.timer)
  #-------------------------------------------------------------------------
    #Task List
    task_listChoices = []
    self.task_list = wx.CheckListBox( self.rightpanel, wx.ID_ANY, wx.DefaultPosition,
                                      wx.DefaultSize, task_listChoices, wx.LB_MULTIPLE )
    #'Delete Task' Button
    self.del_task = wx.Button( self.rightpanel, wx.ID_ANY, u"Delete Selected Tasks", wx.DefaultPosition, wx.DefaultSize, 0 )
    self.Bind(wx.EVT_BUTTON, self.DeleteButton, self.del_task)

def StartButton(self,event):
btn_label = self.start_button.GetLabel()
#check to see if task has been entered
if self.task_list.GetCount() == 0:
print “enter a task”
box=wx.MessageDialog(self,‘Please enter a task first!’,
‘Enter Task!’,wx.OK)
answer=box.ShowModal()
box.Destroy()
else:
self.start_time = datetime.datetime.now()

        if btn_label == "Start":
            #MAIN TIMER FLOW
            for item in range (0, self.task_list.GetCount()):
                if self.task_list.IsChecked(item) == False:
                    print "skipping item"
                    continue
                else:
                    print "starting timer..."
                    self.timer.Start(1000) #start timer
                    self.start_button.SetLabel("Stop")
                    self.current_dur = self.dur_list[item]
                    task = self.task_list.GetString(item)
                    self.current_task.SetLabel(task)
                    #while timer is still ticking: NOT SURE WHAT TO DO HERE!
                        #if seconds are not 0
                            #continue
                        #if seconds are 0
                            #break
            print "all done here." #when nothing else is in the list...
       
        if btn_label != "Start":
            print "timer stopped!"
            self.timer.Stop()
            self.start_button.SetLabel("Start")
def TimerEnd(self):
    btn_label = self.start_button.GetLabel()
    self.timer.Stop()
    print("Timer end.")
    self.start_button.SetLabel("Start")
def Update(self, event):
    
    self.seconds = (self.start_time - datetime.datetime.now() +
               datetime.timedelta(seconds=self.current_dur)).seconds
   
    new_secs = self.seconds % 10
    new_10secs = (self.seconds / 10) % 6
    new_mins = (self.seconds / 60) % 10
    new_10mins = (self.seconds / 600) % 6
    new_hrs = (self.seconds / 3600) % 10
    new_10hrs = (self.seconds / 36000) % 6
    self.timer_text.SetLabel('%d%d:%d%d:%d%d' % (new_10hrs,new_hrs,new_10mins,new_mins,new_10secs,new_secs))
    if self.seconds == 0:
        self.TimerEnd()
        box=wx.MessageDialog(self,'Time for next task!',
                 'Next Task!',wx.OK)
        answer=box.ShowModal()
        box.Destroy()
        return 1
    else:
        return 0

DJ Keebz wrote:

Hello--
I'm going crazy trying to figure out how to get my countdown timer
working. I've attached my source if anyone is willing to take a look at
it to see if they can figure out what's being messed up. Here's the
scoop on what my app *should* do. It's almost in working condition:

* The user enters a task and a duration for that task (say 30 minutes),
and both the task and the duration are stored in separate lists.
* Once the user is finished entering tasks with accompanied durations,
they press the start button.
* At that point, I want the timer to look at the first stored task and
accompanied duration, and begin counting down.
* Once it hits 0, I want the timer to move on to the next task and alert
the user. Right now, all the program does is go straight to the last
task in the list.
* I would like it to continue looping through the tasks until the user
decides to press stop.
* I don't know how to get the timer to move from task A to task B, to
task C, etc. Any help?
* The main issue I'm having is found in my StartButton function. If you
look at my source, you'll see a commented-out while loop section that I
was playing with to get it working.
* I'm assuming it has something to do with the way my StartButton and
Update functions talk to each other, but I'm unsure what to do.

The problem is that you are still thinking about the functionality procedurally, not from an event driven perspective. Your StartButton method is looping through the whole collection of tasks and restarting the timer for each. So basically they are all being started and then immediately you are moving on to the next task, instead waiting and running them sequentially like you want. Your commented out code shows that you are expecting to wait in that method and do stuff while the timer is "ticking."

This is not how event-driven programs work. Events should be caught, handled, and control returned to the main event loop as fast as possible. Blocking in any one event handler for a user-noticable amount of time is a bad thing because it will prevent the system from delivering other events during that time, so things like button clicks won't be handled and windows will not be repainted until the long running task is completed.

In your case you should understand that the timers generate events too, and that the way that you wait for a timer is to let control sit in the main loop sending events like normal, and wait for the system to send you a timer event. When that event arrives and your timer event handler is called you need to quickly do whatever needs done for just that one timeout (updating the countdown text, processing the task, maybe switching to the next task, maybe stopping the timer, etc.) and then return to the main event loop.

···

--
Robin Dunn
Software Craftsman

Thanks Robin, I am new to this and your explanation has shed some light. I will try moving the bulk of the code to the main section rather than putting it in events and hopefully I will find my solution by doing so.