Printing the label of a button when clicked

我们两清 提交于 2019-12-12 04:07:34

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!