I am working on a GUI with PyQt5 and 2 QThreads that would use each their own signals plus a shared signal used to send error code back to the GUI. I know that QThreads are meant to be used with pyqtSignals, but are their behaviour with signals excepting you to emit a shared signal from two different threads? Also, would using a Qmutex on the shared signal ensure that the threads access it at the same time or is it useless when dealing with signals?
I wrote this sample code, which runs properly, but I am not sure to understand how the signals are dealt with:
from PyQt5.QtCore import QObject, pyqtSignal, QThread, QCoreApplication
import time
import sys
class Class2(QThread):
def __init__(self, signal):
super().__init__()
self.signal2 = signal
def run(self):
self.signal2.emit("Class 2 signal emitted")
class Class1(QThread):
def __init__(self, signal):
super().__init__()
self.signal1 = signal
def run(self):
self.signal1.emit("Class 1 signal emitted")
class Action(QObject):
shared_signal = pyqtSignal(str)
def __init__(self):
super().__init__()
class1 = Class1(self.shared_signal)
class2 = Class2(self.shared_signal)
self.shared_signal.connect(self.action)
class1.start()
class2.start()
time.sleep(1)
def action(self, buffer):
print(buffer)
app = QCoreApplication([])
Action = Action()
sys.exit(app.exec_())
Thank you for your help!
The signals themselves are thread-safe since their main task is to enqueue the information and a mutex is used for this.
┌----------------------┐
| |
| QUEUE |
| |
└----------------------┘
▲ ▲ ... ▲ | | |
| | | ▼ ▼ ... ▼
SIGNALS SLOTS
What may not be thread-safe is the connection since it depends on the type of connection. I recommend you read the docs to know what kind of connections are insecure (for example using Qt::DirectConnection
between QObjects that live on different threads).
In your case the connection is safe, on the other hand I see time.sleep unnecessary but that can cause the object to be destroyed before invoking the signal so a possible solution is:
import sys
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QCoreApplication, QObject, QThread
class Class2(QThread):
def __init__(self, signal, parent=None):
super().__init__(parent)
self.signal2 = signal
def run(self):
self.signal2.emit("Class 2 signal emitted")
class Class1(QThread):
def __init__(self, signal, parent=None):
super().__init__(parent)
self.signal1 = signal
def run(self):
self.signal1.emit("Class 1 signal emitted")
class Action(QObject):
shared_signal = pyqtSignal(str)
def __init__(self):
super().__init__()
class1 = Class1(self.shared_signal, self)
class1.finished.connect(class1.deleteLater)
class2 = Class2(self.shared_signal, self)
class2.finished.connect(class2.deleteLater)
self.shared_signal.connect(self.action)
class1.start()
class2.start()
@pyqtSlot(str)
def action(self, buffer):
print(buffer)
def main():
app = QCoreApplication([])
action = Action()
sys.exit(app.exec_())
if __name__ == "__main__":
main()