Search code examples
pythongtkgobject

GObject.add_emission_hook only works once


I want to use GObject.add_emission_hook to connect to catch a signal of all instances of a class. It seems to work, but only once. In the minimal example below "signal received" is only printed once, no matter how many times one of the buttons is clicked. Why is that and how can I receive a signal on every click?

Of course in my applications things are more complicated and the receiver (here the class Foo) does not know the objects emitting the signals. Therefore, connecting to the signals of the objects themselves is not possible.

from gi.repository import Gtk
from gi.repository import GObject

class MyWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Hello World")
        vbox = Gtk.VBox()
        self.add(vbox)
        button = Gtk.Button(label="Click Here")
        vbox.pack_start(button, True, True, 0)
        button = Gtk.Button(label="Or There")
        vbox.pack_start(button, True, True, 0)
        self.show_all()

class Foo:

    def __init__(self):
        GObject.add_emission_hook(Gtk.Button, "clicked", self.on_button_press)

    def on_button_press(self, *args):
        print "signal received"


win = MyWindow()
foo = Foo()
Gtk.main()

Solution

  • You should return True from your event handler for callbacks to be triggered on successive events. If you return False (when you are not returning anything, I'm guessing False is returned) then the hook is removed. This can be illustrated with the following example based on your sample:

    from gi.repository import Gtk
    from gi.repository import GObject
    
    class MyWindow(Gtk.Window):
    
        def __init__(self):
            Gtk.Window.__init__(self, title="Hello World")
            vbox = Gtk.VBox()
            self.add(vbox)
            self.connect("destroy", lambda x: Gtk.main_quit())
            button = Gtk.Button(label="Click Here")
            vbox.pack_start(button, True, True, 0)
            button = Gtk.Button(label="Or There")
            vbox.pack_start(button, True, True, 0)
            self.show_all()
    
    class Foo:
        def __init__(self):
            self.hook_id = GObject.add_emission_hook(Gtk.Button, "button-press-event", self.on_button_press)
            GObject.add_emission_hook(Gtk.Button, "button-release-event", self.on_button_rel)
    
        def on_button_press(self, *args):
            print "Press signal received"
            return False # Hook is removed
    
        def on_button_rel(self, *args):
            print "Release signal received"
            # This will result in a warning
            GObject.remove_emission_hook(Gtk.Button, "button-press-event",self.hook_id)
            return True
    
    
    win = MyWindow()
    foo = Foo()
    Gtk.main()
    

    Hope this helps!