如何在MacOS Catalina pyobjc上创建本地通知? [英] How create local notification on MacOS Catalina pyobjc?

查看:105
本文介绍了如何在MacOS Catalina pyobjc上创建本地通知?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am having some difficulty finding out how to send local notifications on Catalina using pyobjc.

我看到的关闭示例是这样的: PyObjC此应用程序不允许发出通知"

The closes example I have seen is this: PyObjC "Notifications are not allowed for this application"

推荐答案

编辑(2020年6月27日):我创建了一个程序包,该程序包具有在Mac OS 此处.它将使用PyObjC创建和显示通知.如果由于某种原因它不起作用,它将回退到带有osascript的AppleScript通知.我进行了一些测试,发现PyObjC通知可以在某些设备上使用,但不能在某些设备上使用.

Edit (June 27, 2020): I've created a package which has functionality to display notifications on Mac OS here. It will use PyObjC to create and display notifications. If It does not work for whatever reason, it will fallback to AppleScript notifications with osascript. I did some testing and found that the PyObjC notifications work on some devices but don't on some.

答案:

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

I have also been searching for this answer, so I'd like to share what I've found:

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

The first thing you'll notice is that the function notify() defines a class, then returns an instance of it. You might be wonderinf why you can't directly call Notification.send(params). I tried it, but I was getting an error with the PyObjC, which I am unfortunately unable to fix:

# 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是通知将在几秒钟后出现.请注意,如果您设置的延迟时间长于程序的执行时间,则该通知将在程序执行后立即发送.

Explanation

The notify() function takes in quite a few parameters (they are self-explanatory). The delay is how many seconds later the notification will appear. Note that if you set a delay that's longer than the execution of the program, the notification will be sent ever after the program is being executed.

您将看到按钮参数.按钮共有三种类型:

You'll see the button parameters. There are three types of buttons:

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

所有这些if语句都在适当地设置按钮,并且易于说明.例如,如果未提供其他按钮的参数,则不会显示其他"按钮.

All those if statements are setting the buttons appropriately and self explanatory. For instance, if the parameters for the other button are not provided, a Other button will not be shown.

您会注意到的一件事是,如果有按钮,我们将开始控制台事件循环:

One thing you'll notice is that if there are buttons, we are starting the console event loop:

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

这是Python Objective-C的一部分.这不是一个很好的解释,但是它基本上使程序保持打开"状态. (我希望有人能给我更好的解释).

This is a part of Python Objective-C. This is not a good explanation, but it basically keeps program "on" (I hope someone cane give a better explanation).

基本上,如果您指定要按钮,则程序将继续处于打开"状态.直到AppHelper.stopEventLoop()(稍后会对此进行更多介绍).

Basically, if you specify that you want a button, the program will continue to be "on" until AppHelper.stopEventLoop() (more about this later).

现在有一些挂钩"功能:

Now there are some "hook" functions:

  1. userNotificationCenter_didDeliverNotification_(self, notification_center, notification):在传递通知时调用
  2. userNotificationCenter_didActivateNotification_(self, notification_center, notification):在用户与通知进行交互(单击,单击操作按钮或答复)时调用(
  1. userNotificationCenter_didDeliverNotification_(self, notification_center, notification): called when the notification is delivered
  2. userNotificationCenter_didActivateNotification_(self, notification_center, notification): called when the user interacts with the notification (clicks, clicks action button, or reply) (documentation)

肯定还有更多,但是不幸的是,我认为没有被取消或忽略的通知的钩子.

There surely are more, but I do not think there is a hook for the notification being dismissed or ignored, unfortunately.

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

With userNotificationCenter_didActivateNotification_, we can define some callbacks:


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

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

There are different activation types for the types of actions. The text from the reply action can also be retrieved as shown.

最后您还会注意到AppHelper.stopEventLoop().这意味着要结束"消息.程序已执行,因为通知已由用户处理.

You'll also notice the AppHelper.stopEventLoop() at the end. This means to "end" the program from executing, since the notification has been dealt with by the user.

现在让我们解决该解决方案的所有问题.

Now let's address all the problems with this solution.

  1. 如果用户不与通知互动,该程序将永远不会停止.该通知将滑入通知中心,并且可能会或永远不会与之交互.如前所述,通知没有被忽略或通知被取消的钩子,因此我们不能在这样的时间调用AppHelper.stopEventLoop().
  2. 由于AppHelper.stopEventLoop()在交互后正在运行,因此无法发送带有回调的多个通知,因为该程序将在与第一个通知交互后停止执行.
  3. 尽管我可以显示 Other(其他)按钮(并输入文字),但找不到找到回调的方法.这就是为什么我没有在上面的代码块中解决它的原因.我可以给它输入文本,但实际上它是一个虚拟按钮,因为它无法执行任何操作.
  1. The program will never stop if the user does not interact with the notification. The notification will slide away into the notification center and may or may never be interacted with. As I stated before, there's no hook for notification ignored or notification dismissed, so we cannot call AppHelper.stopEventLoop() at times like this.
  2. Because AppHelper.stopEventLoop() is being run after interaction, it is not possible to send multiple notifications with callbacks, as the program will stop executing after the first notification is interacted with.
  3. Although I can show the Other button (and give it text), I couldn't find a way to give it a callback. This is why I haven't addressed it in the above code block. I can give it text, but it's essentially a dummy button as it cannot do anything.

我仍然应该使用此解决方案吗?

如果您要使用回调通知,由于我已解决的问题,您可能不应该这样做.

Should I still use this solution?

If you want notifications with callbacks, you probably should not, because of the problems I addressed.

如果您只想显示通知以提醒用户某些事情,是的.

If you only want to show notifications to alert the user on something, yes.

PYNC Alerter 似乎是终端通知程序的后继者,但是没有Python包装器.

PYNC is a wrapper around terminal-notifier. However, both received their last commit in 2018. Alerter seems to be a successor to terminal-notifier, but there is not Python wrapper.

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

You can also try running applescript to send notifications, but you cannot set callbacks, nor can you change the icon.

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

I hope this answer has helped you. I am also trying to find out how to reliably send notifications with callbacks on Mac OS. I've figured out how to send notifications, but callbacks is the issue.

这篇关于如何在MacOS Catalina pyobjc上创建本地通知?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆