我正在尝试使用Growl Python绑定(来自Growl存储库的Growl.py v0.7)来编写一个小应用程序。目前缺少的一个功能是发送给Python的点击通知。
我知道在Objective-C中,当用户点击通知时,它会向正在运行的应用程序发送一个触发器。我想用Python绑定做类似的事情。当用户点击通知时,我想让Python程序在浏览器中打开一个URL(或以另一种方式处理事件)。
关于如何实现它的任何想法?
更新:感谢合成器配件提供了一个很有前途的解决方案,我认为它适用于Lion。不幸的是,我开始从Mac淡出,所以我不再做很多Mac编程了。虽然,我做了一些调试,因为它仍然没有在Snow Leopard上工作,这就是为什么:
答案 0 :(得分:1)
这是你想要的吗?
#!/usr/bin/env python
#
# pyGrr!
#
# This code was originally found @
# http://www.cocoaforge.com/viewtopic.php?f=6&t=13359&p=91992&hilit=pyobjc+growl
# It is (to the best of our knowledge) the work of user 'tooru' on the same
# website.
#
# I make no claim to this code, all I did was get it working with PyObjC on Lion
# reformatted it a bit and added some more verbose explanations of what the script
# does. To be honest, I haven't touched pyobjc in a couple years and I was amazed
# that it still works! Even more amazed that I was able to get this example working
# in about 20 minutes.
#
# Great job tooru!
#
# I have verified this code works with the following combination of
# packages / versions
#
# * OSX Lion 10.7.3
# * Python 2.7
# * Growl 1.3
# * Growl SDK 1.3.1
#
#
# - Nathan Ramella nar@hush.com (http://www.remix.net)
##################################################################################
import objc
from Foundation import *
from AppKit import *
from PyObjCTools import AppHelper
import time
import sys
import os
myGrowlBundle = objc.loadBundle(
"GrowlApplicationBridge",
globals(),
bundle_path = objc.pathForFramework(
'/Library/Frameworks/Growl.framework'
)
)
class MenuMakerDelegate(NSObject):
"""
This is a delegate for Growl, a required element of using the Growl
service.
There isn't a requirement that delegates actually 'do' anything, but
in this case, it does. We'll make a little menu up on the status bar
which will be named 'pyGrr!'
Inside the menu will be two options, 'Send a Grr!', and 'Quit'.
Send a Grr! will emit a growl notification that when clicked calls back
to the Python code so you can take some sort of action - if you're that
type of person.
"""
statusbar = None
state = 'idle'
def applicationDidFinishLaunching_(self, notification):
"""
Setup the menu and our menu items. Getting excited yet?
"""
statusbar = NSStatusBar.systemStatusBar()
# Create the statusbar item
self.statusitem = statusbar.statusItemWithLength_(NSVariableStatusItemLength)
self.statusitem.setHighlightMode_(1) # Let it highlight upon clicking
self.statusitem.setToolTip_('pyGrr!') # Set a tooltip
self.statusitem.setTitle_('pyGrr!') # Set an initial title
# Build a very simple menu
self.menu = NSMenu.alloc().init()
menuitem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
'Send a Grr!',
'rcNotification:',
''
)
self.menu.addItem_(menuitem)
# Default event
menuitem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
'Quit',
'terminate:',
''
)
self.menu.addItem_(menuitem)
# Bind it to the status item
self.statusitem.setMenu_(self.menu)
def rcNotification_(self,notification):
"""
This is run when you select the 'Send a Grr!' menu item. It
will lovingly bundle up a Grr and send it Growl's way.
"""
print "Sending a growl notification at", time.time()
GrowlApplicationBridge.notifyWithTitle_description_notificationName_iconData_priority_isSticky_clickContext_(
"Grr! - I'm a title!",
"This is where you put notification information.",
"test1",
None,
0,
False,
"this ends up being the argument to your context callback"
)
class rcGrowl(NSObject):
"""
rcGrowl registers us with Growl to send out Grrs on behalf
of the user and do 'something' with the results when a
Grr has been clicked.
For additional information on what the what is going on
please refer to the growl dox @
http://growl.info/documentation/developer/implementing-growl.php
"""
def rcSetDelegate(self):
GrowlApplicationBridge.setGrowlDelegate_(self)
def registrationDictionaryForGrowl(self):
"""
http://growl.info/documentation/developer/implementing-growl.php#registration
"""
return {
u'ApplicationName' : 'rcGrowlMacTidy',
u'AllNotifications' : ['test1'],
u'DefaultNotifications' : ['test1'],
u'NotificationIcon' : None,
}
# don't know if it is working or not
def applicationNameForGrowl(self):
"""
Identifies the application.
"""
return 'rcGrowlMacTidy'
#def applicationIconDataForGrowl(self):
"""
If you wish to include a custom icon with the Grr,
you can do so here. Disabled by default since I didn't
want to bloat up this up
"""
#icon = NSImage.alloc().init()
#icon = icon.initWithContentsOfFile_(u'remix_icon.tiff')
#return icon
def growlNotificationWasClicked_(self, ctx):
"""
callback for onClick event
"""
print "we got a click! " + str(time.time()) + " >>> " + str(ctx) + " <<<\n"
def growlNotificationTimedOut_(self, ctx):
"""
callback for timing out
"""
print "We timed out" + str(ctx) + "\n"
def growlIsReady(self):
"""
Informs the delegate that GrowlHelperApp was launched
successfully. Presumably if it's already running it
won't need to run it again?
"""
print "growl IS READY"
if __name__ == "__main__":
# Both 'growlnotify' and this script seem to have the following
# error after emitting a Grr!
#
# Error Domain=GCDAsyncSocketErrorDomain Code=4 "Read operation timed out"
# UserInfo=0x7fa444e00070 {NSLocalizedDescription=Read operation timed out}
#
# So, we redirect stderr to /dev/null so that it doesn't muck up
# the output of this script. Some folks say upgrading Growl fixes it,
# others still have the problem. Doesn't seem to make much of a difference
# one way or another, things still seem to work regardless.
fp = os.open('/dev/null', os.O_RDWR|os.O_CREAT, 0o666)
dupped = os.dup(2)
os.dup2(fp, 2)
# set up system statusbar GUI
app = NSApplication.sharedApplication()
delegate = MenuMakerDelegate.alloc().init()
app.setDelegate_( delegate )
# set up growl delegate
rcGrowlDelegate=rcGrowl.new()
rcGrowlDelegate.rcSetDelegate()
AppHelper.runEventLoop()