Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Signal disconnect fail



  • Hello,

    I have a (for me, at least) mystery: I cannot disconnect a Qt signal in PyQt5. Here some code:

    import sys, os, time
    from multiprocessing import Process
    from PyQt5 import QtCore, QtGui, QtWidgets
    from PyQt5.QtWidgets import QApplication
    
    def SigEmitProcess():
        while True:
            print ("SigEmitProcess going to emit sig.")
            SigReceiver.GetSig().emit()
            time.sleep(1.0)             # Sleep one second.
    
    Emitter = Process(target=SigEmitProcess)
    
    class SigReceiverClass(QtCore.QObject):
        NoteFromEmitter = QtCore.pyqtSignal()
        
        def __init__(self):
            QtCore.QObject.__init__(self, parent=None)
            self.NoteFromEmitter.connect(self.NoteReceiver)
            print ("ControlDataHandler A", self.NoteFromEmitter)
    
        def GetSig(self): return self.NoteFromEmitter
    
        def DisconnectSig(self):
            print ("DisconnectSig: ", self.NoteFromEmitter.disconnect())
    
        def NoteReceiver(self): print (" NoteReceiver received sig.")
    
    SigReceiver = SigReceiverClass()          # Create a receiver
    
    class MainWindow(QtWidgets.QMainWindow):
        def __init__(self):
            QtWidgets.QMainWindow.__init__(self, parent=None)
            self.StartButton = QtWidgets.QPushButton(self)
            self.StartButton.clicked.connect(SigReceiver.DisconnectSig)
    
    qApp = QApplication(sys.argv)
    MainWin = MainWindow()                  # Create an object for the main window.
    MainWin.show()
    Emitter.start()                         # Fork: set off the Emitter process.
    qApp.exec_()                            # Enter Qt:s event handling loop
    
    Emitter.join()                          # Wait for Emitter process to stop
    sys.exit()                              # Back to operating system
    
    

    So, it's another process sending a signal that should get disconnected when a button is pressed. And this is the output:

    $ python3.8 SigDisconnect.py
    ControlDataHandler A <bound PYQT_SIGNAL NoteFromEmitter of SigReceiverClass object at 0x7f3bbe1d40d0>
    SigEmitProcess going to emit sig.
    NoteReceiver received sig.
    SigEmitProcess going to emit sig.
    NoteReceiver received sig.
    SigEmitProcess going to emit sig.
    NoteReceiver received sig.
    DisconnectSig: None <-- Here I pressed the one and only button.
    SigEmitProcess going to emit sig.
    NoteReceiver received sig.
    SigEmitProcess going to emit sig.
    NoteReceiver received sig.
    SigEmitProcess going to emit sig.
    NoteReceiver received sig.

    As you can see, NoteReceiver happily continues to receive the signal...

    Somehow, I have feeling that this is a problem because I am using multiprocessing, not threads ? But I cannot use threads - I need to run the signal emitting process on another core.

    BUT: If I use this code for the SigEmitProcess, then it works !:

    def SigEmitProcess():
        i = 0
        while True:
            print ("SigEmitProcess going to emit sig.")
            SigReceiver.GetSig().emit()
            if i == 3: SigReceiver.GetSig().disconnect()
            i += 1
            time.sleep(1.0)             # Sleep one second.
    

    $ python3.8 SigDisconnectV3.py
    ControlDataHandler A <bound PYQT_SIGNAL NoteFromEmitter of SigReceiverClass object at 0x7fbf7d8660d0>
    SigEmitProcess going to emit sig.
    NoteReceiver received sig.
    SigEmitProcess going to emit sig.
    NoteReceiver received sig.
    SigEmitProcess going to emit sig.
    NoteReceiver received sig.
    SigEmitProcess going to emit sig.
    NoteReceiver received sig.
    SigEmitProcess going to emit sig.
    SigEmitProcess going to emit sig.
    SigEmitProcess going to emit sig.
    SigEmitProcess going to emit sig.
    SigEmitProcess going to emit sig.
    ...
    Why must I disconnect from the other process ? I can of course program around this, but I don't understand squat...
    Both Qt and PyQt versions are 5.15.1.


  • Lifetime Qt Champion

    Hi,

    Since you require the use of multiprocess, then you should use the interprocess facility that comes with it.

    You are right in the part where there's a copy of everything done. I currently have no idea how everything works so fine mixing Qt's signals and slots and Python's multiprocess. Therefore, I highly recommend that you make proper use of interprocess communication rather than rely on the luck that it worked until now.

    Also, calling a signal from a class externally like that is rather a sign of design issue. Signals shall only be called from with the class it belongs to.



  • @Pernilla Ok, I must answer this myself... :-D

    Now I think I start to understand; the fork copies the whole parent process, signals and all... so the forked (Emitter) process will run independently and has nothing to do with the parent process. That is why the disconnect need to happen from the Emitter process. Right ?

    So, I should use multiprocessing.Event instead...



  • @Pernilla

    from multiprocessing import Process

    We know nothing about this, it is some Python thing. No idea how it behaves, no idea whether it works with signals & slots.

    Unless you can find examples of using this with PyQt5, or ask at riverbank, I would suggest you look at using threads with Qt/PyQt/Python, like others do. Assuming you even need threading in the first place.


  • Lifetime Qt Champion

    Hi,

    Since you require the use of multiprocess, then you should use the interprocess facility that comes with it.

    You are right in the part where there's a copy of everything done. I currently have no idea how everything works so fine mixing Qt's signals and slots and Python's multiprocess. Therefore, I highly recommend that you make proper use of interprocess communication rather than rely on the luck that it worked until now.

    Also, calling a signal from a class externally like that is rather a sign of design issue. Signals shall only be called from with the class it belongs to.



  • @SGaist said in Signal disconnect fail:

    Hi,

    Since you require the use of multiprocess, then you should use the interprocess facility that comes with it.
    You are right in the part where there's a copy of everything done. I currently have no idea how everything works so fine mixing Qt's signals and slots and Python's multiprocess. Therefore, I highly recommend that you make proper use of interprocess communication rather than rely on the luck that it worked until now.

    Yep, this the thing, hence I need to use python multiprocessing.Event, which has nothing to do with Qt...

    What made this confusing is that the Emitter process 'masquerades' as both processes (since it has all code and data to do so), and communicates with itself... :-D


Log in to reply