Growl Python绑定点击反馈?

时间:2010-06-27 06:37:06

标签: python macos pyobjc growl

我正在尝试使用Growl Python绑定(来自Growl存储库的Growl.py v0.7)来编写一个小应用程序。目前缺少的一个功能是发送给Python的点击通知。

我知道在Objective-C中,当用户点击通知时,它会向正在运行的应用程序发送一个触发器。我想用Python绑定做类似的事情。当用户点击通知时,我想让Python程序在浏览器中打开一个URL(或以另一种方式处理事件)。

关于如何实现它的任何想法?

更新:感谢合成器配件提供了一个很有前途的解决方案,我认为它适用于Lion。不幸的是,我开始从Mac淡出,所以我不再做很多Mac编程了。虽然,我做了一些调试,因为它仍然没有在Snow Leopard上工作,这就是为什么:

Discussion Growl PyObjC not working with PyObjC 2.2b3

Source code

1 个答案:

答案 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()