当前位置: 首页 > 面试题库 >

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

卫阳炎
2023-03-14
问题内容

考虑以下设置:

脚本main.py

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

Qt Designer .ui表格mw.ui

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

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

我发现这个和这个,但这些设置是从我的有点不一样,因为我手动不与信号况且我安装一个事件过滤器。

我认为可能由于具有不同签名的信号绑定到同一插槽而发生这种现象,但是(如果我理解正确的话)QPushButton
只有一个 clicked()信号。

有人可以解释吗?


问题答案:

首先,如果使用Qt的信号插槽自动接线html" target="_blank">机制,则使用该方法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')

由PyQt建立的连接如下:

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

但是如果的签名@pyqtSlot(bool)是:

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

由PyQt建立的连接如下:

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

但是在连接到不是插槽的功能的情况下,由于它使用QMetaObject,因此不会考虑这些元素,因此它将使用所有可能的签名进行连接。

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

结论:

  • QMetaObject::connectSlotsByName(...)使用时,如果它连接到@pyqtSlot(...)的签名进行验证。如果信号连接到非函数,则@pyqtSlot(...)它们将连接所有可能的签名,因此,如果信号过载了n个签名,则将其称为n次。

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



 类似资料:
  • 我找到了其他相关的问题,但没有一个答案实际上解决了我的问题。这个按钮在IE上很好用,但在Chrome上不行。我尝试移除span元素,将按钮放在任何其他标记之外,但仍然触发了两次。这个temple扩展了一个'base.html'文件,代码位于'main'元素中。 HTML: jQuery:

  • 我有一个为自动化构建配置的docker hub存储库。请参见下图。当我按下“save and trigger build”时,我看到一个错误页面,上面只写着“Oops,there was a error!” 我试着发邮件给码头工人支持,但没有成功 知道为什么会发生这种情况以及如何解决吗?

  • 我真的需要帮助。我使用JUnit(4.6)和Spring(3.0.5)进行单元测试。当我尝试在测试类中自动连接服务对象时,我得到了一个NoSuchBeanDefinitionException。 JUnit代码: applicationContext-service.xml代码: 错误跟踪: 一些日志:

  • 问题内容: 詹金斯似乎并没有自动提出需要从Gerrit进行审查的更改。 我们正在使用Gerrit触发器。 另外,如果我尝试手动触发一些操作,似乎什么也没发生。 Gerrit触发器管理区域中的“控件”似乎并不多…闪烁“正在启动”,“正在停止”或“正在重新启动” 有什么想法我做错了吗? 问题答案: 我只是遇到了同样的问题,这与我的工作中用于匹配分支的默认Gerritt设置有关。在工作中,在Gerrit

  • 问题: 我有一个基于Spree 3.7和Rails 5.2构建的电子商务应用程序,现在我面临turbolink(5.2.0版)问题,在这个问题中,我已经实现了用于向购物车添加项目的javascript,但js方法触发了两次,类似的项目两次被添加到购物车中。 我在标题中加入了以下js 我已经尝试了数据涡轮链接轨道:真/假,但我的问题解决时删除

  • 我正在努力学习一本书名为《SpringMVC初学者指南》的书,我一直在努力创建存储库对象。我不断地得到一个BeanCreationException。不知道我还错过了什么。我想知道是否有人能帮我解决这个问题。 请在下面找到我的代码。谢谢 BeanCreationException XML文件: ProductCrontroller: 产品存储库: InMemoryProductRepository