I really appreciate your time.
I will start with Objective-C then
I really appreciate your time.
I will start with Objective-C then
Hi kamil,
Technology is really great for teaching. After all, that is what the students grew up with. If you were very serious, you might want to get QuickTime Pro. I think that’s what they still call it.
Anyway, the scripting part is quite easy to learn I think, but if you want to jump into objective c then that might prove to be a little harder especially if you haven’t had any programming experience.
If that’s the case, then there’s this thing called AppleScript-ObjectiveC I think that was the name that you can use in Xcode. What it is is basically ObjectiveC made easy by combining AppleScript (a scripting language) with ObjectiveC (a lower level language). Note that a lower level language is harder as it gets closer to machine language (I think a bunch of zeroes and ones).
Anyway, If you start with a scripting language and then integrate that with a lower language then it might be easier. Of course, it depends on your learning style. Some People can just catch on to it and others cannot do that (me :D). But then everybody has their strong and weak points.
Just my thoughts.
gl,
kel
In your honor, here I present “Steffuhr” “ the Stopwatch app.
â–¸ the app name comes from the German word, “Stoppuhr,” which uncle Google claims it means stopwatch.
You can see the logo here.
I uploaded the Steffuhr app with the fixed logo here (file “Steffuhr Stopwatch.zip”).
Thank you so much for everything. I will be posting here about any updates I will do, or questions I will have.
appreciated, but the German word for stopwatch is actually “Stoppuhr”
Ohh yes, my bad. Uncle Google was right. Fixed it. Still, the app name comes from your name â–¸ STEFan+stoppUHR=Steffuhr (to make the “pp” consistent I doubled the “f”). I hope it doesn’t sound funny in German and sort of makes sense.
My 1st language isn’t English and I know that native English speakers don’t get making up or changing the names. Not the case where I come from (Poland), where people make up names all the time.
Hi kamil,
I rewrote the stopwatch with pause if you’re interested. Was on a little medication, because of a bad cold.
Anyway, here’s an example of stopwatch with pause using the reopen handler:
global run_time, is_running, start_date
-- initialize
on run
set run_time to 0
set is_running to false
reopen
end run
-- reopen to toggle pause
on reopen
set is_running to not is_running
set cur_date to current date
if is_running then -- set start date
-- start or reset start date
set start_date to cur_date
say "running" without waiting until completion
else -- pause (add to total time)
set run_time to run_time + (cur_date - start_date)
say "paused" without waiting until completion
end if
end reopen
on quit
-- display total run time without the pauses
set cur_date to current date
set run_time to run_time + (cur_date - start_date)
try -- trap error bug if user cancel dialog
display dialog "" & run_time & " secs"
end try
continue quit
end quit
You can run this with another AppleScript or Service using a keyboard shortcut or just rerun.
Edited: there’s still a bug if the user quits when it’s paused. Working on that and thinking of the best way to integrate this without an overall timer. I think I have one in the list somewhere, but can’t find it.
Edited: quick fix. Check if it was running or paused in the quit handler:
global run_time, is_running, start_date
-- initialize
on run
set run_time to 0
set is_running to false
reopen
end run
-- reopen to toggle pause
on reopen
set is_running to not is_running
set cur_date to current date
if is_running then -- set start date
-- start or reset start date
set start_date to cur_date
say "running" without waiting until completion
else -- pause (add to total time)
set run_time to run_time + (cur_date - start_date)
say "paused" without waiting until completion
end if
end reopen
on quit
-- display total run time without the pauses
set cur_date to current date
if is_running then
set run_time to run_time + (cur_date - start_date)
end if
try -- trap error bug if user cancel dialog
display dialog "" & run_time & " secs"
end try
continue quit
end quit
Still might need testing.
Edited: didn’t need to get the current date if it was paused:
global run_time, is_running, start_date
-- initialize
on run
set run_time to 0
set is_running to false
reopen
end run
-- reopen to toggle pause
on reopen
set is_running to not is_running
set cur_date to current date
if is_running then -- set start date
-- start or reset start date
set start_date to cur_date
say "running" without waiting until completion
else -- pause (add to total time)
set run_time to run_time + (cur_date - start_date)
say "paused" without waiting until completion
end if
end reopen
on quit
-- display total run time without the pauses
if is_running then
set cur_date to current date
set run_time to run_time + (cur_date - start_date)
end if
try -- trap error bug if user cancel dialog
display dialog "" & run_time & " secs"
end try
continue quit
end quit
gl,
kel
Hello Kel.
I liked your applet, but shouldn’t you have written that it must be exported as a “stay open applet”?
This is a different take on the problem of recording time. I use a script instead of an Applet. It works after the “finite state machine” principle, as it acts accordingly to which state it is currently in, much like your applet, but still not so, since it isn’t running at all times.
The script tracks the time in a property, so you can’t lock the script against modification I believe. You can start, pause and stop the “Stop Watch”.
I haven’t decided, if I should subtract the slack off the end time, or if I should leave a slack column.
It starts off from the state “Stopped”, then it can only bekomme “Running”. From “Running”, it can become “Stopped” or “Paused”. From “Paused” it can become “Running” or “Stopped”. The code really doesn’t look very good, but it should work as expected.
property scriptTitle : "Stop Watch"
property state : "Stopped"
property t0 : 0
property elapsed : 0
property slack : 0
property observation : "Enter what to time:"
global clockIcon
# Copyright © 2015 McUsr, you may not post this as a work of your own on some webpage, or in a book.
-- http://macscripter.net/viewtopic.php?pid=179972#p179972
(*
Now considers daylightsavings time regardless of locale
This works as follows: if the time to gmt has increased, from when we started timing, then we must
subtract the difference, if the time has decreased, then we increase the time of the current lap
likewise.
*)
on run
set clockIcon to (path to library folder from system domain as text) & "CoreServices:CoreTypes.bundle:Contents:Resources:Clock.icns"
if state = "Stopped" then
tell application (path to frontmost application as text)
display dialog "Start StopWatch" default answer observation with title scriptTitle buttons {"Cancel", "Start"} cancel button 1 default button 2 with icon file clockIcon
end tell
-- We never pass this point if the user hits "Cancel"
set observation to text returned of result
set {state, t0, t0_ToGMT} to {"Running", (current date), (time to GMT)}
-- Changes the state to running so we don't enter this block before the script is stopped.
display notification "Clocking: " & observation with title scriptTitle
else if state = "Running" then
tell application (path to frontmost application as text)
display dialog "Pause or Stop: " & observation with title scriptTitle buttons {"Cancel", "Pause", "Stop"} default button 2 with icon file clockIcon
end tell
set btn to button returned of result
if btn is not "Cancel" then
-- Recording "lap"
set Te_ToGMT to (time to GMT)
set elapsed to elapsed + ((current date) - t0) + (t0_ToGMT - Te_ToGMT)
if btn = "Pause" then
-- starting to "slack"
set {state, slackStart, t0_ToGMT} to {"Paused", (current date), (time to GMT)}
display notification "Paused after " & my formatTime(elapsed) & "." with title scriptTitle subtitle observation
else if btn = "Stop" then
set state to "Stopped"
stopDialog(observation, elapsed, slack, scriptTitle)
end if
end if
-- We can come back to the state "running" from the state "Paused", or when we have started afresh again from the state "Stopped"
else if state is "Paused" then
tell application (path to frontmost application as text)
display dialog "Start or Stop: " & observation & "?" with title scriptTitle buttons {"Cancel", "Start", "Stop"} default button 2 with icon file clockIcon
end tell
set btn to button returned of result
if btn is not "Cancel" then
set Te_ToGMT to (time to GMT)
tell (current date)
set {slack, t0} to {slack + (it - slackStart) + (t0_ToGMT - Te_ToGMT), it}
end tell
if btn = "Start" then
set state to "Running"
set t0_ToGMT to (time to GMT)
display notification "Clocking continued at " & t0's time string with title scriptTitle subtitle observation
else if btn = "Stop" then
stopDialog(observation, elapsed, slack, scriptTitle)
set state to "Stopped"
set observation to "Enter what to time:"
end if
end if
-- we can only come back to this state from the state "Running", that is, if we don't stop the script from this "Paused" state.
end if
end run
on stopDialog(observation, obsTime, slack, scriptTitle)
set clockedTime to formatTime(obsTime)
set slackedTime to formatTime(slack)
set resultString to "Final time for:
" & observation & " was: " & clockedTime & "
Slack: " & slackedTime
tell application (path to frontmost application as text)
display dialog resultString with title scriptTitle buttons {"Clipboard", "Ok"} default button 2 with icon file clockIcon
end tell
if button returned of result is "Clipboard" then set the clipboard to resultString
resetVars()
-- we do wipe out the observation here
end stopDialog
on resetVars()
global observation, t0, elapsed, slack
set {observation, t0, elapsed, slack} to {"Enter what to time:", 0, 0, 0}
end resetVars
on formatTime(someSecs)
if someSecs ≥ 3600 then -- we have to consider hours
set tHours to someSecs div 3600
if tHours < 10 then
set tHours to "0" & tHours & ":"
else
set tHours to "" & tHours & ":"
end if
set someSecs to someSecs mod 3600
else
set tHours to ""
end if
set tMinutes to (text -2 thru -1 of ("0" & (someSecs div 60))) & ":"
set someSecs to someSecs mod 60
set tSecs to text -2 thru -1 of ("0" & someSecs)
return tHours & tMinutes & tSecs
end formatTime
Hi McUsr,
Great idea about closing the app instead of having it go into stasis. I’m curious to see how you solved the startup time delay of the app. Need to look at your script.
Btw, I think I did write that it needs to be saved as stay open at the beginning. Will check it out.
Have a good day!
kel
Hi McUsr,
I read your post wrongly in thinking that you were quitting the app. Interesting, you’re switching between the display dialog and notifications.
Great idea with using Notification Center! Also, I didn’t think about the app that is timed being frontmost.
Getting ideas about using AppleScript ObjC and doing it completely scripting UserNotificationCenter. That way, we can skip using display dialog maybe. I haven’t tried it in Yosemite yet, but know that there are at least three places to click using AppleScript ObjC. This also eliminates the frontmost app problem.
Have a great day!
kel
Hello.
I actually added one more notification, when you “restart” the Stopwatch after “pause”. I also removed a bug, (the Cancel button, canceled in Script Debugger without a cancel button property.). I have now added a proper “cancel button 1”.
I am fine with having dialogues for entering input, and confirming, like I have done it. The last dialog confirms that the timing is done.
I really have no need for the notification centre from objective c, there is a blogpost from 2011 or so here that shows you how to do it with ASOC in an applet.
Other than that, if you use regular notifications from an applet, then you should have a delay before quitting, if you send a notification right before you quit. I read that at macosxautomation.com under “Mavericks”.
Have a nice day, I’m signing off for today.
Hi McUsr when you get back,
On the side, found my old Xcode NotificationCenter script template if anybody is interested and it still works!
script AppDelegate
property parent : class "NSObject"
property myNotification : missing value
property notificationCount: 0
on applicationWillFinishLaunching_(aNotification)
-- initialize
return
end applicationWillFinishLaunching_
-- reopen handler
on applicationShouldHandleReopen_hasVisibleWindows_(theApplication, aFlag)
my applicationDidFinishLaunching_(missing value)
return NO
end applicationShouldHandleReopen_hasVisibleWindows_
-- main
on applicationDidFinishLaunching_(aNotification)
-- set the delegate to script
current application's NSUserNotificationCenter's defaultUserNotificationCenter's setDelegate_(me)
-- get current date and target date
set sentDate to current application's NSDate's |date|()
set sentDateAsString to my formatDate_(sentDate,1,2)
set numSeconds to 30
set targetDate to current application's NSDate's dateWithTimeInterval_sinceDate_(numSeconds,sentDate)
-- make user info NSDictionary
set notificationCount to notificationCount + 1
set nsDict to current application's NSDictionary's dictionaryWithObjectsAndKeys_(notificationCount, "notificationIndex", sentDateAsString, "sentDate", "value3", "key3", missing value)
-- send notification to NSUserNotificationCenter
my sendNotification_("MyNotifier",sentDateAsString,"It's time!","Restart","Stop",targetDate,"Boing",nsDict)
say "Notification sent."
return
end applciationDidFinishLaunching_
--
-- format NSDate to string using styles
-- 0 = none, 1 = short, 2 = med, 3 = long, 4 = full
on formatDate_(aNSDate, aDateStyle, aTimeStyle)
set myFormatter to current application's NSDateFormatter's alloc()'s init()
myFormatter's setDateStyle_(aDateStyle)
myFormatter's setTimeStyle_(aTimeStyle)
set formattedDate to myFormatter's stringFromDate_(aNSDate)
return formattedDate
end formatDate_
-- method for sending a notification
on sendNotification_(aTitle, aSubtitle, aMessage, aActionButtonTitle, aOtherButtonTitle, aDeliveryDate, aSound, aDict)
-- make the notification
set myNotification to current application's NSUserNotification's alloc()'s init()
set myNotification's title to aTitle
set myNotification's subtitle to aSubtitle
set myNotification's informativeText to aMessage
set myNotification's actionButtonTitle to aActionButtonTitle
set myNotification's otherButtonTitle to aOtherButtonTitle
set myNotification's deliveryDate to aDeliveryDate
set myNotification's soundName to aSound
set myNotification's userInfo to aDict
-- schedule the notification
current application's NSUserNotificationCenter's defaultUserNotificationCenter's scheduleNotification_(myNotification)
return
end sendNotification_
-- delegate instance methods
-- force presentation for when application process is frontmost
on userNotificationCenter_shouldPresentNotification_(aCenter, aNotification)
return yes
end userNotificationCenter_shouldPresentNotification_
-- deliver
on userNotificationCenter_didDeliverNotification_(aCenter, aNotification)
say "Notification Delivered"
return
end userNotificationCenter_didDeliverNotification_
-- user activation
on userNotificationCenter_didActivateNotification_(aCenter, aNotification)
say "Notification Activated"
-- 0 none
-- 1 contents clicked
-- 2 action button clicked
set userActivationType to (aNotification's activationType) as integer
if userActivationType is 1 then
say "contents clicked"
my contentsClicked_(aNotification)
else if userActivationType is 2 then
say "action button clicked"
my actionButtonClicked_(aNotification)
else -- userActivationType is 0
say "no user activation"
end if
return userActivationType
end userNotificationCenter_didActivateNotification_
--
on contentsClicked_(aNotification)
-- do something
return
end contentsClicked_
-- gets user info and deletes delivered notification from Notification Center
on actionButtonClicked_(aNotification)
-- delete the notification and send a new one (or some other action)
-- first get info on notification
set theInfo to aNotification's userInfo
set theValue to theInfo's valueForKey_("notificationIndex")
say (theValue as string)
-- delete notification from notification center
current application's NSUserNotificationCenter's defaultUserNotificationCenter's removeDeliveredNotification_(aNotification)
-- send a new notification
my applicationDidFinishLaunching_(missing value)
return
end actionButtonClicked_
--
on applicationShouldTerminateAfterLastWindowClosed_()
return true
end applicationShouldTerminateAfterLastWindowClosed_
-- quit
on applicationShouldTerminate_(sender)
-- Insert code here to do any housekeeping before your application quits
return current application's NSTerminateNow
end applicationShouldTerminate_
end script
Anyway, there is no way for user text entry. Still thinking about that. The display dialog might be necessary. Still thinking about that.
I started out with something simple, but it has grown into a new project. Anyone can take things as far as they want to take it.
Have a good day,
kel
ps. Thanks for the updates.
Hello kel.
I am not going out yet. What I think is, that instead of using an applet for timing, you can use a script, and thereby saving resources, if you are going to call an applet from a script anyway. The script approach comes short, if there is timing going on at the very moment daylight savings time is implemented/removed.
It is also harder to stop timing, if you put your computer to sleep. (But it is doable by another script, that sets the state and time properties adequately.)
The script approach should save resources, in that the script aren’t running except for when it is executed, the state machine is kind of adapaptable, so you could, just edit it, to do what you see fit in there, like creating a new record in FileMaker Pro, or fill in some cells in your favourite Spreadsheet, if the object of the stopwatch is to create time slips for instance.
Have a good evening.
Hi McUsr,
The way you see all the places where bugs could occur is amazing! Never thought that the Timer could be running when daylight savings time has changed! There is so much to think about!
So what we need is to monitor the whole time thing (couldn’t think of a word for that :|). Need to look back at your old time scripts.
Edited: and btw, there is no problem with the computer sleeping as we can use caffeinate right.
Great minds,
kel
Well I think caffeinate just keeps the machine from sleeping, I was more thinking if you clocked some task that you could only do while at keyboard, then the timer should be paused when the computer fell asleep, and you should be notified during wakeup. I think displaysleep is the right utility for this, but I am happy with having to edit stuff at the moment, should a ‘sleep incident’ happen. I didn’t bother to fix the issue with daylight savings, because it happens at a most unlikely time of day. But, before using this for shift-working, this is something that has to be considered.
Thanks for your template, templates are handy, you never know when they may be of use.
Hello.
Just mentioning one negativism with regards to notification: Notifications are very limited in size, and can only consists of three lines, so, notifications are not fit for all kinds of messages, with respect to dynamic content.
Hi McUsr,
Yeah, the Notification window is limitted. What I get is 3 clicks with just the plain window, but there is another thing Apple has not added yet. I noticed that they added some stuff like the arrow where you have a drop down list. I’t’s not accessible through AppleScript yet, but interesting. You can see this in the notifications for updates where they ask to notify you later or whatever.
Also, I’ve been looking in the developer pages, but I think you can’t use the extra stuff in ASObjC. You need to do all that extra ObjC stuff. I could probably do it as from the teachings of Stefan, but it takes a long time for a beginner.
Anyway, life is a never ending puzzle.
gday,
kel
Hello kel.
I hope you find a solution to your problems with notifications. Personally, I really like them for short messages, but I can see the use. Well, I had to play some more with the StopWatch, (and test), so now I’ve added a nicer icon, and it resets the text variable, among other things, when you stop the clock. I have also added some meagre comments, all in post #22. I really think it should be easy to adapt the script for journaling purposes, -write stuff to a csv file or something, so I’ll continue with this with FileMaker Pro.
Oops:
I didn’t mean displaysleep, I meant sleepwatcher, which is a command line utility, if not the command line utility, to control what happens when your machine goes to sleep, wakes up, etc. It should be downloadable if you google it.
Hello.
So, I realized, that I didn’t have to dig deep into the locale database, to track when daylight savings times starts and ends, in order for making correct clockings at all times.
All it takes, is to record the time to gmt, when the clocking starts, and take the time to gmt when the clocking ends, and then add time to gmt when the time starts - time to gmt when the timing ends, to the number of perceived seconds (difference in seconds between start and end), to make it all work correctly, all places all of the time.
I have updated the script in post #22, and think I am done with this for now.
Hi McUsr,
Yes, the Notification window is very limited with ASObjC. Another thing is you can’t update the text. Also, you can’t get a response when the user clicks the close button I think. Still checking if they made any changes with that button. The developer page was rewritten, so maybe they made changes.
You’re always on the ball with the updates in your apps. Still checking it out. Wish I had your energy.
Have a good day,
kel