开发者

How to Listen For an Application Launch Event in Mac OS X?

I wrote an AppleScript to mount a SparseBundle image and I want it to be executed exactly when Time Machine launches.

Right now, I periodically check if Time Machine is running with AppleScript using on idle statement:

on idle
    ....
    return <interval开发者_JAVA百科>
end idle

which isn't a robust way. In my opinion adding an event trigger for Application Launch event would be a better approach.

Could you please help?

An Objective-C or Python sample code (I'd prefer Python) is more than welcome.


What you are looking for is, NSDistributedNotificationCenter or NSWorkspace , these cocoa classes post notifications of application events, For workspace, things like application launches, mounting of drives etc.

To do this in python, you need PyObjC, which is basically python bindings for apple's cocoa classes. The documentation is sparse on their website, and there's a reason, as the documentation would be basically be the same as the Apple docs, so they only include the differences between the pyobjc api, and the cocoa API. If you understand how the objective c api is converted to python you are good to go. Check here: http://pyobjc.sourceforge.net/documentation/pyobjc-core/intro.html

I have included an example below which listens for Distributed notifications using python. The code below basically adds an observer and listens for itunes notifications. You could follow a similar structure, but instead add an observer for NSWorkspace. To figure out what you should be listening to, there is an application that will display all notifications going through your system. It's called notification watcher . Use this to figure out what you should be listening to. You could also convert the objective c code to python.

What the code below is doing

  1. Defines a new class which inherits from NSObject, as defined by PyObjC
  2. Defines a method, which gets passed the actual notification and prints it out
  3. Creates an instance of Foundation.NSDistributedNotificationCenter.defaultCenter
  4. Creates an instance of GetSongs
  5. Registers an observer, passing it the class, the method that gets called when a notification is received and which application & event to monitor i.e "com.apple.iTunes.playerInfo"
  6. Runs the event loop,

One thing that will trip you up, accessing attributes (objective c attributes) do not work the same as accessing python attributes. i.e in python you do class_name.att for objective c in python you have to call it like a function i.e from my example below: song.userInfo()

import Foundation
from AppKit import *
from PyObjCTools import AppHelper

class GetSongs(NSObject):
    def getMySongs_(self, song):
        print "song:", song
        song_details = {}
        ui = song.userInfo()
        print 'ui:', ui
        for x in ui:
            song_details[x] = ui.objectForKey_(x)
        print song_details

nc = Foundation.NSDistributedNotificationCenter.defaultCenter()
GetSongs = GetSongs.new()
nc.addObserver_selector_name_object_(GetSongs, 'getMySongs:', 'com.apple.iTunes.playerInfo',None)

NSLog("Listening for new tunes....")
AppHelper.runConsoleEventLoop()

Here's an example of the actual output... (YES BRITNEY ROCKS!, NOT! ;)

song NSConcreteNotification 0x104c0a3b0 {name = com.apple.iTunes.playerInfo; object = com.apple.iTunes.player; userInfo = {
    Album = Circus;
    "Album Rating" = 0;
    "Album Rating Computed" = 1;
    Artist = "Britney Spears";
    "Artwork Count" = 1;
    Genre = Pop;
    "Library PersistentID" = 8361352612761174229;
    Location = "file://localhost/Users/izze/Music/iTunes/iTunes%20Music/Britney%20Spears/Circus/02%20Circus.mp3";
    Name = Circus;
    PersistentID = 4028778662306031905;
    "Play Count" = 0;
    "Play Date" = "2010-06-26 08:20:57 +0200";
    "Player State" = Playing;
    "Playlist PersistentID" = 7784218291109903761;
    "Rating Computed" = 1;
    "Skip Count" = 1;
    "Skip Date" = "2010-06-26 12:20:57 +0200";
    "Store URL" = "itms://itunes.com/link?n=Circus&an=Britney%20Spears&pn=Circus";
    "Total Time" = 192444;
    "Track Count" = 16;
    "Track Number" = 2;
}}
ui {
    Album = Circus;
    "Album Rating" = 0;
    "Album Rating Computed" = 1;
    Artist = "Britney Spears";
    "Artwork Count" = 1;
    Genre = Pop;
    "Library PersistentID" = 8361352612761174229;
    Location = "file://localhost/Users/izze/Music/iTunes/iTunes%20Music/Britney%20Spears/Circus/02%20Circus.mp3";
    Name = Circus;
    PersistentID = 4028778662306031905;
    "Play Count" = 0;
    "Play Date" = "2010-06-26 08:20:57 +0200";
    "Player State" = Playing;
    "Playlist PersistentID" = 7784218291109903761;
    "Rating Computed" = 1;
    "Skip Count" = 1;
    "Skip Date" = "2010-06-26 12:20:57 +0200";
    "Store URL" = "itms://itunes.com/link?n=Circus&an=Britney%20Spears&pn=Circus";
    "Total Time" = 192444;
    "Track Count" = 16;
    "Track Number" = 2;
}
{u'Album Rating Computed': 1, u'Album': u'Circus', u'Rating Computed': True, u'Name': u'Circus', u'Artist': u'Britney Spears', u'Track Number': 2, u'Skip Date': 2010-06-26 12:20:57 +0200, u'Library PersistentID': 8361352612761174229L, u'Player State': u'Playing', u'Total Time': 192444L, u'Genre': u'Pop', u'Playlist PersistentID': 7784218291109903761L, u'Album Rating': 0, u'Location': u'file://localhost/Users/izze/Music/iTunes/iTunes%20Music/Britney%20Spears/Circus/02%20Circus.mp3', u'Skip Count': 1, u'Track Count': 16L, u'Artwork Count': 1, u'Play Date': 2010-06-26 08:20:57 +0200, u'PersistentID': 4028778662306031905L, u'Play Count': 0, u'Store URL': u'itms://itunes.com/link?n=Circus&an=Britney%20Spears&pn=Circus'}


This isn't too tough to do in Objc-C. You can access notifications for all applications through NSWorkspace and NSNotificationCenter. Create an object and register one of it's methods for notifications of type NSWorkspaceDidTerminateApplicationNotification. Something like:

@interface NotificationObserver : NSObject { }
- (void) applicationDidLaunch:(NSNotification*)notification;
@end

@implementation NotificationObserver : NSObject
- (void) applicationDidLaunch:(NSNotification*)notification
{
  // Check the notification to see if Time Machine is being launched.
}
@end

void watch(void)
{
  NSNotificationCenter* notificationCenter
    = [[NSWorkspace sharedWorkspace] sharednotificationCenter];
  NotificationObserver* observer = [[NotificationObserver alloc] init];
  [notificationCenter addObserver:observer
                         selector:@selector(applicationDidTerminate:)
                             name:@"NSWorkspaceDidTerminateApplicationNotification"
                           object:nil];
}


This isn't an answer to your question, but it may solve your problem.

Why not just have your AppleScript launch Time Machine after it mounts the disk image? Then, instead of launching Time Machine directly, always invoke Time Machine via your script. You can even paste the Time Machine icon on to your AppleScript file, and name it "Time Machine" to make the illusion complete. :-)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜