[PyQt] Decorator weirdness

Iosif Spulber iosif.spulber at gmail.com
Sat Aug 15 12:43:19 BST 2015


Hi,

I am using PyQt4 4.10.4.

I'm a bit puzzled by the pyqtSlot decorator. All I can find in the docs is
that it potentially
improves performance, but I'm witnessing actual different behaviour
depending on how I use it.

1.  The decorator seems to properly disconnect slots when the object is
deleted.

    In the snippet below, a slot of a widget to be deleted is connected to
a button's click.
    If I close the SlotWidget and click the button, without the decorator I
get a:
        "RuntimeError: wrapped C/C++ object of type QLabel has been
deleted".
    With the decorator, the signal is disconnected before the object is
destroyed and the click
    has no effect.

    My conclusion thus is that it's quite important to decorate slots like
this.

        from PyQt4 import QtGui, QtCore

        app = QtGui.QApplication([])

        class SlotWidget(QtGui.QWidget):
            def __init__(self, parent=None):
                QtGui.QWidget.__init__(self, parent)

                self.label = QtGui.QLabel('LABEL', parent=self)
                # Make sure the object is deleted so potential problems
arise
                self.setAttribute(QtCore.Qt.WA_DeleteOnClose)

            # Without this decorator, when this widget is closed and the
button is
            # clicked, you get a RuntimeError.
            @QtCore.pyqtSlot()
            def my_slot(self):
                print 'Label text: %s' % self.label.text()

        class MyWidget(QtGui.QWidget):
            def __init__(self, parent=None):
                QtGui.QWidget.__init__(self, parent)

                self.button = QtGui.QPushButton("Button", parent=self)
                self.slot_widget = SlotWidget(parent=None)
                self.slot_widget.show()

                self.button.clicked.connect(self.slot_widget.my_slot)

        if __name__ == "__main__":
            widget = MyWidget()

            widget.show()
            app.exec_()

2.  Consider this mock example; here, I want to provide some base
functionality for my widgets,
    that involves connecting a signal to a slot. I don't want to inherit
from QObject since multiple
    inheritance from QObject creates all sorts of issues in PyQt4.

    What I notice is that decorating the slot leads to an error:
        "TypeError: connect() failed between timeout() and base_slot()".
    Even weirder, calling the Base constructor before the QWidget one
ensures that the slot
    is called properly.

    So in this case it seems to be better not to decorate the slot, but it
can lead to problems (see 1.).

        from PyQt4 import QtGui, QtCore

        app = QtGui.QApplication([])

        class Base(object):
            def __init__(self, timer):
                self.timer = timer
                self.timer.timeout.connect(self.base_slot)

            # Remove the decorator -> works regardless of __init__ order
            @QtCore.pyqtSlot()
            def base_slot(self):
                print 'Fired!'

        class MyWidget(QtGui.QWidget, Base):
            def __init__(self, timer, parent=None):
                # Switch the order -> works regardless of decorator
                QtGui.QWidget.__init__(self, parent)
                Base.__init__(self, timer)

                self.button = QtGui.QPushButton("Button", parent=self)
                self.button.clicked.connect(self.derived_slot)

            @QtCore.pyqtSlot()
            def derived_slot(self):
                print 'Preparing to fire...'
                self.timer.start(2000)

        if __name__ == "__main__":
            timer = QtCore.QTimer()
            timer.setSingleShot(True)

            widget = MyWidget(timer)

            widget.show()
            app.exec_()

3.  Are slots always supposed to be bound? (i.e., should all the slots have
a self first argument?)

4.  I would expect that explicitly decorating the slot with (C++) types
should provide enough
    information to disambiguate between overloaded signals. E.g., if I have
a slot decorated with
    @QtCore.pyqtSlot(str) and connect it to QComboBox.activated it connects
to the int signal
    by default (I have to connect to QComboBox.activated[str]).

I didn't come up with this stuff for the fun of it, all these issues were
raised as they caused
serious problems in my code (e.g., obscure segfaults). Any explanations
would be greatly welcome.

Thanks,
Iosif
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.riverbankcomputing.com/pipermail/pyqt/attachments/20150815/3b708611/attachment.html>


More information about the PyQt mailing list