如何在MacOS Catalina pyobjc上创建本地通知?

2024-06-17 13:36:52 发布

您现在位置:Python中文网/ 问答频道 /正文

我很难找到如何使用pyobjc在Catalina上发送本地通知

我看到的一个例子是: PyObjC "Notifications are not allowed for this application"


Tags: forapplicationnotpyobjcthisnotificationsare例子
1条回答
网友
1楼 · 发布于 2024-06-17 13:36:52

编辑(2020年6月27日):我创建了一个包,它具有在Mac OShere上显示通知的功能。它将使用PyObjC创建和显示通知。如果它因任何原因无法工作,它将返回到带有osascript的AppleScript通知。我做了一些测试,发现PyObjC通知在某些设备上可以工作,但在某些设备上不能工作

回答:

我也一直在寻找这个答案,所以我想与大家分享我的发现:

您会注意到的第一件事是函数notify()定义了一个类,然后返回它的一个实例。您可能想知道为什么不能直接调用Notification.send(params)。我试过了,但PyObjC出现了一个错误,不幸的是我无法修复:

# Error
class Notification(NSObject):
objc.BadPrototypeError: Objective-C expects 1 arguments, Python argument has 2 arguments for <unbound selector send of Notification at 0x10e410180>

现在进入代码:

# vscode may show the error: "No name '...' in module 'Foundation'; you can ignore it"
from Foundation import NSUserNotification, NSUserNotificationCenter, NSObject, NSDate
from PyObjCTools import AppHelper


def notify(
        title='Notification',
        subtitle=None, text=None,
        delay=0,

        action_button_title=None,
        action_button_callback=None,

        other_button_title=None,
        other_button_callback=None,

        reply_placeholder=None,
        reply_callback=None
):

  class Notification(NSObject):
    def send(self):
      notif = NSUserNotification.alloc().init()

      if title is not None:
        notif.setTitle_(title)
      if subtitle is not None:
        notif.setSubtitle_(subtitle)
      if text is not None:
        notif.setInformativeText_(text)

      # notification buttons (main action button and other button)
      if action_button_title:
        notif.setActionButtonTitle_(action_button_title)
        notif.set_showsButtons_(True)

      if other_button_title:
        notif.setOtherButtonTitle_(other_button_title)
        notif.set_showsButtons_(True)

      # reply button
      if reply_callback:
        notif.setHasReplyButton_(True)
        if reply_placeholder:
          notif.setResponsePlaceholder_(reply_placeholder)

      NSUserNotificationCenter.defaultUserNotificationCenter().setDelegate_(self)

      # setting delivery date as current date + delay (in seconds)
      notif.setDeliveryDate_(NSDate.dateWithTimeInterval_sinceDate_(delay, NSDate.date()))

      # schedule the notification send
      NSUserNotificationCenter.defaultUserNotificationCenter().scheduleNotification_(notif)

      # on if any of the callbacks are provided, start the event loop (this will keep the program from stopping)
      if action_button_callback or other_button_callback or reply_callback:
        print('started')
        AppHelper.runConsoleEventLoop()

    def userNotificationCenter_didDeliverNotification_(self, center, notif):
      print('delivered notification')

    def userNotificationCenter_didActivateNotification_(self, center, notif):
      print('did activate')
      response = notif.response()

      if notif.activationType() == 1:
        # user clicked on the notification (not on a button)
        # don't stop event loop because the other buttons can still be pressed
        pass

      elif notif.activationType() == 2:
        # user clicked on the action button
        action_button_callback()
        AppHelper.stopEventLoop()

      elif notif.activationType() == 3:
        # user clicked on the reply button
        reply_text = response.string()
        reply_callback(reply_text)
        AppHelper.stopEventLoop()

  # create the new notification
  new_notif = Notification.alloc().init()

  # return notification
  return new_notif


def main():
  n = notify(
      title='Notification',
      delay=0,
      action_button_title='Action',
      action_button_callback=lambda: print('Action'),
      # other_button_title='Other',
      # other_button_callback=lambda: print('Other'),

      reply_placeholder='Enter your reply please',
      reply_callback=lambda reply: print('Replied: ', reply),
  )
  n.send()


if __name__ == '__main__':
  main()

解释

notify()函数接受相当多的参数(它们是自解释的)。delay是通知出现的秒数。请注意,如果设置的延迟比程序的执行时间长,则将在程序执行后发送通知

您将看到按钮参数。有三种类型的按钮:

  1. 动作按钮:主导动作
  2. 其他按钮:辅助操作
  3. 回复按钮:打开文本字段并接受用户输入的按钮。这在iMessage等消息传递应用程序中很常见

所有这些if语句都适当地设置了按钮,并且不言自明。例如,如果未提供其他按钮的参数,则不会显示其他按钮

您会注意到,如果有按钮,我们将启动控制台事件循环:

      if action_button_callback or other_button_callback or reply_callback:
        print('started')
        AppHelper.runConsoleEventLoop()

这是Python Objective-C的一部分。这不是一个很好的解释,但它基本上保持了程序的“开”(我希望有人能给出更好的解释)

基本上,如果您指定需要一个按钮,程序将继续“打开”,直到AppHelper.stopEventLoop()(稍后将详细介绍这一点)

现在有一些“钩子”功能:

  1. userNotificationCenter_didDeliverNotification_(self, notification_center, notification):在传递通知时调用
  2. userNotificationCenter_didActivateNotification_(self, notification_center, notification):当用户与通知交互时调用(单击、单击操作按钮或回复)(documentation

当然还有更多,但不幸的是,我不认为撤销或忽视通知有什么好处

使用userNotificationCenter_didActivateNotification_,我们可以定义一些回调:


    def userNotificationCenter_didActivateNotification_(self, center, notif):
      print('did activate')
      response = notif.response()

      if notif.activationType() == 1:
        # user clicked on the notification (not on a button)
        # don't stop event loop because the other buttons can still be pressed
        pass

      elif notif.activationType() == 2:
        # user clicked on the action button

        # action button callback
        action_button_callback()
        AppHelper.stopEventLoop()

      elif notif.activationType() == 3:
        # user clicked on the reply button
        reply_text = response.string()

        # reply button callback
        reply_callback(reply_text)
        AppHelper.stopEventLoop()

操作类型有不同的激活类型。还可以检索回复操作中的文本,如图所示

您还将注意到结尾处的AppHelper.stopEventLoop()。这意味着“结束”程序的执行,因为通知已由用户处理

现在,让我们解决这个解决方案的所有问题

问题

  1. 如果用户不与通知交互,程序将永远不会停止。通知将滑入通知中心,并且可能与之交互,也可能永远不会与之交互。正如我前面所说的,没有用于忽略通知或取消通知的钩子,因此我们不能在这样的时间调用AppHelper.stopEventLoop()
  2. 因为AppHelper.stopEventLoop()是在交互之后运行的,所以不可能通过回调发送多个通知,因为程序将在与第一个通知交互之后停止执行
  3. 虽然我可以显示其他按钮(并给它文本),但我找不到方法给它回拨。这就是为什么我没有在上面的代码块中处理它。我可以给它文本,但它本质上是一个虚拟按钮,因为它不能做任何事情

我还应该使用这个解决方案吗

如果需要回调通知,可能不应该,因为我已经解决了这些问题

如果您只想向显示通知,请提醒用户是的,是的

其他解决方案

PYNCterminal-notifier的包装器。然而,这两家公司的最后一次承诺都是在2018年Alerter似乎是终端通知程序的继承者,但没有Python包装器

您也可以尝试运行applescript发送通知,但不能设置回调,也不能更改图标

我希望这个答案对你有所帮助。我还试图找出如何在Mac OS上可靠地通过回调发送通知。我已经知道了如何发送通知,但问题在于回调

相关问题 更多 >