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

前端 未结 1 1261
醉话见心
醉话见心 2020-12-20 05:28

Consider this setup:

Main script, main.py:

import sys
from PyQt5 import uic
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets impor         


        
相关标签:
1条回答
  • 2020-12-20 06:10

    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.

    0 讨论(0)
提交回复
热议问题