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

问题描述 投票:1回答:1

我在查找如何使用pyobjc在Catalina上发送本地通知时遇到了一些困难。

我看到的关闭示例是这样的:PyObjC "Notifications are not allowed for this application"

python objective-c macos macos-catalina pyobjc
1个回答
1
投票

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

您首先注意到的是,函数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)]时调用)

    肯定还有更多,但是不幸的是,我不认为该通知会被撤消或忽略。

  3. 使用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()在交互后正在运行,因此无法发送带有回调的多个通知,因为程序将在与第一个通知交互后停止执行。

  • 尽管我可以显示Other button(并给它文本),但是我找不到找到回调的方法。这就是为什么我在上面的代码块中没有解决这个问题。我可以给它输入文本,但实际上它是一个虚拟按钮,因为它无法执行任何操作。
  • 我仍应使用此解决方案吗?

    如果您希望通过回调通知,由于我已解决的问题,您可能不应该这样做。

    如果您只想显示关于警告用户的通知,是的。

    其他解决方案

    PYNCterminal-notifier的包装。但是,它们都在2018年接受了最后一次提交。Alerter似乎是terminal-notifier的后继者,但没有Python包装器。

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

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

    © www.soinside.com 2019 - 2024. All rights reserved.