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.
Copyright 1997-2024, Director Online. Article content copyright by respective authors.