PDA

View Full Version : [PYTHON] Create Button in for loop


Luckycat
12-15-2010, 11:21 AM
Hello all,

I'm stuck with an UI creation exercise.
I got a list of object that run into a for loop, each object creating a specific button.
For the button themselves, no problem. Where I got stuck is in the creation of command for each button. I'm pretty new to python so after so research I come with setting my UI controls in a class and using the lambda function for the commands.
My script looks like this:

class UI:
def __init__(self):
for obj in wrongTopoObj: #wrongTopoObj is the object list
self.button = cmds.iconTextButton( label=obj, command=lambda *args: self.fromButtonSelect(obj))
def fromButtonSelect(self, *args):
selectCurObj = cmds.select(*args)

The result is ok for button, their label is ok BUT the command is wrong because it's always selecting the last object of the wrongTopoObj list.
Any idea ??
Thanks

Sam

Luckycat
12-15-2010, 01:15 PM
ok problem solved.
Need to force the refresh of the lamba giving it a value directly.
Code looks like this - I make it simplier by the way, no need for extra function.:rolleyes:

self.button=cmds.iconTextButton(label=obj, command=lambda v=obj: cmds.select(v))


check this link
http://mail.python.org/pipermail/tutor/2005-November/043360.html

meuH
12-15-2010, 05:56 PM
you might want to look into functools.partial

I had to do a tool with buttons generated through a loop, and partial seems to be a tiny bit more elegant than lamdba for this use.

I read about it on Ryan's blog over there http://www.rtrowbridge.com/blog/2010/02/maya-python-ui-example/


ok problem solved.
Need to force the refresh of the lamba giving it a value directly.

haggi
12-15-2010, 06:30 PM
In pymel you have a Callback function, it works like this:

command = pm.Callback(function, arguments)

very elegant too.

blazelet
12-18-2010, 08:53 PM
I'm having a similar problem, I have never programmed in any language and am trying to learn Python for Maya. So far I have been able to put together the following :


import maya.cmds as mc
lightList = mc.ls( lights=True )
if mc.window('LightList', exists=True):
mc.deleteUI('LightList', window=True)

lWin = mc.window('LightList', title="Light List", wh=(800,512))

mc.rowColumnLayout( numberOfColumns=4, columnWidth=[(1, 160), (2, 85), (3, 45), (4, 100)] )

for i in lightList:
def lightIntensity(bright):
mc.setAttr(i + '.intensity', float(bright))
print (i+".intensity")

selectedLights = mc.ls(sl=True, lights=True)

if i in selectedLights:
color=[0, 126, 255]
else:
color=[167, 167, 167]

getlightIntensity = mc.getAttr(i + ".intensity")
mc.text( i, backgroundColor=color )
mc.checkBox(label="Enabled", value=lightVisible, onCommand="mc.setAttr( i + '.visibility', 1)", offCommand="mc.setAttr( i + '.visibility', 0)")
mc.text( " intensity " )
mc.floatField(editable=True, value=getlightIntensity, changeCommand=(lightIntensity))
mc.showWindow(lWin)

It's doing the same thing where I do get my UI and the values do update, but they update for the last light in the list regardless of which one I'm changing. I understand why this is happening.

I have tried to follow the advice in this thread and after four hours am still in the same place. I've tried lambda and partial but I don't think I get the overall construct of how they fit in.

Are there any source you guys are aware of that would help clarify?

Thanks

haggi
12-18-2010, 11:31 PM
I fear this is a problem because you have to modify the function which you are calling in the changeCommand. Try to seperate the lightIntensity() function from the loop and call it with lightname and intensity.

blazelet
12-19-2010, 10:25 AM
I don't think it's the function because also the checkbox for visibility of the light enacts on the last light only - and the checkbox doesn't use the function. To be sure I moved the function out of the loop and still had the same issue.

I imagine it has something to do with the variable (i) with the light name - by the time everything is generated the i variable has the last lights name, so that's what is impacted by all the fields.

The Lambda and partial approaches looked promising based on what I've read, but it seems to require a specifically written function and I'm not understanding how that logic works.

haggi
12-19-2010, 02:33 PM
Yes and no. It seems that the interface uses references to all variables and function variables. Then when the interface will be built, all variables with the same name point to the same element here the very last light.

To do this, you have to find a way to do a valid copy of your variable. For the visibility you can do something like this:

mc.checkBox(label="Enabled", value=1, onCommand=lambda dummy, name = i+'.visibility', value=1:mc.setAttr( name , value), offCommand=lambda dummy, name=i+'.visibility', value=0:mc.setAttr( name, value))

This way the variable i or the attribute i.visibility is saved into a temporary variable and the function will be called with this value. The dummy variable is used to throw away mayas internal result. If you create a change command for a checkbox, it is called with the value of the box as argument what you dont need.
Same will work with your intensity function. Here you need the default argument of the internal call:

mc.floatField(editable=True, value=getlightIntensity, changeCommand=lambda intensity, name=i+'.intensity', :lightIntensity(intensity, name))

NaughtyNathan
12-19-2010, 02:49 PM
Ryan, each time through the loop you define the same function. at the end of running your script you still only have one function called lightIntensity, which is called by every floatField you made, yet it can only reference the light that it was given during the last time you defined it. You really don't ever want to be defining functions inside other functions/bits of code until you are well on your way to mastering python programming and fully understand exactly what it is you are doing and why.

Unfortunately calling functions in basic Maya UI using basic python is a horrible big mess. This would be a cinch in MEL or in PyMel (as Haggi has already mentioned). lambda doesn't work inside a loop (like this) and Maya's defulat system for passing in the ui control value as *args precludes the passing of additional variables (in your case the light name would be helpful).

is PyMel an option for you?

:nathaN

haggi
12-19-2010, 06:03 PM
Basically in a complete python environment this approach would work because the functions are stored as function references not as strings. So every time the function is created, a new function is created that could be referenced later. As you mentioned it is the maya envionment which causes the problems.

blazelet
12-19-2010, 06:42 PM
This is all very good information, thank you both Nathan and Haggi.

So I think what you're saying is making sense and I completely understand why this approach won't work as I have it. But it also sounds like from the Original Posters comments he was able to get it to work in Maya ...

PyMel - is the Python portion any different or just how it relates to the Maya commands? Really I'm wanting to get a grasp on Python and figured Maya would be a useful place to do it (as I'm primarily a lighter). I've been resisting doing anything other than what natively comes with the program because I don't want to get too hooked on additional things until I have the basics. If this is a bad idea I'll happily stand corrected and look into PyMel.

Ill be staring at this stuff all day, hopefully I can say I got it tonight :)

Ryan

haggi
12-20-2010, 10:07 AM
Did you test the solutions in my last post with the code examples? They work fine for me.

PyMel is a wrapper which is a little bit slower, but it is much more pythonic than maya python.
Most things are much easier to use with pymel.You can derive interface subclasses with pymel and implement your own behaviour.

But your intention is very good too, to learn python as it is in maya until you know exactly what you do and then switch later to another tool.

blazelet
12-20-2010, 08:49 PM
@Nathan - I installed PyMel and started playing with it, from what I see it works more closely to the way Python typically works in the Python environment. I am definitely going to put time into learning it next.

@haggi - I had to change some things around but I got it to work using the Lambda additions you specified. Here's my new code :


import maya.cmds as cmds
lightList = cmds.ls( lights=True )
lightNum = len(lightList)

class Win(object):
def __init__(self):
if cmds.window('LightList', exists=True):
cmds.deleteUI('LightList', window=True)


cmds.window('LightList', title="Light List", wh=(800,512))
cmds.rowColumnLayout( numberOfColumns=6, columnWidth=[(1, 160), (2, 85), (3, 45), (4, 100), (5, 30), (6, 100)])


for num in range(0,lightNum,1):
getlightIntensity = cmds.getAttr(lightList[num] + ".intensity")
cmds.text( lightList[num] )
cmds.checkBox(label="Enabled", value=1, onCommand=Callback( self.doVis, num, 1), offCommand=Callback( self.doVis, num, 0))
cmds.text( " intensity " )
cmds.floatField(editable=True, value=getlightIntensity, changeCommand = lambda intensity, name=lightList[num]+'.intensity' :self.lightIntensity (intensity, name))
cmds.text( " " )
cmds.attrColorSliderGrp( columnWidth = [1,40], at="%s.color" % lightList[num])
cmds.showWindow()


def doVis(self, num, val):
cmds.setAttr(lightList[num] + '.visibility', val)
print lightList[num] + ".visibility", val

def lightIntensity(self, bright, name):
cmds.setAttr(name, float(bright))
print name, float(bright)

myWindow = Win()


So I'm sure it's not the most elegant or well written code but it's working - so now I'm going to try and figure out how to make it more efficient, then start adding on functionality.

Thanks for all your help guys. I'm finally starting to understand :)

Ryan

haggi
12-21-2010, 01:10 PM
I'd change the loop a little bit.
Insteaed of:

listlen = len(somelist)
for i in range(1,listlen,1):
....

I'd use

for i in somelist:
....

This way you dont have to use the somelist[i] statements. If you need the index you can do:

for index, value in enumerate(somelist):
....

CGTalk Moderation
12-21-2010, 01:10 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.