Обратные вызовы и основной цикл GTK - PullRequest
4 голосов
/ 04 января 2012

Я пытаюсь создать простое настольное приложение 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 не запускается, когда должна.Поскольку эту функцию нужно поместить в mainloop gtk, я застрял с тем, чтобы mainloop gtk вызывал сам себя или функция action_callback не запускалась.

Заранее спасибо за любую помощь;)

1 Ответ

3 голосов
/ 09 сентября 2012

Проблема в том, что в pynotify есть ошибка с обратными вызовами для объектов, на которые нет ссылок.В вашем первом фрагменте n становится без ссылки (при условии подсчета ссылок в cPython) при выходе из функции main().К сожалению, это означает, что объект уведомления уничтожен, и действие не вызывается (хотя ваш демон уведомления будет по-прежнему отображать уведомление).

Обходной путь - сохранить ссылку на это уведомление.Самое простое, что нужно сделать, это взять свой первый фрагмент и изменить n = pynotify.Notification на self.last_notification = n = pynotify.Notication.

Если у вас есть несколько уведомлений, вам нужно будет выбросить их в список или набор, но затемВам нужно будет убедиться, что они удалены, как в случае, когда запускается действие, так и по истечении времени ожидания.

...