Custom signal: when do handlers run?

I gather that the way to create custom signals with Python in Gtk 3 is with the GObject.Signal decorator, not with __gsignal__. Consider this simple test program:

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import GObject

class MyClass(GObject.Object):
    @GObject.Signal(flags=GObject.SignalFlags.RUN_FIRST)
    def my_signal(self):
        print('In my_signal')

    def do_my_signal(self):
        print("In do_my_signal")

def on_my_signal(obj):
    print('In on_my_signal', obj)

myclass = MyClass()
myclass.connect('my_signal', on_my_signal)
myclass.emit('my_signal')

When I run this program, I get a response from do_my_signal first and then from on_my_signal. I do not get any output from my_signal (although I do in my actual program). If I change to RUN_LAST then I get output from on_my_signal first and then from do_my_signal.

Does anyone have any general comments on:

  1. When might I want a do_ method? If I need to do something before emitting the signal, could I not just put the necessary statements before the emit statement? The only useful application I can think of is related to handling of events, which do not emanate from a widget and so cannot be connected.

  2. Why might I want to put any statements in a signal other than “pass”? Putting statements there seems equivalent to putting them in a do_ method.

  3. Why is the print statement in my_signal not producing any output in this test program? Here’s the answer: I get output from my_signal only when do_my_signal is not present. Is there a rationale for this behavior?

  4. When does it matter whether I choose RUN_FIRST or RUN_LAST?

Thanks for any comments on these general, philosophical questions.

I can’t really answer the pygobject questions, except that:

  • do_* methods are the equivalent of overriding a virtual function in a sub-class; if you inherit from MyClass and you wish to override the default signal handler, you’re typically supposed to use the do_* variant. This also matches the behaviour when subclassing a GObject-based class defined in C.

  • The GObject.Signal decorator will use the method’s prototype to determine the signature of the signal, as well as its name for emission; if you have logic in the default signal handler, you should put it in the method, and leave the do_* method for subclasses.

This is more of a GObject question, so I can answer it.

Whether to choose RUN_FIRST or RUN_LAST depends on the presence of a default signal handler in your class; if you want your code to run first, and thus ensure that all callbacks will have some state already set up for them, you should use RUN_FIRST. The downside is that, to override the default behaviour, you’ll have to create your own derived type. Conversely, if you want your class to provide a default behaviour at the end of the signal emission chain—for instance, to catch some state that could potentially not be set by handlers connected to the signal from third party code—then you should use RUN_LAST. This has the downside that a third party handler may stop the signal emission chain before your code has a chance to run.

Of course, since nobody but your own code should emit the signals you defined, and since signal emission is synchronous, if you have precise guarantees on the state of your objects prior and following the signal emission, you should write a method that does precisely that, without necessarily relying on the signal emission flags, e.g.:

def emit_my_signal(self):
    self._set_up_state()
    self.emit('my-signal')
    self._tear_down_state()
3 Likes

Very helpful. Thanks again.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.