1

I have been reading the section "Connecting Slots By Name" on this PyQt5 documentation page which basically describes new signals and slots functionality. This piece caught my eye:

For example the QtGui.QSpinBox class has the following signals:

void valueChanged(int i);
void valueChanged(const QString &text);

When the value of the spin box changes both of these signals will be emitted.

So I sketched up the following script to test this double call behaviour:

#!/usr/bin/env python3

from PyQt5 import QtCore, QtGui
from PyQt5.QtWidgets import QMainWindow, QSpinBox

class Test(QMainWindow):

    def __init__(self):
        super().__init__()

        self.spb = QSpinBox()
        self.spb.valueChanged.connect(self.onValueChanged)
        self.setCentralWidget(self.spb)

    def onValueChanged(self, x):
        print("QSpinBox: value changed! " + str(x))

if __name__ == "__main__":
    import sys
    from PyQt5.QtWidgets import QApplication

    app = QApplication(sys.argv)
    t = Test()
    t.show()

    sys.exit(app.exec_())

And it looks to me like only one signal is coming through. What am I missing? Please note that I am a complete PyQt noob.

Community
  • 1
  • 1
alisianoi
  • 2,003
  • 3
  • 31
  • 46

2 Answers2

2

You only connected to one of the two signal overloads.

Since you also didn't specify which overload you wanted, a default will be selected - which in this case will be valueChanged(int).

To explicitly select both overloads, you would need to do:

    self.spb.valueChanged[int].connect(self.onValueChanged)
    self.spb.valueChanged[str].connect(self.onValueChanged)
    ...

def onValueChanged(self, x):
    print("QSpinBox: value changed! ", x, type(x))
ekhumoro
  • 115,249
  • 20
  • 229
  • 336
2

Also note that the documentation is talking about slots by name, so you should first add a name to your spinbox.

self.spb.setObjectName("spb")

Then, connect all the slots:

QtCore.QMetaObject.connectSlotsByName(self)

And finally create the slot using the on_widgetName_signalName form.

def on_spb_valueChanged(self, x):
    print("QSpinBox: value changed! " + str(x))

It will be called twice now.

Use the pyqtSlot decorator to indicate which signal you want to receive.

@QtCore.pyqtSlot(int)
def on_spb_valueChanged(self, x):

Or...

@QtCore.pyqtSlot(str)
def on_spb_valueChanged(self, x):

Hope it helps.

cdonts
  • 9,304
  • 4
  • 46
  • 72
  • 1
    But the key point is that the `pyqtSlot` decorator must be used to specify which overload is needed. It's almost always a bug to get multiple signals - see [Why is PyQt Executing my actions 3 times?](http://stackoverflow.com/q/22852094/984421), for instance (and there are probably several other SO questions in a similar vein). – ekhumoro Mar 05 '15 at 20:29
  • @ekhumoro I agree, but the title says "PyQt5: one signal comes instead of two as per documentation". Here's how to receive two signals instead of one, as the documentation explains. I'm just adding some information. – cdonts Mar 05 '15 at 21:08
  • 1
    I think the OP was just testing to see what actually happens. Anyway, I only meant to suggest you might add a little bit about `pyqtSlot`, as this would then complement my answer quite nicely (i.e. showing two different ways to achieve the same thing). – ekhumoro Mar 06 '15 at 00:14