Example #1
0
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
Example #2
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, 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)
Example #3
0
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()
Example #4
0
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
Example #5
0
#    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):
Example #7
0
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
Example #8
0
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
Example #9
0
#    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
Example #10
0
#    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()
Example #11
0
#    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"
Example #12
0
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):
        '''
Example #14
0
#    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
        '''
Example #15
0
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,
Example #16
0
#    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
Example #17
0
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)
Example #18
0
#        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
Example #20
0
#    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):
Example #21
0
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()
Example #23
0
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;
Example #25
0
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):
Example #26
0
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
Example #27
0
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:]
Example #28
0
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
Example #29
0
#    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
Example #30
0
#        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)
Example #32
0
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)
Example #34
0
#    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
        '''
Example #35
0
    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',
Example #37
0
#         
#    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'