class MllInterface(object): ''' A wrapper object to call functionality from ngSkinLayer command. Most operations operate on current selection, or on target mesh that was set in advance. All edit operations are undoable. Example usage: .. code-block:: python from ngSkinTools.mllInterface import MllInterface mll = MllInterface() mll.setCurrentMesh('myMesh') mll.initLayers() id = mll.createLayer('initial weights') mll.setInfluenceWeights(id,0,[0.0,0.0,1.0,1.0]) ... ''' log = LoggerFactory.getLogger("MllInterface") TARGET_REFERENCE_MESH = 'ngSkinTools#TargetRefMesh' def __init__(self, mesh=None): self.setCurrentMesh(mesh) def setCurrentMesh(self, mesh): ''' Set mesh we'll be working on with in this wrapper. Use None to operate on current selection instead. :param str mesh: mesh node name/path ''' self.mesh = mesh def initLayers(self): ''' initializes layer data node setup for target mesh ''' self.ngSkinLayerCmd(lda=True) def getLayersAvailable(self): ''' returns true if layer data is available for target mesh :rtype: bool ''' try: result = self.ngSkinLayerCmd(q=True, lda=True) return result except Exception, err: self.log.error(err) import traceback traceback.print_exc() return False
# http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode # http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode.txt # # A copy of the license can be found in file 'LICENSE.txt', which is part # of this source code package. # from ngSkinTools.mllInterface import MllInterface from ngSkinTools.utils import MessageException, Utils from ngSkinTools.log import LoggerFactory from maya import OpenMayaAnim as oma from maya import OpenMaya as om from maya import cmds from ngSkinTools.skinClusterFn import SkinClusterFn log = LoggerFactory.getLogger("ImportInfluences") class ImportInfluences(object): def __init__(self): self.sourceSkinCluster = None self.destinationSkinCluster = None def __detectSkinCluster(self, mesh): mll = MllInterface() mll.setCurrentMesh(mesh) try: _, skinCluster = mll.getTargetInfo() except TypeError: raise MessageException("cannot find skin cluster attached to %s" % mesh)
class BaseToolWindow(object): log = LoggerFactory.getLogger("BaseToolWindow") windowInstances = {} def __init__(self,windowName): self.updateAvailable = False self.windowTitle = '' self.windowName = windowName self.sizeable = False self.menuBar = True self.defaultWidth = 300 self.defaultHeight = 300 self.useUserPrefSize = True BaseToolWindow.windowInstances[self.windowName]=self @staticmethod def closeAll(): for _, window in BaseToolWindow.windowInstances.items(): window.closeWindow() @staticmethod def getWindowInstance(windowName,windowClass=None): if BaseToolWindow.windowInstances.has_key(windowName): return BaseToolWindow.windowInstances[windowName] if windowClass is None: return None BaseToolWindow.destroyWindow(windowName); instance = windowClass(windowName) instance.createWindow() return instance def createWindow(self): self.log.debug("creating window "+self.windowName) if self.windowExists(self.windowName): raise Exception("window %s already opened" % self.windowName) if not self.useUserPrefSize: try: cmds.windowPref(self.windowName,remove=True) cmds.windowPref(self.windowName,width=self.defaultWidth,height=self.defaultHeight) except: pass cmds.window(self.windowName, title=self.windowTitle, maximizeButton=False, minimizeButton=False, width=self.defaultWidth, height=self.defaultHeight, sizeable=self.sizeable, menuBar=self.menuBar) cmds.scriptJob(uiDeleted=[self.windowName,self.onWindowDeleted]) HeadlessDataHost.HANDLE.addReference(self) def onWindowDeleted(self): if not HeadlessDataHost.HANDLE.removeReference(self): return BaseToolWindow.windowInstances.pop(self.windowName) def showWindow(self): if self.windowExists(self.windowName): cmds.showWindow(self.windowName) def closeWindow(self): if self.windowExists(self.windowName): self.onWindowDeleted() cmds.window(self.windowName,e=True,visible=False) @staticmethod def windowExists(windowName): return cmds.window(windowName, exists=True) @staticmethod def destroyWindow(windowName): if BaseToolWindow.windowExists(windowName): instance = BaseToolWindow.getWindowInstance(windowName) if instance is not None: instance.onWindowDeleted(); cmds.deleteUI(windowName, window=True) @staticmethod def closeAllWindows(): for i in BaseToolWindow.windowInstances.values(): i.closeWindow()
from __future__ import with_statement from maya import cmds from ngSkinTools.ui.uiWrappers import FormLayout, TextEdit, RadioButtonField from ngSkinTools.ui.constants import Constants from ngSkinTools.layerUtils import NamedPaintTarget from ngSkinTools.ui.layerDataModel import LayerDataModel from ngSkinTools.ui.events import LayerEvents, Signal, MayaEvents from ngSkinTools.utils import Utils from ngSkinTools.ui.options import Options, PersistentValueModel from ngSkinTools.log import LoggerFactory from ngSkinTools.InfluenceNameTransforms import InfluenceNameTransform from ngSkinTools.InfluenceNameFilter import InfluenceNameFilter from ngSkinTools.ui import uiWrappers log = LoggerFactory.getLogger("layerListsUI") class IdToNameEntry(object): def __init__(self, itemId=None, name=None, displayName=None, suffix=None): if displayName is None: displayName = name self.internalId = None self.id = itemId self.name = name self.displayName = displayName self.suffix = suffix @property
# of this source code package. # from maya import cmds from ngSkinTools.utils import Utils, MessageException from ngSkinTools.ui.basetab import BaseTab from ngSkinTools.ui.intensityslider import IntensitySlider from ngSkinTools.doclink import SkinToolsDocs from ngSkinTools.ui.uiWrappers import IntField, FloatField, CheckBoxField from ngSkinTools.ui.softSelectionRow import SoftSelectionRow from ngSkinTools.log import LoggerFactory log = LoggerFactory.getLogger("tabSkinRelax") class TabSkinRelax(BaseTab): ''' Defines a UI function set for Weights Relax operations. ''' # prefix for environment variables for this tab VAR_RELAX_PREFIX = 'ngSkinToolsRelaxTab_' def __init__(self): BaseTab.__init__(self)
from ngSkinTools.ui.layerDataModel import LayerDataModel from ngSkinTools.utils import Utils from maya import cmds from ngSkinTools.ui.events import MayaEvents, restartEvents from ngSkinTools.doclink import SkinToolsDocs from ngSkinTools.log import LoggerFactory log = LoggerFactory.getLogger("HeadlessDataHost") class RefCountedHandle: ''' reference counted handle to a dynamically allocated instance; creates reference after first addReference() call, and destroys it when last reference is removed via removeReference() ''' def __init__(self,instantiator): self.instantiator=instantiator self.instance = None self.references = set() def getCurrentInstance(self): ''' returns handle to currently created instance ''' return self.instance def addReference(self,refSource):
from __future__ import with_statement import unittest from ngSkinToolsTest import allTests from ngSkinTools.utils import Utils from ngSkinTools.log import LoggerFactory log = LoggerFactory.getLogger("mayaTestRunner") lastTestResult = "Tests were not run" 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
from maya import cmds from ngSkinTools.ui.uiWrappers import FormLayout, TextEdit, RadioButtonField from ngSkinTools.ui.constants import Constants from ngSkinTools.layerUtils import LayerUtils from ngSkinTools.ui.layerDataModel import LayerDataModel from ngSkinTools.ui.events import LayerEvents, Signal, MayaEvents from ngSkinTools.utils import Utils from ngSkinTools.ui.options import Options, PersistentValueModel from ngSkinTools.log import LoggerFactory from ngSkinTools.InfluenceNameTransforms import InfluenceNameTransform from ngSkinTools.InfluenceNameFilter import InfluenceNameFilter from ngSkinTools.ui.basetab import BaseTab log = LoggerFactory.getLogger("layerListsUI") class IdToNameEntry: def __init__(self,id=None,name=None,displayName=None,suffix=None): self.id = id self.name = name self.displayName = displayName if displayName is not None else name self.suffix = suffix def __repr__(self): return "IdToNameEntry(%d:%s,%s)" % (self.id,self.name,self.suffix) class TreeViewIDList: def __init__(self,allowMultiSelection=True): self.items = [] selectCommand = self.selectionChanged
# Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License: # http://creativecommons.org/licenses/by-nc-nd/3.0/ # http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode # http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode.txt # # A copy of the license can be found in file 'LICENSE.txt', which is part # of this source code package. # from ngSkinTools.mllInterface import MllInterface from ngSkinTools.utils import MessageException from ngSkinTools.log import LoggerFactory from ngSkinTools.layerUtils import LayerUtils, NamedPaintTarget from ngSkinTools.mllInterface import MllInterface log = LoggerFactory.getLogger("WeightsClipboard") class WeightsClipboard: def __init__(self, mllInterface): ''' :param MllInterface mllInterface: ''' self.copiedWeights = None self.mll = mllInterface self.layer = None self.influence = None def withCurrentLayerAndInfluence(self): ''' :rtype: WeightsClipboard
# material (collectively the "Data") in these files are subject to the terms # and conditions defined by # Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License: # http://creativecommons.org/licenses/by-nc-nd/3.0/ # http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode # http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode.txt # # A copy of the license can be found in file 'LICENSE.txt', which is part # of this source code package. # from maya import cmds from ngSkinTools.utils import Utils from ngSkinTools.log import LoggerFactory log = LoggerFactory.getLogger("events") class Signal: ''' Signal class collects observers, interested in some particular event,and handles signaling them all when some event occurs. Both handling and signaling happens outside of signal's own code ''' TOTAL_HANDLERS = 0 def __init__(self, name=None): self.name = name self.reset()
# and conditions defined by # Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License: # http://creativecommons.org/licenses/by-nc-nd/3.0/ # http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode # http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode.txt # # A copy of the license can be found in file 'LICENSE.txt', which is part # of this source code package. # from maya import cmds from ngSkinTools.ui.uiWrappers import FormLayout from ngSkinTools.ui.constants import Constants from ngSkinTools.log import LoggerFactory log = LoggerFactory.getLogger("base dialog") class BaseDialog: currentDialog = None # I'm so sorry, but it's late at night and I can't come up with a better idea to make this work in 2009 tests stuffToRunInNextModalDialogHack = [] BUTTON_OK = "ok" BUTTON_CANCEL = "cancel" BUTTON_CLOSE = "close" BUTTON_LABELS = {BUTTON_OK:"Ok",BUTTON_CANCEL:"Cancel",BUTTON_CLOSE:"Close"} def __init__(self): self.title = "Untitled"
class TransferWeightsTab(BaseTab): log = LoggerFactory.getLogger("Transfer Weights Tab") VAR_PREFIX = 'ngSkinToolsTransferTab_' axisValues = ('X', 'Y', 'Z') def __init__(self): BaseTab.__init__(self) self.items = [] self.dataModel = None self.currentSelection = None self.currentInfluencesSelection = [] self.manualOverrides = {} self.mirrorMode = True def setDataModel(self, dataModel): self.dataModel = dataModel self.dataModel.parent = self def createUI(self, parent): buttons = [] buttons.append(('Done', self.execContinue, '')) buttons.append(('Cancel', self.closeWindow, '')) self.cmdLayout = self.createCommandLayout( buttons, SkinToolsDocs.INITWEIGHTTRANSFER_INTERFACE) if self.mirrorMode: self.createMirrorOptionsGroup() if not self.mirrorMode: self.createTransferOptionsGroup() self.createInfluenceMappingGroup() LayerEvents.layerAvailabilityChanged.addHandler( self.updateLayoutEnabled, self.cmdLayout.outerLayout) LayerEvents.mirrorCacheStatusChanged.addHandler( self.updateUIValues, self.cmdLayout.outerLayout) MayaEvents.nodeSelectionChanged.addHandler(self.updateUIValues, self.cmdLayout.outerLayout) self.updateLayoutEnabled() self.updateUIValues() self.updatePreferedValues() def releaseUI(self): LayerEvents.layerAvailabilityChanged.removeHandler( self.updateLayoutEnabled) LayerEvents.mirrorCacheStatusChanged.removeHandler(self.updateUIValues) MayaEvents.nodeSelectionChanged.removeHandler(self.updateUIValues) def updatePreferedValues(self): if self.mirrorMode: preferedMirrorAxis = LayerDataModel.getInstance( ).mirrorCache.mirrorAxis if preferedMirrorAxis is None: preferedMirrorAxis = 'X' self.controls.mirrorAxis.setValue( TransferWeightsTab.axisValues.index( preferedMirrorAxis.upper())) if self.mirrorMode and LayerDataModel.getInstance().layerDataAvailable: self.manualOverrides = LayerDataModel.getInstance( ).mll.getManualMirrorInfluences() else: self.manualOverrides = {} def updateUIValues(self): # when selection changes between update UI calls, overwrite UI with preferred values in the mesh selection = cmds.ls(sl=True) if selection != self.currentSelection: self.updatePreferedValues() self.currentSelection = selection self.previewInfluenceMapping() def createMirrorOptionsGroup(self): group = self.createUIGroup(self.cmdLayout.innerLayout, 'Mirror Options') self.createFixedTitledRow(group, 'Mirror Axis') cmds.columnLayout() self.controls.mirrorAxis = DropDownField(self.VAR_PREFIX + 'mirrorAxis') self.controls.mirrorAxis.beginRebuildItems() for i in TransferWeightsTab.axisValues: self.controls.mirrorAxis.addOption(i) self.controls.mirrorAxis.endRebuildItems() self.controls.mirrorAxis.changeCommand.addHandler( self.previewInfluenceMapping, group) def createTransferOptionsGroup(self): group = self.createUIGroup(self.cmdLayout.innerLayout, 'Transfer Options') self.createTitledRow(parent=group, title="Vertex Transfer Mode") self.controls.transferMode = DropDownField(self.VAR_PREFIX + 'vertexTransferMode') for opt in CopyWeightsModel.vertexTransferModes.keys(): self.controls.transferMode.addOption(opt) self.createTitledRow(parent=group, title="Influence namespaces") self.controls.ignoreNamespaces = CheckBoxField( self.VAR_PREFIX + 'IgnoreNamespaces', label="Ignore when matching by name", annotation='ignore when matching by name', defaultValue=1) self.controls.ignoreNamespaces.changeCommand.addHandler( self.previewInfluenceMapping, ownerUI=group) self.createTitledRow(parent=group, title=None) self.controls.keepExistingLayers = CheckBoxField( self.VAR_PREFIX + 'KeepExistingLayers', label="Keep existing layers", annotation= 'when unselected, will delete existing layers in destination', defaultValue=1) def ignoreModeChanged(self): if not self.mirrorMode: return self.controls.prefixesGroup.setVisible(self.isPrefixIgnoreMode()) self.controls.suffixesGroup.setVisible(not self.isPrefixIgnoreMode()) self.previewInfluenceMapping() def isPrefixIgnoreMode(self): return self.controls.ignorePrefixes.getValue() == 1 def createInfluenceMappingGroup(self): group = self.createUIGroup(self.cmdLayout.innerLayout, 'Influence Mapping') self.createFixedTitledRow(group, 'Infl. Distance Error') self.controls.influenceDistanceError = FloatField( self.VAR_PREFIX + 'distanceError', minValue=0, maxValue=None, step=0.01, defaultValue=0.001, annotation= 'Defines maximum inaccuracy between left and right influence positions' ) self.controls.influenceDistanceError.changeCommand.addHandler( self.previewInfluenceMapping, group) if self.mirrorMode: self.createTitledRow(group, 'Ignore') cmds.radioCollection() for index, i in enumerate(['Prefixes', 'Suffixes']): ctrl = self.controls.__dict__['ignore' + i] = RadioButtonField( self.VAR_PREFIX + 'ignoreMode' + i, defaultValue=1 if index == 0 else 0, label=i) ctrl.changeCommand.addHandler(self.ignoreModeChanged, group) self.controls.prefixesGroup = self.createTitledRow( group, 'Influence Prefixes') self.controls.influencePrefixes = StoredTextEdit( self.VAR_PREFIX + 'inflPrefix', annotation= 'Specifies influence prefixes to be ignored when matching by name;\nUsually you would put your left/right influence prefixes here;\nseparate them with commas, e.g. "L_, R_"' ) self.controls.influencePrefixes.changeCommand.addHandler( self.previewInfluenceMapping, group) self.controls.suffixesGroup = self.createTitledRow( group, 'Influence Suffixes') self.controls.influenceSuffixes = StoredTextEdit( self.VAR_PREFIX + 'inflSuffix', annotation= 'Specifies influence suffixes to be ignored when matching by name;\nUsually you would put your left/right influence suffixes here;\nseparate them with commas, e.g. "_Lf, _Rt"' ) self.controls.influenceSuffixes.changeCommand.addHandler( self.previewInfluenceMapping, group) influencesLayout = cmds.columnLayout( parent=group, adjustableColumn=1, rowSpacing=Constants.MARGIN_SPACING_VERTICAL) cmds.text(parent=influencesLayout, label="Influence mapping to be used:", align='left') self.controls.influencesList = cmds.textScrollList( parent=influencesLayout, height=200, numberOfRows=5, allowMultiSelection=True, selectCommand=self.onInfluenceSelected, deleteKeyCommand=lambda *args: self.removeSelectedManualMappings()) manualGroup = self.createUIGroup(self.cmdLayout.innerLayout, 'Manual influence mapping') cmds.rowLayout(parent=manualGroup, nc=3) buttonWidth = 110 cmds.button( 'Disconnect link', width=buttonWidth, command=lambda *args: self.doDisconnectMapping(), annotation= 'Disconnect two associated influences, and make each influence point to itself' ) if self.mirrorMode: cmds.button( 'Link, both ways', width=buttonWidth, command=lambda *args: self.doConnectMapping(bidirectional=True ), annotation= 'Connect two selected influences and link them both ways') cmds.button( 'Link, one way' if self.mirrorMode else "Link", width=buttonWidth, command=lambda *args: self.doConnectMapping(bidirectional=False), annotation= 'Connect two selected influences and link on source to destination' ) cmds.rowLayout(parent=manualGroup, nc=2) cmds.button( 'Remove manual rule', width=buttonWidth, command=lambda *args: self.removeSelectedManualMappings(), annotation= 'Remove manual rule; influence will be a subject to automatic matching' ) self.ignoreModeChanged() cmds.setParent(group) def getSelectedPairs(self): selection = cmds.textScrollList(self.controls.influencesList, q=True, sii=True) if selection is not None: for i in selection: yield self.items[i - 1] def closeWindow(self, *args): self.parentWindow.closeWindow() def buildInfluenceMappingEngine(self): result = self.dataModel.buildInfluenceMappingEngine(self.controls) result.manualOverrides = self.manualOverrides return result def previewInfluenceMapping(self): if not self.dataModel.isEnabled(): return engine = self.buildInfluenceMappingEngine() engine.calculate() self.contructInfluenceList() self.currentInfluencesSelection = [] def execContinue(self, *args): self.previewInfluenceMapping() self.dataModel.execute() self.closeWindow() def onInfluenceSelected(self, *args): newSelection = list(self.getSelectedPairs()) newHighlight = [] for i in newSelection: if i.source is not None: newHighlight.append(i.source.path) if i.destination is not None: newHighlight.append(i.destination.path) SelectHelper.replaceHighlight(newHighlight) # the weird way of forming this currentInfluencesSelection like that # is because we want the items to be ordered in first to last selected order # when new selection happens, first all items that are not selected anymore # are removed from the current selection cache, # then all new items that are selected are added at the end. for i in self.currentInfluencesSelection[:]: if i not in newSelection: self.currentInfluencesSelection.remove(i) for i in newSelection: if i not in self.currentInfluencesSelection: self.currentInfluencesSelection.append(i) @staticmethod def findAssociation(list, source, destination, automatic): for i in list: if i.automatic != automatic: continue if i.source.path == source and i.destination.path == destination: return i if i.bidirectional and i.destination.path == source and i.source.path == destination: return i return None def influencesListSort(self, entry1, entry2): # priority for non-automatic entries if entry1.automatic != entry2.automatic: return 1 if entry1.automatic else -1 # priority for non-self references if entry1.isSelfReference() != entry2.isSelfReference(): return 1 if entry1.isSelfReference() else -1 # priority for bidirectional entries if entry1.bidirectional != entry2.bidirectional: return 1 if not entry1.bidirectional else -1 if entry1.source is not None and entry2.source is not None: return cmp(entry1.source.path, entry2.source.path) if entry1.destination is not None and entry2.destination is not None: return cmp(entry1.destination.path, entry2.destination.path) return 0 def contructInfluenceList(self): self.items = [] mapper = self.dataModel.mapper unmatchedSources = mapper.sourceInfluences[:] unmatchedDestinations = mapper.destinationInfluences[:] sourceInfluencesMap = dict( (i.logicalIndex, i) for i in mapper.sourceInfluences) destinationInfluencesMap = dict( (i.logicalIndex, i) for i in mapper.destinationInfluences) def isSourceAutomatic(source): return source.logicalIndex not in self.manualOverrides.keys() for source, destination in mapper.mapping.items(): source = sourceInfluencesMap[source] destination = None if destination is None else destinationInfluencesMap[ destination] if source is None or destination is None: continue if source in unmatchedSources: unmatchedSources.remove(source) if destination in unmatchedDestinations: unmatchedDestinations.remove(destination) automatic = isSourceAutomatic(source) item = None if self.mirrorMode and destination is not None: item = self.findAssociation(self.items, destination.path, source.path, automatic) if item is not None: item.bidirectional = True else: item = InfluencesListEntry() item.targetAndDestinationIsSameMesh = self.mirrorMode item.source = source item.destination = destination item.bidirectional = False self.items.append(item) item.automatic = automatic self.items = sorted(self.items, self.influencesListSort) if len(unmatchedSources) > 0 and not self.mirrorMode: self.items.append( InfluencesListEntry( specialValue="Unmatched source influences:")) for source in unmatchedSources: self.items.append( InfluencesListEntry(source=source, automatic=isSourceAutomatic(source))) if len(unmatchedDestinations) > 0 and not self.mirrorMode: self.items.append( InfluencesListEntry( specialValue="Unmatched destination influences:")) for destination in unmatchedDestinations: self.items.append(InfluencesListEntry(destination=destination)) cmds.textScrollList(self.controls.influencesList, e=True, removeAll=True, append=[i.asLabel() for i in self.items]) def updateLayoutEnabled(self): ''' updates UI enabled/disabled flag based on layer data availability ''' enabled = self.dataModel.isEnabled() cmds.layout(self.cmdLayout.innerLayout, e=True, enable=enabled) cmds.layout(self.cmdLayout.buttonForm, e=True, enable=enabled) def addManualInfluenceMapping(self, source, destination): self.manualOverrides[ source. logicalIndex] = None if destination is None else destination.logicalIndex def doDisconnectMapping(self): for mapping in self.currentInfluencesSelection: if mapping.source is None: continue if mapping.destination is None: continue if mapping.source == mapping.destination: continue # for both source and destination, create a mapping for just itself self.addManualInfluenceMapping( mapping.source, mapping.source if mapping.targetAndDestinationIsSameMesh else None) if mapping.bidirectional: self.addManualInfluenceMapping(mapping.destination, mapping.destination) self.previewInfluenceMapping() def doConnectMapping(self, bidirectional=True): if len(self.currentInfluencesSelection) < 2: return if bidirectional and len(self.currentInfluencesSelection) != 2: return validSources = [] for item in self.currentInfluencesSelection[:-1]: if item.isConnectedElsewhere() or item.source is None: continue validSources.append(item) # second selected list item destinationItem = self.currentInfluencesSelection[-1] if destinationItem.isConnectedElsewhere(): return destination = destinationItem.destination if destinationItem.destination is not None else destinationItem.source if destination is None: return destination = destination.logicalIndex for sourceItem in validSources: source = sourceItem.source.logicalIndex self.manualOverrides[source] = destination if bidirectional: self.manualOverrides[destination] = source self.previewInfluenceMapping() def removeSelectedManualMappings(self): for item in self.currentInfluencesSelection: if item.source.logicalIndex in self.manualOverrides: del self.manualOverrides[item.source.logicalIndex] if item.bidirectional and item.destination.logicalIndex in self.manualOverrides: del self.manualOverrides[item.destination.logicalIndex] self.previewInfluenceMapping()
from ngSkinTools.mllInterface import MllInterface from ngSkinTools.utils import MessageException, Utils from ngSkinTools.log import LoggerFactory from maya import OpenMayaAnim as oma from maya import OpenMaya as om from maya import cmds from ngSkinTools.skinClusterFn import SkinClusterFn log = LoggerFactory.getLogger("ImportInfluences") class ImportInfluences(object): def __init__(self): self.sourceSkinCluster = None self.destinationSkinCluster = None def __detectSkinCluster(self,mesh): mll = MllInterface() mll.setCurrentMesh(mesh) try: _,skinCluster = mll.getTargetInfo() except TypeError: raise MessageException("cannot find skin cluster attached to %s" % mesh) log.info("detected skin cluster %s on mesh %s" % (skinCluster, mesh)) return skinCluster def setSourceFromMesh(self,sourceMesh): '''
# A copy of the license can be found in file 'LICENSE.txt', which is part # of this source code package. # from __future__ import with_statement from maya import cmds, mel from ngSkinTools.ui.events import Signal, LayerEvents, MayaEvents from ngSkinTools.ui.layerDataModel import LayerDataModel from ngSkinTools.ui.dlgLayerProperties import LayerPropertiesDialog from ngSkinTools.ui.basedialog import BaseDialog from ngSkinTools.utils import Utils from ngSkinTools.ui.basetoolwindow import BaseToolWindow from ngSkinTools.importExport import JsonExporter, XmlExporter, Formats from ngSkinTools.log import LoggerFactory log = LoggerFactory.getLogger("actions") class BaseAction(object): def __init__(self, ownerUI): self.ownerUI = ownerUI self.onExecuted = Signal("influence filter visibility changed") self.updateControls = [] def addUpdateControl(self, control, menu=False): self.updateControls.append((control, menu)) def isEnabled(self): ''' override this method to provide enabled/disabled state of this action '''
from maya import cmds from ngSkinTools.ui.basetab import BaseTab from ngSkinTools.doclink import SkinToolsDocs from ngSkinTools.ui.events import LayerEvents from ngSkinTools.ui.uiWrappers import TextLabel, FloatField, StoredTextEdit,\ DropDownField, FormLayout, CheckBoxField from ngSkinTools.ui.layerDataModel import LayerDataModel from ngSkinTools.ui.constants import Constants from ngSkinTools.utils import Utils from ngSkinTools.ui.SelectHelper import SelectHelper from ngSkinTools.ui.basedialog import BaseDialog from ngSkinTools.ui.actions import BaseAction from ngSkinTools.ui.options import ValueModel from ngSkinTools.log import LoggerFactory log = LoggerFactory.getLogger("initTransferWindow") class AddPairDialog(BaseDialog): CTRL_PREFIX = 'ngSkinToolsAddPairDialog_' def __init__(self): BaseDialog.__init__(self) self.title = "Add Influences Association" self.sourceValue = ValueModel() self.destinationValue = ValueModel() self.buttons = [self.BUTTON_OK, self.BUTTON_CANCEL] def updateEnabled(self): cmds.layout(self.destinationRow, e=True,
# The coded instructions, statements, computer programs, and/or related # material (collectively the "Data") in these files are subject to the terms # and conditions defined by # Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License: # http://creativecommons.org/licenses/by-nc-nd/3.0/ # http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode # http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode.txt # # A copy of the license can be found in file 'LICENSE.txt', which is part # of this source code package. # from maya import cmds from ngSkinTools.log import LoggerFactory log = LoggerFactory.getLogger("LayerUtils") class NamedPaintTarget: MASK = "mask" DUAL_QUATERNION = "dq" class LayerUtils: @staticmethod def iterCustomNodes(): for nodeType in ['ngSkinLayerData', 'ngSkinLayerDisplay']: items = cmds.ls(type=nodeType) if items is not None: for i in items: yield i
class TransferWeightsTab(BaseTab): log = LoggerFactory.getLogger("Transfer Weights Tab") VAR_PREFIX = 'ngSkinToolsTransferTab_' def __init__(self): BaseTab.__init__(self) self.items = [] self.manualItems = [] def addManualPair(self, source, destination, bidirectional): self.getMll().addManualMirrorInfluenceAssociation(source, destination) if bidirectional: self.getMll().addManualMirrorInfluenceAssociation( destination, source) self.execInitMirrorData() def getMll(self): return LayerDataModel.getInstance().mll def removePairs(self, pairList): changed = False for i in pairList: changed = True self.getMll().removeManualMirrorInfluenceAssociation( i.source, i.destination) if i.bidirectional: self.getMll().removeManualMirrorInfluenceAssociation( i.destination, i.source) if changed: self.execInitMirrorData() def createUI(self, parent): # base layout self.addPairAction = AddPairAction(self.parentWindow.windowName) self.removePairAction = RemovePairAction(parent) buttons = [] buttons.append(('Initialize', self.doInitAssociations, '')) buttons.append(('Close', self.closeWindow, '')) self.cmdLayout = self.createCommandLayout( buttons, SkinToolsDocs.INITWEIGHTTRANSFER_INTERFACE) self.createInitializationGroup() self.createCacheContentsGroup() self.updateCacheInfo() self.updateLayoutEnabled() LayerEvents.layerAvailabilityChanged.addHandler( self.updateLayoutEnabled, self.cmdLayout.outerLayout) LayerEvents.mirrorCacheStatusChanged.addHandler( self.updateCacheInfo, self.cmdLayout.outerLayout) def createInitializationGroup(self): group = self.createUIGroup(self.cmdLayout.innerLayout, 'Initialization') self.createFixedTitledRow(group, 'Infl. Distance Error') self.controls.influenceDistanceError = FloatField( self.VAR_PREFIX + 'distanceError', minValue=0, maxValue=None, step=0.01, defaultValue=0.001, annotation= 'Defines maximum inaccuracy between left and right influence positions' ) self.createTitledRow(group, 'Influence Prefixes') self.controls.influencePrefixes = StoredTextEdit( self.VAR_PREFIX + 'inflPrefix', annotation= 'Defines maximum inaccuracy between left and right influence positions' ) self.createFixedTitledRow(group, 'Mirror Axis') cmds.columnLayout() self.controls.mirrorAxis = DropDownField(self.VAR_PREFIX + 'mirrorAxis') self.controls.mirrorAxis.beginRebuildItems() self.controls.mirrorAxis.addOption("X") self.controls.mirrorAxis.addOption("Y") self.controls.mirrorAxis.addOption("Z") self.controls.mirrorAxis.endRebuildItems() def createCacheContentsGroup(self): group = self.createUIGroup(self.cmdLayout.innerLayout, 'Cache Contents') self.createTitledRow(group, 'Current status') self.controls.labelCacheInfo = TextLabel(align='left') self.controls.labelCacheInfo.setBold() influencesLayout = cmds.columnLayout( parent=group, adjustableColumn=1, rowSpacing=Constants.MARGIN_SPACING_VERTICAL) self.createTitledRow(influencesLayout, "Influence Associations") self.controls.influencesList = cmds.textScrollList( parent=influencesLayout, height=100, numberOfRows=5, allowMultiSelection=True, selectCommand=self.onInfluenceSelected, deleteKeyCommand=self.removePairAction.execute) self.controls.influencesMenu = cmds.popupMenu( parent=self.controls.influencesList) self.addPairAction.newMenuItem("Add manual association...") self.removePairAction.newMenuItem("Remove manual association...") cmds.setParent(group) def getSelectedPairs(self): selection = cmds.textScrollList(self.controls.influencesList, q=True, sii=True) if selection is not None: for i in selection: yield self.items[i - 1] def doInitAssociations(self, *args): self.execInitMirrorData() def closeWindow(self, *args): self.parentWindow.closeWindow() def execInitMirrorData(self): kargs = {} kargs["initMirrorData"] = True kargs[ "influenceAssociationDistance"] = self.controls.influenceDistanceError.getValue( ) kargs["mirrorAxis"] = self.controls.mirrorAxis.getSelectedText() # create a comma-delimited prefix string, stripping away any spaces # that might be specified in the user input prefixes = self.controls.influencePrefixes.getValue() kargs["influenceAssociationPrefix"] = ",".join( [prefix.strip() for prefix in prefixes.split(",")]) cmds.ngSkinLayer(**kargs) LayerDataModel.getInstance().updateMirrorCacheStatus() self.updateInfluenceList() def updateCacheInfo(self): ''' updates UI according to new mirror cache status ''' self.controls.labelCacheInfo.setLabel( LayerDataModel.getInstance().mirrorCache.message) self.updateInfluenceList() def onInfluenceSelected(self, *args): newHighlight = [] for i in self.getSelectedPairs(): newHighlight.append(i.source) newHighlight.append(i.destination) SelectHelper.replaceHighlight(newHighlight) self.removePairAction.updateEnabled() @staticmethod def findAssociation(list, source, destination, automatic): for i in list: if i.automatic != automatic: continue if i.source == source and i.destination == destination: return i if i.bidirectional and i.destination == source and i.source == destination: return i return None def updateInfluenceList(self): influenceAssociations = cmds.ngSkinLayer(q=True, mirrorCacheInfluences=True) if influenceAssociations is None: influenceAssociations = [] self.items = [] while len(influenceAssociations) != 0: source = influenceAssociations.pop(0) destination = influenceAssociations.pop(0) automatic = int(influenceAssociations.pop(0)) == 0 item = self.findAssociation(self.items, destination, source, automatic) if item is not None: item.bidirectional = True else: item = InfluencesListEntry() item.source = source item.destination = destination item.bidirectional = False self.items.append(item) item.automatic = automatic def defaultSort(entry1, entry2): # priority for non-automatic entries if entry1.automatic != entry2.automatic: return 1 if entry1.automatic else -1 # priority for non-self references if entry1.isSelfReference() != entry2.isSelfReference(): return 1 if entry1.isSelfReference() else -1 # priority for bidirectional entries if entry1.bidirectional != entry2.bidirectional: return 1 if not entry1.bidirectional else -1 return cmp(entry1.source, entry2.source) self.items = sorted(self.items, defaultSort) newLabels = [] for i in self.items: newLabels.append(i.asLabel()) cmds.textScrollList(self.controls.influencesList, e=True, removeAll=True, append=newLabels) def updateLayoutEnabled(self): ''' updates UI enabled/disabled flag based on layer data availability ''' enabled = LayerDataModel.getInstance().layerDataAvailable == True cmds.layout(self.cmdLayout.innerLayout, e=True, enable=enabled) cmds.layout(self.cmdLayout.buttonForm, e=True, enable=enabled)
# http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode.txt # # A copy of the license can be found in file 'LICENSE.txt', which is part # of this source code package. # from maya import cmds from ngSkinTools.utils import Utils, MessageException from ngSkinTools.ui.basetab import BaseTab from ngSkinTools.ui.intensityslider import IntensitySlider from ngSkinTools.doclink import SkinToolsDocs from ngSkinTools.ui.uiWrappers import IntField, FloatField, CheckBoxField from ngSkinTools.ui.softSelectionRow import SoftSelectionRow from ngSkinTools.log import LoggerFactory log = LoggerFactory.getLogger("tabSkinRelax") class TabSkinRelax(BaseTab): ''' Defines a UI function set for Weights Relax operations. ''' # prefix for environment variables for this tab VAR_RELAX_PREFIX = 'ngSkinToolsRelaxTab_' def __init__(self): BaseTab.__init__(self) @Utils.visualErrorHandling def execRelax(self, *args):
from ngSkinTools.utils import Utils from maya import cmds import maya.utils as mutils from ngSkinTools.ui.uiWrappers import CheckBoxField from ngSkinTools.ui.options import Options from ngSkinTools.ui.constants import Constants from ngSkinTools.ui.basetoolwindow import BaseToolWindow from ngSkinTools.versioncheck import VersionChecker, HttpPostTransport from datetime import datetime from ngSkinTools.log import LoggerFactory from ngSkinTools.context import applicationContext import logging import maya log = LoggerFactory.getLogger("updateCheckWindow") class UpdateCheckWindow(BaseToolWindow): ''' An interface for Check-For-Updates feature. ''' UPDATE_THREAD = None def __init__(self,windowName): BaseToolWindow.__init__(self,windowName) self.updateAvailable = False self.windowTitle = 'NgSkinTools Update' self.defaultWidth = 500 self.defaultHeight = 400
# and conditions defined by # Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License: # http://creativecommons.org/licenses/by-nc-nd/3.0/ # http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode # http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode.txt # # A copy of the license can be found in file 'LICENSE.txt', which is part # of this source code package. # from maya import cmds from ngSkinTools.utils import Utils from ngSkinTools.log import LoggerFactory log = LoggerFactory.getLogger("events") class Signal: ''' Signal class collects observers, interested in some particular event,and handles signaling them all when some event occurs. Both handling and signaling happens outside of signal's own code ''' TOTAL_HANDLERS = 0 def __init__(self,name=None): self.name = name self.reset(); def reset(self):
import sys from maya import OpenMaya as om from maya import cmds from maya import mel from ngSkinTools.log import LoggerFactory from functools import wraps import logging class MessageException(Exception): def __init__(self,message): self.message = message def __str__(self, *args, **kwargs): return self.message log = LoggerFactory.getLogger("utils") class Utils: ''' various utility methods that didn't fit into any specific category ''' ERROR_PLUGINNOTAVAILABLE = 'Skin tools plugin is not loaded.' ERROR_NO_MESH_VERT_SELECTED = 'No mesh vertices are selected' # constants for maya version # earlier maya versions have smaller constant value, so comparisions can be made (e.g. getMayaVersion()<MAYA2011) # (new constants must follow this pattern, e.g., "2008 release 2" should be 2008.2 instead of 200802) MAYA_UNKNOWN_VERSION = 0 # /// special constant for unknown version. assumed earlier version in code MAYA2011 = 2011 MAYA2012 = 2012
from maya import cmds from ngSkinTools.ui.basetab import BaseTab from ngSkinTools.ui.uiWrappers import FloatField, CheckBoxField, DropDownField from ngSkinTools.doclink import SkinToolsDocs from ngSkinTools.ui.initTransferWindow import InitTransferWindow from ngSkinTools.ui.layerDataModel import LayerDataModel from ngSkinTools.ui.events import LayerEvents from ngSkinTools.log import LoggerFactory log = LoggerFactory.getLogger("mirror UI") class TabMirror(BaseTab): TOOL_PAINT = 'ngSkinToolsLayerPaintCtx' # prefix for environment variables for this tab VAR_PREFIX = 'ngSkinToolsMirrorTab_' MIRROR_TEXTS = { 'x': ("Left to right (+X to -X)", "Right to left (-X to +X)"), 'y': ("Top to bottom (+Y to -Y)", "Bottom to top (-Y to +Y)"), 'z': ("Front to back (+Z to -Z)", "Back to front (-Z to +Z)"), } MIRROR_GUESS = 'Guess from stroke' def __init__(self): BaseTab.__init__(self) def getMirrorSideTexts(self): dataModel = LayerDataModel.getInstance()
class LayerDataModel: log = LoggerFactory.getLogger("layerDataModel") class MirrorCacheStatus: def __init__(self): self.isValid = None self.message = None self.mirrorAxis = None # holds instance of singleton object __instance = None @staticmethod def getInstance(): ''' returns singleton instance of LayerDataModel ''' if LayerDataModel.__instance is None: LayerDataModel.__instance = LayerDataModel() return LayerDataModel.__instance @staticmethod def reset(): LayerDataModel.__instance = None def __init__(self): self.layerListsUI = None self.layerDataAvailable = None self.mirrorCache = self.MirrorCacheStatus() self.mll = MllInterface() self.clipboard = WeightsClipboard(self.mll) MayaEvents.undoRedoExecuted.addHandler(self.updateLayerAvailability) MayaEvents.nodeSelectionChanged.addHandler( self.updateLayerAvailability) self.updateLayerAvailability() def setLayerListsUI(self, ui): self.layerListsUI = ui def getSelectedLayer(self): if self.layerListsUI is None: return None return self.layerListsUI.getLayersList().getSelectedID() def updateLayerAvailability(self): ''' updates interface visibility depending on availability of layer data ''' self.log.info("updating layer availability") oldValue = self.layerDataAvailable self.layerDataAvailable = self.mll.getLayersAvailable() if self.layerDataAvailable != oldValue: LayerEvents.layerAvailabilityChanged.emit() self.updateMirrorCacheStatus() def updateMirrorCacheStatus(self): def setStatus(newStatus, message, axis=None): change = newStatus != self.mirrorCache.isValid or self.mirrorCache.message != message or self.mirrorCache.mirrorAxis != axis self.mirrorCache.message = message self.mirrorCache.isValid = newStatus self.mirrorCache.mirrorAxis = axis if change: self.log.info("mirror cache status changed to %s." % self.mirrorCache.message) LayerEvents.mirrorCacheStatusChanged.emit() self.log.info("updating mirror cache status") if not self.layerDataAvailable: setStatus(False, "Layer Data is not available") return try: cacheInfo = cmds.ngSkinLayer(q=True, mirrorCacheInfo=True) if cacheInfo[0] == 'ok': setStatus(True, 'Mirror Data Initialized', cmds.ngSkinLayer(q=True, mirrorAxis=True)) else: setStatus(False, cacheInfo[1]) except: setStatus(False, 'Cache check failed') #log.error("error: "+str(err)) def addLayer(self, name): id = self.mll.createLayer(name) if id is None: return LayerEvents.layerListModified.emit() self.setCurrentLayer(id) def removeLayer(self, id): cmds.ngSkinLayer(rm=True, id=id) LayerEvents.layerListModified.emit() LayerEvents.currentLayerChanged.emit() def setCurrentLayer(self, id): cmds.ngSkinLayer(cl=id) LayerEvents.currentLayerChanged.emit() def getCurrentLayer(self): return self.mll.getCurrentLayer() return cmds.ngSkinLayer(q=True, cl=True) def attachLayerData(self): self.mll.initLayers() self.addLayer('Base Weights') self.updateLayerAvailability() def cleanCustomNodes(self): ''' removes all custom nodes from current scene ''' LayerUtils.deleteCustomNodes() self.updateLayerAvailability() def getLayerName(self, id): return mel.eval('ngSkinLayer -id %d -q -name' % id) def setLayerName(self, id, name): cmds.ngSkinLayer(e=True, id=id, name=name) LayerEvents.nameChanged.emit() def getLayerOpacity(self, id): return mel.eval('ngSkinLayer -id %d -q -opacity' % id) def getLayerEnabled(self, id): return mel.eval('ngSkinLayer -id %d -q -enabled' % id) def setLayerEnabled(self, id, enabled): cmds.ngSkinLayer(e=True, id=id, enabled=1 if enabled else 0) def toggleLayerEnabled(self, id): self.setLayerEnabled(id, not self.getLayerEnabled(id)) def getLayersCandidateFromSelection(self): ''' for given selection, returns mesh and skin cluster node names where skinLayer data is (or can be) attached. ''' try: return cmds.ngSkinLayer(q=True, ldt=True) except: return [] def getLayersAvailable(self): self.updateLayerAvailability() return self.layerDataAvailable
from maya import cmds from ngSkinTools.utils import Utils from ngSkinTools.ui.basetab import BaseTab from ngSkinTools.ui.constants import Constants from ngSkinTools.ui.events import MayaEvents from ngSkinTools.log import LoggerFactory log = LoggerFactory.getLogger("tabInfluenceList") class TabInfluenceList(BaseTab): ''' Defines UI for Influence Lister operations. ''' # prefix for preset variables for this tab VAR_RELAX_PREFIX = 'ngSkinToolsInflLister_' LIST_METHOD_CONTAINED_WEIGHTS = 'By Contained Weights' LIST_METHOD_CLOSEST_JOINT = 'By Closest Joint' INFO_NAME_FILTER = 'type influence name fragment (e.g. "spine") to filter items by name. separate more names by space to get multiple results (e.g. "spine neck")' def __init__(self): BaseTab.__init__(self) self.listedInfluences = [] def filterByName(self, name): ''' returns true when influence with given name should appear on the list * true if filter does not exist;
from ngSkinTools.ui.layerDataModel import LayerDataModel from ngSkinTools.utils import Utils from maya import cmds from ngSkinTools.ui.events import MayaEvents, restartEvents from ngSkinTools.doclink import SkinToolsDocs from ngSkinTools.log import LoggerFactory log = LoggerFactory.getLogger("HeadlessDataHost") class RefCountedHandle: """ reference counted handle to a dynamically allocated instance; creates reference after first addReference() call, and destroys it when last reference is removed via removeReference() """ def __init__(self, instantiator): self.instantiator = instantiator self.instance = None self.references = set() def getCurrentInstance(self): """ returns handle to currently created instance """ return self.instance def addReference(self, refSource):
from maya import cmds,OpenMaya as om from ngSkinTools.log import LoggerFactory log = LoggerFactory.getLogger("LayerUtils") class LayerUtils: PAINT_TARGET_UNDEFINED = -1 PAINT_TARGET_MASK = -2 @staticmethod def iterCustomNodes(): for nodeType in ['ngSkinLayerData','ngSkinLayerDisplay']: items = cmds.ls(type=nodeType) if items is not None: for i in items: yield i @staticmethod def deleteCustomNodes(): log.info("removing ngSkinTools nodes from current scene") nodes = list(LayerUtils.iterCustomNodes()) if len(nodes)>0: cmds.delete(nodes) @staticmethod def hasCustomNodes(): for _ in LayerUtils.iterCustomNodes(): return True
from ngSkinTools.ui.uiWrappers import TextLabel, FloatField, StoredTextEdit,\ DropDownField, FormLayout, CheckBoxField, RadioButtonField from ngSkinTools.ui.layerDataModel import LayerDataModel from ngSkinTools.ui.constants import Constants from ngSkinTools.utils import Utils from ngSkinTools.ui.SelectHelper import SelectHelper from ngSkinTools.ui.basedialog import BaseDialog from ngSkinTools.ui.actions import BaseAction from ngSkinTools.ui.options import ValueModel from ngSkinTools.log import LoggerFactory from ngSkinTools.influenceMapping import InfluenceMapping from ngSkinTools.mllInterface import MllInterface from ngSkinTools.orderedDict import OrderedDict log = LoggerFactory.getLogger("initTransferWindow") class InfluencesListEntry: def __init__(self,source=None,destination=None,specialValue=None,automatic=True): self.source = source self.destination = destination self.bidirectional = False self.automatic = automatic self.targetAndDestinationIsSameMesh = False self.specialValue = specialValue @staticmethod def shortName(longName): separator = longName.rfind("|") if separator>=0: return longName[separator+1:]
from ngSkinTools.version import Version from ngSkinTools.utils import Utils from maya import cmds import maya.utils as mutils from ngSkinTools.ui.uiWrappers import CheckBoxField from ngSkinTools.ui.options import Options from ngSkinTools.ui.constants import Constants from ngSkinTools.ui.basetoolwindow import BaseToolWindow from ngSkinTools.versioncheck import VersionChecker, HttpPostTransport from datetime import datetime from ngSkinTools.log import LoggerFactory from ngSkinTools.context import applicationContext import logging import maya log = LoggerFactory.getLogger("updateCheckWindow") class UpdateCheckWindow(BaseToolWindow): ''' An interface for Check-For-Updates feature. ''' UPDATE_THREAD = None def __init__(self, windowName): BaseToolWindow.__init__(self, windowName) self.updateAvailable = False self.windowTitle = 'NgSkinTools Update' self.defaultWidth = 500 self.defaultHeight = 400
# and conditions defined by # Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License: # http://creativecommons.org/licenses/by-nc-nd/3.0/ # http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode # http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode.txt # # A copy of the license can be found in file 'LICENSE.txt', which is part # of this source code package. # from maya import cmds from ngSkinTools.ui.uiWrappers import FormLayout from ngSkinTools.ui.constants import Constants from ngSkinTools.log import LoggerFactory log = LoggerFactory.getLogger("base dialog") class BaseDialog: currentDialog = None BUTTON_OK = "ok" BUTTON_CANCEL = "cancel" BUTTON_CLOSE = "close" BUTTON_LABELS = {BUTTON_OK:"Ok",BUTTON_CANCEL:"Cancel",BUTTON_CLOSE:"Close"} def __init__(self): self.title = "Untitled" self.buttons = [] class Controls: pass
# http://creativecommons.org/licenses/by-nc-nd/3.0/ # http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode # http://creativecommons.org/licenses/by-nc-nd/3.0/legalcode.txt # # A copy of the license can be found in file 'LICENSE.txt', which is part # of this source code package. # from maya import cmds from ngSkinTools.utils import Utils from ngSkinTools.ui.basetab import BaseTab from ngSkinTools.ui.constants import Constants from ngSkinTools.ui.events import MayaEvents from ngSkinTools.log import LoggerFactory log = LoggerFactory.getLogger("tabInfluenceList") class TabInfluenceList(BaseTab): ''' Defines UI for Influence Lister operations. ''' # prefix for preset variables for this tab VAR_RELAX_PREFIX = 'ngSkinToolsInflLister_' LIST_METHOD_CONTAINED_WEIGHTS = 'By Contained Weights' LIST_METHOD_CLOSEST_JOINT = 'By Closest Joint' INFO_NAME_FILTER = 'type influence name fragment (e.g. "spine") to filter items by name. separate more names by space to get multiple results (e.g. "spine neck")' def __init__(self):
from ngSkinTools.mllInterface import MllInterface from ngSkinTools.utils import MessageException from ngSkinTools.log import LoggerFactory from ngSkinTools.layerUtils import LayerUtils log = LoggerFactory.getLogger("WeightsClipboard") class WeightsClipboard: def __init__(self,mllInterface): self.copiedWeights = None self.mll = mllInterface self.layer = None self.influence = None def withCurrentLayerAndInfluence(self): self.layer = self.mll.getCurrentLayer() log.debug("weights clipboard setting current layer to %r" % self.layer) self.influence = self.mll.getCurrentPaintTarget() log.debug("weights clipboard setting current influence to %r" % self.influence) return self def getPaintTargetWeights(self,paintTarget): if paintTarget==LayerUtils.PAINT_TARGET_MASK: return self.mll.getLayerMask(self.layer) else: return self.mll.getInfluenceWeights(self.layer,paintTarget) def copy(self): self.copiedWeights = self.getPaintTargetWeights(self.influence) log.debug("copied weights: %r" % self.copiedWeights)
from __future__ import with_statement import unittest from ngSkinToolsTest import allTests from ngSkinTools.utils import Utils from ngSkinTools.log import LoggerFactory log = LoggerFactory.getLogger("mayaTestRunner") lastTestResult = "Tests were not run" 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
class BaseTab(object): ''' base class for ui group (tab) classes ''' VAR_GROUP_COLLAPSE = 'nguitab_group%s_collapse' log = LoggerFactory.getLogger("BaseToolWindow") def __init__(self): # create container for all controls. # using fake class instead of dictionary to simplify usage code self.controls = Controls() self.controls.groups = [] self.parentWindow = None self.title = 'Untitled' def getTitle(self): ''' return title of this gui set. override for functionality ''' return self.title def setTitle(self, title): self.title = title def getGroupVariable(self, group): title = cmds.frameLayout(group, q=True, label=True) title = title.replace(" ", "") return self.VAR_GROUP_COLLAPSE % title @staticmethod 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 createUIGroup(self, layout, title): ''' creates collapsable UI group ''' cmds.setParent(layout) group = cmds.frameLayout( label=title, marginWidth=Constants.MARGIN_SPACING_HORIZONTAL, marginHeight=Constants.MARGIN_SPACING_VERTICAL, collapsable=True, expandCommand=self.saveOptions, collapseCommand=self.saveOptions, borderStyle='etchedIn') self.controls.groups.append(group) cmds.frameLayout(group, e=True, collapse=Options.loadOption( self.getGroupVariable(group), 0)) return cmds.columnLayout(adjustableColumn=1, rowSpacing=Constants.MARGIN_SPACING_VERTICAL) def createUI(self, parent): ''' override this method to implement gui creation ''' cmds.setParent(parent) self.baseLayout = cmds.columnLayout( rowSpacing=Constants.MARGIN_SPACING_VERTICAL, adjustableColumn=1) def saveOptions(self, *args): ''' save gui options for this tab. this can be directly supplied as a handler to control's "change value" events ''' for group in self.controls.groups: Options.saveOption(self.getGroupVariable(group), cmds.frameLayout(group, q=True, collapse=True)) @staticmethod 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)) @staticmethod def createTitledRow(parent, title, innerContentConstructor=None, adjustable=True): ''' creates a layout piece with a title and inner content layout ''' result = FormLayout(parent=parent) if innerContentConstructor is None: innerContent = cmds.columnLayout(width=1) if adjustable: cmds.columnLayout(innerContent, e=True, adjustableColumn=1) else: innerContent = innerContentConstructor() result.attachForm(innerContent, 0, 0, None, Constants.MARGIN_COLUMN2) if title != None: label = cmds.text(parent=result, label=title + ':', width=Constants.MARGIN_COLUMN2 - Constants.MARGIN_SPACING_HORIZONTAL, align="right") result.attachForm(label, 0, None, 0, 0) if cmds.layout(innerContent, q=True, exists=True): cmds.setParent(innerContent) return result @staticmethod def createFixedTitledRow(parent, title): ''' similar to titled row, but not flexible and does not allow inner constructor or multiple elements inside; great for numeric fields, drop down fields, other non-stretchy UI elements ''' return BaseTab.createTitledRow(parent, title, adjustable=False) @staticmethod def layoutButtonForm(buttonForm, buttons): ''' horizontally spaces buttons in a form with a grid size of 100 ''' for index, button in enumerate(buttons): spaceWidth = 100 / len(buttons) cmds.formLayout(buttonForm, e=True, attachForm=[(button, 'top', 0), (button, 'bottom', 0)], attachPosition=[ (button, 'right', Constants.MARGIN_SPACING_HORIZONTAL / 2, spaceWidth * (index + 1)), (button, 'left', Constants.MARGIN_SPACING_HORIZONTAL / 2, spaceWidth * index) ]) def createCommandLayout(self, commandIterator, helpLink): ''' creates a layout that has a help button and command buttons at the bottom of the page, with a scroll layout created for the rest of the page; inside scroll layout, column layout is created for adding rows of interface commandIterator should be an iterator or a list of tuples with: * button label * button handler - either BaseAction action, or an executable object ''' return CommandLayout(helpLink, commandIterator)
# of this source code package. # from __future__ import with_statement from maya import cmds,mel from ngSkinTools.ui.events import Signal, LayerEvents, MayaEvents from ngSkinTools.ui.layerDataModel import LayerDataModel from ngSkinTools.ui.dlgLayerProperties import LayerPropertiesDialog from ngSkinTools.ui.basedialog import BaseDialog from ngSkinTools.utils import Utils, MessageException from ngSkinTools.ui.basetoolwindow import BaseToolWindow from ngSkinTools.log import LoggerFactory from ngSkinTools.ui.options import deleteCustomOptions from ngSkinTools.mllInterface import MllInterface, MirrorDirection log = LoggerFactory.getLogger("actions") class BaseAction(object): def __init__(self,ownerUI): self.ownerUI = ownerUI self.onExecuted = Signal("action executed") self.updateControls = [] def addUpdateControl(self,control,menu=False): self.updateControls.append((control,menu)) def isEnabled(self): ''' override this method to provide enabled/disabled state of this action '''
EnableDisableLayerAction, ExportAction, ImportAction from ngSkinTools.ui.basetoolwindow import BaseToolWindow from ngSkinTools.log import LoggerFactory from ngSkinTools.importExport import Formats from ngSkinTools.ui.utilities.importInfluencesUi import ImportInfluencesAction from ngSkinTools.utilities.duplicateLayers import DuplicateLayers from ngSkinTools.ui.utilities.duplicateLayerAction import DuplicateLayersAction from ngSkinTools.ui.headlessDataHost import HeadlessDataHost from ngSkinTools.doclink import SkinToolsDocs from ngSkinTools.ui.utilities.weightsClipboardActions import CopyWeights,\ CutWeights, PasteWeightsAdd, PasteWeightsReplace from ngSkinTools.layerUtils import LayerUtils from ngSkinTools.ui.tabSettings import TabSettings from ngSkinTools.ui.options import PersistentValueModel log = LoggerFactory.getLogger("MainWindow") class MainMenu: def __init__(self): pass def execCheckForUpdates(self,*args): UpdateCheckWindow.execute(silent=False) @Utils.undoable 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
EnableDisableLayerAction, ExportAction, ImportAction from ngSkinTools.ui.basetoolwindow import BaseToolWindow from ngSkinTools.log import LoggerFactory from ngSkinTools.importExport import Formats from ngSkinTools.ui.utilities.importInfluencesUi import ImportInfluencesAction from ngSkinTools.utilities.duplicateLayers import DuplicateLayers from ngSkinTools.ui.utilities.duplicateLayerAction import DuplicateLayersAction from ngSkinTools.ui.headlessDataHost import HeadlessDataHost from ngSkinTools.doclink import SkinToolsDocs from ngSkinTools.ui.utilities.weightsClipboardActions import CopyWeights,\ CutWeights, PasteWeightsAdd, PasteWeightsReplace from ngSkinTools.layerUtils import LayerUtils from ngSkinTools.ui.tabSettings import TabSettings from ngSkinTools.ui.options import PersistentValueModel log = LoggerFactory.getLogger("MainWindow") class MainMenu: def __init__(self): pass def execCheckForUpdates(self, *args): UpdateCheckWindow.execute(silent=False) @Utils.undoable def execCleanNodes(self, *args): if not LayerUtils.hasCustomNodes(): Utils.confirmDialog( icon='information', title='Info',
# # A copy of the license can be found in file 'LICENSE.txt', which is part # of this source code package. # from maya import cmds from ngSkinTools.ui.basetab import BaseTab from ngSkinTools.ui.uiWrappers import FloatField, CheckBoxField, DropDownField from ngSkinTools.doclink import SkinToolsDocs from ngSkinTools.ui.initTransferWindow import InitTransferWindow,\ MirrorWeightsWindow from ngSkinTools.ui.layerDataModel import LayerDataModel from ngSkinTools.ui.events import LayerEvents, MayaEvents from ngSkinTools.log import LoggerFactory log = LoggerFactory.getLogger("mirror UI") class TabMirror(BaseTab): TOOL_PAINT = 'ngSkinToolsLayerPaintCtx' # prefix for environment variables for this tab VAR_PREFIX = 'ngSkinToolsMirrorTab_' MIRROR_TEXTS = {'x':("Left to right (+X to -X)", "Right to left (-X to +X)"), 'y':("Top to bottom (+Y to -Y)", "Bottom to top (-Y to +Y)"), 'z':("Front to back (+Z to -Z)", "Back to front (-Z to +Z)"), } MIRROR_GUESS = 'Guess from stroke' MIRROR_FLIP = 'Flip'