4

我正在尝试制作一个简单的 Unix 桌面应用程序,它使用 pynotify 通知系统向用户显示一些警报,并允许他们从放置在警报上的按钮启动相关应用程序。

这是相关的简化代码:

import subprocess, pynotify, gobject, gtk

class Notifier():
    def __init__(self):
        pynotify.init('Notifications')
        n = pynotify.Notification("Some stuff")
        n.add_action("action", "Action", self.action_callback)
        n.show()
        gtk.main()

    def action_callback(self, n, action):
        subprocess.Popen(['ls', '/'])

if __name__ == '__main__':
    Notifier()

这工作正常(它显示一个带有“操作”按钮的通知弹出窗口,该按钮在激活时触发 ls /),直到我实际尝试将通知部分放入循环中(我需要定期轮询服务器以获取通知然后显示) .

我试过这个:

import subprocess, pynotify, gobject, gtk

class Notifier():
    def __init__(self):
        pynotify.init('Notifications')
        gobject.timeout_add(0, self.main)
        gtk.main()

    def action_callback(self, n, action):
        subprocess.Popen(['ls', '/'])

    def main(self):
        n = pynotify.Notification("Some stuff")
        n.add_action("action", "Action", self.action_callback)
        n.show()
        gobject.timeout_add(10000, self.main)

if __name__ == '__main__':
    Notifier()

但由于某种原因,单击“操作”按钮时不再调用“action_callback”函数。

看来这是我使用 Gtk 主循环的方式的问题。做这样的事情会使函数实际被触发:

import subprocess, pynotify, gobject, gtk

class Notifier():
    def __init__(self):
        pynotify.init('Notifications')
        self.main()

    def action_callback(self, n, action):
        subprocess.Popen(['ls', '/'])

    def main(self):
        n = pynotify.Notification("Some stuff")
        n.add_action("action", "Action", self.action_callback)
        n.show()
        gobject.timeout_add(10000, self.main)
        gtk.main()

if __name__ == '__main__':
    Notifier()

但当然这不是正确的解决方案,我很快就得到了“超出最大递归深度”的 Python RuntimeError。然而,它表明改变 gtk.main() 调用的位置是有发生的。

我试图查看有关主循环的 Gtk 和 Pygtk 文档,但最终没有找到解决方案。

所以我的问题是:什么是正确的做法,它背后的逻辑是什么?

TL;DR:如果我不将 gtk.main() 放在显示通知的同一函数中,则不会触发 action_callback 函数。由于这个函数需要放在 gtk 主循环中,所以我坚持让 gtk 主循环调用自身或不触发 action_callback 函数。

在此先感谢您的帮助;)

4

1 回答 1

3

这里的问题是 pynotify 在未引用对象上存在回调错误。在您的第一个片段中,当函数退出时,n它会被未引用(假设在 cPython 中进行引用计数) 。main()不幸的是,这意味着通知对象被销毁,并且不会调用该操作(尽管您的通知守护程序仍会显示通知)。

解决方法是保留对该通知的引用。最简单的做法是获取您的第一个片段并更改n = pynotify.Notificationself.last_notification = n = pynotify.Notication.

如果您有多个通知,则需要将它们放入列表或集合中,但是您需要确保它们被删除,无论是在触发操作的情况下,还是在超时到期时。

于 2012-09-09T02:21:32.663 回答