使用 .ui 表单自动装配时 QPushButton.clicked() 会触发两次

2024-01-15

考虑这个设置:

主要脚本,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 的信号槽自动连线机制将按钮单击绑定到相应的回调。为什么裸回调会被调用两次,而槽回调只按预期调用一次?

I found this https://stackoverflow.com/questions/46747317/when-a-qpushbutton-is-clicked-it-fires-twice and this https://stackoverflow.com/questions/46543792/python-pyqt5-qevent-keypress-executes-double-times但这些设置与我的有点不同,因为我不手动绑定信号,也不安装事件过滤器。

我认为这种行为可能是由于具有不同签名的信号绑定到同一个插槽而发生的,但是(如果我理解正确的话)QPushButton 只有一个 http://doc.qt.io/qt-5/qabstractbutton.html#clicked clicked() signal.

有人可以解释一下吗?


首先,如果使用Qt的signal-slot autowire机制,则使用该方法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) is:

@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)

综上所述:

  • When QMetaObject::connectSlotsByName(...)被使用,如果它连接到@pyqtSlot(...),签名得到验证。如果信号连接到的函数不是@pyqtSlot(...)它们将与所有可能的签名连接,因此如果信号因 n 个签名而过载,它将被调用 n 次。

  • 你必须使用@pyqtSlot() http://pyqt.sourceforge.net/Docs/PyQt5/signals_slots.html#the-pyqtslot-decorator避免了前面的问题,因为它具有快速和节省资源的优点。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 .ui 表单自动装配时 QPushButton.clicked() 会触发两次 的相关文章

随机推荐

  • 如何解决 R Markdown (Knit)“‘closure’不可子集化”?

    我第一次尝试使用 RMarkdown Knit 来生成 pdf 默认文件 文件 gt 新建文件 gt R Markdown 运行良好 它显示编译时生成的 pdf 例如 运行以下代码 r cars summary cars 但是 如果我只是用
  • Meteor collection.insert回调问题

    根据 Meteor 文档 collection insert doc callback callback功能 选修的 如果存在 则使用错误对象作为第一个参数和 id 作为第二个参数进行调用 然后往下 在服务器上 如果您不提供回调 则插入块直
  • Unity单例代码

    我是新来的Unity http unity codeplex com 我正在尝试编写一些 Unity 逻辑来初始化和注册 解析 Email 对象的单例实例 以便它可以在多个其他对象中使用 下面的一个示例是 OperationEntity 因
  • Microsoft.ML 和 Xamarin

    Microsoft ML 在 Xamarin 中工作吗 我已经关注了许多教程和视频https dotnet microsoft com learn ml dotnet https dotnet microsoft com learn ml
  • 如何使用JAVA从html页面获取表格

    我正在开发一个项目 尝试从互联网获取财务报表并在 JAVA 应用程序中使用它们来自动创建比率和图表 我正在使用的网站使用登录名和密码才能进入牌桌 标签是 TBODY 但 html 中还有另外 2 个 TBODY 如何使用 java 将表打印
  • Matplotlib - 添加与轴底部对齐的标签

    我想向图表上的垂直线添加标签 本例中为洋红色线 TEXT TO GO HERE 问题是我不知道 Y 轴的最小值 因为该轴是自动的 并且 text 函数需要 x 和 y 值 我希望能够将文本与图的底部对齐 或者找到最小 Y 轴值 在本例中 m
  • 如何在应用程序中的所有类之间共享数组?

    我想共享一个数组 所有类都可以 获取 和 更改 该数组内的数据 类似于全局数组或多路访问数组 这如何通过 ActionScript 3 0 实现 有几种方法可以解决这个问题 一种是使用全局变量 如 unkiwii 的答案中所建议的 但这在
  • 在终端中运行每个命令后看到“致命:拒绝将 HEAD 指向 refs/ 之外”

    我已经几周没有使用终端了 在运行 Brew Upgrade 来升级 更新我的所有软件包后 我开始在运行每个命令后看到 致命 拒绝将 HEAD 指向 refs 之外 我不太熟悉终端或 Git 所以我不知道这意味着什么 请提供一些建议 场景来解
  • 错误:当我用 gradle 中的实现替换编译时(依赖项)

    我将 Android Studio 从 3 0 1 更新到 3 1 0 但更新后 当我构建我的项目时 它显示2 警告 1 用实现代替编译 编译支持将于 2018 年底结束 2 将 testCompile 替换为 testImplementa
  • 火花作业卡桑德拉错误

    每次我使用 cassandra 连接器在 Spark 中运行 scala 程序时都会收到此错误 Exception during preparation of SELECT count FROM eventtest simpletbl WH
  • 如何将 useRef 与 Typescript/Formik 一起使用?

    我正在通过一个ref属性到我的自定义 FieldInput 中 我使用它来验证表单的 Formik 然而 它给出了一些 Typescript 错误 例如 在我的函数中 const handleSubmitForm values FormVa
  • 错误:找不到模块“index”\n需要堆栈:\n- /var/runtime/index.mjs

    我正在尝试通过 CDK 部署在打字稿中实现的 lambda 我能够成功部署 lambda 但是当我测试它时 出现以下错误 errorType Runtime ImportModuleError errorMessage Error Cann
  • 将图像作为二进制数据写入文本文件 C#

    我需要创建一个文件 将图像作为文本嵌入到某些记录中 我在将图像写入文本时遇到一些问题 我正在做的是将图像作为字节数组从 SQL 数据库 图像类型 收集 然后通过遍历每个字节并将该字节的 ASCII 等效项写入该文件 将该图像写入文本文件 在
  • 尝试比较两个分布

    我在互联网上找到了这段代码 它将正态分布与不同的学生分布进行了比较 x lt seq 4 4 length 100 hx lt dnorm x degf lt c 1 3 8 30 colors lt c red blue darkgree
  • 有人将 WPF 用于真正的 LOB 应用程序吗?

    有人将 WPF 用于真正的 LOB 应用程序吗 我们都看过 WPF 的巧妙演示 展示了映射到 3D 元素的视频 这些看起来很棒 但构成大多数开发人员努力的业务线应用程序的现实世界又如何呢 WPF 仅仅是为了美观吗 当我们讨论它时 聪明人正在
  • 将文本框中的数据显示到 wpf 中的列表视图中

    我有一个 C 项目 XAML 代码
  • 如何阻止无人值守升级升级我自定义安装的 Debian 软件包?

    我自定义编译了我自己的 ffmpeg 版本 并从此包中制作了一个 deb 文件 封装说明如下 control txt Package ffmpeg Version 4 3 ubuntu1804 hwaccel cuda 20200806 A
  • 当用户按回键时取消正在进行的连接

    我一直在搜索这个问题 但没有找到我的具体问题 我明白那个AskyncTask可以使用取消 cancel true 但这仅当我有一个可以检查值的循环时才会发生isCanceled 但我的问题是 我怎样才能取消AsyncTask 即卡在http
  • SQL Server 按组累计总和

    我有一个以下格式的表 SQL Server 2005 dummy id 注册日期 item id 数量 价格 我想添加一个新列 累计 它按 date registered 计算每个 item id 订单的累计总计 如下所示 dummy id
  • 使用 .ui 表单自动装配时 QPushButton.clicked() 会触发两次

    考虑这个设置 主要脚本 main py import sys from PyQt5 import uic from PyQt5 QtCore import pyqtSlot from PyQt5 QtWidgets import QAppl