0

Due to certain constraints, I can't use a class for this, like I normally would.

I need to pass a function a variable, but the function is inside another function.

Here's the code I'm using, please be gentle, I'm not a python wizard and I'm self-taught. The problem I'm running into is nButtons is returning False in my function reColor.

import maya.cmds as cmds

nButtons = 4

def ColorMeButtonsUI(nButtons):
    def reColor(nButtons):
        for i in range(nButtons):
            cmds.button(str(i), edit = True, bgc = (1,1,1))

    if cmds.window('colorUI', exists= True):
        cmds.deleteUI('colorUI')

    if not nButtons:
        nButtons = 3

    if nButtons >= 2 and nButtons < 10:
        colorUI = cmds.window('colorUI', title='Color me, Buttons', widthHeight=(200, 55), rtf = True  )
        cmds.columnLayout( adjustableColumn=True)
        cmds.button('Color', label='Color', command = reColor)
        for i in range(nButtons):
            cmds.button(str(i), label = 'Color'+str(i+1))
        cmds.setParent( '..' )
        cmds.showWindow( colorUI )
    else:
        cmds.error ('Input is invalid. Please confirm input >1 and <10')
    return nButtons

ColorMeButtonsUI(nButtons)

edit: the command is being run by a GUI button: cmds.button('Color', label='Color', command = reColor)

Julien
  • 13,986
  • 5
  • 29
  • 53
Eric L
  • 1
  • 1

3 Answers3

0

welcome to stackoverflow, you already trying dont repeating yourself and iterating a range for editing the bgc, nice. But use classes like you would normally do, try to use a library Dictionary for your created ui elements and call it with the ui dag path. Instance iterate the range you would iterate the items of your dict with more information for your handling.

import maya.cmds as cmds
nButtons = 5
# create a dict for the buttons
ui_dict  = {}
def ColorMeButtonsUI(nButtons):
    def reColor(*args):
        # iterate the dict with the created buttons inside 
        for ui_id, ui_dag in ui_dict.iteritems():
            print ui_id, ui_dag
            # and edit ther color
            cmds.button(ui_dag, edit = True, bgc = (1,1,1))
            # you can also search for ids eg:
            if str(2) in str(ui_id):
               print ui_dag

    if cmds.window('colorUI', exists= True):
        cmds.deleteUI('colorUI')

    if not nButtons:
        nButtons = 3
    # you can also use
    # if 10>nButtons>= 2:
    if nButtons >= 2 and nButtons < 10:
        colorUI = cmds.window('colorUI', title='Color me, Buttons', widthHeight=(200, 55), rtf = True  )
        cmds.columnLayout( adjustableColumn=True)
        cmds.button('Color', label='Color', command = reColor)
        #use the range start,end 
        for i in range(1,nButtons):
            #create a relativ linking instance hard coded ids
            #add element to dict 
            # we stay on your case and add the index too(not more needed except searching)
            ui_dict[(i)] = cmds.button(label = 'Color'+str(i))
        cmds.setParent( '..' )
        cmds.showWindow( colorUI )
    else:
        cmds.error ('Input is invalid. Please confirm input >1 and <10')
    return nButtons

ColorMeButtonsUI(nButtons)
Ari Gold
  • 1,528
  • 11
  • 18
0

I suggest you avoid creating a function inside the other function.

If you add the function outside:

def reColor(nButtons):
        for i in range(nButtons):
            cmds.button(str(i), edit = True, bgc = (1,1,1))


def ColorMeButtonsUI(nButtons): 
(...)

you will be able to use reColor from inside ColorMeButtonsUI.

Also, note that you cannot send an argument (nButtons in this case) calling a command through a button unless you use

from functools import partial

And then use partial() on that commands argument. I suggest you read this other post.

My advice, just for the record:

  • Watch the identation. 4 spaces every identation level. No tabs.
  • Don't use caps for function definitions. Only for Class names. So it should be "colorMeButtonsUI".
  • if you are only going to use reColor inside colorMeButtons, you can call it __reColor(nButtons) instead, so that it is private (not completely private, since Python has nothing like this but just hiding the function name from the list of available components of this file). Or just _reColor with one underscore to note that this function is meant to be used only inside this file.
Community
  • 1
  • 1
Darkgaze
  • 2,280
  • 6
  • 36
  • 59
0

The problem has to do with the way Python handles loop variables. When you're trying to create something in a loop, Python will remember the loop variables for you (this is technically a closure).

Usually this is handy - but in a loop, the closure will always remember the last value of the loop, not the value you wanted when the loop was running. So if you tried something obvious like this:

for n in range (nbuttons):
    def button_func (*_):
        print "color", n
   cmds.button(label = "color %i" %n, c = button_func)

You'd get buttons correctly labeled -- but they would all print out the value of nbuttons since the closure would happen at the end of the loop. It's definitely confusing.

The rule for closures is that they happen when a scope finishes. So you just need make that happen a little earlier by creating a sub-function (which creates its own scope) and calling that function from the loop. Thus:

def ButtonUI(nbuttons =3):

    colorUI = cmds.window(title='Color me, Buttons', widthHeight=(200, 55), rtf = True  )
    cmds.columnLayout( adjustableColumn=True)
    def generate_button(idx):
        def callback(*_):  # < will inherit the value of idx automatically
            print "color", idx
        cmds.button(label = 'color %i' %idx, c = callback)

    for n in range(1, nbuttons +1):
        generate_button(n)
    cmds.showWindow(colorUI)

In this arangement generate_button will grab the value of idx and pair it up with the private callback function correctly, so each button will print out the same index value as its label. The sub-sub-function uses the closure to store the right value. You could do the same thing with a functools.partial object but once you see how the closures work that's not needed.

More on creating UI in loops here

theodox
  • 12,028
  • 3
  • 23
  • 36