Is it possible to inherit signals from a base class and in the derived class connect methods to them? If yes, how?
Instantiates a MyObject
in a MyWidget
, and in the widget reacts to a signal emitted by the object.
from PySide.QtGui import QApplication, QMainWindow
from PySide.QtCore import QObject, QTimer, Signal
from PySide.QtGui import QLabel
class MyObject(QObject):
sig = Signal()
def __init__(self, parent=None):
super().__init__(parent)
QTimer.singleShot(3000, self.alert)
QTimer.singleShot(5000, self.exit)
def alert(self):
self.sig.emit()
def exit(self):
print('All done')
exit(0)
class MyWidget(QLabel):
def __init__(self, parent=None):
super().__init__(parent)
self.monitor = MyObject(self)
self.monitor.sig.connect(self.update)
def update(self):
print(2)
app = QApplication([])
w = MyWidget()
w.show()
app.exec_()
It is a small but working example that opens a minimal, empty window, the self.monitor
object instantiated by the widget emits a timer signal after 3 and 5 seconds. The first prompts the widget to just print a number to the console, the second signal causes the application to quit.
For inheritance only the widget class is changed to:
class MyWidget(MyObject, QLabel):
def __init__(self, parent=None):
super().__init__(parent)
self.sig.connect(self.update)
def update(self):
print(2)
If this is run in the console nothing is printed but a Segmentation Fault happens. Why? And can this be salvaged?
super()
Interestingly, if both classes are changed to not use super()
, the example works again:
class MyObject(QObject):
sig = Signal()
def __init__(self, parent=None):
QObject.__init__(self, parent)
QTimer.singleShot(3000, self.alert)
QTimer.singleShot(5000, self.exit)
def alert(self):
self.sig.emit()
def exit(self):
print('All done')
exit(0)
class MyWidget(MyObject, QLabel):
def __init__(self, parent=None):
MyObject.__init__(self, parent)
QLabel.__init__(self, parent)
self.sig.connect(self.update)
def update(self):
print(2)
Generally I prefer to use super()
, but maybe I need to reconsider? I've purposefully linked to two controversial articles about the usage of super()
in Python. And now would be interested in how to use super()
properly with pyside, or in explanations why it doesn't work in pyside at all.
Minor update:
When using inheritance and super()
but removing all signal related code the example works in that it does open up the window and does not segfault. So there seems to be some indication that the combination of super()
initialization and signals causes a problem.
Minor update2:
..and when commenting out the self.sig.connect
the window starts up and only segfaults when the 5-second signal fires to exit the application.
(This is Qt 4.8.4, Pyside 1.1.2 on an Ubuntu 13.04 system with a CPython 3.3.1 interpreter)
The only solution - or rather workaround - I could come up with so far that satisfies the criteria (a) inheritance and (b) use of super()
is to prevent the diamond relationship as inspired by a comment by user tcaswell.
For my use case it is essential to retain the inheritance of any consumer classes (for custom widgets). On the other hand it is guaranteed - currently - that all consuming classes will be derived (indirectly) from QObject
. Therefore there's no need to derive MyObject
from QObject
, though this in fact creates a true abstract MixIn class: It is not usable standalone and has interface requirements to consuming classes. I'm not sure if I like it.
Here's the working code:
from PySide.QtGui import QApplication, QMainWindow
from PySide.QtCore import QObject, QTimer, Signal
from PySide.QtGui import QLabel
class MyObject:
sig = Signal()
def __init__(self, parent=None):
super().__init__(parent)
QTimer.singleShot(3000, self.alert)
QTimer.singleShot(5000, self.exit)
def alert(self):
self.sig.emit()
def exit(self):
print('All done')
exit(0)
class MyWidget(MyObject, QLabel):
def __init__(self, parent=None):
super().__init__(parent)
self.sig.connect(self.update)
def update(self):
print(2)
app = QApplication([])
w = MyWidget()
w.show()
app.exec_()