问题
So I'm trying to output the label of the button as it's pressed (Button label should replace the XXX in the print of the buttonNumber function). I do not know how to retrieve the label data from the button to output. Any thoughts? Thanks in advance
import maya.cmds as mc
def buttonNumber(*args):
print 'Button XXX was pressed'
def openWindow():
if mc.window('windowTest', ex=True):
mc.deleteUI('windowTest', window=True)
mc.window('windowTest', title='', s=True, resizeToFitChildren = True,)
mc.rowColumnLayout(numberOfColumns = 3, columnWidth = [ (1, 150), (2, 150), (3, 150)])
mc.button(label='1', command=buttonNumber)
mc.button(label='2', command=buttonNumber)
mc.button(label='3', command=buttonNumber)
mc.button(label='4', command=buttonNumber)
mc.button(label='5', command=buttonNumber)
mc.button(label='6', command=buttonNumber)
mc.button(label='7', command=buttonNumber)
mc.button(label='8', command=buttonNumber)
mc.button(label='9', command=buttonNumber)
mc.showWindow('windowTest')
openWindow()
回答1:
I recommend you to read my other posts on stackoverflow about Partial (note that there is another method with Lambda)
I didn't test the code below (I don't have maya here but you will get the idea)
import maya.cmds as mc
from functools import partial
def buttonNumber(label, *args):
print('Button {0:03d} was pressed'.format(label))
def openWindow():
if mc.window('windowTest', ex=True):
mc.deleteUI('windowTest', window=True)
mc.window('windowTest', title='', s=True, resizeToFitChildren = True,)
mc.rowColumnLayout(numberOfColumns = 3, columnWidth = [ (1, 150), (2, 150), (3, 150)])
mc.button(label='1', command=partial(buttonNumber, 1))
mc.showWindow('windowTest')
openWindow()
回答2:
As @DeWeeny says, you can bind a value to the button commands with a functools.partial
object. You could also do it with a function factory for this very simple application, or with a callable class that remembered a value for each instance.
function factory
def make_callback(value):
def inner_callback(_):
print value, "was clicked"
return inner_callback
w = cmds.window()
c = cmds.columnLayout()
for n in range(9):
cmds.button( label = str(n), command = make_callback(n))
cmds.showWindow(w)
The only subtlety there is that inner_callback
has an unused argument (the _
) which it needs because button callbacks always fire a useless parameter
class
This is useful if the data is more complex or needs more calculation than in the function factory example
class NumberCallback(object):
def __init__(self, id):
self.id =
def __call__(self, _):
print self.id, "was clicked"
w = cmds.window()
c = cmds.columnLayout()
for n in range(9):
cmds.button( label = str(n), command = NumberCallback(n))
cmds.showWindow(w)
This is functionally identical but if you had to do something with more complex behavior a class would make it tidy
not lambda
You may find advice on the web to do it with a lambda
. For lots of applications that would be great -- but don't do it in a loop. If you do this:
def clicked(num):
print num, "was clicked"
w = cmds.window()
c = cmds.columnLayout()
for n in range(9):
cmds.button( label = str(n), command = lambda p: clicked(n))
cmds.showWindow(w)
All the buttons will respond
8 was clicked
because the lambdas will all capture the last variable in the loop which is not what you want.
More details here. And a maya-specific module for exactly this sort of thing here
回答3:
I did a little reminder on my blog some time ago, to be able to test all the different native Maya UI way of using commands with a fast way of testing them:
- classic maya string
- function as argument
- lambda
- functools.partial
- pymel.core.Callback
Each case is also given with examples with passing variables as arguments to those functions. Because sometimes you have to be able to. Overall I totally recommend the use functools.partial, it gives only advantages over the others (if you forget about PySide).
Maya UI types
def function(*args):
print args
cmds.textFieldGrp(text, edit=True, text=str(args))
variable = 'Variable'
width = [1, 250]
align = [1, 'left']
window = cmds.window(title='UI and commands arguments.')
cmds.columnLayout()
cmds.textFieldGrp(label="\"function()\"", changeCommand="function()", columnWidth=width, columnAlign=align)
cmds.textFieldGrp(label="function", changeCommand=function, columnWidth=width, columnAlign=align)
cmds.textFieldGrp(label="\"function(variable)\"", changeCommand="function(variable)", columnWidth=width, columnAlign=align)
cmds.textFieldGrp(label="lambda x: function(variable)", changeCommand=lambda x: function(variable), columnWidth=width, columnAlign=align)
cmds.separator(style="double", height=20)
import functools
cmds.textFieldGrp(changeCommand=functools.partial(function), label='functools.partial(function)', columnWidth=width, columnAlign=align)
cmds.textFieldGrp(changeCommand=functools.partial(function, variable), label='functools.partial(function, variable)', columnWidth=width, columnAlign=align)
cmds.separator(style="single", height=20)
import pymel.core
cmds.textFieldGrp(changeCommand=pymel.core.Callback(function), label='pymel.core.Callback(function)', columnWidth=width, columnAlign=align)
cmds.textFieldGrp(changeCommand=pymel.core.CallbackWithArgs(function), label='pymel.core.CallbackWithArgs(function)', columnWidth=width, columnAlign=align)
cmds.textFieldGrp(changeCommand=pymel.core.CallbackWithArgs(function, variable), label='pymel.core.CallbackWithArgs(function, variable)', columnWidth=width, columnAlign=align)
cmds.separator(style="single", height=20)
text = cmds.textFieldGrp(label='RESULT: ', text='', width=500)
cmds.showWindow()
When using a class
Because it was not made with the use of class in mind, some ways does not work at all when you are in a class.
class MayaUI():
def __init__(self):
self.variable = 'Variable'
self.width = [1, 250]
self.align = [1, 'left']
self.window = cmds.window(title='UI and commands arguments.')
cmds.columnLayout()
cmds.textFieldGrp(label="\"self.function()\"", changeCommand="self.function()", columnWidth=self.width, columnAlign=self.align)
cmds.textFieldGrp(label="self.function", changeCommand=self.function, columnWidth=self.width, columnAlign=self.align)
cmds.textFieldGrp(label="\"self.function(self.variable)\"", changeCommand="self.function(self.variable)", columnWidth=self.width, columnAlign=self.align)
cmds.textFieldGrp(label="lambda x: self.function(self.variable)", changeCommand=lambda x: self.function(self.variable), columnWidth=self.width, columnAlign=self.align)
cmds.separator(style="double", height=20)
import functools
cmds.textFieldGrp(changeCommand=functools.partial(self.function), label='functools.partial(self.function)', columnWidth=self.width, columnAlign=self.align)
cmds.textFieldGrp(changeCommand=functools.partial(self.function, self.variable), label='functools.partial(self.function, self.variable)', columnWidth=self.width, columnAlign=self.align)
cmds.separator(style="single", height=20)
import pymel.core
cmds.textFieldGrp(changeCommand=pymel.core.Callback(self.function), label='pymel.core.Callback(self.function)', columnWidth=self.width, columnAlign=self.align)
cmds.textFieldGrp(changeCommand=pymel.core.CallbackWithArgs(self.function), label='pymel.core.CallbackWithArgs(self.function)', columnWidth=self.width, columnAlign=self.align)
cmds.textFieldGrp(changeCommand=pymel.core.CallbackWithArgs(self.function, self.variable), label='pymel.core.CallbackWithArgs(self.function, self.variable)', columnWidth=self.width, columnAlign=self.align)
# A bit more complicated
_map = {'textFieldGrp': lambda arg:cmds.textFieldGrp(arg, query=True, text=True)}
_com = lambda *args:args[0](self.variable, _map[args[1]](args[2]))
cmds.textFieldGrp('textfieldName', changeCommand=pymel.core.Callback(_com, self.function, 'textFieldGrp', 'textfieldName'), label="pymel.core.Callback(_com, self.function, 'textFieldGrp', 'textfieldName') + lambdas", columnWidth=self.width, columnAlign=self.align)
cmds.separator(style="single", height=20)
self.text = cmds.textFieldGrp(label='RESULT: ', text='', width=500)
cmds.showWindow()
def function(self, *args):
print args
cmds.textFieldGrp(self.text, edit=True, text=str(args))
MayaUI()
回答4:
Here's my code that does what you need.
Instead of hard-coding the argument of the label of each button to pass to the function at button creation time, I pass the object name of the button into the function. This way, all attributes of the button (including the label) can be queried at any point.
To show this I made a randomise function that generates random alphanumeric strings and assigns them to the button labels.
It'll entertain you for 30 seconds. Try it!
import maya.cmds as mc
import random, string
from functools import partial
def randomButtonTest():
buttonsList = []
numButtons = 5
winName = 'randomButtonWin'
winWidth = 250
statusTfgName = 'myStatusTfg'
buttonLabelLength = 10
def generateRandomLabel(myLength):
# generates a random string of characters of myLength
randomCandidatesList = list(string.ascii_letters + string.digits)
random.shuffle(randomCandidatesList)
randomCandidatesStr = ''.join(randomCandidatesList)
return ''.join([randomCandidatesStr[random.randint(0,len(randomCandidatesStr)-1)] for x in range(myLength)])
def reportButtonLabel(whichButton, *args):
buttonLabel = mc.button(whichButton, q=True, label=True)
print 'reportButtonLabel: clicked - %s' % buttonLabel
mc.textFieldGrp(statusTfgName, e=True, text=buttonLabel)
return
def randomiseButtonClicked(*args):
mc.textFieldGrp(statusTfgName, e=True, text='')
for thisButton in buttonsList:
currentLabel = mc.button(thisButton, q=True, label=True)
newLabel = '%s: %s' %(currentLabel.split(':')[0], generateRandomLabel(buttonLabelLength))
mc.button(thisButton, e=True, label=newLabel)
return
# build UI
if mc.window(winName, exists=True):
mc.deleteUI(winName)
mc.window(winName, title='Randon Button Labels', w=winWidth )
mc.columnLayout(w=winWidth)
# create numButtons number of buttons
for buttonId in range(numButtons):
buttonsList.append(mc.button(label='button %i:'%buttonId, w=winWidth ))
mc.button(buttonsList[-1], e=True, command=partial(reportButtonLabel, buttonsList[-1]))
mc.text(label='')
mc.textFieldGrp(statusTfgName, label='Button clicked', w=winWidth, columnWidth2=[winWidth*0.3, winWidth*0.65])
mc.text(label='')
mc.button('Randomise labels', w=winWidth, command=randomiseButtonClicked)
randomiseButtonClicked()
mc.window(winName, e=True, w=winWidth, h=150, resizeToFitChildren=True)
mc.showWindow(winName)
randomButtonTest()
来源:https://stackoverflow.com/questions/37034838/printing-the-label-of-a-button-when-clicked