如何在 Mac OS X 中监听应用程序启动事件?

发布于 2024-10-05 04:20:20 字数 468 浏览 0 评论 0原文

我编写了一个 AppleScript 来挂载 SparseBundle 图像,并且我希望它在 Time Machine 启动时准确执行。

现在,我定期使用 onidle 语句检查 Time Machine 是否正在使用 AppleScript 运行:

on idle
    ....
    return <interval>
end idle

这不是一个可靠的方法。在我看来,为 Application Launch 事件添加事件触发器将是更好的方法。

你能帮忙吗?

非常欢迎 Objective-CPython 示例代码(我更喜欢 Python)。

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>
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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

猫瑾少女 2024-10-12 04:20:20

您正在寻找的是 NSDistributedNotificationCenterNSWorkspace ,这些可可类发布应用程序事件的通知,对于工作区,例如应用程序启动、驱动器安装等。

要在 python 中执行此操作,您需要PyObjC,它基本上是苹果可可类的 python 绑定。他们网站上的文档很少,这是有原因的,因为文档基本上与 Apple 文档相同,所以它们只包含 pyobjc api 和 cocoa API 之间的差异。如果您了解目标 c api 是如何转换为 python 的,那么您就可以开始了。检查此处: http://pyobjc.sourceforge.net/documentation/pyobjc-core/ intro.html

我在下面提供了一个示例,它使用 python 监听分布式通知。下面的代码基本上添加了一个观察者并监听 itunes 通知。您可以遵循类似的结构,但为 NSWorkspace 添加一个观察者。为了弄清楚您应该听什么,有一个应用程序可以显示通过您系统的所有通知。它称为 通知观察程序 。用它来确定你应该听什么。您还可以将 Objective C 代码转换为 Python。

下面的代码在做什么

  1. 定义一个继承自 NSObject 的新类,由 PyObjC 定义
  2. 定义一个方法,该方法获取实际通知并将其打印出来
  3. 创建 Foundation.NSDistributedNotificationCenter.defaultCenter 的实例
  4. 创建 GetSongs 的
  5. 实例注册观察者,向其传递类、收到通知时调用的方法以及哪个应用程序和通知。要监视的事件,即“com.apple.iTunes.playerInfo”
  6. 运行事件循环,

有一件事会让您感到困惑,访问属性(目标 c 属性)与访问 python 属性的工作方式不同。即在 python 中,您在 python 中为目标 c 执行 class_name.att ,您必须像函数一样调用它,即从我下面的示例中: 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()

这是一个示例实际输出...(是的布兰妮摇滚!,不是!;)

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'}

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'}
愚人国度 2024-10-12 04:20:20

这在 Objc-C 中并不太难做到。您可以通过 NSWorkspace 和 NSNotificationCenter 访问所有应用程序的通知。创建一个对象并注册其方法之一以获取 NSWorkspaceDidTerminateApplicationNotification 类型的通知。像这样的东西:

@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 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];
}
一笔一画续写前缘 2024-10-12 04:20:20

这不是您问题的答案,但它可能会解决您的问题。

为什么不让 AppleScript 在安装磁盘映像后启动 Time Machine?然后,不要直接启动 Time Machine,而是始终通过脚本调用 Time Machine。您甚至可以将 Time Machine 图标粘贴到您的 AppleScript 文件中,并将其命名为“Time Machine”以使幻觉变得完整。 :-)

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. :-)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文