pyqt 按钮自动绑定到 on_..._clicked 函数,无需连接或 pyqtSlot

Posted

技术标签:

【中文标题】pyqt 按钮自动绑定到 on_..._clicked 函数,无需连接或 pyqtSlot【英文标题】:pyqt button automatically binds to on_..._clicked function without connect or pyqtSlot 【发布时间】:2019-02-25 07:18:59 【问题描述】:

我已经使用 pyqt5 和 qt-designer 几个星期了。我习惯于使用连接语句将信号连接到处理函数。

昨天我做了一段代码,它也自动将按钮点击信号连接到没有 pyqtSlot 装饰器的处理函数。

将 clicked 信号连接到函数导致单击按钮一次时执行该函数三次。

删除连接语句导致单击按钮一次时执行两次函数。

在函数中添加@pyqtSlot 会导致正常的一次性执行。

将处理函数重命名为 'on_button-name_clicked' 以外的其他名称也会导致正常的一次性执行。

以下代码显示了这种“自动连接”行为。

谁能解释为什么在没有 connect 或 pyqtSlot 的情况下发生这种自动连接双处理程序调用?

提前致谢,梅斯

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_())

上面的代码打印出来

点击

点击

单击按钮一时。

这是示例中的 MainWindowTestButton.ui 文件

<?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】:

为什么要自动连接?

loadUi() 函数在内部调用 connectSlotsByName() 方法,如果某些方法具有以下结构,则该方法会自动连接:

C++:

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

Python:

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

在您的情况下,on_pushButton_clicked 方法满足该要求,因为您有一个对象名为 "pushButton"QWidget(继承自 QObject)按钮:

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

有一个名为clicked的信号。

为什么要两次调用同一个方法?

QPushButton 有一个被重载的 clicked 信号,也就是说有几个同名但参数不同的信号。如果the docs 被审核:

void QAbstractButton::clicked(bool checked = false)

虽然理解起来可能比较复杂,上面的代码相当于:

clicked = pyqtSignal([], [bool])

这类似于:

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

所以总而言之 QPushButton 有 2 个 clicked 信号将连接到 on_pushButton_clicked 函数,所以当你按下按钮时,两个信号将通过调用相同的方法发出,这样 clicked 将被打印 2 次。


连接不考虑前一个信号是否使用相同的方法连接,因此手动连接将有 3 个连接(2 个不带参数单击的信号 [1 个自动单击,另一个手动单击],1 个使用信号点击参数),所以该方法将被调用 3 次。

当您使用装饰器@pyqtSlot 时,您正在指示签名(即参数的类型),因此connect 方法只会与与插槽签名匹配的信号进行连接,因此您不会再看到问题了因为你使用信号没有参数

【讨论】:

你好 eyllanesc。感谢您的出色而清晰的回答。它解释了我所描述的问题的所有方面。 你好@eyllanesc,不知道为什么装饰器在我的情况下似乎不起作用。我有以下代码:self.save_button.clicked.connect(self.on_save),在课堂上的某处有:@pyqtSlot() def on_save(self): print("LOL") 当我单击按钮时,“LOL”显示两次。我的语法不正确还是我遗漏了什么。

以上是关于pyqt 按钮自动绑定到 on_..._clicked 函数,无需连接或 pyqtSlot的主要内容,如果未能解决你的问题,请参考以下文章

使用pyqt4写GUI小程序时一个clicked信号调用三次槽函数

每次单击按钮时移动一个标签 PyQt5

on() 和 click() 的区别

在循环中连接 PyQt4 中的插槽和信号

PyQt4发射信号

关于toggle事件委托的处理