2012-01-04 12 views
4

私は、pynotify通知システムを使用してユーザーにいくつかの警告を表示し、アラートに配置されたボタンから関連するアプリケーションを起動できるようにする簡単なUnixデスクトップアプリケーションを作成しようとしています。私は実際に通知部を配置しようとするまで(それが活性化されると、LS /をトリガーする「アクション」ボタンで通知ポップアップを示して)これは正常に動作コールバックと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() 

:ここ

は、関連する単純化されたコードです(私は定期的にサーバーをポーリングして、通知を表示する必要があります)。

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のmainloopを呼び出すか、action_callback関数がトリガされないようにしています。任意の助けを事前に

おかげで;)

+0

gtk.mainはブロッキング呼び出しですが、関数内で再帰的に呼び出すことはお勧めできません。タイムアウト機能としてself.mainを追加するのではなく、サーバーをポーリングするコードを追加するのはなぜですか?残念ながら私はpynotifyやpythonについては全く知らない): –

+0

ポーリングコードを独自の関数に入れても問題は解決しない。なぜなら、すべてのサーバーポーリングの後に通知の内容を呼び出す必要があり、それは分離できないからであるgtkのmainloop呼び出しから。私が必要と思われるのは、gtk.main()が起動されているものと同じ関数でセットアップしないと、コールバックを動作させる方法です。私はgtk.main()を呼び出す場所がここに影響を与える理由を理解していません。 – gentledevil

答えて

3

ここでの問題pynotifyが参照されていないオブジェクトにコールバックしてバグを持っているということです。あなたの最初のスニペットでは、関数が終了したときにnが参照されなくなります(cPythonでの参照カウントを仮定します)。 残念ながら、これは、通知オブジェクトが破棄され、アクションが呼び出されないことを意味します(ただし、通知デーモンは通知を表示します)。

これを回避するには、その通知への参照を保持することです。 最も簡単なことは、最初のスニペットをとり、n = pynotify.Notificationself.last_notification = n = pynotify.Noticationに変更することです。

複数の通知がある場合は、それらをリストまたはセットにスローする必要がありますが、アクションがトリガーされた場合と削除された場合の両方で削除されていることを確認する必要がありますタイムアウトは終了します。

+0

ありがとう、これは魅力のように動作します! – gentledevil

+0

これはPyGObjectとNotify APIの直接使用にも適用されます。あなたは私といくつかの主要な頭痛を救った! – Fenikso