Search code examples
pythonpyside2

Is a signal emit waits for the receiving end to finish?


Does PySide2 signal emit waits for the receiving end to finish? I always thought that emit just sends a message and ends there, but when I wrote this code I can see that isn't

from PySide2.QtCore import QObject, Signal, Slot

class MySignal(QObject):
    data_signal = Signal(int)


class FirstClass:
    def __init__(self):
        self.sig = MySignal()
        self.data_signal = self.sig.data_signal

    def next_line(self):
        self.data_signal.emit(1)

class SecondClass:
    def __init__(self):
        self.count = 0
        self.first_class = FirstClass()
        self.first_class.data_signal.connect(self.get_data)
        self.get_data(1)

    @Slot(int)
    def get_data(self, data):
        print(self.count)

        if not self.count > 100:
            self.first_class.next_line()
            self.count += 1


sec = SecondClass()

I get a long list of 0 (the counter never increases) and at the end this error

>>> RecursionError: maximum recursion depth exceeded while calling a Python object

What's the best to performe this in PySide2, I know the for this example if I just preformed a yield or return I wouldn't have this problem but I want to understand PySide2 signals and slots.


Solution

  • By default the connection type is Qt :: AutoConnection which decides the type of connection depending on which thread the sender and receiver belong to. In this case, they both belong to the same thread, so using the Qt :: DirectConnection connection that will invoke the slot at the same moment that the signal is emitted, that is, your code is equivalent to:

    @Slot(int)
    def get_data(self, data):
        print(self.count)
    
        if not self.count > 100:
            self.get_data(1)
            self.count += 1
    

    And obviously that code is recursive since self.count += 1 never executed and therefore the while loop does not end.

    The solution is to make the execution not direct but an instant later (when the eventloop is executed) for this the connection must be of type Qt::QueuedConnection and use QCoreApplication.

    from PySide2.QtCore import QObject, Signal, Slot, QCoreApplication, Qt
    
    
    class MySignal(QObject):
        data_signal = Signal(int)
    
    
    class FirstClass:
        def __init__(self):
            self.sig = MySignal()
            self.data_signal = self.sig.data_signal
    
        def next_line(self):
            self.data_signal.emit(1)
    
    
    class SecondClass:
        def __init__(self):
            self.count = 0
            self.first_class = FirstClass()
            self.first_class.data_signal.connect(self.get_data, Qt.QueuedConnection)
            self.get_data(1)
    
        @Slot(int)
        def get_data(self, data):
            print(self.count)
    
            if not self.count > 100:
                self.first_class.next_line()
                self.count += 1
    
    
    app = QCoreApplication()
    sec = SecondClass()
    app.exec_()