我想使用装饰器处理我的PyQt5应用程序中的异常:
def handle_exceptions(func):
def func_wrapper(*args, **kwargs):
try:
print(args)
return func(*args, **kwargs)
except Exception as e:
print(e)
return None
return func_wrapper
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
loadUi("main_window.ui",self)
self.connect_signals()
def connect_signals(self):
self.menu_action.triggered.connect(self.fun)
@handle_exceptions
def fun(self):
print("hello there!")
运行时,出现以下异常:
fun() takes 1 positional argument but 2 were given
输出为False
(装饰器中的打印参数)。
有趣的是,当我在构造函数中直接由fun()
运行self.fun()
函数或对装饰程序进行注释时,一切正常。似乎装饰器添加了一个附加参数,但仅在信号调用该函数时才添加。发生了什么事?
答案 0 :(得分:3)
装饰器不是问题;您只是没有为fun
定义正确数量的参数。
@handle_exceptions
def fun(self, foo):
print("hello there!")
fun
是普通函数; self.fun
是一个绑定方法,该方法在被调用时以fun
作为第一个参数调用self
,并将自己的参数作为附加参数传递给fun
。凡是叫self.fun
的人都会传递 additional 参数,因此fun
的定义必须接受。
答案 1 :(得分:3)
问题是QAction.triggered
发出时会发出布尔值。当插槽接收到信号时,信号参数将在插槽的签名中提交。当插槽的签名比信号短时,多余的参数将被忽略。在您的情况下,非装饰函数除了self外没有其他输入参数,因此当非装饰函数接收到信号时,QAction.triggered
的选中参数将被忽略。但是,修饰函数接收任意数量的参数,因此当修饰函数接收触发信号时,不会忽略选中的参数,这是Python抱怨的额外参数。
答案 2 :(得分:2)
该问题是由于triggered
信号过载导致的,也就是说它具有2个签名:
void QAction::triggered(bool checked = false)
QAction.triggered()
QAction.triggered(bool checked)
因此,默认情况下,它发送一个布尔值(false),该布尔值显然不接受导致错误的“ fun”方法。
在这种情况下,解决方案是使用@pyqtSlot()
装饰器来指示您必须接受的签名:
@pyqtSlot()
@handle_exceptions
def fun(self):
print("hello there!")
答案 3 :(得分:0)
不是装饰者添加了这个参数,而是您正在处理方法的事实。
如果省略了@handle_exceptions
,它的行为将相同。
会发生什么?
self.fun
并将其传递给self.menu_action.triggered.connect()
。self.fun
时,您并没有获得函数对象本身,而是得到了MainWindow.fun.__get__(self)
(或类似的,我不记得确切的语法)给您的内容,所谓的“绑定方法对象”。它是一个包装对象,可以调用该包装对象,并转移对原始函数对象的调用,但将给定的self
附加到参数列表中。
self
和事件对象)调用原始函数。由于尚未准备好使用此附加事件对象,因此会出现上述错误。