pyqt button automatically binds to on_…_clicked function without connect or pyqtSlot

旧街凉风 提交于 2019-12-29 08:21:21

问题


I've been using pyqt5 with qt-designer for some weeks now. I'm used to connect signals to handler functions with connect statements.

Yesterday I made a piece of code that also automatically connected a button clicked signal to a handler function without a pyqtSlot decorator.

  • Connecting the clicked signal to a function resulted in executing the function three times when clicking the button once.

  • Deleting the connect statements resulted in twice executing the function when clicking the button once.

  • Adding @pyqtSlot to the function resulted in normal one time execution.

  • Renaming the handler function to something other than 'on_button-name_clicked' also resulted in normal one time execution.

The following code shows this 'auto connect' behavior.

Can anyone explain why this auto connect with double handler call occurs without connect or pyqtSlot?

Thanks in advance, Mace

from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QMainWindow
from PyQt5.uic import loadUi
import sys

class MainWindow(QMainWindow):

    def __init__(self):

        super().__init__()

        loadUi('MainWindowTestButton.ui', self)
        self.show()

    def on_pushButton_clicked(self):
        print('clicked')


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MainWindow()
    sys.exit(app.exec_())

The above code prints

clicked

clicked

when clicking the button one.

This is the MainWindowTestButton.ui file from the example

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindowTestButton</class>
 <widget class="QMainWindow" name="MainWindowTestButton">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QPushButton" name="pushButton">
    <property name="geometry">
     <rect>
      <x>70</x>
      <y>60</y>
      <width>75</width>
      <height>23</height>
     </rect>
    </property>
    <property name="text">
     <string>PushButton</string>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>21</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

回答1:


Why this auto connect?

The loadUi() function internally calls the connectSlotsByName() method that makes an automatic connection if some method has the structure:

C++:

void on_<object name>_<signal name>(<signal parameters>);

Python:

def on_<object name>_<signal name>(<signal parameters>):

In your case, the on_pushButton_clicked method meets that requirement because you have a QWidget(inherited from QObject) pushButton with objectname "pushButton":

<widget class="QPushButton" name="pushButton">

that has a signal called clicked.

Why do you call the same method twice?

The QPushButton has a clicked signal that is overloaded, that is to say that there are several signals with the same name but with different arguments. If the docs are reviewed:

void QAbstractButton::clicked(bool checked = false)

Although it may be complicated to understand the above code is equivalent to:

clicked = pyqtSignal([], [bool])

And this is similar to:

clicked = pyqtSignal()
clicked = pyqtSignal([bool])

So in conclusion QPushButton has 2 clicked signals that will be connected to the on_pushButton_clicked function, so when you press the button both signals will be emitted by calling both the same method so that clicked will be printed 2 times.


The connections do not take into account if the previous signal was connected with the same method, therefore with your manual connection there will be 3 connections (2 of the signal clicked without arguments [1 automatically and another manually] and 1 with the signal clicked with argument) so the method will be called 3 times.

When you use the decorator @pyqtSlot you are indicating the signature (ie the type of arguments), so the connect method will only make the connection with the signal that matches the slot signature, so you do not see the problem anymore since you use the signal no arguments



来源:https://stackoverflow.com/questions/54861232/pyqt-button-automatically-binds-to-on-clicked-function-without-connect-or-py

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!