【问题标题】:Issue with pyqt signals when used with decorators与装饰器一起使用时 pyqt 信号问题
【发布时间】:2026-02-20 05:10:01
【问题描述】:

当使用 UI 元素的 pyqt 信号(例如带有修饰方法的按钮)时,信号似乎不起作用。请在下面找到最小可重现代码。

import sys
from PyQt5.QtWidgets import (QWidget, QToolTip, QPushButton, QApplication)
from PyQt5.QtGui import QFont

def ui_decorator(target_func):
    def call(self, *args, **kwargs):
        print("Init.")
        ret_code = target_func(self, *args, **kwargs)
        print("Deinit.")
        return ret_code
    return call

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        QToolTip.setFont(QFont('SansSerif', 10))
        self.setToolTip('This is a <b>QWidget</b> widget')
        btn = QPushButton('Button', self)
        btn.setToolTip('This is a <b>QPushButton</b> widget')
        btn.clicked.connect(self.button_action)
        btn.resize(btn.sizeHint())
        btn.move(50, 50)

        self.setGeometry(300, 300, 300, 200)
        self.setWindowTitle('Tooltips')
        self.show()

    @ui_decorator
    def button_action(self):
        print("Button Clicked")

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

在上面的代码中,如果单击按钮,则对 button_action 函数的调用将失败,并显示以下消息:TypeError: button_action() takes 1 positional argument but 2 were given。但是当我不使用装饰器 (ui_decorator) 时,代码可以正常工作,即使它仍然只需要 1 个位置参数。

谢谢

【问题讨论】:

    标签: python pyqt pyqt5


    【解决方案1】:

    当 PyQt 调用一个插槽(可以是任何可调用的)时,它不仅仅是“调用”它。为了实现忽略附加插槽参数的行为,它分析可调用对象并仅传递适当数量的参数。通过让装饰器返回一个接受任意数量参数的可调用对象并将所有参数简单地传递给实际函数,您可以使这种机制变得无用。

    你有两个选择:

    • 调整您的装饰器,使其返回一个接受正确数量参数的可调用对象(我怀疑这相当困难)。
    • 在您自己的装饰器上使用PyQt pyqtSlot decorator。该装饰器定义了槽的签名并使上述行为再次起作用,因为 PyQt 知道槽没有参数:
    @pyqtSlot()
    @ui_decorator
    def button_action(self):
        print("Button Clicked")
    

    【讨论】: