command repeatLast

821 views
Skip to first unread message

Rémi Deletrain

unread,
May 22, 2015, 10:24:06 AM5/22/15
to python_in...@googlegroups.com
hi everyone,

I try to make a decorator to add a command in the "repeatlast" python maya command.

Only every time it returns me an error :
// Error: Line 2.91: Invalid use of Maya object "False". //


So I would try to do the same command but mel and it works.
But! the decorator needs a language Python and when I repeat command (G key) I have an error message.
I can shunt the problem with a try / except but I think it's not very clean. I would like to run the Python command.

Here is my code:
def repeat_command(function):
def wrapper(*args, **kwargs):

modules = ''
arguments = args
if [x for x in ['cls', 'self'] if x in function.func_code.co_varnames]:
arguments = args[1:]
modules = '%s.' % args[0]


args_string = ''
if args:
for each in arguments:
args_string = '%s, ' % each


kwargs_string = ''
if kwargs:
for key, item in kwargs.iteritems():
kwargs_string = '%s=%s, ' % (key, item)


ret = function(*args, **kwargs)


repeat_command = '%s%s(%s%s)' % (modules, function.__name__, args_string, kwargs_string)
if not args_string == '' and not kwargs_string == '':
repeat_command = '%s%s(%s, %s)' % (modules, function.__name__, args_string, kwargs_string)

pmc.repeatLast(addCommand='import qdTools\n%s\n' % repeat_command, addCommandLabel='qdCommand')
# try:
# pm.callLastCommand('python("import qdTools;%s")' % repeat_command)
# except:
# pass



return ret
return wrapper


Any one have idea?

damon shelton

unread,
May 22, 2015, 11:46:49 AM5/22/15
to python_in...@googlegroups.com
I believe you have to format the command into a mel formatted string
you are doing it in your commented out version but not in your running code
'python("import qdTools;%s")' % repeat_command

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/16534d51-4209-4ce5-882f-2b8dbf976d38%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Rémi Deletrain

unread,
May 22, 2015, 1:18:35 PM5/22/15
to python_in...@googlegroups.com
For works in mel add this script  in start : 

import maya.mel as mel
import pymel.core as pmc
from pymel.all import mel as pm


mel.eval("""
global proc callLastCommand(string $function)
{
    repeatLast -ac $function -acl "blah-blah....";
}
"""
)


and with mel proedure:

def repeat_command(function):
def wrapper(*args, **kwargs):

modules = ''
arguments = args
if [x for x in ['cls', 'self'] if x in function.func_code.co_varnames]:
arguments = args[1:]
modules = '%s.' % args[0]


args_string = ''
if args:
for each in arguments:
args_string = '%s, ' % each


kwargs_string = ''
if kwargs:
for key, item in kwargs.iteritems():
kwargs_string = '%s=%s, ' % (key, item)


ret = function(*args, **kwargs)


repeat_command = '%s%s(%s%s)' % (modules, function.__name__, args_string, kwargs_string)
if not args_string == '' and not kwargs_string == '':
repeat_command = '%s%s(%s, %s)' % (modules, function.__name__, args_string, kwargs_string)

        # pmc.repeatLast(addCommand='import qdTools\n%s\n' % repeat_command, addCommandLabel='qdCommand')
try:
            pm.callLastCommand('python("import qdTools;%s")' % repeat_command)
        except:

Rémi Deletrain

unread,
May 22, 2015, 1:29:10 PM5/22/15
to python_in...@googlegroups.com
with a command format in mel I have a problem.

The first time you run the script the decorator working properly because we past a python command. Only when you press G key you past a mel command in the decorator and it's don't work

So the command is excecuted properly because it was well implemented, but the decorator give an error message. It is for this reason that there is a try / except the party comment.

Justin Israel

unread,
May 22, 2015, 4:58:28 PM5/22/15
to python_in...@googlegroups.com
I thought it looked pretty limited to have to approach this by formatting the last command into a string, because that would really limit the functionality to having the correct scope, and wouldn't work very well with arguments that don't string format to a useful value.  There is a neat approach in the last comment of this blog post:

http://blog.3dkris.com/2011/08/python-in-maya-how-to-make-commands.html

In that example, instead of trying to save the command to a string, he saves the actual python object to a private module global, and sets the repeat command to point at a simple function callback in the same module. 




--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.

Rémi Deletrain

unread,
May 23, 2015, 6:55:39 AM5/23/15
to python_in...@googlegroups.com
Hi Justin,

Than you for tour reply!
I saw this blog but I have not seen th elast post

Y try Monday,

Rémi Deletrain

unread,
May 25, 2015, 2:53:08 AM5/25/15
to python_in...@googlegroups.com
The script works in the latest comments!

I applied some little changes to match with my tech but otherwise it work fine!

Thank you very much Justin

Justin Israel

unread,
May 25, 2015, 4:14:11 AM5/25/15
to python_in...@googlegroups.com
Nice! If you have a cleaned up version of it that is generic, maybe you can reply with a pastebin or a gist, so that future readers can benefit? The version on that blog post has problematic indenting.

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.

Rémi Deletrain

unread,
May 25, 2015, 4:30:45 AM5/25/15
to python_in...@googlegroups.com
Yes of course!

I past the cleaned script that works for me.
If you have problems with the one made me know. If I find problems with and I corrected them I'll post an update.

It's just a version corrected and with a vocabulary adapted to my scripts. Otherwise it is the same as the link

#-----------------------------------------------------------------
#   REPEAT COMMAND
#-----------------------------------------------------------------

import pymel.core as pmc

_repeat_function = None
_args = None
_kwargs = None


def repeat_command():

    if _repeat_function is not None:
        _repeat_function(*_args, **_kwargs)


    def wrap(function):
        def wrapper(*args, **kwargs):

            global _repeat_function
            global _args
            global _kwargs

            _repeat_function = function
            _args = args
            _kwargs = kwargs

            commandToRepeat = ('python("%s.repeat_command()")' % __name__)

            ret = function(*args, **kwargs)

            pmc.repeatLast(addCommand=commandToRepeat, addCommandLabel=function.__name__)

            return ret
        return wrapper
    return wrap

Justin Israel

unread,
May 25, 2015, 6:56:04 AM5/25/15
to python_in...@googlegroups.com
Thanks for posting that. 

I'm interested to see how you are actually using this version of the code, because it does work differently than the original example. Your version seems to combine the decorator and the callback into a single function which will always attempt to call the saved function, and also create and return a wrapping function. But I don't seem to be able to get it to work very well. I'm assuming you want to use it like this?

@repeat_command()
def test():
    print "foo"

The problem I see is that because you have combined everything into one function, every time a new function is defined that uses that decorator, it will run a previously saved function because it may not be None at the time. Also, the wrapped function can no longer return a value, since it is discarded and a new wrap() function object is returned and printing in the script editor.

I threw together a cleaned up version of the example from the bottom of that blog post:

It keeps the decorator and callback separate, only defines the callback string once, and also catches a potential exception that cmds.repeatLast() might raise, when you are using it to run code within the script editor (which already sets repeatLast to the thing you are executing). 

Justin


--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.

Rémi Deletrain

unread,
May 25, 2015, 8:04:55 AM5/25/15
to python_in...@googlegroups.com
I deliberately remove the possibility of catche an exception to see the various error. The problem of try / except it is difficult to debug with that.

If I understand what you say. The problem in my code is to combine repeat and callback into a single function.
So if I repeated my command I also repeated the decorator. I am wrong?

If that's right, I did a test with a print in my wrapper. When I apply my command once it print correctly. But when I apply the repeat (G key) my print is not enabled.
So I guess it does not raise the decorator but just my command.



This will be useful for repeated small script like that one. It's not my script completely, just a tip for you to try: 


import pymel.core as pmc
from path import yourDecorator

class MyClass():

    #   Snap Position
    @classmethod
    @decorator.undo_chunk
    @decorator.repeat_command()
    def snap_position(cls, driver=None, driven=None, position=True, rotation=True, space='world'):

        #   Get Selection
        if driver is None:
            driver = pmc.selected()[0]
        if driven is None:
            driven = pmc.selected()[1:]


        #   Get Position
        world_position = driver.getTranslation(space=space)
        world_rotation = driver.getRotation(space=space)


        #   Set
        for MNode in driven:

            if position is True:
                MNode.setTranslation(world_position, space=space)

            if rotation is True:
                MNode.setRotation(world_rotation, space=space)



I did not even try on large script. But he'll have to do it to see if it still works properly or not.

Justin Israel

unread,
May 25, 2015, 4:55:07 PM5/25/15
to python_in...@googlegroups.com
Yea, your version was problematic for me when I tested it, because each time the decorator is used, it would run the last command again. Not ideal. Best to keep them separated. One is a decorator. One is a private function that runs the last cached command. 

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.

Rémi Deletrain

unread,
May 26, 2015, 3:09:03 AM5/26/15
to python_in...@googlegroups.com
Hi Justin,

Okay, I understand what you mean. It is true that it is more logical.
I think with other tools does having the command and a single callback function could cause problems.

I had to get lucky the first time I tried this because the decorator does not restarted. But after further testing I actually had this problem.

In any case I always remove the try / except to see all errors and have an easier to debug.

Thank you for this support and this solution!
Reply all
Reply to author
Forward
0 new messages