I came across an interesting discovery when I was trying to find a solution for an issue that I see others on the MacScripter forum also have.
I have an AppleScript saved as an application that shuts down the computer after a 15 second delay. The script works but upon restart the computer shuts down again.
When I was trying various ideas put forward by people in the forum to solve this I made a few different AppleScripts and saved them as an application. I decided to trash a couple of earlier scripts I made and even though I had quit Script Editor the system wouldn’t let me trash them because it said they were open.
I had to force quit them from the Apple menu drop down. I saw all the AppleScript applications that I had created in the list of open apps.
Would this partially explain why the scripts firing on computer restart is such a chronic issue?
I encountered the same shut-down issue as the OP when using the Finder but System Events seems to work OK.
display dialog "The computer will shut down in 15 seconds." buttons {"Cancel", "Shut Down Now"} default button 1 giving up after 15
tell application "System Events" to shut down with state saving preference
For this script to function correctly, the option in the macOS shutdown dialog–which has the label “reopen windows when logging back in”–cannot be enabled (thanks KniazidisR).
One of the explanations for this phenomenon may be this: the normal operation Shut Down (manually) has the checkbox “Reopen windows when logging back”. The user can set this check box, if desired. It seems that with a programmable restart (without parameters), this flag sets System Events automatically. Just in case. What happens in this case? When restarting, the script opens again (with it saved state) and turns off the computer again.
When the appllication which shut downs the Mac sets state saving preference to true (“not reopen windows when logging back”) then this phenomenon is avoided, as I think.
I rewrote the shut-down script utilizing an Applescript progress dialog. A few comments:
The script has to be saved and run as an application to display the dialog.
The dialog title is the file name.
The Stop dialog button cannot be changed and an additional button cannot be added.
set shutdownDelay to 15
set progress total steps to shutdownDelay
repeat with i from shutdownDelay to 1 by -1
set progress description to "The computer will shut down in " & i & " seconds."
set progress additional description to " "
set progress completed steps to i
delay 1
end repeat
tell application "System Events" to shut down with state saving preference
I found this old post of yours while looking for a solution to “my” shut down which produced an endless loop of startup/shut downs. Works perfectly, but would it be possible to have a countdown clock rather than the progress bar that moves from right to left? I haven’t had any luck creating a countdown clock (just numbers, 15, 14, 13, etc.) with AppleScript.
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
property wController : missing value -- outlet equivalent in AsObjC
property notificationTitle : "Restarting... "
property aWidth : 360
property aHeight : 30
my performSelectorOnMainThread:"displayNotification:" withObject:({aWidth, aHeight, notificationTitle}) waitUntilDone:true
tell application "System Events" to shut down with state saving preference
------------------------------------ HANDLERS ------------------------------------------------
on displayNotification:paramObj
copy paramObj to {aWidth, aHeight, aTitle}
set aColor to current application's NSColor's colorWithDeviceRed:0.0 green:0.0 blue:0.0 alpha:0.9
set aView to current application's NSTextView's alloc()'s initWithFrame:(current application's NSMakeRect(0, 0, aWidth, aHeight))
aView's setRichText:true
aView's useAllLigatures:true
aView's setTextColor:(current application's NSColor's cyanColor()) --
aView's setBackgroundColor:aColor
aView's setEditable:false
aView's setFont:(current application's NSFont's fontWithName:"Menlo" |size|:20)
repeat with i from 15 to 1 by -1
set my wController to current application's NSWindowController's alloc()
(aView's setString:(" " & i & " sec"))
set aWin to makeWinWithView(aView, aWidth, aHeight, aTitle, 0.9)
my (wController's initWithWindow:aWin)
my (wController's showWindow:me)
delay 1
my wController's |close|()
end repeat
end displayNotification:
on makeWinWithView(aView, aWinWidth, aWinHeight, aTitle, alphaV)
set aScreen to current application's NSScreen's mainScreen()
set aFrame to {{0, 0}, {aWinWidth, aWinHeight}}
set aBacking to current application's NSTitledWindowMask
set aDefer to current application's NSBackingStoreBuffered
set aWin to current application's NSWindow's alloc()
(aWin's initWithContentRect:aFrame styleMask:aBacking backing:aDefer defer:false screen:aScreen)
aWin's setTitle:notificationTitle
aWin's setDelegate:me
aWin's setDisplaysWhenScreenProfileChanges:true
aWin's setHasShadow:true
aWin's setIgnoresMouseEvents:false
aWin's setLevel:(current application's NSNormalWindowLevel)
aWin's setOpaque:false
aWin's setAlphaValue:alphaV --append
aWin's setReleasedWhenClosed:true
aWin's |center|()
aWin's setContentView:aView
return aWin
end makeWinWithView
Thank you, works beautifully. I did change the System Event from shut down to restart on your original, then made a copy so as to have both Shut Down and a Restart.
Tried a few things (still learning) but was unable to center the countdown (see image). If you can just tell me where this is controlled I’ll have something to play around with over morning coffee.
If you take a look at the two images I’ve uploaded, take a look at the “aView’s setString:(” location. If you put the cursor at the far right " mark. and step backwards. there are 15 spaces in the original, which when changed to 12 spaces, centers the numbers. I’ve included the modified script.
use scripting additions
use framework "Foundation"
use framework "AppKit"
property wController : missing value -- outlet equivalent in AsObjC
property notificationTitle : "Restarting... "
property aWidth : 360
property aHeight : 30
my performSelectorOnMainThread:"displayNotification:" withObject:({aWidth, aHeight, notificationTitle}) waitUntilDone:true
tell application "System Events" to restart with state saving preference
------------------------------------ HANDLERS ------------------------------------------------
on displayNotification:paramObj
copy paramObj to {aWidth, aHeight, aTitle}
set aColor to current application's NSColor's colorWithDeviceRed:0.0 green:0.0 blue:0.0 alpha:0.9
set aView to current application's NSTextView's alloc()'s initWithFrame:(current application's NSMakeRect(0, 0, aWidth, aHeight))
aView's setRichText:true
aView's useAllLigatures:true
aView's setTextColor:(current application's NSColor's cyanColor()) --
aView's setBackgroundColor:aColor
aView's setEditable:false
aView's setFont:(current application's NSFont's fontWithName:"Menlo" |size|:20)
repeat with i from 15 to 1 by -1
set my wController to current application's NSWindowController's alloc()
(aView's setString:(" " & i & " sec"))
set aWin to makeWinWithView(aView, aWidth, aHeight, aTitle, 0.9)
my (wController's initWithWindow:aWin)
my (wController's showWindow:me)
delay 1
my wController's |close|()
end repeat
end displayNotification:
on makeWinWithView(aView, aWinWidth, aWinHeight, aTitle, alphaV)
set aScreen to current application's NSScreen's mainScreen()
set aFrame to {{0, 0}, {aWinWidth, aWinHeight}}
set aBacking to current application's NSTitledWindowMask
set aDefer to current application's NSBackingStoreBuffered
set aWin to current application's NSWindow's alloc()
(aWin's initWithContentRect:aFrame styleMask:aBacking backing:aDefer defer:false screen:aScreen)
aWin's setTitle:notificationTitle
aWin's setDelegate:me
aWin's setDisplaysWhenScreenProfileChanges:true
aWin's setHasShadow:true
aWin's setIgnoresMouseEvents:false
aWin's setLevel:(current application's NSNormalWindowLevel)
aWin's setOpaque:false
aWin's setAlphaValue:alphaV --append
aWin's setReleasedWhenClosed:true
aWin's |center|()
aWin's setContentView:aView
return aWin
end makeWinWithView```
I know this an old thread, but I have been trying to add a cancel button to both the Shut Down and Restart scripts, with absolutely no success. Any help would be appreciated.
This is quite difficult because you have to add the NSButton to the attached NSView in the AppleScriptObjC part and handle the mouse events.
Apart from that it likely won’t work in your environment because you create new windows/views in each iteration of the repeat loop and close them again which is not a good practice.
I may have found a perfectly acceptable solution. I used Peavine’s script in post #5, and it works well for both restart and shut down.
Restart:
display dialog "The computer will restart in 15 seconds." buttons {"Cancel", "Restart Now"} default button 1 giving up after 15
tell application "System Events" to restart with state saving preference
Shut Down:
display dialog "The computer will shut down in 15 seconds." buttons {"Cancel", "Shut Down Now"} default button 1 giving up after 15
tell application "System Events" to shut down with state saving preference
As mentioned, your script can be adjusted, since I think it looks better than the vanilla dialog. The main things are to get rid of recreating the window and just change the textView, add a cancel button, and manually process events so that you can actually press the button while in the repeat loop.
Depending on how the main script is split up, another option would be to use osascript to run a script using the system ScriptMonitor application. That puts a status item with a gear icon in the menubar that includes a cancel button for each script/workflow it watches - from my testing, that will pretty much kill a script no matter what it is doing (or not doing).
I did work up a version of the ASObjC script that uses an accessory view to place a button (one of those small round ones with an X) on the right side of the window’s title bar. The sample script is about twice the size, which is to say way bigger than a single display dialog statement, if you still want to see that.
Why not simply taking advantage of the progress view skills of AppleScript, save the script as application and uncomment the System Events line at the end.
set theCounter to 15
set progressCounter to 0
set progress total steps to theCounter
set progress completed steps to progressCounter
set progress additional description to ""
repeat with a from theCounter to 1 by -1
set progress description to "The computer will shut down in " & a & " seconds."
set progress completed steps to progressCounter
set progressCounter to progressCounter + 1
delay 1
end repeat
-- Reset the progress information
set progress total steps to 0
set progress completed steps to 0
set progress description to ""
set progress additional description to ""
-- tell application "System Events" to shut down with state saving preference