Maya Python:cmds.button() 传递UI变量并调用函数?
首先,这里似乎是一个很好的地方,可以学习更多关于编程的知识。
我写了一个Maya的Python脚本,里面有两个功能都能正常工作,但我在让UI按钮调用superExtrude()这个函数时遇到了麻烦。
第一个功能负责处理几何网格的操作,第二个功能应该是为用户输入提供界面:
import maya.cmds as cmds
def superExtrude(extrScale, extrDist):
"""Loops through a list of selected meshes and extrudes all of the mesh faces to produce a polygon frame, based on existing mesh tesselations"""
myObjectLt = cmds.ls(selection=True)
for i in range(len(myObjectLt)):
numFaces = cmds.polyEvaluate(face=True)
item = myObjectLt[i] + ".f[:]"
cmds.select(clear=True)
cmds.select(item, replace=True)
#extrude by scale
cmds.polyExtrudeFacet(constructionHistory=True, keepFacesTogether=False, localScaleX=extrScale, localScaleY=extrScale, localScaleZ=extrScale)
selFaces = cmds.ls(selection=True)
cmds.delete(selFaces)
#extrude by height
cmds.select(item, replace=True)
cmds.polyExtrudeFacet(constructionHistory=True, keepFacesTogether=True, localTranslateZ=extrDist)
def extrWindow():
"""Creates the user interface UI for the user input of the extrusion scale and height"""
windowID = "superExtrWindow"
if cmds.window(windowID, exists=True):
cmds.deleteUI(windowID)
cmds.window(windowID, title="SuperExtrude", sizeable=False, resizeToFitChildren=True)
cmds.rowColumnLayout(numberOfColumns=2, columnWidth=[(1,120),(2,120)], columnOffset=[1,"right",3])
cmds.text(label="Extrusion Scale:")
extrScaleVal = cmds.floatField(text=0.9)
cmds.text(label="Extrusion Height:")
extrDistVal = cmds.floatField(text=-0.3)
cmds.separator(height=10, style="none")
cmds.separator(height=10, style="none")
cmds.separator(height=10, style="none")
cmds.button(label="Apply", command=superExtrude(extrScaleVal, extrDistVal))
cmds.showWindow()
extrWindow()
我对Python和Maya脚本还很陌生,所以任何帮助都会非常感激。:)
4 个回答
我把所有东西都换成了pymel,你应该学习这个。cmds真是糟糕透了。花点时间看看你和我之间的区别。像这样的脚本能帮助你入门。如果需要进一步的解释,随时告诉我。
为了你的学习,建议你去学pymel。
pymel的在线文档在这里 = http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/
import pymel.core as pm
def superExtrude(*args):
"""Loops through a list of selected meshes and extrudes all of the mesh faces to produce a polygon frame, based on existing mesh tesselations"""
#pymel uses python classes to make things easier
#its ok to not understand what a class is but just think of it the same as if you were to add an attribute to a polycube.
#your code variable now has attributes
#so with that pymel ls returns a list of PyNodes that correspond to the objects
#cmds ls returns a list of strings which is very unuseful
#if you look at the help docs you can find most of whats available
myObjectLt = pm.ls(selection=True)
for i in myObjectLt:
#instead of cycling through by a number were gonna cycle through the list itself
#i is now the item in the list
#its unnecessary to select the objects because we can specify it in the polyExtrude
#cmds.select(item, replace=True)
#the extrude commands selects things but im not sure what your trying to achive here by seperating
#the scale extrude and translate extrude
pm.select(cl=True)
#the way poly objects wrok is that you have a transform node and a shape node
# if you graph it in the hypershade you'll see the two nodes
#the faces are part of the shape node i like accessing things by this node but just know you can do it like this
#i.f <-- f is your attribute and i is the item
#using i.getShape() returns the shape node
# http://download.autodesk.com/global/docs/maya2014/en_us/PyMel/generated/classes/pymel.core.uitypes/pymel.core.uitypes.FloatField.html?highlight=floatfield#pymel.core.uitypes.FloatField
#since were using pymel the extrScaleVal has function that lets you get the value
thisScale = extrScaleVal.getValue()
pm.polyExtrudeFacet(i.getShape().f, constructionHistory=True, keepFacesTogether=False, localScaleX=thisScale, localScaleY=thisScale, localScaleZ=thisScale)
#selFaces = cmds.ls(selection=True)
pm.delete()
#same as before
thisDist = extrDistVal.getValue()
#extrude by height
pm.polyExtrudeFacet(i.getShape().f, constructionHistory=True, keepFacesTogether=True, localTranslateZ=thisDist)
def extrWindow():
#global is a way to transfer variables from function to function the way you had it
# you would have had to query the value from your parameters in superExtrude
#instead do this
global extrScaleVal, extrDistVal
#which will makes these parameters to the other function
"""Creates the user interface UI for the user input of the extrusion scale and height"""
windowID = "superExtrWindow"
#instead of having a query run just use try except
#which will just go to except when the try fails
try:
pm.deleteUI(windowID)
except:
pass
pm.window(windowID, title="SuperExtrude", sizeable=False, resizeToFitChildren=True)
pm.rowColumnLayout(numberOfColumns=2, columnWidth=[(1,120),(2,120)], columnOffset=[1,"right",3])
pm.text(label="Extrusion Scale:")
extrScaleVal = pm.floatField(v=0.9)
pm.text(label="Extrusion Height:")
extrDistVal = pm.floatField(v=-0.3)
pm.separator(height=10, style="none")
pm.separator(height=10, style="none")
pm.separator(height=10, style="none")
pm.button(label="Apply", c=superExtrude)
pm.showWindow()
extrWindow()
St4rb0y
首先,你在第33和35行的floatField调用中使用了一个无效的标志,叫做'text'。你可能是想用'value',所以把这两行改一下。
extrScaleVal = cmds.floatField(v=0.9)
extrDistVal = cmds.floatField(v=-0.3)
其次,在创建用户界面控件类型时,'command'标志需要一个字符串,所以你得把命令和它的参数用引号括起来:
cmds.button(label="Apply", command='superExtrude(extrScaleVal, extrDistVal)')
把那三行改了之后,应该就能正常工作了。
小贴士:
如果你想注释一行代码,可以用#,而不是把整行用三个单引号括起来。用三个引号更适合注释多行代码。
另一个关于控制命令标志的小技巧:你可以定义一个字符串变量来传递命令,然后用这个变量代替直接写字符串。这个技巧在创建动态控件时会很有用,比如根据用户的选择来组装命令:
comStr = "superExtrude(extrScaleVal, extrDistVal)"
cmds.button(label="Apply", command=comStr)
cmds.button(label="Apply", command=superExtrude(extrScaleVal, extrDistVal))
这一行代码调用了 superExtrude
并把它的返回值赋给了 command
。因为 superExtrude
没有返回任何东西,所以这个按钮的命令实际上是 None
。
也许你是想让 superExtrude
在按钮被点击时才被调用,这样的话你应该把它放在一个 lambda 函数里,这样就可以避免它被立即调用:
cmds.button(label="Apply", command=lambda *args: superExtrude(extrScaleVal, extrDistVal))
我不确定这是否是你想要的答案,但你需要了解关于Maya“命令”标志的一些事情:
如果你想在按钮的调用中放一个函数,你需要传入函数的名字,不要带任何参数(比如:command = myFunction),记得去掉最后的括号“()”。
在你的函数里,你需要加上“*args”,因为Maya的按钮总是会传递一个参数(我想是“False”)(例如:def myFunction(customArg1, customArg2, *args))。
如果你想在按钮信号中传递参数,你需要使用functools模块里的部分函数(from functools import partial),用法是这样的: cmds.button(command = partial(myFunction, arg1, arg2, kwarg1=value1, kwarg2=value2))。
还有一件事,关于pymel和cmds...这可能是个无止境的话题,但pymel并不是万能的... 当你需要处理大量信息(比如获取网格上的顶点列表)时,pymel的速度可能会比简单的Maya命令慢40倍。 它有优点也有缺点... 如果你刚开始学习Python,我不建议你现在就去接触pymel。 先熟悉语法和命令,当你掌握了之后,再转向pymel(在处理对象创建时非常有用)。
希望这些对你有帮助,
祝好
编辑:
根据你第一条帖子里的内容,你需要在代码中做的更改是:
import maya.cmds as cmds
from functools import partial
#You need to add the *args at the end of your function
def superExtrude(extrScaleField, extrDistField, *args):
"""Loops through a list of selected meshes and extrudes all of the mesh faces to produce a polygon frame, based on existing mesh tesselations"""
myObjectLt = cmds.ls(selection=True)
#In the function, we are passing the floatFields, not their values.
#So if we want to query the value before running the script, we need to
#use the floatField cmds with the "query" flag
extrScale = cmds.floatField(extrScaleField, q=1, v=1)
extrDist = cmds.floatField(extrDistField, q=1, v=1)
for i in range(len(myObjectLt)):
numFaces = cmds.polyEvaluate(face=True)
item = myObjectLt[i] + ".f[:]"
cmds.select(clear=True)
cmds.select(item, replace=True)
#extrude by scale
cmds.polyExtrudeFacet(constructionHistory=True, keepFacesTogether=False, localScaleX=extrScale, localScaleY=extrScale, localScaleZ=extrScale)
selFaces = cmds.ls(selection=True)
cmds.delete(selFaces)
#extrude by height
cmds.select(item, replace=True)
cmds.polyExtrudeFacet(constructionHistory=True, keepFacesTogether=True, localTranslateZ=extrDist)
def extrWindow():
"""Creates the user interface UI for the user input of the extrusion scale and height"""
windowID = "superExtrWindow"
if cmds.window(windowID, exists=True):
cmds.deleteUI(windowID)
cmds.window(windowID, title="SuperExtrude", sizeable=False, resizeToFitChildren=True)
cmds.rowColumnLayout(numberOfColumns=2, columnWidth=[(1,120),(2,120)], columnOffset=[1,"right",3])
cmds.text(label="Extrusion Scale:")
# There were an error here, replace 'text' with 'value'
# to give your floatField a default value on its creation
extrScaleVal = cmds.floatField(value=0.9)
cmds.text(label="Extrusion Height:")
extrDistVal = cmds.floatField(value=-0.3)
cmds.separator(height=10, style="none")
cmds.separator(height=10, style="none")
cmds.separator(height=10, style="none")
# As said above, use the partial function to pass your arguments in the function
# Here, the arguments are the floatFields names, so we can then query their value
# everytime we will press the button.
cmds.button(label="Apply", command=partial(superExtrude,extrScaleVal, extrDistVal))
cmds.showWindow(windowID)
extrWindow()