Articles Archive
Articles Search
Director Wiki
 

Multiple Timer Behavior

July 25, 1999
by Pat McClellan

Dear Multimedia Handyman,

I'm trying to develop a sort of Punch Clock for our designers here at work to help them keep track of the time they spend on individual projects. I'm trying to accomplish this by having 5 individual timers, each with their own Start, Stop, and Reset button as well as a self-updating field that displays the Hours and Minutes. I don't see a need to save a log or file of these times as the designers would copy the times onto their time sheets at the end of the day.

Thanks, and keep up the good work!

Pete Wagner

Pete,

Time has typically been a difficult thing to deal with in Director, because the time functions in Lingo return string data. For example, if you...

put the long time
-- "12:26:07 PM"

... the output is a string. I've always thought this was stupid, because what we really need is something like a property list. Wouldn't it be nice to have...

put the long time
-- [#hour: 13, #minute: 26, #second: 7]

... or better yet, a time datatype which we could run comparisons and calculations on.

They've finally added date functionality to Director, so we can only hope that the related time functions get added soon. In the meantime, I've got a workaround.

Keeping track of multiple timers is really a breeze if you use behaviors. With behaviors, we store the start, stop, and cumulative time data in properties. Since we're not using global variables, there's no conflict or sharing of data between the timers.

I decided to make the behavior so that it attaches to the field or text member which will display the time. I'm using a different text member for each timer. For the sake of the layout, I made 6 timers instead of 5... I hope you don't mind. :>

Download a sample movie in Mac or PC format

The behavior that is attached to the text field doesn't do anything until it is commanded to do so. It takes its instructions from the buttons, which send commands to startTimer, stopTimer, and clearTimer using the sendSprite() command. Those commands are included in the scripts attached to the buttons, which I'll show below.

Here's the main behavior (attached to the text member). There are handlers for startTimer; stopTimer; clearTimer; parseTime -- which changes the string data into hours, minutes and seconds; calculateTime -- which does the actual time calculation; and displayTime -- which plugs the calculated time into the text member.

-- JobTimer behavior
-- copyright © 1999, ZZP Online, LLC
-- free use for Director Online readers
-- apply to field or text member displaying timer
property pStartTime, pStopTime, pTimeWorked
property pSprite,  pMem
on beginSprite me
  set pSprite = sprite the spriteNum of me
  set pMem = the member of pSprite
  set pTimeWorked = 0
  
  clearTimer me
  
end
on startTimer me
  set realTime = parseTime(me)
  set pStartTime = HMStoFrames(realTime, 1, 0, 0)
  set the text of pMem = "TIMING"
  
end startTimer
on stopTimer me
  set realTime = parseTime(me)
  set pStopTime = HMStoFrames(realTime, 1, 0, 0)
  
  calculateTime me
  displayTime me
  
end stopTime
on clearTimer me
  set pTimeWorked = 0
  displayTime me
end
on parseTime me
  set timeNow = the long time
  set the itemDelimiter = ":"
  set hour = value(item 1 of timeNow)
  
  if timeNow contains "PM" then
    set hour = hour + 12
  end if
  
  set minute = item 2 of timeNow
  set second = char 1 to 2 of item 3 of timeNow
  set realTime = hour & ":" & minute & ":" & second
  
  return realTime
  
end parseTime
on calculateTime me
  if pStopTime < pStartTime then
  
    set pStopTime = pStopTime + 86400 
    -- accounts for crossing midnight
    
    alert "You've worked past midnight on this one." ¬
      & RETURN & "We're not tracking the date, so we're ¬
      assuming that" & RETURN & "you're still in the ¬
      same 24 hour work period.  If not," & RETURN & ¬
      "you'll need to add 24 hours to the output."
      
  end if
  
  set thisTimeSegment = pStopTime - pStartTime
  set pTimeWorked = pTimeWorked + thisTimeSegment  
  
end calculateTime
on displayTime me
  set HMSWorked = FramestoHMS(pTimeWorked,1,FALSE,FALSE)
  -- this next part shouldn't be necessary, but 
  -- the FramestoHMS kept returning fractional 
  -- seconds, example, "01:13:50.02"
  
  set the itemDelimiter to "."
  set HMSworked = item 1 of HMSworked
  set timeString = string(HMSworked)
  set the text of pMem = timeString
  
end

The key to doing these calculations is a seldom-used Lingo function which can convert hours, minutes and seconds into frames -- or the other way around. Check your Lingo dictionary for these two items:

HMStoFrames
FramesToHMS

Why would we want to convert time to frames anyway? Well, it's a real hassle to add and subtract hours, minutes and seconds because we're not in base ten. Instead, when you get to sixty seconds or sixty minutes, or 12 or 24 hours, it's like "carrying a 1". So if we're going to be subtracting our startTime from the stopTime to get time worked, it would be great to have it all as seconds. As seconds, then it's just standard subtraction. Make sense?

Okay, now the fastest way to convert hours, minutes, and seconds down to just seconds is... use HMStoFrames, where you assume a tempo of 1 frame per second. Don't worry, you're not actually changing the tempo of your movie, we're just using this function in a way they didn't really expect.

That means that when the startTimer handler is called, the long time (example, "12:26:07 PM") is run through the parseTime handler to convert it to a 24 hour clock and getting rid of the AM/PM. It returns a string such as "13:26:07". Then the startTimer handler converts that to frames (seconds, really) using the HMStoFrames function. That value in this example is 48367 and represents the number of seconds since midnight. We store that value in pStartTime.

When the stopTimer handler is called, we do the same conversion process on the current time. Then calculateTime subtracts pStartTime from pStopTime to give us a value for the time worked. You can think of this value as the number of seconds elapsed. Now, we can convert this value back to hours, minutes, and seconds by using the FramesToHMS function. The final step is to display that value, using the displayTime handler.

The timer functions are called from the buttons. Here's the behavior that is attached to each of the Start Time buttons.

property pTimerSprite
on mouseUp me
  sendSprite pTimerSprite, #startTimer
end
on getPropertyDescriptionList me
  set pdlist to [:]
  addprop pdlist, #pTimerSprite, [#comment:"Which ¬
    Timer Sprite?", #format:#integer, #default:1]
    
  return pdlist
  
end getPropertyDescriptionList

The stopTime and clearTime behaviors are virtually identical, though they call different handlers in the sendSprite command line.

For your punch clock utility, you'll probably want to add an editable field above each timer so that you know which job is being timed on each timer. Good luck with your project.

Patrick McClellan is Director Online's co-founder. Pat is Vice President, Managing Director for Jack Morton Worldwide, a global experiential marketing company. He is responsible for the San Francisco office, which helps major technology clients to develop marketing communications programs to reach enterprise and consumer audiences.

Copyright 1997-2017, Director Online. Article content copyright by respective authors.