CGTalk > Software > Autodesk Maya > Maya Programming
Login register
Thread Closed share thread « Previous Thread | Next Thread »  
 
Thread Tools Search this Thread Display Modes
Old 12-15-2010, 11:21 AM   #1
Luckycat
New Member
portfolio
Samuel Stévenin
Viet Nam
 
Join Date: Dec 2003
Posts: 21
[PYTHON] Create Button in for loop - [solved]

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:
Code:
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

Last edited by Luckycat : 12-15-2010 at 01:17 PM. Reason: [solved]
 
Old 12-15-2010, 01:15 PM   #2
Luckycat
New Member
portfolio
Samuel Stévenin
Viet Nam
 
Join Date: Dec 2003
Posts: 21
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.
Code:
self.button=cmds.iconTextButton(label=obj, command=lambda v=obj: cmds.select(v))


check this link
http://mail.python.org/pipermail/tu...ber/043360.html

Last edited by Luckycat : 12-15-2010 at 02:56 PM. Reason: add link
 
Old 12-15-2010, 05:56 PM   #3
meuH
lorem ipsum
 
meuH's Avatar
greuH a
enviro guy
Eidos Montreal
Montreal, CA
 
Join Date: Jan 2002
Posts: 68
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/201...hon-ui-example/


Quote:
Originally Posted by Luckycat
ok problem solved.
Need to force the refresh of the lamba giving it a value directly.
 
Old 12-15-2010, 06:30 PM   #4
haggi
Expert
3d freelancer
 
Join Date: Apr 2004
Posts: 955
In pymel you have a Callback function, it works like this:

command = pm.Callback(function, arguments)

very elegant too.
 
Old 12-18-2010, 08:53 PM   #5
blazelet
Expert
 
blazelet's Avatar
Ryan Wing
Designer
Tribune
Indianapolis,
 
Join Date: Nov 2005
Posts: 305
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 :


Code:
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
 
Old 12-18-2010, 11:31 PM   #6
haggi
Expert
3d freelancer
 
Join Date: Apr 2004
Posts: 955
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.
 
Old 12-19-2010, 10:25 AM   #7
blazelet
Expert
 
blazelet's Avatar
Ryan Wing
Designer
Tribune
Indianapolis,
 
Join Date: Nov 2005
Posts: 305
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.
 
Old 12-19-2010, 02:33 PM   #8
haggi
Expert
3d freelancer
 
Join Date: Apr 2004
Posts: 955
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:
Code:
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:
Code:
mc.floatField(editable=True, value=getlightIntensity, changeCommand=lambda intensity, name=i+'.intensity', :lightIntensity(intensity, name))

Last edited by haggi : 12-19-2010 at 02:37 PM.
 
Old 12-19-2010, 02:49 PM   #9
NaughtyNathan
HighEnd3D deserter
 
NaughtyNathan's Avatar
Naughty Nathan
Technical Artist
Sony SCEE Liverpool
Manchester, United Kingdom
 
Join Date: May 2008
Posts: 2,608
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
 
Old 12-19-2010, 06:03 PM   #10
haggi
Expert
3d freelancer
 
Join Date: Apr 2004
Posts: 955
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.
 
Old 12-19-2010, 06:42 PM   #11
blazelet
Expert
 
blazelet's Avatar
Ryan Wing
Designer
Tribune
Indianapolis,
 
Join Date: Nov 2005
Posts: 305
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
 
Old 12-20-2010, 10:07 AM   #12
haggi
Expert
3d freelancer
 
Join Date: Apr 2004
Posts: 955
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.
 
Old 12-20-2010, 08:49 PM   #13
blazelet
Expert
 
blazelet's Avatar
Ryan Wing
Designer
Tribune
Indianapolis,
 
Join Date: Nov 2005
Posts: 305
@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 :

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
 
Old 12-21-2010, 01:10 PM   #14
haggi
Expert
3d freelancer
 
Join Date: Apr 2004
Posts: 955
I'd change the loop a little bit.
Insteaed of:
Code:
listlen = len(somelist) for i in range(1,listlen,1): ....

I'd use
Code:
for i in somelist: ....

This way you dont have to use the somelist[i] statements. If you need the index you can do:
Code:
for index, value in enumerate(somelist): ....
 
Old 12-21-2010, 01:10 PM   #15
CGTalk Moderation
Lord of the posts
CGTalk Forum Leader
 
Join Date: Sep 2003
Posts: 1,066,481
Thread automatically closed

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.
__________________
CGTalk Policy/Legalities
Note that as CGTalk Members, you agree to the terms and conditions of using this website.
 
Thread Closed share thread


Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
CGSociety
Society of Digital Artists
www.cgsociety.org

Powered by vBulletin
Copyright 2000 - 2006,
Jelsoft Enterprises Ltd.
Minimize Ads
Forum Jump
Miscellaneous

All times are GMT. The time now is 12:35 AM.


Powered by vBulletin
Copyright ©2000 - 2016, Jelsoft Enterprises Ltd.