def parseSelectionList(self,selectionList): ''' calculates compacted internal selection list from the MSelectionList ''' selection = [] if selectionList is None or selectionList.isEmpty(): return selection #compact selection list first mergedList = om.MSelectionList() mergedList.merge(selectionList,om.MSelectionList.kMergeNormal) for i in Utils.mIter(om.MItSelectionList(mergedList)): # read selection item path = om.MDagPath() compSelection = om.MObject() i.getDagPath(path,compSelection) if not i.hasComponents() or not compSelection.hasFn(self.componentType): continue # create selection entry and fill it with components selEntry = MeshSelectEntry(path) for c in Utils.mIter(om.MItMeshVertex(path,compSelection)): selEntry.components.append(c.index()) selection.append(selEntry) return selection
def open(): ''' just a shortcut method to construct and display main window ''' window = MainWindow.getInstance() if cmds.control(MainWindow.DOCK_NAME,q=True,exists=True): cmds.control(MainWindow.DOCK_NAME,e=True,visible=True) else: cmds.dockControl(MainWindow.DOCK_NAME,l=window.createWindowTitle(),content=MainWindow.WINDOW_NAME, area='right',allowedArea=['right', 'left'], width=window.preferedWidth.get(), floating=window.preferedFloating.get(), visibleChangeCommand=window.visibilityChanged) if window.preferedFloating.get(): cmds.window(MainWindow.DOCK_NAME,e=True, topEdge=window.preferedTop.get(),leftEdge=window.preferedLeft.get(), w=window.preferedWidth.get(),h=window.preferedHeight.get()) Utils.silentCheckForUpdates() # bring tab to front; evaluate lazily as sometimes UI can show other errors and this command somehow fails cmds.evalDeferred(lambda *args: cmds.dockControl(MainWindow.DOCK_NAME,e=True,r=True)); # a bit of a fake, but can't find a better place for an infrequent save LayerEvents.layerAvailabilityChanged.addHandler(window.savePrefs, MainWindow.DOCK_NAME) return window
def downloadInfo(self, successCallback, failureCallback): ''' executes version info download in separate thread, then runs callback in main thread when download completes or fails ''' import maya.utils import urllib2 import urllib import json import threading mayaVersion = str(self.roundMayaVersion(Utils.getMayaVersion())) resource = "ngSkinTools-v1-" + Utils.getOs() + "-maya" + mayaVersion endpoint = "https://versiondb.ngskintools.com/releases/" + resource + "?" + urllib.urlencode( { 'currentVersion': version.pluginVersion(), 'buildWatermark': version.buildWatermark(), 'uniqueClientId': version.uniqueClientId(), }) def runnerFunc(): try: result = urllib2.urlopen(endpoint).read() maya.utils.executeDeferred(successCallback, json.loads(result)) except Exception, err: maya.utils.executeDeferred(failureCallback, str(err))
def __init__(self, allowMultiSelection=True): self.items = [] selectCommand = self.selectionChanged editCommand = self.editLabelCommand if Utils.getMayaVersion() < Utils.MAYA2012: selectCommand = Utils.createMelProcedure(self.selectionChanged, [('int', 'item'), ('int', 'state')], returnType="int") editCommand = Utils.createMelProcedure(self.editLabelCommand, [('string', 'item'), ('string', 'newName')]) self.control = cmds.treeView( numberOfButtons=0, height=100, selectCommand=selectCommand, editLabelCommand=editCommand, dragAndDropCommand=self.handleDragDropCommand) cmds.treeView(self.control, e=True, enableKeys=True) # list of selected IDs self.selectedItems = [] self.onSelectionChanged = Signal() self.__selectionChanging = False self.__itemNameChanging = False
def closeNextDialogWithResult(result): ''' close next modal dialog with given result ''' if Utils.getMayaVersion()>=Utils.MAYA2011: mUtils.executeDeferred(lambda:BaseDialog.currentDialog.closeDialogWithResult(result)) else: Utils.displayError("hurray for maya 2009, close dialog manually with result "+result)
def selectPaintWeightsInfluence(self, infl): ''' tries to select influence (provided as string) in current maya's paint weights context and UI if skin paint context is not available, nothing happens ''' if not Utils.isCurrentlyPaintingWeights(): return # influence name can come in any form ('joint3', 'joint2|joint3', 'joint1|joint2|joint3') # get the absolute shortest possible (but still unique) and longest try: longName = cmds.ls(infl, l=True)[0] shortName = cmds.ls(longName, l=False)[0] log.info("selecting in paint weights: influence %s" % str(infl)) # try to work around with the mess in the earlier versions of # maya's paint weights UI: if Utils.getMayaVersion() < Utils.MAYA2011: itemName = Utils.mel('artAttrSkinShortName("%s")' % shortName) Utils.mel( 'artSkinSelectInfluence("artAttrSkinPaintCtx","%s","%s");' % (shortName, itemName)) else: Utils.mel( 'artSkinSelectInfluence("artAttrSkinPaintCtx","%s");' % shortName) # show paint weights interface cmds.toolPropertyWindow() except: # problems listing given influence.. just die here Utils.displayError('problem selecting influence %s' % infl)
def selectPaintWeightsInfluence(self,infl): ''' tries to select influence (provided as string) in current maya's paint weights context and UI if skin paint context is not available, nothing happens ''' if not Utils.isCurrentlyPaintingWeights(): return # influence name can come in any form ('joint3', 'joint2|joint3', 'joint1|joint2|joint3') # get the absolute shortest possible (but still unique) and longest try: longName = cmds.ls(infl,l=True)[0] shortName = cmds.ls(longName,l=False)[0] log.info("selecting in paint weights: influence %s" % str(infl)) # try to work around with the mess in the earlier versions of # maya's paint weights UI: if Utils.getMayaVersion()<Utils.MAYA2011: itemName = Utils.mel('artAttrSkinShortName("%s")'%shortName) Utils.mel('artSkinSelectInfluence("artAttrSkinPaintCtx","%s","%s");' % (shortName,itemName)); else: Utils.mel('artSkinSelectInfluence("artAttrSkinPaintCtx","%s");' % shortName); # show paint weights interface cmds.toolPropertyWindow() except: # problems listing given influence.. just die here Utils.displayError('problem selecting influence %s' % infl)
def execute(self): ldm = LayerDataModel.getInstance() for layerId in ldm.layerListsUI.getSelectedLayers(): if ldm.mll.getLayerIndex(layerId) == 0: Utils.displayError("Cannot merge lowest layer") return ldm.mll.layerMergeDown(layerId) LayerEvents.layerListModified.emit() self.onExecuted.emit()
def initialize(self): log.debug("creating headless data host") LayerDataModel.reset() restartEvents() Utils.loadPlugin() MayaEvents.registerScriptJobs() LayerDataModel.getInstance()
def initialize(self): log.debug("creating headless data host") Utils.loadPlugin() MayaEvents.registerScriptJobs() LayerDataModel.bindAll() selectionState.bindAll() LayerDataModel.getInstance().updateLayerAvailability() selectionState.selectionInfo.dropCache() selectionState.mirrorInfo.dropCache()
def closeNextDialogWithResult(result): ''' close next modal dialog with given result ''' if Utils.getMayaVersion() >= Utils.MAYA2011: mUtils.executeDeferred( lambda: BaseDialog.currentDialog.closeDialogWithResult(result)) else: Utils.displayError( "hurray for maya 2009, close dialog manually with result " + result)
def closeDialogWithResult(self, buttonID): if buttonID==self.BUTTON_OK: self.controls.name.setValue(self.controls.name.getValue().strip()) # check if valid layer name was entered # new layer mode should allow empty value if not self.newLayerMode and self.layerNameValue.get().strip()=='': Utils.confirmDialog( title='Validation Error', message="layer name cannot be blank", button=['Ok'], defaultButton='Ok') return BaseDialog.closeDialogWithResult(self, buttonID)
def execute(self): ''' button handler for "Mirror Skin Weights" ''' from ngSkinTools.ui.mainwindow import MainWindow from ngSkinTools.ui.tabMirror import TabMirror # any problems? maybe cache is not initialized/outdated? layerData = LayerDataModel.getInstance() layerData.updateMirrorCacheStatus() if not layerData.mirrorCache.isValid: Utils.displayError(layerData.mirrorCache.message) return False try: mirrorTab = MainWindow.getInstance().tabMirror mirrorDirection = MirrorDirection.DIRECTION_POSITIVETONEGATIVE if mirrorTab.controls.mirrorDirection.getValue() == 0: # guess mirrorDirection = MirrorDirection.DIRECTION_GUESS if mirrorTab.controls.mirrorDirection.getValue( ) == 2: # negative to positive mirrorDirection = MirrorDirection.DIRECTION_NEGATIVETOPOSITIVE if mirrorTab.controls.mirrorDirection.getSelectedText( ) == TabMirror.MIRROR_FLIP: mirrorDirection = MirrorDirection.DIRECTION_FLIP with LayerDataModel.getInstance().mll.batchUpdateContext(): for layerId in LayerDataModel.getInstance( ).layerListsUI.getSelectedLayers(): LayerDataModel.getInstance().mll.mirrorLayerWeights( layerId, mirrorWidth=mirrorTab.controls.mirrorWidth.getValue(), mirrorLayerWeights=mirrorTab.controls.mirrorWeights. getValue(), mirrorLayerMask=mirrorTab.controls.mirrorMask.getValue( ), mirrorDualQuaternion=mirrorTab.controls.mirrorDq. getValue(), mirrorDirection=mirrorDirection) # if layer list is filtered, might be handy to refresh influence list now - not that it costs much LayerEvents.influenceListChanged.emit() return True except: import traceback traceback.print_exc() Utils.displayError("unknown error") # we're not sure what's wrong in this case, just re-raise raise
def execCleanNodes(self,*args): if not LayerUtils.hasCustomNodes(): Utils.confirmDialog(icon='information', title='Info', message='Scene does not contain any custom ngSkinTools nodes.', button=['Ok']); return message = 'This command deletes all custom nodes from ngSkinTools plugin. Skin weights will be preserved, but all layer data will be lost. Do you want to continue?' if Utils.confirmDialog( icon='warning', title='Warning', message=message, button=['Yes','No'], defaultButton='No')!='Yes': return LayerDataModel.getInstance().cleanCustomNodes()
def execute(self): ldm = LayerDataModel.getInstance() for layerId in ldm.layerListsUI.getSelectedLayers(): if ldm.mll.getLayerIndex(layerId)==0: Utils.displayError("Cannot merge lowest layer") return ldm.mll.layerMergeDown(layerId) LayerEvents.layerListModified.emit() self.onExecuted.emit()
def createLayerListsUI(self, parent): cmds.setParent(parent) #self.outerFrame = cmds.frameLayout(label='Skinning Layers',collapsable=False,borderVisible=True,borderStyle="etchedIn",labelAlign="center") if Utils.getMayaVersion() < Utils.MAYA2011: # pane layout is ugly if it's non-QT UI; just use simple 50:50 form layout paneLayout = FormLayout(numberOfDivisions=100) else: paneLayout = cmds.paneLayout(configuration="vertical2", width=100, height=200) leftForm = form = FormLayout() label = cmds.text("Layers:", align="left", font='boldLabelFont') list = self.controls.layerDisplay = LayersTreeView() list.onSelectionChanged.addHandler(self.layerSelectionChanged) form.attachForm(label, 10, 0, None, Constants.MARGIN_SPACING_HORIZONTAL) form.attachForm(list.control, None, 0, 0, Constants.MARGIN_SPACING_HORIZONTAL) form.attachControl(list.control, label, 3, None, None, None) cmds.setParent("..") rightForm = form = FormLayout() label = cmds.text("Influences:", align="left", font='boldLabelFont') list = self.controls.influenceDisplay = TreeViewIDList( allowMultiSelection=True) list.onSelectionChanged.addHandler(self.execInfluenceSelected) self.createLayersListRMBMenu() self.createInfluenceListRMBMenu() form.attachForm(label, 10, Constants.MARGIN_SPACING_HORIZONTAL, None, 0) form.attachForm(list.control, None, Constants.MARGIN_SPACING_HORIZONTAL, 0, 0) form.attachControl(list.control, label, 3, None, None, None) if Utils.getMayaVersion() < Utils.MAYA2011: paneLayout.attachForm(leftForm, 0, None, 0, 0) paneLayout.attachForm(rightForm, 0, 0, 0, None) cmds.formLayout(paneLayout, e=True, attachPosition=[[leftForm, 'right', 3, 50], [rightForm, 'left', 3, 50]]) return paneLayout
def initialize(self): log.debug("creating headless data host") self.scriptJobs = [] LayerDataModel.reset() restartEvents() Utils.loadPlugin() self.registerScriptJob('SelectionChanged',MayaEvents.nodeSelectionChanged.emit) self.registerScriptJob('Undo',MayaEvents.undoRedoExecuted.emit) self.registerScriptJob('Redo',MayaEvents.undoRedoExecuted.emit) self.registerScriptJob('ToolChanged',MayaEvents.toolChanged.emit) LayerDataModel.getInstance()
def closeDialogWithResult(self, buttonID): if buttonID == self.BUTTON_OK: self.controls.name.setValue(self.controls.name.getValue().strip()) # check if valid layer name was entered # new layer mode should allow empty value if not self.newLayerMode and self.layerNameValue.get().strip( ) == '': Utils.confirmDialog(title='Validation Error', message="layer name cannot be blank", button=['Ok'], defaultButton='Ok') return BaseDialog.closeDialogWithResult(self, buttonID)
def initialize(self): log.debug("creating headless data host") self.scriptJobs = [] LayerDataModel.reset() restartEvents() Utils.loadPlugin() self.registerScriptJob("SelectionChanged", MayaEvents.nodeSelectionChanged.emit) self.registerScriptJob("Undo", MayaEvents.undoRedoExecuted.emit) self.registerScriptJob("Redo", MayaEvents.undoRedoExecuted.emit) self.registerScriptJob("ToolChanged", MayaEvents.toolChanged.emit) LayerDataModel.getInstance()
def getFormats(): ''' returns iterator to available exporters ''' yield Formats.getXmlFormat() if Utils.getMayaVersion() > Utils.MAYA2010: yield Formats.getJsonFormat()
def update(self): cmds.control(self.controls.nonCommercialBanner, e=True, visible=not license.status.isLicenseActive()) attachPoint = self.data.getLayersCandidateFromSelection() attachPossible = bool(attachPoint) selection = cmds.ls(sl=True,o=True) selectionAvailable = selection is not None and len(selection)>0 if attachPossible: self.controls.label1.setLabel('Skin selected:') self.controls.label2.setLabel("%s (%s)" % tuple(map(Utils.shortName,tuple(attachPoint)))) elif selectionAvailable: self.controls.label1.setLabel("Layer data cannot be attached to:") self.controls.label2.setLabel(Utils.shortName(selection[0])) else: self.controls.label1.setLabel("Nothing is selected") self.controls.label2.setLabel('') self.controls.label1.setEnabled(selectionAvailable) self.controls.label2.setEnabled(selectionAvailable) cmds.button(self.controls.addLayerDataButton,e=True,enable=attachPossible)
def createBrushShapeButtons(self): class ButtonClickHandler: def __init__(self, number, parent): self.number = number self.parent = parent def __call__(self, *args): self.parent.brushButtonClicked(self.number) newIcons = [ 'circleGaus.png', 'circlePoly.png', 'circleSolid.png', 'rect.png' ] oldIcons = [ 'circleGaus.xpm', 'circlePoly.xpm', 'circleSolid.xpm', 'rect.xpm' ] icons = newIcons if Utils.getMayaVersion( ) >= Utils.MAYA2011 else oldIcons for index, i in enumerate(icons): btn = cmds.symbolCheckBox(w=33, h=36, i=i, changeCommand=ButtonClickHandler( index, self), value=index == self.brushShape.get()) self.controls.brushShapeButtons.append(btn)
def useSkinClusterInputMesh(self, skinCluster): ''' sets skincluster's input mesh as source mesh ''' skinClusterObject = Utils.getMObjectForNode(skinCluster) geomFilter = oma.MFnGeometryFilter(skinClusterObject) self.meshMObject = geomFilter.inputShapeAtIndex(0)
def useSkinClusterInputMesh(self,skinCluster): ''' sets skincluster's input mesh as source mesh ''' skinClusterObject = Utils.getMObjectForNode(skinCluster) geomFilter = oma.MFnGeometryFilter(skinClusterObject) self.meshMObject = geomFilter.inputShapeAtIndex(0)
def update(self): if self.data.layerDataAvailable: return attachPoint = self.data.getLayersCandidateFromSelection() attachPossible = len(attachPoint)!=0 selection = cmds.ls(sl=True,o=True) selectionAvailable = selection is not None and len(selection)>0 if attachPossible: self.controls.label1.setLabel('Skin selected:') self.controls.label2.setLabel("%s (%s)" % tuple(map(Utils.shortName,tuple(attachPoint)))) elif selectionAvailable: self.controls.label1.setLabel("Layer data cannot be attached to:") self.controls.label2.setLabel(Utils.shortName(selection[0])) else: self.controls.label1.setLabel("Nothing is selected") self.controls.label2.setLabel('') self.controls.label1.setEnabled(selectionAvailable) self.controls.label2.setEnabled(selectionAvailable) cmds.button(self.controls.addLayerDataButton,e=True,enable=attachPossible)
def selectFile(self, forSave): ''' shows UI for file selection; returns file name or None ''' extensionList = ";".join( map(lambda a: "*.%s" % a, self.ioFormat.recommendedExtensions)) caption = ('Export as %s' if forSave else 'Import from %s') % self.ioFormat.title fileFilter = 'Layer data in %s format (%s);;All Files (*.*)' % ( self.ioFormat.title, extensionList) if Utils.getMayaVersion() >= Utils.MAYA2011: result = cmds.fileDialog2(dialogStyle=1, caption=caption, fileFilter=fileFilter, fileMode=0 if forSave else 1, returnFilter=True) if result is None: return None return result[0] else: result = cmds.fileDialog(title=caption, directoryMask=extensionList, mode=1 if forSave else 0) if result == "": return None return result
def getLogicalInfluenceIndex(self,influenceName): try: path = Utils.getDagPathForNode(influenceName) except: raise MessageException("Could not find influence '%s' in %s" % (influenceName, self.skinCluster)) return self.fn.indexForInfluenceObject(path)
def open(): ''' just a shortcut method to construct and display main window ''' window = MainWindow.getInstance() window.showWindow() # don't know where to fit this in, it's just an utility warning for those trying to run # this on a different maya version if Utils.getMayaVersion()==Utils.MAYAUNSUPPORTEDVERSION: Utils.displayError('unsupported Maya version detected.') Utils.silentCheckForUpdates() return window
def update(self): if self.data.layerDataAvailable: return attachPoint = self.data.getLayersCandidateFromSelection() attachPossible = len(attachPoint) != 0 selection = cmds.ls(sl=True, o=True) selectionAvailable = selection is not None and len(selection) > 0 if attachPossible: self.controls.label1.setLabel('Skin selected:') self.controls.label2.setLabel( "%s (%s)" % tuple(map(Utils.shortName, tuple(attachPoint)))) elif selectionAvailable: self.controls.label1.setLabel("Layer data cannot be attached to:") self.controls.label2.setLabel(Utils.shortName(selection[0])) else: self.controls.label1.setLabel("Nothing is selected") self.controls.label2.setLabel('') self.controls.label1.setEnabled(selectionAvailable) self.controls.label2.setEnabled(selectionAvailable) cmds.button(self.controls.addLayerDataButton, e=True, enable=attachPossible)
def exit(self, *args): """ exits tool and resets it to the ngSkinTools settings """ init_mel = Utils.createMelProcedure(ngLayerPaintCtxInitialize, [('string', 'mesh')], returnType='string') cmds.artUserPaintCtx("ngSkinToolsLayerPaintCtx", e=True, setValueCommand="ngLayerPaintCtxSetValue", initializeCmd=init_mel, finalizeCmd="ngLayerPaintCtxFinalize", value=self.defaultVal) cmds.rowColumnLayout(self.ng_radio_layout[0], e=True, enable=True) cmds.checkBox(self.ng_checkboxes[0], e=True, enable=True) cmds.checkBox(self.ng_checkboxes[1], e=True, enable=True) cmds.rowLayout(self.ng_slider_rows[0], e=True, enable=True) cmds.formLayout(self.radio_form, e=True, enable=False) cmds.button(self.floodButton, e=True, enable=False) cmds.rowLayout(self.intensity_row, e=True, enable=False) cmds.rowLayout(self.volume_row, e=True, enable=False) ng_paint_mode = [(self.ng_radio_buttons.index(i) + 1) for i in self.ng_radio_buttons if cmds.radioButton(i, q=True, select=True) is True] ng_intensity_value = cmds.floatSlider(self.ng_intensity[0], q=True, value=True) cmds.ngSkinLayer(paintOperation=ng_paint_mode[0], paintIntensity=ng_intensity_value)
def getSelectionDagPaths(hilite): ''' similar functionality to cmds.ls, but returns transform nodes where shapes might be selected, and does not return components. ''' from maya import OpenMaya as om selection = om.MSelectionList() if hilite: om.MGlobal.getHiliteList(selection) else: om.MGlobal.getActiveSelectionList(selection) result = [] for i in Utils.mIter(om.MItSelectionList(selection)): path = om.MDagPath() i.getDagPath(path) selectionPath = path.fullPathName() # if it's a shape node, extend upwards if path.node().hasFn(om.MFn.kShape): parentPath = om.MDagPath() om.MFnDagNode( om.MFnDagNode(path).parent(0)).getPath(parentPath) selectionPath = parentPath.fullPathName() if not selectionPath in result: result.append(selectionPath) return result
def getSelectionDagPaths(hilite): """ similar functionality to cmds.ls, but returns transform nodes where shapes might be selected, and does not return components. """ from maya import OpenMaya as om selection = om.MSelectionList() if hilite: om.MGlobal.getHiliteList(selection) else: om.MGlobal.getActiveSelectionList(selection) result = [] for i in Utils.mIter(om.MItSelectionList(selection)): path = om.MDagPath() i.getDagPath(path) selectionPath = path.fullPathName() # if it's a shape node, extend upwards if path.node().hasFn(om.MFn.kShape): parentPath = om.MDagPath() om.MFnDagNode(om.MFnDagNode(path).parent(0)).getPath(parentPath) selectionPath = parentPath.fullPathName() if not selectionPath in result: result.append(selectionPath) return result
def open(): ''' just a shortcut method to construct and display main window ''' window = MainWindow.getInstance() window.showWindow() # don't know where to fit this in, it's just an utility warning for those trying to run # this on a different maya version if Utils.getMayaVersion() == Utils.MAYAUNSUPPORTEDVERSION: Utils.displayError('unsupported Maya version detected.') Utils.silentCheckForUpdates() return window
def doInflListPaintWeights(self, *args): ''' opens paint weights tool and paints on locally selected influence ''' if not Utils.isCurrentlyPaintingWeights(): cmds.ArtPaintSkinWeightsTool() self.doInfluenceListItemSelected()
def execRelax(self,*args): ''' relax button click handler. this is where it actually executes skin relax, whoop-tee-doo. ''' try: args = {} args['numSteps']=self.controls.numSteps.getValue() args['stepSize']=self.controls.stepSize.getValue() # do we need soft selection? self.controls.softSelection.addToArgs(args) # do we need volume association? if self.controls.useVolumeAssociation.getValue(): args['abv']=1 args['avr']=self.controls.volumeAssociationRadius.getValue() # add selection+highlight as arguments # we need highlight as second argument because # other meshes for simulation might be included only through highlight. # # This will never be an empty list as we tested for vertex selection available earlier def makeList(listOrNull): if listOrNull is None: return [] return listOrNull objects = makeList(cmds.ls(sl=True))+makeList(cmds.ls(hl=True)) if len(objects)==0: raise MessageException("Nothing is selected") # execute stuff try: cmds.waitCursor(state=True) cmds.ngSkinRelax(objects,**args) finally: cmds.waitCursor(state=False) Utils.refreshPaintWeightsTool() except MessageException,err: raise err
def execRelax(self, *args): ''' relax button click handler. this is where it actually executes skin relax, whoop-tee-doo. ''' try: args = {} args['numSteps'] = self.controls.numSteps.getValue() args['stepSize'] = self.controls.stepSize.getValue() # do we need soft selection? self.controls.softSelection.addToArgs(args) # do we need volume association? if self.controls.useVolumeAssociation.getValue(): args['abv'] = 1 args['avr'] = self.controls.volumeAssociationRadius.getValue() # add selection+highlight as arguments # we need highlight as second argument because # other meshes for simulation might be included only through highlight. # # This will never be an empty list as we tested for vertex selection available earlier def makeList(listOrNull): if listOrNull is None: return [] return listOrNull objects = makeList(cmds.ls(sl=True)) + makeList(cmds.ls(hl=True)) if len(objects) == 0: raise MessageException("Nothing is selected") # execute stuff try: cmds.waitCursor(state=True) cmds.ngSkinRelax(objects, **args) finally: cmds.waitCursor(state=False) Utils.refreshPaintWeightsTool() except MessageException, err: raise err
def doStartPaint(self): if not cmds.artUserPaintCtx(self.TOOL_PAINT,exists=True): cmds.artUserPaintCtx(self.TOOL_PAINT); cmds.artUserPaintCtx(self.TOOL_PAINT,e=True, tsc=Utils.createMelProcedure(self.paintCtxSetupProcedure, [('string','toolContext')]), toolCleanupCmd=Utils.createMelProcedure(self.paintCtxCleanupProcedure, [('string','toolContext')]), initializeCmd="ngLayerPaintCtxInitialize", finalizeCmd="ngLayerPaintCtxFinalize", setValueCommand="ngLayerPaintCtxSetValue", getValueCommand="ngLayerPaintCtxGetValue", whichTool="userPaint", fullpaths = True ) self.configurePaintValues() cmds.setToolTo(self.TOOL_PAINT);
def getLogicalInfluenceIndex(self, influenceName): try: path = Utils.getDagPathForNode(influenceName) except: raise MessageException("Could not find influence '%s' in %s" % (influenceName, self.skinCluster)) return self.fn.indexForInfluenceObject(path)
def doInflListPaintWeights(self,*args): ''' opens paint weights tool and paints on locally selected influence ''' if not Utils.isCurrentlyPaintingWeights(): cmds.ArtPaintSkinWeightsTool() self.doInfluenceListItemSelected()
def createWindow(self): ''' creates main GUI window and it's contents ''' BaseToolWindow.createWindow(self) self.targetUI = TargetDataDisplay() self.actions = MainUiActions(self.windowName) self.mainMenu = MainMenu() self.mainMenu.create() # putting tabs in a from targetUiLayout is needed to workaround maya2011 # bug with an additional empty tab appearing otherwise # form = FormLayout(parent=self.windowName) # targetUiLayout = self.targetUI.create(form) # form.attachForm(targetUiLayout, 0, Constants.MARGIN_SPACING_HORIZONTAL,None,Constants.MARGIN_SPACING_HORIZONTAL) # # self.mainTabLayout = cmds.tabLayout(childResizable=True,parent=form,scrollable=False,innerMarginWidth=3) # form.attachControl(self.mainTabLayout, targetUiLayout, Constants.MARGIN_SPACING_VERTICAL, None,None,None) # form.attachForm(self.mainTabLayout, None, 0,0,0) self.splitPosition = PersistentValueModel( name="ngSkinTools_mainWindow_splitPosition", defaultValue=50) def updateSplitPosition(*args): size = cmds.paneLayout(horizontalSplit, q=True, paneSize=True) # returns (widht, height, width, height) self.splitPosition.set(size[1]) horizontalSplit = cmds.paneLayout( configuration="horizontal2", width=100, height=200, separatorMovedCommand=updateSplitPosition) if Utils.getMayaVersion() >= Utils.MAYA2011: cmds.paneLayout(horizontalSplit, e=True, staticHeightPane=2) cmds.paneLayout(horizontalSplit, e=True, paneSize=(1, 100, self.splitPosition.get())) cmds.paneLayout(horizontalSplit, e=True, paneSize=(2, 100, 100 - self.splitPosition.get())) targetUiLayout = self.targetUI.create(horizontalSplit) self.mainTabLayout = cmds.tabLayout(childResizable=True, parent=horizontalSplit, scrollable=False, innerMarginWidth=3) self.tabPaint = self.addTab(TabPaint()) self.tabMirror = self.addTab(TabMirror()) self.tabRelax = self.addTab(TabSkinRelax()) self.tabAssignWeights = self.addTab(TabAssignWeights()) self.tabSettings = self.addTab(TabSettings()) self.actions.updateEnabledAll()
def execute(self): ''' button handler for "Mirror Skin Weights" ''' from ngSkinTools.ui.mainwindow import MainWindow from ngSkinTools.ui.tabMirror import TabMirror # any problems? maybe cache is not initialized/outdated? layerData = LayerDataModel.getInstance() layerData.updateMirrorCacheStatus() if not layerData.mirrorCache.isValid: Utils.displayError(layerData.mirrorCache.message) return False try: mirrorTab = MainWindow.getInstance().tabMirror mirrorDirection = MirrorDirection.DIRECTION_POSITIVETONEGATIVE if mirrorTab.controls.mirrorDirection.getValue()==0: # guess mirrorDirection = MirrorDirection.DIRECTION_GUESS; if mirrorTab.controls.mirrorDirection.getValue()==2: # negative to positive mirrorDirection = MirrorDirection.DIRECTION_NEGATIVETOPOSITIVE; if mirrorTab.controls.mirrorDirection.getSelectedText()==TabMirror.MIRROR_FLIP: mirrorDirection = MirrorDirection.DIRECTION_FLIP; with LayerDataModel.getInstance().mll.batchUpdateContext(): for layerId in LayerDataModel.getInstance().layerListsUI.getSelectedLayers(): LayerDataModel.getInstance().mll.mirrorLayerWeights(layerId, mirrorWidth=mirrorTab.controls.mirrorWidth.getValue(), mirrorLayerWeights=mirrorTab.controls.mirrorWeights.getValue(), mirrorLayerMask=mirrorTab.controls.mirrorMask.getValue(), mirrorDualQuaternion=mirrorTab.controls.mirrorDq.getValue(), mirrorDirection=mirrorDirection ) # if layer list is filtered, might be handy to refresh influence list now - not that it costs much LayerEvents.influenceListChanged.emit() return True except: import traceback;traceback.print_exc(); Utils.displayError("unknown error") # we're not sure what's wrong in this case, just re-raise raise
def createScrollLayout(parent): if Utils.getMayaVersion() >= Utils.MAYA2011: return cmds.scrollLayout(parent=parent, childResizable=True) # scrollbar fake shamelessly stolen from Autodesk's own UI code return cmds.tabLayout(parent=parent, tv=False, childResizable=True, scrollable=True)
def execCleanNodes(self, *args): if not LayerUtils.hasCustomNodes(): Utils.confirmDialog( icon='information', title='Info', message='Scene does not contain any custom ngSkinTools nodes.', button=['Ok']) return message = 'This command deletes all custom nodes from ngSkinTools plugin. Skin weights will be preserved, but all layer data will be lost. Do you want to continue?' if Utils.confirmDialog(icon='warning', title='Warning', message=message, button=['Yes', 'No'], defaultButton='No') != 'Yes': return LayerDataModel.getInstance().cleanCustomNodes()
def createLayerListsUI(self,parent): cmds.setParent(parent) #self.outerFrame = cmds.frameLayout(label='Skinning Layers',collapsable=False,borderVisible=True,borderStyle="etchedIn",labelAlign="center") if Utils.getMayaVersion()<Utils.MAYA2011: # pane layout is ugly if it's non-QT UI; just use simple 50:50 form layout paneLayout = FormLayout(numberOfDivisions=100) else: paneLayout = cmds.paneLayout(configuration="vertical2",width=100,height=200) leftForm = form = FormLayout() label = cmds.text("Layers:",align="left",font='boldLabelFont') list = self.controls.layerDisplay = LayersTreeView() list.onSelectionChanged.addHandler(self.layerSelectionChanged) form.attachForm(label,10,0,None,Constants.MARGIN_SPACING_HORIZONTAL) form.attachForm(list.control,None,0,0,Constants.MARGIN_SPACING_HORIZONTAL) form.attachControl(list.control,label,3,None,None,None) cmds.setParent("..") rightForm = form = FormLayout() label = cmds.text("Influences:",align="left",font='boldLabelFont') list = self.controls.influenceDisplay = TreeViewIDList(allowMultiSelection=True) list.onSelectionChanged.addHandler(self.execInfluenceSelected) self.createLayersListRMBMenu() self.createInfluenceListRMBMenu() form.attachForm(label,10,Constants.MARGIN_SPACING_HORIZONTAL,None,0) form.attachForm(list.control,None,Constants.MARGIN_SPACING_HORIZONTAL,0,0) form.attachControl(list.control,label,3,None,None,None) if Utils.getMayaVersion()<Utils.MAYA2011: paneLayout.attachForm(leftForm, 0, None, 0, 0) paneLayout.attachForm(rightForm, 0, 0, 0, None) cmds.formLayout(paneLayout,e=True,attachPosition=[[leftForm,'right',3,50],[rightForm,'left',3,50]]) return paneLayout
def __init__(self,allowMultiSelection=True): self.items = [] selectCommand = self.selectionChanged editCommand = self.editLabelCommand if Utils.getMayaVersion()<Utils.MAYA2012: selectCommand = Utils.createMelProcedure(self.selectionChanged, [('int','item'),('int','state')],returnType="int") editCommand = Utils.createMelProcedure(self.editLabelCommand, [('string','item'),('string','newName')]) self.control = cmds.treeView(numberOfButtons=0, height=100, selectCommand=selectCommand, editLabelCommand=editCommand,dragAndDropCommand=self.handleDragDropCommand) cmds.treeView(self.control,e=True,enableKeys=True) # list of selected IDs self.selectedItems = [] self.onSelectionChanged = Signal() self.__selectionChanging = False self.__itemNameChanging = False
def createHelpButton(helpLink): import os.path as path imageName = path.join(path.dirname(__file__),'images','help.xpm') if Utils.getMayaVersion()>=Utils.MAYA2011: imageName = path.join(path.dirname(__file__),'images','help.png') return cmds.symbolButton('?',image=imageName,height=Constants.BUTTON_HEIGHT,width=Constants.BUTTON_WIDTH_SMALL, annotation='Open manual at: '+helpLink.getTitle(), command=lambda *args:HeadlessDataHost.get().documentation.openLink(helpLink))
def runSuite(suite): global lastTestResult lastTestResult = "Tests are being executed" try: Utils.DEBUG_MODE = True Utils.loadPlugin() result = unittest.TextTestRunner(verbosity=2).run(suite) lastTestResult = "tests executed: %d, tests failed: %d" % (result.testsRun,len(result.errors)+len(result.failures)) for i in result.errors+result.failures: lastTestResult += "\n" lastTestResult += "FAILED: "+str(i[0]) except: lastTestResult = "Tests failed to finish" raise print lastTestResult return result
def createWindow(self): ''' creates main GUI window and it's contents ''' BaseToolWindow.createWindow(self) self.targetUI = TargetDataDisplay() self.actions = MainUiActions(self.windowName) self.mainMenu = MainMenu() self.mainMenu.create(); # putting tabs in a from targetUiLayout is needed to workaround maya2011 # bug with an additional empty tab appearing otherwise # form = FormLayout(parent=self.windowName) # targetUiLayout = self.targetUI.create(form) # form.attachForm(targetUiLayout, 0, Constants.MARGIN_SPACING_HORIZONTAL,None,Constants.MARGIN_SPACING_HORIZONTAL) # # self.mainTabLayout = cmds.tabLayout(childResizable=True,parent=form,scrollable=False,innerMarginWidth=3) # form.attachControl(self.mainTabLayout, targetUiLayout, Constants.MARGIN_SPACING_VERTICAL, None,None,None) # form.attachForm(self.mainTabLayout, None, 0,0,0) self.splitPosition = PersistentValueModel(name="ngSkinTools_mainWindow_splitPosition", defaultValue=50) def updateSplitPosition(*args): size = cmds.paneLayout(horizontalSplit,q=True,paneSize=True) # returns (widht, height, width, height) self.splitPosition.set(size[1]) horizontalSplit = cmds.paneLayout(configuration="horizontal2",width=100,height=200,separatorMovedCommand=updateSplitPosition) if Utils.getMayaVersion()>=Utils.MAYA2011: cmds.paneLayout(horizontalSplit,e=True,staticHeightPane=2) cmds.paneLayout(horizontalSplit,e=True,paneSize=(1,100,self.splitPosition.get())) cmds.paneLayout(horizontalSplit,e=True,paneSize=(2,100,100-self.splitPosition.get())) targetUiLayout = self.targetUI.create(horizontalSplit) self.mainTabLayout = cmds.tabLayout(childResizable=True,parent=horizontalSplit,scrollable=False,innerMarginWidth=3) self.tabPaint = self.addTab(TabPaint()) self.tabMirror = self.addTab(TabMirror()) self.tabRelax = self.addTab(TabSkinRelax()) self.tabAssignWeights = self.addTab(TabAssignWeights()) self.tabSettings = self.addTab(TabSettings()) self.actions.updateEnabledAll()
def getImportOptions(self): options = ImportOptions() if self.getMll().getLayersAvailable(): if Utils.confirmDialog( icon='question', title='Import', message='Do you want to keep existing layers?', button=['Yes','No'], defaultButton='Yes')=='No': options.keepExistingLayers = False return options
def runSuite(suite): global lastTestResult lastTestResult = "Tests are being executed" try: Utils.DEBUG_MODE = True Utils.loadPlugin() result = unittest.TextTestRunner(verbosity=2).run(suite) lastTestResult = "tests executed: %d, tests failed: %d" % ( result.testsRun, len(result.errors) + len(result.failures)) for i in result.errors + result.failures: lastTestResult += "\n" lastTestResult += "FAILED: " + str(i[0]) except: lastTestResult = "Tests failed to finish" raise print lastTestResult return result
def testShortName(self): self.assertEquals(Utils.shortName("a|b|c"), "c") self.assertEquals(Utils.shortName("c"), "c") self.assertEquals(Utils.shortName("a:c"), "a:c") self.assertEquals(Utils.shortName("a:b|a:c"), "a:c") self.assertEquals(Utils.shortName("a:a|a:b|a:c"), "a:c") self.assertEquals(Utils.shortName(None), None)
def openLink(self,link): ''' open given documentation link (should be an instance of DocLink) if documentation root is local, checks for link validity on success, opens a web browser ''' if self.documentationRoot is None: return uri = self.getURL(link) # if possible, check for link availability if not self.linkExists(uri): Utils.displayError('documentation path does not exist: '+uri) return if self.isLocal(uri): uri = uri.replace("\\", "/"); if not uri.lower().startswith("file:///"): uri = "file:///"+uri webbrowser.open_new(uri)
def export(self): ''' returns mesh triangles: first vertex list, then vertex ID list for each triangle; meshTransform (supplied as transform node name) is required to transform each vertex to world-space ''' # get triangles for the mesh fnMesh = om.MFnMesh(self.meshMObject) counts = om.MIntArray() vertices = om.MIntArray() fnMesh.getTriangles(counts,vertices) idList = [i for i in Utils.mIter(vertices)] # get point values points = om.MPointArray() fnMesh.getPoints(points) pointList = [] for p in Utils.mIter(points): p = p*self.transformMatrix pointList.extend((p.x,p.y,p.z)) # return point values, id values return pointList,idList
def createBrushShapeButtons(self): class ButtonClickHandler: def __init__(self,number,parent): self.number=number self.parent=parent def __call__(self,*args): self.parent.brushButtonClicked(self.number) newIcons = ['circleGaus.png','circlePoly.png','circleSolid.png','rect.png'] oldIcons = ['circleGaus.xpm','circlePoly.xpm','circleSolid.xpm','rect.xpm'] icons = newIcons if Utils.getMayaVersion()>=Utils.MAYA2011 else oldIcons for index,i in enumerate(icons): btn = cmds.symbolCheckBox(w=33,h=36,i=i,changeCommand=ButtonClickHandler(index,self),value=index==self.brushShape.get()) self.controls.brushShapeButtons.append(btn)
def loadPluginFromBuildTab(): from ngSkinTools.debug import reloadplugin from ngSkinTools.utils import Utils from os import path import os mayaVersion = Utils.getMayaVersion() pluginPath = path.join(path.dirname(path.dirname(path.dirname(__file__))),'build-target','windows','maya%d-64bit'%mayaVersion,'plugin','ngSkinTools.mll') targetDir = path.join(getUserMayaFolder(mayaVersion, 64),"plug-ins") if not os.path.exists(targetDir): os.makedirs(targetDir) targetPath = path.join(targetDir,"ngSkinTools.mll") print "copying plugin from '%s' to '%s'" % (pluginPath,targetPath) reloadplugin.reload(pluginPath,targetPath)
def selectFile(self,forSave): ''' shows UI for file selection; returns file name or None ''' extensionList = ";".join(map(lambda a: "*.%s" % a,self.ioFormat.recommendedExtensions)) caption = ('Export as %s' if forSave else 'Import from %s') %self.ioFormat.title fileFilter = 'Layer data in %s format (%s);;All Files (*.*)' % (self.ioFormat.title,extensionList) if Utils.getMayaVersion()>=Utils.MAYA2011: result = cmds.fileDialog2(dialogStyle=1,caption=caption,fileFilter=fileFilter,fileMode=0 if forSave else 1,returnFilter=True) if result is None: return None return result[0] else: result = cmds.fileDialog(title=caption,directoryMask=extensionList,mode=1 if forSave else 0); if result=="": return None return result