I have a basic signal/slot code where I want to emit a signal in one widget, and connect that signal in another widget.
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from PyQt5.QtWidgets import QMainWindow, QAction, QApplication, QWidget, QPushButton, qApp, QLabel, QHBoxLayout, QVBoxLayout, QSplitter, QFileDialog
from PyQt5.QtGui import QIcon, QPixmap, QPainter, QImage
from PyQt5.QtCore import QSize, Qt, pyqtSignal, QObject
import sys, random
import qdarkstyle
from os import path
class SignalFactory(QObject):
selectedTextureToLoad = pyqtSignal()
class Application(QMainWindow):
def __init__(self):
super().__init__()
self.signals = SignalFactory()
self.mainArea = MainArea()
# Option 1 - Uncomment below / Works but strong coupling between widgets
# self.signals.selectedTextureToLoad.connect(self.mainArea.doSomeStuff)
self.setCentralWidget(self.mainArea)
self.setGeometry(300, 300, 800, 400)
self.show()
def emitStuff(self):
print("Emitting...")
self.signals.selectedTextureToLoad.emit()
class MainArea(QWidget):
def __init__(self):
super().__init__()
self.signals = SignalFactory()
# Option 2 - Uncomment below / Does not work
#self.signals.selectedTextureToLoad.connect(self.doSomeStuff)
def doSomeStuff(self):
print("Receiving...")
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Application()
ex.emitStuff()
sys.exit(app.exec_())
If I uncomment Option 1, the code works, and the signal is received. However, there is a coupling between the two widgets. In this case it's fine, because one widget is the parent of the other and naturally keeps track of its child. But in a more complex scenario, that means keeping track of many widgets just to configure the signals, which is not great.
If I uncomment Option 2, the code does not work, and the console only displays "Emitting...". It's kind of annoying, since this is in my opinion the cleanest way to configure signal in one place and emit it from another place, without introducing coupling.
Am I missing something fundamental here ?
EDIT:
If you modify the code like this, by adding the returnASignal
function
from PyQt5.QtWidgets import QMainWindow, QAction, QApplication, QWidget, QPushButton, qApp, QLabel, QHBoxLayout, QVBoxLayout, QSplitter, QFileDialog
from PyQt5.QtGui import QIcon, QPixmap, QPainter, QImage
from PyQt5.QtCore import QSize, Qt, pyqtSignal, QObject
import sys, random
import qdarkstyle
from os import path
def returnASignal():
print('Returning a signal')
return pyqtSignal()
class SignalFactory(QObject):
selectedTextureToLoad = returnASignal()
class Application(QMainWindow):
def __init__(self):
super().__init__()
self.signals = SignalFactory()
self.mainArea = MainArea()
# Option 2 - Uncomment below / Works but strong coupling between widgets
# self.signals.selectedTextureToLoad.connect(self.mainArea.doSomeStuff)
self.setCentralWidget(self.mainArea)
self.setGeometry(300, 300, 800, 400)
self.show()
def emitStuff(self):
print("Emitting...")
self.signals.selectedTextureToLoad.emit()
class MainArea(QWidget):
def __init__(self):
super().__init__()
self.signals = SignalFactory()
# Option 1 - Uncomment below / Does not work
self.signals.selectedTextureToLoad.connect(self.doSomeStuff)
def doSomeStuff(self):
print("Receiving...")
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Application()
ex.emitStuff()
sys.exit(app.exec_())
When running it the console displays this:
Returning a signal
Emitting...
"Returning a signal" is printed only once and not twice, which shows that although there are multiple SignalFactory
instances, they all share the same selectedTextureToLoad
object. This is definitely a proper static class member and not an instance variable. Therefore, the signal object is the same everywhere, and I still don't understand why Option 2 does not work.