maya.cmds - best way to pass a list to a proc when hitting a button?

23 views
Skip to first unread message

Doug Brooks

unread,
Jan 7, 2009, 7:54:16 PM1/7/09
to python_inside_maya
Hello,

I’m having a bit of trouble passing a list to a proc from a ui button
from python in maya. Something like the following….

import maya.cmds as cmds

def someDef(someList):
print someList

#build a ui here and add a button .

cmds.button(command=someDef(someList))

I’m getting # TypeError: Invalid arguments for flag 'command'.
Expected string or function, got NoneType #

What's the best way to pass a list to a proc when hitting a button?

Thanks!!!!
Doug



Any help would be appreciated! Thanks!



Doug Brooks



Matthew Chapman

unread,
Jan 7, 2009, 10:44:39 PM1/7/09
to python_in...@googlegroups.com
       This is a common mistake, the way you have written this python will call 'someDef(someList)' and pass its results to named argument 'command'. There are a couple ways to get this to work the way you would like. My personal choice is this

# Define a function that can take any named or unnamed arguments
# the * tells python to put any unnamed arguments into a list
# the ** tell python to put any names arguments into a dictionary

def someDef(  *args, **kwargs ):
    # call function or have inline code that creates someList
    someList = getSomeList()
    print someList

def buildUI():
     # <insert all stuff that defines window>

     # create button and pass the function it self to the argument
     # command=someDef() # will pass the result where as
     # command=someDef   # passes the actual function to the argument
 cmds.button(command=someDef)

      If you really want to pass  in data in the call as its being defined you should use a lambda. A lambda is an unamed function. I have heard of lambdas being buggy in certain instances because of how they are defined and stored in memory in modules like pyqt. Here is how you could write is.

def buildUI():
    # ' ' ' ' ' ' ' ' ' ' '  \/
 cmds.button(command=lambda *args : someDef(someList ))

If you new the namespace of you function you could always pass it as a string to 'command'.

def buildUI():
    # ' ' ' ' ' ' ' ' ' ' '  \/
 cmds.button(command="myModule.someDef([%s] )" % str(someList))

 

Ofer Koren

unread,
Jan 8, 2009, 2:24:05 AM1/8/09
to python_in...@googlegroups.com
Another way, somewhat similar to lambdas but without those 'buggy' behaviours, is to use a 'callback' object:

class Callback:
    def __init__(self,func,*args,**kwargs):
        self.func = func
        self.args = args
        self.kwargs = kwargs
    def __call__(self,*args, **kwargs):
        return self.func(*self.args, **self.kwargs)


def someDef(theList, someParameter):
       ....

cmds.button(command = Callback(someDef, someList, someParameter = 2))



(FYI - Pymel has a more robust version of this object which supports Undo:  from pymel import Callback)

katrin schmid

unread,
Jan 8, 2009, 3:50:32 AM1/8/09
to python_in...@googlegroups.com
hi,
so the idea is to animate a camera with a hotkey pressed and stop the animation when its released.
Its pretty easy to get the camera going but hard to stop the loop on key release. Maya will finish the "on press" command before ever doing the "on release" which is supoosed to stop the animation. I guess i will need threads. Anyone has experience doing something like that or hints?
Regards,
katrin
--
Psssst! Schon vom neuen GMX MultiMessenger gehört? Der kann`s mit allen: http://www.gmx.net/de/go/multimessenger

Doug Brooks

unread,
Jan 8, 2009, 12:50:31 PM1/8/09
to python_inside_maya
Thanks alot fellas. The someList = getSomeList() was kind of were I
was heading but wanted to make sure my logic is correct.

Cheers!

On Jan 7, 11:24 pm, "Ofer Koren" <kor...@gmail.com> wrote:
> Another way, somewhat similar to lambdas but without those 'buggy'
> behaviours, is to use a 'callback' object:
> class Callback:
>     def __init__(self,func,*args,**kwargs):
>         self.func = func
>         self.args = args
>         self.kwargs = kwargs
>     def __call__(self,*args, **kwargs):
>         return self.func(*self.args, **self.kwargs)
>
> def someDef(theList, someParameter):
>        ....
>
> cmds.button(command = Callback(someDef, someList, someParameter = 2))
>
> (FYI - Pymel has a more robust version of this object which supports Undo:
>  from pymel import Callback)
>

Chris G

unread,
Jan 8, 2009, 6:41:31 PM1/8/09
to python_in...@googlegroups.com
Also you can use functools.partial for this :

from functools import partial

def someDef(theList, someParameter):
    ...

cmds.button(command=partial(someDef, someList, someParameter=2))

John Creson

unread,
Jan 8, 2009, 9:35:04 PM1/8/09
to python_in...@googlegroups.com
Also, you could put a query into the function you are calling that finds the list for itself without relying on the button click to pass in the list.
The query in the function could look into a string variable on a node in the scene, or query an environment variable, or query a text field control on the gui window.

Jakob Welner

unread,
Jan 9, 2009, 4:42:25 AM1/9/09
to Python_inside_maya
As a little sidenote on using lambdas I've found that when executing a procedure through a GUI using lambdas that includes undo-worthy Maya calls, Maya is logging each call seperately in the undo array which can be a pain if you are doing a lot of different things during that procedure.

With some help from Ofer I've found the only workaround is to use a callback object somewhat similar to the one implimented into pymel, but executing it's content through maya.mel.eval().
My callback object looks like this:


class Callback(object):
   
    _callData = None
   
    @staticmethod
    def _doCall():
        (func, args, kwargs) = Callback._callData
        Callback._callData = func(*args, **kwargs)


    def __init__(self, func, *args, **kwargs):

        self.func = func
        self.args = args
        self.kwargs = kwargs
       
    def __call__(self, *args):
        Callback._callData = (self.func, self.args, self.kwargs)
        if __name__ != '__main__':
            mm.eval('python("' + __name__ + '.Callback._doCall()")')
        else:
            mm.eval('python("Callback._doCall()")')
        return Callback._callData


the if-else in __call__() could maybe be replaced by:
mm.eval('python("import sys; sys.modules["%s"].Callback._doCall()")' % __name__)
but I haven't tested yet, so I didn't wanna post it.

If you are not doing any undo-ish maya calls though, this issue isn't visible.
--
JAKOB WELNER
   _____________
   Animator | R&D
   jakob.welner.dk



--
JAKOB WELNER
   _____________
   Animator | R&D
   jakob.welner.dk

Jakob Welner

unread,
Jan 9, 2009, 6:48:17 AM1/9/09
to python_in...@googlegroups.com
I don't know if this is useful but I once made this sticky key script that enables framestep on press and playback on holds of a hotkey where release stops the playback again.
This can pretty much be implimented into any procedure where you need sticky key. Only problem though is that if you want instant feedback on the press procedure, you can't check for a hold prior to that so on a hold, the press command will always get executed.. It can be annoying in some cases.

I don't know if you can use this for anything, as I'm not quite sure what your issue is exactly - haven't had time to test it myself - but it's what I got on the matter.
OMT_hk_delayedStepKeyframe.py

Jakob Welner

unread,
Jan 9, 2009, 6:48:17 AM1/9/09
to python_in...@googlegroups.com
I don't know if this is useful but I once made this sticky key script that enables framestep on press and playback on holds of a hotkey where release stops the playback again.
This can pretty much be implimented into any procedure where you need sticky key. Only problem though is that if you want instant feedback on the press procedure, you can't check for a hold prior to that so on a hold, the press command will always get executed.. It can be annoying in some cases.

I don't know if you can use this for anything, as I'm not quite sure what your issue is exactly - haven't had time to test it myself - but it's what I got on the matter.



On Thu, Jan 8, 2009 at 9:50 AM, katrin schmid <Kat...@gmx.de> wrote:
OMT_hk_delayedStepKeyframe.py

katrin schmid

unread,
Jan 9, 2009, 6:52:57 AM1/9/09
to python_in...@googlegroups.com
hi,
i take a look, thanks for helping.
Regards,
katrin


-------- Original-Nachricht --------
> Datum: Fri, 9 Jan 2009 12:48:17 +0100
> Von: "Jakob Welner" <jakob....@gmail.com>
> An: python_in...@googlegroups.com
> Betreff: [Maya-Python Club:1443] Re: animate a camera with a hotkey pressed

Matthew Chapman

unread,
Jan 9, 2009, 3:35:43 PM1/9/09
to python_in...@googlegroups.com
I don't remember having this issue on 8.5  in windows, but maya.cmds calls each get their own entry in the undo buffer in this code format. I knew there were issues with lambdas but I thought functions and methods worked fine.

class buildUI:
    def __init__(self):
        mc.window( width=150 )
        mc.columnLayout( adjustableColumn=True )
        mc.button( label='Default', command=self.someDef)
        mc.showWindow()
   
    def someDef(self, *args):
        mc.sphere()
        mc.sphere()
        mc.sphere()
        mc.sphere()
buildUI()

I am on 8.5 linux 64, can some confirm that the undo does not work properly on windows?

Matthew Chapman

unread,
Jan 10, 2009, 2:26:47 AM1/10/09
to python_in...@googlegroups.com
I just tried this on a windows machine running 2009, it doesn't undo properly there either. Does this mean the only way to get undos to work is using a mel.eval wrapper or passing a string value to command argument to be evaled?

katrin schmid

unread,
Jan 10, 2009, 5:05:52 AM1/10/09
to python_in...@googlegroups.com
hi,
really nice solution i wouldnt have thought off.
Thanks for sharing.
katrin
Reply all
Reply to author
Forward
0 new messages