pynotify: “关闭”信号回调从未触发

1 投票
2 回答
2015 浏览
提问于 2025-04-17 08:54

我正在尝试用pynotify写一个程序,pynotify是libnotify的Python绑定。我想在某个时刻显示一个重要的通知,并且希望它能随着信息的变化每隔几秒更新一次,直到用户点击关闭它。这个过程都能正常工作,除了处理用户关闭通知后的情况。

为了更新通知,我需要在调用Notification.update之后再调用Notification.show。这没问题,但这就意味着我需要记录用户是否关闭了通知,否则它会一直弹出来。

我想到有两种方法可以做到这一点:

  1. 检测通知是否可见。我还没找到任何方法来判断这一点。
  2. 在通知关闭时存储一个变量,然后在更新之前检查这个变量,再调用Notification.show

第二种方法应该是可行的。我找到的示例代码(似乎没有pynotify的正式文档)让我调用Notification.connect来把一个"closed"信号连接到一个回调函数。我试着这么做了,但回调函数从来没有被触发。

我在网上搜索和调试了很长时间,但没有任何进展。最后我找到了一些随pynotify提供的示例脚本。其中一个脚本为"closed"信号附加了一个处理程序:test-xy-stress.py

它的内容如下:

#!/usr/bin/env python

import pygtk
pygtk.require('2.0')
import gobject
import gtk
import gtk.gdk
import pynotify
import sys
import random

exposed_signal_id = 0
count = 0

def handle_closed(n):
    print "Closing."

def emit_notification(x, y):
    n = pynotify.Notification("X, Y Test",
        "This notification should point to %d, %d." % (x, y))
    n.set_hint("x", x)
    n.set_hint("y", y)
    n.connect('closed', handle_closed)
    n.show()

def popup_random_bubble():
    display = gtk.gdk.display_get_default()
    screen = display.get_default_screen()
    screen_x2 = screen.get_width() - 1
    screen_y2 = screen.get_height() - 1

    x = random.randint(0, screen_x2)
    y = random.randint(0, screen_y2)
    emit_notification(x, y)
    return True


if __name__ == '__main__':
    if not pynotify.init("XY Stress"):
        sys.exit(1)

    gobject.timeout_add(1000, popup_random_bubble)

    gtk.main()

我运行了这个脚本,发现这里的回调函数也从来没有被触发。

这可能只是我系统的问题,还是pynotify或libnotify中有bug呢?如果现在无法解决这个问题,那第一种方法有没有可能实现呢?

我似乎安装的是libnotify 0.4.5和pynotify 0.1.1。

2 个回答

0

试试下面的方法:

添加:

from time import  sleep

然后在你的 emit_notification 的最后加上:

sleep(2)
n.close()

(在和提问者讨论后更新:) 这样应该能触发你的回调!这样你可以和 dbus-monitor 一起测试,看看你的 DBus 服务器是否正常工作。(这个想法来自提问者。)

这可能还不是你真正想要的,但至少可以解释你对未发出信号的困惑。

你可能应该关注一下动作属性。我在这里发现了一些有趣的东西 这里。看起来你可以直接在通知中与用户互动。

因为 pynotify 只是 DBus 通信的一个封装,你也可以尝试一个变通的方法:

from dbus import SessionBus, Interface
from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)

notify_busname = 'org.freedesktop.Notifications'
notify_obj_path ='/org/freedesktop/Notifications'
lbus = SessionBus()

notify_server = lbus.get_object(notify_busname, notify_obj_path)

然后在你的处理函数定义后:

notify_server.connect_to_signal(None, handle_closed)

我还为了测试目的,修改了你的函数签名,以适应 DBus 信号:

def handle_closed(*arg, **kwargs):
2

我也在找同样的东西。我发现有人用 gobject.MainLoop 来代替 gtk.main,这样可以解决问题:linuxquestions.org.

我发现这个方法对我有效:

#!/usr/bin/python

import pynotify
import gobject

def OnClicked(notification, signal_text):
    print '1: ' + str(notification)
    print '2: ' + str(signal_text)
    notification.close()
    global loop
    loop.quit()

def OnClosed(notification):
    print 'Ignoring fire'
    notification.close()
    global loop
    loop.quit()

def Main():
    pynotify.init('ProgramName')

    global loop
    loop = gobject.MainLoop()

    notify = pynotify.Notification('Fire!', 'I\'m just kidding...')
    # optionalm, just changes notification color
    notify.set_urgency(pynotify.URGENCY_CRITICAL)
    # optional, it will expire eventually
    notify.set_timeout(pynotify.EXPIRES_NEVER)

    notify.add_action('You Clicked The Button', 'Remove Fire', OnClicked)
    notify.connect("closed",OnClosed)

    notify.show()

    loop.run()

if __name__ == '__main__':
    Main()

撰写回答