Search code examples
pythonpyqt4decoratorpyside

Decoraters in class object while passing class variables


I am wondering how I can setup a decorator in a class that can accept class variables. The example where I think this could be useful is in Pyside/PyQt where I need to block and unblock signals on widgets at the beginning and ending of functions.

Example:

class Window(QtGui.QMainWindow):
    ....

    def updateList(self, *args):
        self.list.blockSignals(False)
        //do things on self.list
        self.list.blockSignals(True)

Now, there is the potential of a lots of places on different widgets this could be done. Sure I can do this method of blocking and unblocking each item, but that's tedious. And I have to remember to unblock everything when I'm done.

Moving into another step, I can move the blocking into it's own function.

class Window(QtGui.QMainWindow):
    ....

    def updateList(self, *args):
        self.block([self.list])
        //do things on self.list
        self.block([self.list])

    def block(self, items):
        for item in items:
            if item.signalsBlocked():
                item.blockSignals(False)
            else:
                item.blockSignals(True)

Sweet! I can pepper that around my code and feels pretty useful. But I feel like there is some kind of final boss form I'm missing here to make it truely globally useful. Like some kind of decorator.

Using this answer here I can pass variables to my decorator and decorate my function! Decorators with parameters?

def block(items):
    def dec(fn):
        def wrapped(*args, **kwargs):
            for item in items:
                item.blockSignals(True)
            fn(*args, **kwargs)
            for item in items:
                item.blockSignals(False)
        return wrapped
    return dec


class Window(QtGui.QMainWindow):
    ....

    @block([self.list])
    def updateList(self, *args):
        //do things on self.list

Now I feel that could truly be useful! Except, @block([self.list]) has no idea what self is.

So, what I'm attempting to do, is that reasonable to assume I can do this? Is it possible, or am I chasing wild dragons? If it's possible, what's the correct way to attempt this?


Solution

  • You can't refer to the attribute values at class definition time, but you can use their names:

    def block(*attrs):
        def dec(fn):
            @functools.wraps(fn)
            def wrap(self,*args,**kwargs):
                for a in attrs: getattr(self,a).blockSignals(True)
                ret=fn(self, *args, **kwargs)
                for a in attrs: getattr(self,a).blockSignals(False)
                return ret
            return wrap
        return dec
    

    It's a bit ugly to have to write

    class Window(QtGui.QMainWindow):
        @block("list1","list2")
        def updateList(self, *args): # ...
    

    with the string quotes and all, but it works.