2012-01-04 85 views
4

我想做一個簡單的Unix桌面應用程序,它使用pynotify通知系統向用戶顯示一些警報,並允許他們從放置在警報上的按鈕啓動相關應用程序。回調和gtk主循環

下面是相關簡化代碼:

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函數未被觸發的地方。

預先感謝任何幫助;)

+0

gtk.main是一個阻塞調用,在函數中遞歸調用它並不是一個好主意!爲了不添加self.main作爲超時函數,爲什麼不添加代碼來輪詢服務器?不幸的是,我不知道關於pynotify或python):... – 2012-01-04 17:13:46

+0

把輪詢代碼放在它自己的函數中並不能解決問題,因爲通知的東西需要在每個服務器輪詢後立即調用,並且它不能分開來自gtk主循環調用。我想我需要的是一種在未啓動gtk.main()的同一功能中設置回調的方法。我甚至不明白爲什麼從我稱之爲gtk.main()的地方產生影響。 – gentledevil 2012-01-05 16:16:12

回答

3

這裏的問題是,pynotify具有與未引用的對象回調的錯誤。在你的第一個代碼片段中,當main()函數退出時,n會被重新引用(假定在cPython中引用計數)。 不幸的是,這意味着通知對象被銷燬,並且不會調用該操作(儘管您的通知守護程序仍將顯示通知)。

解決此問題的方法是保留對該通知的引用。 最簡單的事情就是把你的第一個片段,n = pynotify.Notification改爲self.last_notification = n = pynotify.Notication

如果你有多個通知,你需要把它們放在一個列表或一個集合中,但是你需要確保它們被刪除,在觸發操作的情況下,以及超時到期。

+0

謝謝,這工作就像一個魅力! – gentledevil 2012-09-10 10:13:32

+0

這也適用於PyGObject和直接使用Notify API。你救了我和其他幾個人頭疼! – Fenikso 2015-09-29 09:01:26