【问题标题】:Strange behavior using PyQt4's 'pyqtSlot' decorator before another decorator在另一个装饰器之前使用 PyQt4 的“pyqtSlot”装饰器的奇怪行为
【发布时间】:2014-10-17 15:28:03
【问题描述】:

在使用不同装饰器装饰的函数前面使用 QtCore.pyqtSlot 装饰器时,我遇到了一个奇怪的问题。下面的问题演示了这个问题。它用一个按钮实例化一个简约的 Qt 程序。按下按钮时,会发生以下情况:

(1) 信号“signal_A”和“signal_B”分别连接到实例 method_A 和 method_B。

(2) signal_A 和 signal_B 信号被发射。

(3) 预期的行为是method_A和method_B被执行。然而,事实证明,signal_A 和 signal_B 都触发了 method_A 或 method_B(奇怪的是,这是不确定的,并且每次运行程序都不同!)。

这似乎是一个错误。

使用 PyQt4 绑定到 Qt 时会出现此问题。使用 PySide 时不存在(使用“--pyside”参数进行验证)。我在 Python 2 和 3 以及 Windows 和 Linux 中都看到了这个问题。

问题似乎与在“@QtS​​lot”和方法定义之间插入装饰器有关。测试程序中的“null_decorator”应该什么都不做(如果我对装饰器的理解是正确的),但是当我删除它时程序的行为会发生变化。

任何帮助理解这里发生的事情将不胜感激。

#! /usr/bin/env python3

import sys, time

if "--pyside" not in sys.argv:
    # default to PyQt4 bindings
    from PyQt4 import QtCore, QtGui
    QtSignal = QtCore.pyqtSignal
    QtSlot   = QtCore.pyqtSlot
else:
    # alternatively, use PySide bindings
    from PySide import QtCore, QtGui
    QtSignal = QtCore.Signal
    QtSlot   = QtCore.Slot


def null_decorator(f):
    def null_decorator_wrapper(self, *args, **kwargs):
        return f(self, *args, **kwargs)
    return null_decorator_wrapper


class TestClass(QtCore.QObject):

    def __init__(self, *args, **kwargs):
        super(TestClass, self).__init__(*args, **kwargs)

    @QtSlot()
    @null_decorator
    def method_A(self):
        print("method_A() executing!")

    @QtSlot()
    @null_decorator
    def method_B(self):
        print("method_B() executing!")


class DemonstrateProblemButton(QtGui.QPushButton):

    signal_A = QtSignal()
    signal_B = QtSignal()

    def __init__(self, *args, **kwargs):
        super(DemonstrateProblemButton, self).__init__(*args, **kwargs)
        self.clicked.connect(self.on_clicked)

    @QtSlot()
    def on_clicked(self):

        # Create TestClass instance
        instance = TestClass()

        # connect the signals
        self.signal_A.connect(instance.method_A)
        self.signal_B.connect(instance.method_B)

        # emit the signals
        self.signal_A.emit()
        self.signal_B.emit()

def main():

    # instantiate the GUI application
    app = QtGui.QApplication(sys.argv)

    button = DemonstrateProblemButton("Demonstrate Problem")
    button.show()

    return QtGui.QApplication.exec_()


if __name__ == "__main__":
    exitcode = main()
    sys.exit(exitcode)

【问题讨论】:

    标签: python qt pyqt4 decorator signals-slots


    【解决方案1】:

    像这样使用null_decorator 会导致所有插槽都具有相同的名称(即“null_decorator_wrapper”),因此 PyQt 可能无法区分它们。

    在您的示例中有几种方法可以解决此问题。

    首先,您可以确保插槽具有不同的签名:

    @QtSlot()
    @null_decorator
    def method_A(self):
        print("method_A() executing!")
    
    @QtSlot(int)
    @null_decorator
    def method_B(self, foo=0):
        print("method_B() executing!")
    

    其次,您可以明确指定插槽名称:

    @QtSlot(name="method_A")
    @null_decorator
    def method_A(self):
        print("method_A() executing!")
    
    @QtSlot(name="method_B")
    @null_decorator
    def method_B(self, foo=0):
        print("method_B() executing!")
    

    第三,您可以在null_decorator 中自动设置名称:

    def null_decorator(f):
        def null_decorator_wrapper(self, *args, **kwargs):
            return f(self, *args, **kwargs)
        null_decorator_wrapper.__name__ = f.__name__
        return null_decorator_wrapper
    

    PSpyqtSlot 装饰器的行为在PyQt docs 中有明确的说明(具体见name 参数的说明),并且是当然不是错误。

    【讨论】:

    • 谢谢,这很有用。为什么我在程序的不同运行中观察到不同的行为,我仍然有点困惑。你有什么想法吗?
    • @reddish。我不知道。我无法重现这种行为 - 对我来说,method_B 总是被调用,无论如何。这真的很重要吗?
    猜你喜欢
    • 2021-07-11
    • 2018-04-17
    • 2021-12-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-25
    • 2011-09-03
    • 2017-11-16
    相关资源
    最近更新 更多