2014-10-17 32 views
2

在裝飾有不同裝飾器的函數前面使用QtCore.pyqtSlot裝飾器有一個奇怪的問題。下面的問題證明了這個問題。它用一個按鈕實例化一個簡約的Qt程序。在按下按鈕時,發生以下情況:(1)信號'signal_A'和'signal_B'分別連接到實例method_A和method_B。 (2)發出signal_A和signal_B信號。在另一個裝飾器之前使用PyQt4的'pyqtSlot'裝飾器的奇怪行爲

(3)預計行爲是method_A和method_B被執行。然而,事實證明,signal_A和signal_B觸發method_A或method_B(奇怪的是,這是非確定性的,並且對於程序的每次運行都是不同的!)。

這似乎是一個錯誤。

當使用PyQt4綁定到Qt時,問題就出現了。使用PySide時不存在(使用' - pyside'參數進行驗證)。我在Python 2和3以及Windows和Linux都看到了這個問題。

該問題似乎與'@QtSlot'和方法定義之間插入裝飾器有關。測試程序中的'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) 

回答

2

使用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(特別參見名稱參數的說明)中有明確說明,並且肯定不是錯誤。

+0

謝謝,這非常有用。我仍然有點困惑,爲什麼我在程序的不同運行中觀察到不同的行爲。你有什麼主意嗎? – reddish 2014-10-17 19:11:13

+0

@reddish。我不知道。我無法重現那種行爲 - 對我而言,無論如何總是調用'method_B'。真的有關係嗎? – ekhumoro 2014-10-17 19:32:52

相關問題