[PYTHON] Create Button in for loop

Become a member of the CGSociety

Connect, Share, and Learn with our Large Growing CG Art Community. It's Free!

Thread Tools Search this Thread Display Modes
  12 December 2010
[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:

  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 ??


Last edited by Luckycat : 12 December 2010 at 01:17 PM. Reason: [solved]
  12 December 2010
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.

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

check this link

Last edited by Luckycat : 12 December 2010 at 02:56 PM. Reason: add link
  12 December 2010
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/

Originally Posted by Luckycat: ok problem solved.
Need to force the refresh of the lamba giving it a value directly.
  12 December 2010
In pymel you have a Callback function, it works like this:

command = pm.Callback(function, arguments)

very elegant too.
  12 December 2010
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]
		 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))

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?

  12 December 2010
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.
  12 December 2010
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.
  12 December 2010
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)) 

Last edited by haggi : 12 December 2010 at 02:37 PM.
  12 December 2010
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?

  12 December 2010
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.
  12 December 2010
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

  12 December 2010
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.
  12 December 2010
@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])
	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

  12 December 2010
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):
  12 December 2010
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

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
Society of Digital Artists

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

All times are GMT. The time now is 02:30 PM.

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