K.Mulier
K.Mulier

Reputation: 9650

PyQt signals: will a fired signal still do its job after a disconnect()?

I'm dealing with an application where many signals get fired after which a reconnect follows. I'll explain in detail how the application works, and also where my confusion starts.

 

1. Reconnecting a signal

In my application, I reconnect signals frequently. I will use the following static function for that, taken from the answer of @ekhumoro (and slightly modified) from this post: PyQt Widget connect() and disconnect()

def reconnect(signal, newhandler):
    while True:
        try:
            signal.disconnect()
        except TypeError:
            break
    if newhandler is not None:
        signal.connect(newhandler)

 

2. The application

Imagine the function emitterFunc(self) looping through a list of objects. Upon each iteration, the function connects mySignal to an object, fires the signal and then disconnects mySignal again at the start of the next iteration step. The fired signal also carries some payload, for example an object Foo().

 
enter image description here

EDIT:

 

3. A mental image of the Signal-Slot mechanism

Over time, I have built up a mental image of how the Signal-Slot mechanism works. I imagine a Signal-Slot engine absorbing all fired signals and putting them in a Queue. Each signal awaits its turn. When time is ready, the engine passes the given signal to the appropriate handler. To do that correctly, the engine has some 'bookkeeping' work to ensure each signal ends up in the right slot.

enter image description here

 

4. Behavior of the Signal-Slot engine

Imagine we're at the nth iteration step. We connect self.mySignal to object_n. Then we fire the signal with its payload. Almost immediately after doing that, we break the connection and establish a new connection to object_n+1. At the moment we break the connection, the fired signal probably didn't do its job yet. I can imagine three possible behaviors of the Signal-Slot engine:

 

5. My questions

My first question is pretty obvious by now. What is the correct Signal-Slot engine behavior? I hope it is the third option.

As a second question, I'd like to know to what extent the given mental image is correct. For example, can I rely on the signals getting out of the Queue in order? This question is less important - it's certainly not vital to my application.

The third question has to do with time efficiency. Is reconnecting to another handler time-consuming? Once I know the answer to the first question, I will proceed building the application and I could measure the reconnection time myself. So this question is not so vital. But if you know the answer anyway, please share :-)

Upvotes: 3

Views: 723

Answers (1)

p-a-o-l-o
p-a-o-l-o

Reputation: 10077

I would start with your second question, to say that your mental image is partially correct, because a queue is involved, but not always. When a signal is emitted, there are three possible ways of calling the connected slot(s) and two of them use the event queue (a QMetaCallEvent is instantiated on the fly and posted with QCoreApplication's method postEvent, where the event target is the slot holder, or the signal receiver, if you prefer). The third case is a direct call, so emitting the signal is just like calling the slot, and nothing get queued.

Now to the first question: in any case, when the signal is emitted, a list of connections (belonging to the signal emitter) is traversed and slots are called one after the other using one of the three methods hinted above. Whenever a connection is made, or broken, the list will be updated, but this necessarily happens before or after the signal is emitted. In short: there is very little chances to succeed in blocking a call to a connected slot after the signal has been emitted, at least not breaking the connection with disconnect(). So I would mark the [OPTION 3] as correct.

If you want to dig further, start from the ConnectionType enum documentation where the three fundamental types of connection (direct, queued and blocking-queued) are well explained. A connection type can be specified as a fifth argument to QObject's method connect, but, as you'll learn from the above linked docs, very often is Qt itself to choose the connection type that best suits the situation. Spoiler: threads are involved :)

About the third question: I have no benchmark tests at hand to show, so I will give a so called primarily opinion based answer, the kind of answer that starts with IMHO. I think that the signal/slot realm is one of those where the keep-it-simple rules do rule, and your reconnect pattern seems to make things much complicated than they need to be. As I hinted above, when a connection is made, a connection object is appended to a list. When the signal is emitted, all the connected slots will be called somehow, one after the other. So, instead of disconnect/reconnect/emit at each cycle in your loop, why don't just connect all items first, then emit the signal, then disconnect them all?

I hope my (long and maybe tldr) answer helped. Good read.

Upvotes: 3

Related Questions