Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can we emit signals of a base class in pyside?

Is it possible to inherit signals from a base class and in the derived class connect methods to them? If yes, how?

Working testcase with composition

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.

Failing testcase with inheritance

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?

Salvaged by replacing 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)

like image 881
cfi Avatar asked Oct 16 '13 20:10

cfi


1 Answers

Qt does not support multiple inheritance from QObjects, and the same limitation applies to PySide and PyQt. Although there are sometimes ways to work around this limitation, it is generally a bad idea to attempt to create subclasses with two or more QObject base classes.

For the inheritance of signals, using a simple non-QObject mixin is probably the best way to go - although I think this solution will only work with PySide; for PyQt4, signals can only be defined on QObject subclasses.

UPDATE:

This latter restriction was removed in PyQt5: it is now possible to define properties, signals and slots in classes that do not inherit from QObject.

like image 127
ekhumoro Avatar answered Sep 30 '22 03:09

ekhumoro