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. -
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...
-
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.
-
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