Rafe
Rafe

Reputation: 2065

Python PyQt Decorated Method Errors when Triggered

I have used decorators in the past without any issue, but I'm stuck on this one. It seems that when my decorated function is called by a PyQt action, it fails. My theory is that PyQt is somehow wrapping the connected function internally and messing with the signature, or some such thing, but I can't find any evidence of this.

By connected I mean like this:

from PyQt4 import QtGui

# In my widget init...
self.action_my_method = QtGui.QAction("action_my_method", self)
self.action_my_method.triggered.connect(self.my_method)

Here is my simple decorator:

from functools import wraps

def simple_decorator(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        from inspect import getargspec
        print getargspec(fn)

        return fn(*args, **kwargs)
    return wrapper

When I use this to decorate a method without any arguments (def my_method(self)), and execute it directly from my QWidget class, it works fine. When I used a menu to trigger the action, I get:

# Traceback (most recent call last):
#   File "[...].py", line 88, in wrapper
#     return fn(*args, **kwargs)
# TypeError: my_method() takes exactly 1 argument (2 given)

I'm seeing an arg spec:

ArgSpec(args=['self'], varargs=None, keywords=None, defaults=None)

If I add *args to my method, so the signature is my_method(self, *args), the decorator works fine and the arg spec is:

ArgSpec(args=['self'], varargs='args', keywords=None, defaults=None)

I don't see any clues here, since the decorator works fine when called directly, but fails when triggered by the action.

Can anyone shed some light on this, or offer something I can use to debug it further?

Upvotes: 1

Views: 266

Answers (2)

agomcas
agomcas

Reputation: 705

Alternatively you can redefine the signature of every method that may be called via a .connect as:

def my_method(self, qtcallparam=None, **kwargs):

So the method will ignore the first unnamed argument, at the price of use strictly keyword arguments in that method (not such a bad thing)

But the solution of @Rafe is probably cleaner

Upvotes: 0

Rafe
Rafe

Reputation: 2065

This isn't an answer so much as a workaround. I hope there aren't any downsides to this. I thought of it while I was typing out a simple re-direct function. This uses a lambda to do the same thing at run-time:

self.action_my_method.triggered.connect(lambda: self.my_method())

This sufficiently pads my decorated function against being mangled and prevents the error.

Upvotes: 1

Related Questions