Lorem Ipsum
Lorem Ipsum

Reputation: 4534

What's the difference between QtCore.Signal and SIGNAL?

I'm reading Rapid GUI programming with Python and Qt. It was published in 2008, prior to the API change intoduced in PyQt4.5.

What was SIGNAL() and how is it different from the PySide Signal() and PyQt's pyqtSignal() classes?

I can't find any old documentation. However, I see lots of the old-style syntax for connecting signals to slots:

self.connect(self, SIGNAL('valueChanged(int)'), my_slot)

It's not clear to me if SIGNAL is a function, classmethod, or class, if it was used to define new signals, and if it's still supported. There's a lot of old code out there I'm not sure how to interpret.

Upvotes: 1

Views: 4457

Answers (1)

eyllanesc
eyllanesc

Reputation: 243993

SIGNAL is a method that implements the Qt macro SIGNAL that is present in PyQt4 / PySide / PySide2 and no longer in pyqt5.

To understand the difference, you must understand the different connection syntax in Qt:

  • Old style:
connect(sender, SIGNAL(foo_signal(parameters)), receiver, SLOT(foo_slot(parameters))
  • New style:
connect(sender, &Sender_Klass::foo_signal, receiver, &Receiver_Klass::foo_slot)

The main differences are:

  • Verification of the existence of signals and slots in run-time or compile-time,
  • If the slots do not necessarily have to be QSlot but they can be any function.

For more information read here. Currently the second method is recommended as it foresees bugs.


Considering the above, the last releases of PyQt4 and from the beginning PyQt5 implements the new declaration syntax through pyqtSignal (Signal in PySide) that allows to declare signals.

In conclusion, SIGNAL is a remnant of the old connection style that still works in PySide2 but no longer in PyQt5, this method allows to create signals in runtime. On the other hand, pyqtSignal or Signal allow us to declare signals when creating the class.

The following example shows the differences:

from PySide2 import QtCore


class Foo(QtCore.QObject):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.connect(self, QtCore.SIGNAL("foo()"), self.slot_foo)

    def slot_foo(self):
        print("bye")
        QtCore.QCoreApplication.quit()


    def send_signal(self):
        self.emit(QtCore.SIGNAL("foo()"))

app = QtCore.QCoreApplication([])
foo = Foo()
QtCore.QTimer.singleShot(1000, foo.send_signal)
app.exec_()
from PySide2 import QtCore


class Foo(QtCore.QObject):
    foo = QtCore.Signal()

    def __init__(self, parent=None):
        super().__init__(parent)

        self.foo.connect(self.slot_foo)

    def slot_foo(self):
        print("bye")
        QtCore.QCoreApplication.quit()

    def send_signal(self):
        self.foo.emit()


app = QtCore.QCoreApplication([])
foo = Foo()
QtCore.QTimer.singleShot(1000, foo.send_signal)
app.exec_()

Going into more detail, the first method creates signals in runtime that modify the QMetaObject and that can cause an issue since the QMetaObject has a predetermined order that can be taken into account in some optimization. Therefore, when using the first method, it will launch the warning and it fails:

from PySide2 import QtCore


class Foo(QtCore.QObject):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.connect(self, QtCore.SIGNAL("bar()"), self.slot_bar)
        self.connect(self, QtCore.SIGNAL("foo()"), self.slot_foo)

    def slot_foo(self):
        print("ok")
        self.emit(QtCore.SIGNAL("foo()"))

    def slot_bar(self):
        print("bye")
        QtCore.QCoreApplication.quit()

    def send_signal(self):
        print("send foo signal")
        self.emit(QtCore.SIGNAL("foo()"))

app = QtCore.QCoreApplication([])
foo = Foo()
QtCore.QTimer.singleShot(1000, foo.send_signal)
app.exec_()
main.py:8: RuntimeWarning: 

*** Sort Warning ***
Signals and slots in QMetaObject 'Foo' are not ordered correctly, this may lead to issues.
1  Signal bar()
2  Slot   slot_bar()
3! Signal foo()

  self.connect(self, QtCore.SIGNAL("foo()"), self.slot_foo)
main.py:8: RuntimeWarning: 

*** Sort Warning ***
Signals and slots in QMetaObject 'Foo' are not ordered correctly, this may lead to issues.
1  Signal bar()
2  Slot   slot_bar()
3! Signal foo()
4! Slot   slot_foo()

  self.connect(self, QtCore.SIGNAL("foo()"), self.slot_foo)
main.py:26: RuntimeWarning: 

*** Sort Warning ***
Signals and slots in QMetaObject 'Foo' are not ordered correctly, this may lead to issues.
1  Signal bar()
2  Slot   slot_bar()
3! Signal foo()
4! Slot   slot_foo()
5! Slot   send_signal()

  app.exec_()
send

So, therefore, it is not recommended to use SIGNAL currently in the recent versions of PySide2 and PyQt5, but rather the new syntax.

Upvotes: 5

Related Questions