使用.ui表单自动连线时,QPushButton.clicked()会触发两次

时间:2018-11-01 22:33:16

标签: python pyqt pyqt5

考虑此设置:

主脚本 Input: prd-abcd-efgh-i-0dflnk55f5d45df Output: prd-abcd-efgh Tried Splunk Query : index=aws-* (host=prd-abcd-efgh*) | rex field=host "^(?<host>[^.]+)"| dedup host | stats count by host,methodPath

main.py

Qt Designer .ui表单,import sys from PyQt5 import uic from PyQt5.QtCore import pyqtSlot from PyQt5.QtWidgets import QApplication, QMainWindow class MainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.ui = uic.loadUi("mw.ui", self) def on_btnFunc_clicked(self): print('naked function call') @pyqtSlot() def on_btnSlot_clicked(self, bool): print('slotted function call') app = QApplication(sys.argv) win = MainWindow() win.show() sys.exit(app.exec_())

mw.ui

此设置使用Qt的信号槽自动接线机制将按钮单击绑定到相应的回调。为什么裸露的回调被两次调用,而空槽仅被调用一次?

我发现了thisthis,但是这些设置与我的设置有些不同,因为我不是手动绑定信号,也不安装事件过滤器。

我认为这种行为可能是由于具有不同签名的信号绑定到同一插槽而引起的,但是(如果我理解正确的话)是<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>MainWindow</class> <widget class="QMainWindow" name="MainWindow"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>153</width> <height>83</height> </rect> </property> <property name="windowTitle"> <string>MainWindow</string> </property> <widget class="QWidget" name="centralwidget"> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QPushButton" name="btnFunc"> <property name="text"> <string>naked func</string> </property> </widget> </item> <item> <widget class="QPushButton" name="btnSlot"> <property name="text"> <string>slotted func</string> </property> </widget> </item> </layout> </widget> </widget> <resources/> <connections/> </ui> has only one QPushButton信号。

有人可以解释吗?

1 个答案:

答案 0 :(得分:2)

首先,如果使用Qt的信号槽自动接线机制,则使用QMetaObject::connectSlotsByName()方法,因此,此行为是由于该函数从c ++转换为Python所致,对于C ++ QMetaObject :: connectSlotsByName()函数仅连接到插槽,但是在Python中它扩展为调用非插槽的函数。

问题在于,当您单击时是一个重载信号,在C ++的情况下,它允许您使用默认参数来实现:

void QAbstractButton::clicked(bool checked = false)

但在python中必须使用2个签名:

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

因此,在PyQt与插槽的连接中,它用于QMetaObject::connectSlotsByName(),该QMetaObject使用对象的QMetaMethod,该对象使用@pyqtSlot()获取签名,但是如果不是插槽,您无法获取该信息,因此连接等效于调用。


对于@pyqtSlot() def on_btnSlot_clicked(self): print('slotted function call') ,具有以下签名:

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

PyQt建立的连接如下:

@pyqtSlot(bool)

但是如果@pyqtSlot(bool) def on_btnSlot_clicked(self, checked): print('slotted function call', checked) 的签名是:

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

PyQt建立的连接如下:

QMetaObject

但是在连接到不是插槽的功能的情况下,它不考虑这些元素,因为它使用self.btnSlot.clicked[bool].connect(self.on_btnFunc_clicked) self.btnSlot.clicked.connect(self.on_btnFunc_clicked) ,因此它将使用所有可能的签名进行连接

QMetaObject::connectSlotsByName(...)

结论:

  • 使用@pyqtSlot(...)时,如果将其连接到@pyqtSlot(...),则会验证签名。如果信号连接到不是ColA ColB ColV .. ColX ... ... .... ... 的函数,则它们将连接所有可能的签名,因此,如果信号过载了n个签名,则将其称为n次。

  • 您必须使用@pyqtSlot()来避免前面的问题,因为它除了具有快速性和节省资源的优点。