1

我正在使用 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(...):
      ...

我已经开发了一个装饰器(在此处此处此处的帮助下),但我不愿展示它,因为它感觉非常笨重。它使用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(...):

如您所见,它并不漂亮。我希望能够摆脱字符串解析,摆脱execs,整理出令人困惑self的 s 并能够通过将实际的小部件对象传递给它来实现它@BlockSignals(self.widget.myWidget)。不幸的是,我已经达到了我的能力极限,有人可以帮忙吗?

4

1 回答 1

2

您正在寻找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(...):
        ...

您必须传递小部件的名称,而不是小部件本身,因为方法是在定义时定义的,而不是在实例化实例时定义的。在类定义时,实例self和实际小部件self.widget不存在。

functools.wraps装饰器将原始函数的名称及其文档字符串复制到装饰函数。

于 2013-06-17T11:52:10.577 回答