QPushButton.clicked() fires twice when autowired using .ui form

First of all, if the Qt’s signal-slot autowire mechanic is used, the method is used QMetaObject::connectSlotsByName(), so this behavior is due to the translation of that function from C++ to Python, in the case of C++ the QMetaObject::connectSlotsByName() function only connect to slots, but in Python it extended to invoke functions that are not slots.

The problem is that when you click is an overloaded signal, which in the case of C++ allows you to implement using a default parameter:

void QAbstractButton::clicked(bool checked = false)

but in python 2 signatures must be used:

clicked = QtCore.pyqtSignal([], [bool])

Therefore, in the connection made by PyQt to a slot it is used to QMetaObject::connectSlotsByName() that uses the QMetaObject of the object that obtains the signatures using the QMetaMethod, however if it is not a slot you can not get that information so the connection is equivalent to an invocation.


In the case of @pyqtSlot() have the following signature:

@pyqtSlot()
def on_btnSlot_clicked(self):
    print('slotted function call')

The connection made by PyQt the following:

self.btnSlot.clicked.connect(self.on_btnSlot_clicked)

but if the signature of the @pyqtSlot(bool) is:

@pyqtSlot(bool)
def on_btnSlot_clicked(self, checked):
    print('slotted function call', checked)

The connection made by PyQt the following:

self.btnSlot.clicked[bool].connect(self.on_btnSlot_clicked)

But in the case that it is connected to a function that is not a slot, it does not take into account those elements, since it uses the QMetaObject, so it will make the connections with all the possible signatures.

self.btnSlot.clicked[bool].connect(self.on_btnFunc_clicked)
self.btnSlot.clicked.connect(self.on_btnFunc_clicked)

In conclusion:

  • When QMetaObject::connectSlotsByName(...) is used, if it is connected to a @pyqtSlot(...), the signatures are verified. If a signal is connected to a function that is not an @pyqtSlot(...) they will connect with all possible signatures, so if the signal is overloaded with n signatures it will be called n-times.

  • You must use @pyqtSlot() to avoid the previous problem, since apart it has advantages for the rapidity and the saving of resources.

Leave a Comment