我正在使用 Qt/PySide 开发一个 GUI,其中包含许多处理各种小部件的单独类。每个小部件管理按钮和其他用户输入之间的信号。我发现自己必须重用代码来在方法函数开始时阻止小部件信号,然后在最后释放信号。我决定尝试编写一个通用装饰器来为我做这件事。
我已经搜索过SO,并尝试在使用装饰器的经验很少的情况下尽我所能地实现这一点,所以我对我的解决方案不满意。
我的问题是,编写一个可以访问和运行该类中遵循明确格式的方法的通用装饰器的最佳方法是什么?我的方法到底是不是一个好方法?
为了清楚起见,以下是我的代码与重复代码的样子(为了简洁起见,我删除了一些代码):
class WidgetController(...):
def __init__(...):
self.widget.myWidget.currentIndexChanged.connect(reactToChange)
def reactToChange(...):
self.widget.myWidget.blockSignals(True) # Repetetive line...
...
self.widget.myWidget.blockSignals(False)
def anotherFunction(...):
self.widget.anotherWidget.blockSignals(True)
...
self.widget.anotherWidget.blockSignals(False)
我想要类似以下的内容:
class WidgetController(...):
@blockSignals(myWidget)
def reactToChange(...):
...
@blockSignals(anotherWidget, alsoBlockThisWidget)
def anotherFunction(...):
...
我开发了一个装饰器(在 here 、 here 和 here 的帮助下),但我犹豫是否要展示它,因为它感觉非常笨重。它使用 self
,我不明白它以及嵌套函数中的 exec
,并且需要将小部件名称作为字符串传递,但它似乎确实有效。这是:
class BlockSignals(object):
def __init__(self, *toBeBlocked):
self.toBeBlocked = toBeBlocked
def __call__(self, f):
toBeBlocked = self.toBeBlocked
def wrapped_f(self, *args):
for b in toBeBlocked:
exec 'self.widget.' + b + '.blockSignals(False)' in locals()
f(self, *args)
for b in toBeBlocked:
exec 'self.widget.' + b + '.blockSignals(False)' in locals()
return wrapped_f
用法:
class WidgetController(...):
@BlockSignals("myWidget")
def reactToChange(...):
...
@BlockSignals("anotherWidget", "alsoBlockThisWidget")
def anotherFunction(...):
正如你所看到的,它并不漂亮。我希望能够摆脱字符串解析,摆脱 exec
,理清令人困惑的 self
并能够通过传递来实现它它是实际的小部件对象@BlockSignals(self.widget.myWidget)。不幸的是我已经达到了我的能力极限,有人能帮忙吗?
最佳答案
您正在寻找getattr :
import functools
def blockSignals(*widgetnames):
def decorator(func):
@functool.wraps(func)
def method(self, *args, **kwargs):
widgets = [getattr(self.widget, name) for name in widgetnames]
for widget in widgets:
widget.blockSignals(True)
result = func(self, *args, **kwargs)
for widget in widgets:
widget.blockSignals(False)
return result
return method
return decorator
class WidgetController(...):
def __init__(...):
self.widget.myWidget.currentIndexChanged.connect(reactToChange)
@blockSignals('myWidget')
def reactToChange(...):
...
@blockSignals('anotherWidget', 'alsoBlockThisWidget')
def anotherFunction(...):
...
您必须传递小部件的名称,而不是小部件本身,因为这些方法是在定义类时定义的,而不是在类定义时定义的>instance 被实例化。实例 self
和 self.widget
中的实际小部件在类定义时不存在。
functools.wraps装饰器将原始函数的名称及其文档字符串复制到装饰函数中。
关于python - 装饰器来代替重复的面向对象代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17146510/