回调函数与GTK主循环
我正在尝试制作一个简单的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运行时错误。不过这表明,改变gtk.main()调用的位置确实有影响。
我尝试查看Gtk和Pygtk的文档,了解主循环的使用,但最终没有找到解决办法。
所以我的问题是:正确的做法是什么,背后的逻辑是什么?
总结:如果我不把gtk.main()放在显示通知的同一个函数里,当应该触发时,action_callback函数就不会被调用。由于这个函数需要放在gtk主循环中,我就陷入了gtk主循环自我调用或action_callback函数不被触发的困境。
谢谢大家的帮助;)
1 个回答
这里的问题是,pynotify在处理未被引用的对象时有个bug。在你第一个代码片段中,当main()
函数结束时,n
就不再被引用了(这是基于cPython的引用计数机制)。
不幸的是,这意味着通知对象会被销毁,而相应的操作不会被调用(尽管你的通知守护进程仍然会显示通知)。
解决这个问题的方法是保持对这个通知的引用。最简单的做法是把你第一个代码片段中的n = pynotify.Notification
改成self.last_notification = n = pynotify.Notification
。
如果你有多个通知,你需要把它们放在一个列表或集合里,但你还需要确保在触发操作和超时过期时都能正确删除它们。