maya命令创建基本工具
1、构建窗口基类,并继承创建窗口,实现基本物体创建
optwin.py文件
import maya.cmds as cmds
class AR_OptionsWindow(object):
"""选项窗口的基类"""
@classmethod
def showUI(cls):
"""用于实例化选项窗口的函数"""
win = cls()
win.create()
return win
def __init__(self):
"""初始化公共数据属性"""
## 唯一的窗口句柄
self.window = 'ar_optionsWindow'
## 窗口标题
self.title = 'Options Window'
## 窗口大小
self.size = (546, 350)
## 指定是否支持工具/操作切换
self.supportsToolAction = False
## 要在操作按钮上显示的名称
self.actionName = 'Apply and Close'
def create(self):
"""绘制窗口"""
# 如果窗口存在句柄,则删除该窗口
if cmds.window(self.window, exists=True):
cmds.deleteUI(self.window, window=True)
# 初始化窗口
self.window = cmds.window(
self.window,
title=self.title,
widthHeight=self.size,
menuBar=True
)
# 窗口的主窗体
self.mainForm = cmds.formLayout(nd=100)
# 创建常用菜单项和按钮
self.commonMenu()
self.commonButtons()
# 请参见getOptionBox.mel,了解我们为什么要实现此布局模式以模拟maya的选项框
self.optionsBorder = cmds.tabLayout(
scrollable=True,
tabsVisible=False,
height=1,
childResizable=True
)
cmds.formLayout(
self.mainForm, e=True,
attachForm=(
[self.optionsBorder,'top',0],
[self.optionsBorder,'left',2],
[self.optionsBorder,'right',2]
),
attachControl=(
[self.optionsBorder,'bottom',5,self.applyBtn]
)
)
# 在displayOptions()中附加控件的窗体
self.optionsForm = cmds.formLayout(nd=100)
self.displayOptions()
# 显示窗口
cmds.showWindow()
def commonMenu(self):
"""为所有选项框创建公用菜单项"""
self.editMenu = cmds.menu(label='Edit')
self.editMenuSave = cmds.menuItem(
label='Save Settings',
command=self.editMenuSaveCmd
)
self.editMenuReset = cmds.menuItem(
label='Reset Settings',
command=self.editMenuResetCmd
)
self.editMenuDiv = cmds.menuItem(d=True)
self.editMenuRadio = cmds.radioMenuItemCollection()
self.editMenuTool = cmds.menuItem(
label='As Tool',
radioButton=True,
enable=self.supportsToolAction,
command=self.editMenuToolCmd
)
self.editMenuAction = cmds.menuItem(
label='As Action',
radioButton=True,
enable=self.supportsToolAction,
command=self.editMenuActionCmd
)
self.helpMenu = cmds.menu(label='Help')
self.helpMenuItem = cmds.menuItem(
label='Help on %s'%self.title,
command=self.helpMenuCmd
)
def helpMenuCmd(self, *args):
"""重写此方法以显示自定义帮助"""
cmds.launch(web='http://maya-python.com')
def editMenuSaveCmd(self, *args):
"""重写此方法以实现保存设置"""
pass
def editMenuResetCmd(self, *args):
"""重写此方法以实现重置设置"""
pass
def editMenuToolCmd(self, *args):
"""重写此方法以实现工具模式"""
pass
def editMenuActionCmd(self, *args):
"""重写此方法以实现操作模式"""
pass
def actionBtnCmd(self, *args):
"""应用操作并关闭窗口"""
self.applyBtnCmd()
self.closeBtnCmd()
def applyBtnCmd(self, *args):
"""重写此方法以应用操作"""
pass
def closeBtnCmd(self, *args):
"""关闭窗口"""
cmds.deleteUI(self.window, window=True)
def commonButtons(self):
"""为所有选项框创建公用按钮"""
self.commonBtnSize = ((self.size[0]-18)/3, 26)
self.actionBtn = cmds.button(
label=self.actionName,
height=self.commonBtnSize[1],
command=self.actionBtnCmd
)
self.applyBtn = cmds.button(
label='Apply',
height=self.commonBtnSize[1],
command=self.applyBtnCmd
)
self.closeBtn = cmds.button(
label='Close',
height=self.commonBtnSize[1],
command=self.closeBtnCmd
)
cmds.formLayout(
self.mainForm, e=True,
attachForm=(
[self.actionBtn,'left',5],
[self.actionBtn,'bottom',5],
[self.applyBtn,'bottom',5],
[self.closeBtn,'bottom',5],
[self.closeBtn,'right',5]
),
attachPosition=(
[self.actionBtn,'right',1,33],
[self.closeBtn,'left',0,67]
),
attachControl=(
[self.applyBtn,'left',4,self.actionBtn],
[self.applyBtn,'right',4,self.closeBtn]
),
attachNone=(
[self.actionBtn,'top'],
[self.applyBtn,'top'],
[self.closeBtn,'top']
)
)
def displayOptions(self):
"""重写此方法以显示选项控件"""
pass
#AR_OptionsWindow.showUI() #创建预览窗口
polywin.py文件
import maya.cmds as cmds
from optwin import AR_OptionsWindow
class AR_PolyOptionsWindow(AR_OptionsWindow):
"""用于创建多边形基本体的简单窗口的类"""
def __init__(self):
"""初始化基类并重写数据属性"""
AR_OptionsWindow.__init__(self)
# 基类属性重写
self.title = 'Polygon Creation Options'
# 基类属性重写
self.actionName = 'Create'
def displayOptions(self):
"""从基类重写"""
# 基本类型选择器
self.objType = cmds.radioButtonGrp(
label='Object Type: ',
labelArray4=[
'Cube',
'Cone',
'Cylinder',
'Sphere'
],
numberOfRadioButtons=4,
select=1,
)
# 一组变换控件
self.xformGrp = cmds.frameLayout(
label='Transformations',
collapsable=True,
)
cmds.formLayout(
self.optionsForm, e=True,
attachControl=(
[self.xformGrp,'top',2,self.objType]
),
attachForm=(
[self.xformGrp,'left',0],
[self.xformGrp,'right',0]
)
)
self.xformCol = cmds.columnLayout()
self.position = cmds.floatFieldGrp(
label='Position: ',
numberOfFields=3
)
self.rotation = cmds.floatFieldGrp(
label='Rotation (XYZ): ',
numberOfFields=3
)
self.scale = cmds.floatFieldGrp(
label='Scale: ',
numberOfFields=3,
value=[1.0,1.0,1.0,1.0]
)
cmds.setParent('..')
cmds.setParent('..')
# 顶点颜色选择器
self.color = cmds.colorSliderGrp(
label='Vertex Colors: '
)
cmds.formLayout(
self.optionsForm, e=True,
attachControl=(
[self.color,'top',0,self.xformGrp]
),
attachForm=(
[self.color,'left',0],
)
)
def applyBtnCmd(self, *args):
"""从基类重写"""
# 确定要创建的对象的类型
self.objIndAsCmd = {
1:cmds.polyCube,
2:cmds.polyCone,
3:cmds.polyCylinder,
4:cmds.polySphere
}
objIndex = cmds.radioButtonGrp(
self.objType, q=True,
select=True
)
# 创建新对象
newObject = self.objIndAsCmd[objIndex]()
# 转换新对象
pos = cmds.floatFieldGrp(
self.position, q=True,
value=True
)
rot = cmds.floatFieldGrp(
self.rotation, q=True,
value=True
)
scale = cmds.floatFieldGrp(
self.scale, q=True,
value=True
)
cmds.xform(newObject[0], t=pos, ro=rot, s=scale)
# 将顶点颜色应用于新对象
col = cmds.colorSliderGrp(
self.color, q=True,
rgbValue=True
)
cmds.polyColorPerVertex(
newObject[0],
colorRGB=col,
colorDisplayOption=True
)
#实例化窗口
AR_PolyOptionsWindow.showUI()
输出效果:
maya命令创建动作姿势管理器
import maya.cmds as cmds
import maya.mel as mel
import os, cPickle, sys, time
kPoseFileExtension = 'pse'
def showUI():
"""实例化姿势管理器窗口的函数"""
return AR_PoseManagerWindow.showUI()
class AR_PoseManagerWindow(object):
"""基本姿势管理器窗口的类"""
@classmethod
def showUI(cls):
"""实例化姿势管理器窗口的函数"""
win = cls()
win.create()
return win
def __init__(self):
"""初始化数据属性"""
## 独特的窗把手
self.window = 'ar_poseManagerWindow'
## 窗口标题
self.title = 'Pose Manager'
## 窗口大小
self.size = (300, 174)
if mel.eval('getApplicationVersionAsFloat()') > 2010.0:
self.size = (300, 150)
## 在可写位置存储姿势的临时文件
self.tempFile = os.path.join(
os.path.expanduser('~'),
'temp_pose.%s'%kPoseFileExtension
)
## 当前剪贴板状态消息
self.clipboardStat = 'No pose currently copied.'
if (os.path.exists(self.tempFile)):
self.clipboardStat = 'Old pose currently copied to clipboard.'
## 要在文件浏览器中显示的文件筛选器
self.fileFilter = 'Pose (*.%s)'%kPoseFileExtension
def create(self):
"""绘制窗口"""
# 如果窗口存在句柄,则删除该窗口
if(cmds.window(self.window, exists=True)):
cmds.deleteUI(self.window, window=True)
# 初始化窗口
self.window = cmds.window(self.window, title=self.title, wh=self.size, s=False)
# 主窗体布局
self.mainForm = cmds.formLayout()
# 复制/粘贴框架
self.copyPasteFrame = cmds.frameLayout(l='Copy and Paste Poses')
# 框架内的窗体布局
self.copyPasteForm = cmds.formLayout()
# 在两列网格中创建按钮
self.copyPasteGrid = cmds.gridLayout(cw=self.size[0]/2-2, nc=2)
self.copyBtn = cmds.button(l='Copy Pose', c=self.copyBtnCmd)
self.pasteBtn = cmds.button(l='Paste Pose', c=self.pasteBtnCmd)
# 带剪贴板状态标签的滚动视图
cmds.setParent(self.copyPasteForm)
self.clipboardLayout = cmds.scrollLayout(h=42, w=self.size[0]-4)
self.clipboardLbl = cmds.text(l=self.clipboardStat)
# 在复制粘贴窗体中附加控件
ac = []; af = []
ac.append([self.clipboardLayout,'top',0,self.copyPasteGrid])
af.append([self.copyPasteGrid,'top',0])
af.append([self.clipboardLayout,'bottom',0])
cmds.formLayout(
self.copyPasteForm, e=True,
attachControl=ac, attachForm=af
)
# 保存/加载框架
cmds.setParent(self.mainForm)
self.loadSaveFrame = cmds.frameLayout(l='Save and Load Poses')
# 在两列网格中创建按钮
self.loadSaveBtnLayout = cmds.gridLayout(cw=self.size[0]/2-2, nc=2)
self.saveBtn = cmds.button(l='Save Pose', c=self.saveBtnCmd)
self.loadBtn = cmds.button(l='Load Pose', c=self.loadBtnCmd)
# 将框架附加到主窗体
ac = []; af = []
ac.append([self.loadSaveFrame,'top',0,self.copyPasteFrame])
af.append([self.copyPasteFrame,'top',0])
af.append([self.copyPasteFrame,'left',0])
af.append([self.copyPasteFrame,'right',0])
af.append([self.loadSaveFrame,'bottom',0])
af.append([self.loadSaveFrame,'left',0])
af.append([self.loadSaveFrame,'right',0])
cmds.formLayout(
self.mainForm, e=True,
attachControl=ac, attachForm=af
)
# 显示窗口
cmds.showWindow(self.window)
# 强制窗口大小
cmds.window(self.window, e=True, wh=self.size)
def getSelection(self):
rootNodes = cmds.ls(sl=True, type='transform')
if rootNodes is None or len(rootNodes) < 1:
cmds.confirmDialog(t='Error', b=['OK'],
m='Please select one or more transform nodes.')
return None
else: return rootNodes
def copyBtnCmd(self, *args):
"""当按下复制姿势按钮时调用"""
rootNodes = self.getSelection()
if rootNodes is None: return
cmds.text(
self.clipboardLbl, e=True,
l='Pose copied at %s for %s.'%(
time.strftime('%I:%M'),
''.join('%s, '%t for t in rootNodes)[:-3]
)
)
exportPose(self.tempFile, rootNodes)
def pasteBtnCmd(self, *args):
"""当按下粘贴姿势按钮时调用"""
if not os.path.exists(self.tempFile): return
importPose(self.tempFile)
def saveBtnCmd(self, *args):
"""当按下保存姿势按钮时调用"""
rootNodes = self.getSelection()
if rootNodes is None: return
filePath = ''
# 2011及更新版本的使用 fileDialog2
try:
filePath = cmds.fileDialog2(
ff=self.fileFilter, fileMode=0
)
# BUG: 在某些版本的OS X上,Maya 2008和更早的版本可能会返回目录名和文件名之间没有分隔符的路径:e.g., /users/adam/Desktopuntitled.pse
except:
filePath = cmds.fileDialog(
dm='*.%s'%kPoseFileExtension, mode=1
)
# 提前退出对话被取消
if filePath is None or len(filePath) < 1: return
if isinstance(filePath, list): filePath = filePath[0]
exportPose(filePath, cmds.ls(sl=True, type='transform'))
def loadBtnCmd(self, *args):
"""按下加载姿势按钮时调用"""
filePath = ''
# 玛雅2011及更新版本的使用 fileDialog2
try:
filePath = cmds.fileDialog2(
ff=self.fileFilter, fileMode=1
)
except:
filePath = cmds.fileDialog(
dm='*.%s'%kPoseFileExtension, mode=0
)
# 提前退出对话被取消
if filePath is None or len(filePath) < 1: return
if isinstance(filePath, list): filePath = filePath[0]
importPose(filePath)
def exportPose(filePath, rootNodes):
"""在文件路径处为根节点及其子节点保存姿势文件"""
# try to open the file
try: f = open(filePath, 'w')
except:
cmds.confirmDialog(
t='Error', b=['OK'],
m='Unable to write file: %s'%filePath
)
raise
# 建立层次结构数据列表
data = saveHiearchy(rootNodes, [])
# 保存序列化数据
cPickle.dump(data, f)
# 关闭文件
f.close()
def saveHiearchy(rootNodes, data):
"""将所有可设置关键帧属性的属性值附加到数据数组"""
# 遍历提供的节点
for node in rootNodes:
# 跳过非转换节点
nodeType = cmds.nodeType(node)
if not (nodeType=='transform' or
nodeType=='joint'): continue
# 获取动画属性
keyableAttrs = cmds.listAttr(node, keyable=True)
if keyableAttrs is not None:
for attr in keyableAttrs:
data.append(['%s.%s'%(node,attr),
cmds.getAttr('%s.%s'%(node,attr))])
# 如果有子级,则重复相同的过程并附加其数据
children = cmds.listRelatives(node, children=True)
if children is not None: saveHiearchy(children, data)
return data
def importPose(filePath):
"""导入存储在filePath中的姿势数据"""
# 尝试打开文件
try: f = open(filePath, 'r')
except:
cmds.confirmDialog(
t='Error', b=['OK'],
m='Unable to open file: %s'%filePath
)
raise
# 取消pickle数据
pose = cPickle.load(f)
# 关闭文件
f.close()
# 将属性设置为存储的姿势
errAttrs = []
for attrValue in pose:
try: cmds.setAttr(attrValue[0], attrValue[1])
except:
try: errAttrs.append(attrValue[0])
except: errAttrs.append(attrValue)
# 如果需要,显示错误消息
if len(errAttrs) > 0:
importErrorWindow(errAttrs)
sys.stderr.write('Not all attributes could be loaded.')
def importErrorWindow(errAttrs):
"""导入姿势时,如果有未知属性,将显示一个错误窗口"""
win='ar_errorWindow'
# 关闭窗口的函数
def dismiss(*args):
cmds.deleteUI(win, window=True)
# 如果存在该窗口,则将其销毁
if cmds.window(win, exists=True):
dismiss()
# 创建窗口
size = (300, 200)
cmds.window(
win, wh=size, s=False,
t='Unknown Attributes'
)
mainForm = cmds.formLayout()
# 信息标签
infoLbl = cmds.text(l='The following attributes could not be found.\nThey are being ignored.', al='left')
# 显示无法加载的属性列表
scroller = cmds.scrollLayout(w=size[0])
errStr = ''.join('\t- %s\n'%a for a in errAttrs).rstrip()
cmds.text(l=errStr, al='left')
# 关闭按钮
btn = cmds.button(l='OK', c=dismiss, p=mainForm, h=26)
# 附加控件
ac = []; af=[];
ac.append([scroller,'top',5,infoLbl])
ac.append([scroller,'bottom',5,btn])
af.append([infoLbl,'top',5])
af.append([infoLbl,'left',5])
af.append([infoLbl,'right',5])
af.append([scroller,'left',0])
af.append([scroller,'right',0])
af.append([btn,'left',5])
af.append([btn,'right',5])
af.append([btn,'bottom',5])
cmds.formLayout(
mainForm, e=True,
attachControl=ac, attachForm=af
)
# 显示窗口
cmds.window(win, e=True, wh=size)
cmds.showWindow(win)
showUI()
输出结果: