返回介绍

进阶篇:使用 PyObjC 发送通知

发布于 2025-04-20 18:52:18 字数 4444 浏览 0 评论 0 收藏

越来越多的开发者选择 Mac 作为个人电脑,并用它来完成开发工作。OS X 平台上有一些独有的 Python 标准库和第三方库,它们很有用但是却鲜为人知。

当邮件客户端收到新邮件或在日历中出现新预约时,开发者会收到通知。这种通知机制也广泛应用在第三方团队协作工具(如 Slack)软件上。事实上,我们还能把通知机制应用于日常开发中,比如一个操作耗时较长,我们希望尽早了解到操作的反馈以便继续进行其他步骤,而不必一直在屏幕上等待它完成,可以切换屏幕,或者让它后台运行,我们去做其他的工作,待它完成后给我们发一个通知就好了。

Cocoa 是苹果公司为 Mac OS X 所创建的原生面向对象的编程环境,Cocoa 应用一般使用 Objective-C 开发,Python 开发者可以使用 PyObjC 这样的桥接技术编写 Cocoa 应用。Mac 自带的 Python 版本已经内置了 PyObjC,直接使用即可。

我们来实现这个发通知的应用:

import Foundation
from AppKit import NSImage
import objc
 
NSUserNotification=objc.lookUpClass('NSUserNotification')
NSUserNotificationCenter=objc.lookUpClass('NSUserNotificationCenter')

ICON_PATH= '~/web_develop/chapter14/section5'


def notify(title, subtitle, info_text, delay=0, sound=False, userInfo={},
           is_error=False):
    icon=NSImage.alloc().initByReferencingFile_(
        os.path.join(ICON_PATH, 'douban.png'))
 
    notification=NSUserNotification.alloc().init()
    notification.setTitle_(title)
    notification.setSubtitle_(subtitle)
    notification.setInformativeText_(info_text)
    notification.setUserInfo_(userInfo)
    notification.set_identityImage_(icon)
    if is_error:
        error_image=NSImage.alloc().initByReferencingFile_(
            os.path.join(ICON_PATH, 'error.png'))
        notification.setContentImage_(error_image)
    if sound:
        notification.setSoundName_('NSUserNotificationDefaultSoundName')
    notification.setDeliveryDate_(
        Foundation.NSDate.dateWithTimeInterval_sinceDate_(
            delay, Foundation.NSDate.date()))
    NSUserNotificationCenter.defaultUserNotificationCenter(
    ).scheduleNotification_(notification)

setTitle_方法设置标题;setSubtitle_方法设置子标题;setInformativeText_设置内容;set_identityImage_会添加自定义图标;notify 接受 sound 参数,如果为 True,在发通知的时候会带系统默认声音。发送的通知效果如图 14.1 所示。

图 14.1 发送通知的效果

如果这是一个错误类型的通知(is_error=True),会通过 setContentImage_方法额外地在通知文本右侧添加一张错误的图片,效果如图 14.2 所示。

图 14.2 错误类型的通知

这是一个命令行的程序,应该使用命令行选项与参数的解析器模块 argparse,它在 Python 2.7 的时候进入标准库,用来替代 optparse 模块。我们来编写发送通知的命令行接口:

unicode=lambda s:s.decode('utf-8')
parser=argparse.ArgumentParser(
    description='Send a custom notification on OS X.')
parser.add_argument('-t', '--title', help='title of notification',
                    default='', type=unicode)
parser.add_argument('-s', '--subtitle', help='subtitle of notification',
                    default='', type=unicode)
parser.add_argument('-m', '--message', help='message of notification',
                    default='', type=unicode)
parser.add_argument('--sound', help='include audible alert',
                    action='store_true', default=True)
args=parser.parse_args()
notify(args.title, args.subtitle,
       args.message, sound=args.sound)

notify 需要接收 unicode 类型的值,但是 argparse 的解析结果是字符串,argparse 可以灵活地支持解析参数的值的类型转换,所以使用了“unicode=lambda s:s.decode('utf-8')”之类的语句就可以用“type=unicode”,这样解析的结果就是 unicode 类型的值了。

除了发送通知,还需要把 notification.py 封装成系统命令,让发通知智能一些。举个例子,现在要执行:

> python runscript.py -c 4

封装之后就成为了:

> notify python runscript.py -c 4

这个 notify 命令接收要执行的命令及参数,通过 subprocess.call 的返回值判断执行是否成功,来决定通知的标题、类型(是否是错误类型的通知),通知内容为执行的命令。再者还要考虑发送通知的脚本的位置,因为可能开发调试是在虚拟机或者远程服务器上进行的,这就需要把消息传回到 Mac 电脑上。实现这样的功能的方法很多,比如在 Mac 搭建 Web 服务,RPC 服务或者使用 SSH 服务。本节将展示在虚拟机里面通过 ssh 连接 Mac 宿主机发通知的方式:

#!/usr/bin/python
# coding=utf-8
import sys
import subprocess


def notify(title, msg, is_error=False):
    cmd=('ssh user@host '
         '"python/Users/dongweiming/bin/notify.py '
         '-t\'{}\'-m\'{}\'"').format(title, msg)
    if is_error:
        cmd+='--error'
    subprocess.call(cmd, shell=True)


def main():
    cmd=' '.join(sys.argv[1:])
    retcode=subprocess.call(cmd, shell=True)
    if len(cmd)>33:
        cmd=cmd[:30]+'...'
    if retcode:
        title='执行失败'
        is_error=True
    else:
        title='执行成功'
        is_error=False
    notify(title, cmd, is_error=is_error)
 
 
if __name__=='__main__':
    main()

把这个脚本放在$PATH 变量中指定的一个目录下,并添加执行权限:

> mkdir ~/bin
> cp chapter14/section5/notify ~/bin
> chmod+x ~/bin/notify
> export PATH=$PATH: ~/bin

$PATH 变量决定了 Shell 将到哪些目录中寻找命令或程序。现在不必输入这个命令的完整路径,直接输入 notify 就可以了。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。