How do I return the output of my Apple Script to the Status Bar in macOS?

随声附和 提交于 2020-01-25 10:15:10

问题


I am working on a script that looks in one app for the amount of time you've spent doing a certain activity, then displays that number in the status bar of the Mac, just like the clock that continuously counts up in the upper right corner. I've seen others like it that can show you your IP in the same area, which is close to what I'm trying to accomplish.

I think I have the script functioning to where it will run continuously until the application where I'm working is fully quit, however, I'm unsure of how to display that number up top in the status bar where it can be seen without needing to open said application.

I've been looking into AppleScriptObjC as an option, however, that is new ground for me, and I'd like to know if that is what should be used before I completely dive in.

I created a menu bar applet with Python, however, I learned that using Python might not be needed at all, and I wasn't sure how I would combine the AppleScript with what I created in Python.

tell application "System Events"
    set appName to "App I'm Using"
    tell process "App I'm Using"
        set activityState to value of menu button 1 of group 1 of group 4 of toolbar 1 of window of application process "App I'm Using" of application "System Events" as list
        return first item of activityState as string
    end tell
end tell

repeat
    tell application "System Events"
        if "App I'm Using" is not in (name of application processes) then exit repeat
    end tell
    delay 5
end repeat

As of now, I'm not encountering any error messages; I just don't know how to move forward with returning the continuous output of the script in the status bar up top.


回答1:


If you want a simple status item that displays text, this script (saved as a stay-open script application) should do the trick. Do this:

  1. Copy the script below into Script Editor
  2. Save it, choosing 'Application' from the File Format pull popup, and click the 'Stay open after run handler' checkbox.
  3. Run the applet like normal (you'll have to give it authorization to control apps).
use framework "AppKit"
use scripting additions

property ca : current application
property NSStatusBar : class "NSStatusBar"

property appName : "App Name"

global statusItem

on run
    set statusItem to NSStatusBar's systemStatusBar's statusItemWithLength:(ca's NSVariableStatusItemLength)
    set statusItem's button's title to "Initializing"
end run

on idle
    -- Update the status item's text here.
    tell application "System Events"
        if not (exists process appName) then
            display alert "Application " & appName & " is not running" as warning giving up after 6
            quit me
        end if
        tell process appName
            tell first window's first toolbar's fourth group's first group's first menu button
                set activityState to first item of (value as list) as text
            end tell
        end tell
    end tell

    set statusItem's button's title to activityState

    (*
      The return value gives the idle time, so if you want the menu item 
      to update (say) every half second, use 'return .5'
    *)
    return 1
end idle

on quit
    -- remove status item and quit
    NSStatusBar's systemStatusBar's removeStatusItem:statusItem
    continue quit
end quit

If you want more complex behavior — like a functional menu for the status item, or a clickable item — you'll have to shift to a cocoa-applescript application, I think.




回答2:


An AppleScriptObjC application can also be created from the Script Editor, as this is simple enough to not need the Cocoa-AppleScript template (note that experimenting should be done with the saved application, as menus and observers would also be added to the editor).

You can create an NSStatusItem and set its button title to however you are wanting to display the elapsed time, with a couple of observers set up to get notifications when the desired application is stopped/started in order to pause and continue the elapsed time. Note that something like an NSTimer should be used instead of that repeat loop, otherwise you will block your timer app’s user interface. An example that counts while you are in the Finder would be something like:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Cocoa" -- Foundation, AppKit, and CoreData
use scripting additions -- just in case

# Watch for specified application activation and update status item timer while it is active.
# Add LSUIElement key to Info.plist to make an agent (no app menu or dock tile).

property watchedApp : "Finder" -- the name of the application to watch/time
property statusItem : missing value -- the status bar item
property statusMenu : missing value -- the status bar item's menu
property timer : missing value -- a repeating timer for updating elapsed time
property updateInterval : 1 -- time between updates (seconds)
property colorIntervals : {30, 60} -- green>yellow and yellow>red color change intervals (seconds)

global elapsed, paused -- total elapsed time and a flag to pause the update
global titleFont
global greenColor, yellowColor, redColor

on run -- set stuff up and start timer
    set elapsed to 0
    set paused to true

    # font and colors
    set titleFont to current application's NSFont's fontWithName:"Courier New Bold" |size|:16 -- boldSystemFontOfSize:14
    set greenColor to current application's NSDictionary's dictionaryWithObjects:{current application's NSColor's systemGreenColor} forKeys:{current application's NSForegroundColorAttributeName}
    set yellowColor to current application's NSDictionary's dictionaryWithObjects:{current application's NSColor's systemYellowColor} forKeys:{current application's NSForegroundColorAttributeName}
    set redColor to current application's NSDictionary's dictionaryWithObjects:{current application's NSColor's systemRedColor} forKeys:{current application's NSForegroundColorAttributeName}

    # status item and menu
    set my statusItem to current application's NSStatusBar's systemStatusBar's statusItemWithLength:(current application's NSVariableStatusItemLength)
    statusItem's button's setFont:titleFont
    statusItem's button's setTitle:formatTime(0)
    set my statusMenu to current application's NSMenu's alloc's initWithTitle:""
    statusMenu's addItemWithTitle:(watchedApp & " Elapsed Time") action:(missing value) keyEquivalent:""
    (statusMenu's addItemWithTitle:"Reset Time" action:"reset:" keyEquivalent:"")'s setTarget:me
    (statusMenu's addItemWithTitle:"Quit" action:"terminate" keyEquivalent:"")'s setTarget:me
    statusItem's setMenu:statusMenu

    # notification observers
    set activateNotice to current application's NSWorkspaceDidActivateApplicationNotification
    set deactivateNotice to current application's NSWorkspaceDidDeactivateApplicationNotification
    tell current application's NSWorkspace's sharedWorkspace's notificationCenter
        its addObserver:me selector:"activated:" |name|:activateNotice object:(missing value)
        its addObserver:me selector:"deactivated:" |name|:deactivateNotice object:(missing value)
    end tell

    # add a repeating timer
    set my timer to current application's NSTimer's timerWithTimeInterval:updateInterval target:me selector:"updateElapsed:" userInfo:(missing value) repeats:true
    current application's NSRunLoop's mainRunLoop's addTimer:timer forMode:(current application's NSDefaultRunLoopMode)
end run

on activated:notification -- notification when app is activated
    set appName to (notification's userInfo's NSWorkspaceApplicationKey's localizedName()) as text
    if appName is watchedApp then set paused to false -- resume elapsed count
end activated:

on deactivated:notification -- notification when app is deactivated
    set appName to (notification's userInfo's NSWorkspaceApplicationKey's localizedName()) as text
    if appName is watchedApp then
        set paused to true -- pause elapsed count
        statusItem's button's setTitle:formatTime(elapsed)
    end if
end deactivated:

to updateElapsed:sender -- called by the repeating timer to update the elapsed time display
    if paused then return -- skip it
    set elapsed to elapsed + updateInterval
    try
        set attrText to current application's NSMutableAttributedString's alloc's initWithString:formatTime(elapsed)
        if elapsed ≤ colorIntervals's first item then -- first color
            attrText's setAttributes:greenColor range:{0, attrText's |length|()}
        else if elapsed > colorIntervals's first item and elapsed ≤ colorIntervals's second item then -- middle color
            attrText's setAttributes:yellowColor range:{0, attrText's |length|()}
        else -- last color
            attrText's setAttributes:redColor range:{0, attrText's |length|()}
        end if
        attrText's addAttribute:(current application's NSFontAttributeName) value:titleFont range:{0, attrText's |length|()}
        statusItem's button's setAttributedTitle:attrText
    on error errmess -- for experimenting
        display alert "Error" message errmess
    end try
end updateElapsed:

to reset:sender -- reset the elapsed time
    set elapsed to 0
    statusItem's button's setTitle:formatTime(elapsed)
end reset:

to formatTime(theSeconds) -- return formatted string (hh:mm:ss) from seconds
    if class of theSeconds is integer then tell "000000" & ¬
        (10000 * (theSeconds mod days div hours) ¬
            + 100 * (theSeconds mod hours div minutes) ¬
            + (theSeconds mod minutes)) ¬
            to set theSeconds to (text -6 thru -5) & ":" & (text -4 thru -3) & ":" & (text -2 thru -1)
    return theSeconds
end formatTime

to terminate() -- quit handler not called from normal NSApplication terminate:
    current application's NSWorkspace's sharedWorkspace's notificationCenter's removeObserver:me
    tell me to quit
end terminate

You shouldn’t need to add the application to the privacy settings, since it doesn’t control anything, but note that regular AppleScripts save properties and globals in the script file, so you will need to code sign or make the script resource read-only to keep from having to re-add the changed applications.



来源:https://stackoverflow.com/questions/57421381/how-do-i-return-the-output-of-my-apple-script-to-the-status-bar-in-macos

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!