Esempio n. 1
0
    def __init__(self, allowMultiSelection=True):
        self.items = []
        selectCommand = self.selectionChanged
        editCommand = self.editLabelCommand

        if Utils.getMayaVersion() < Utils.MAYA2012:
            selectCommand = Utils.createMelProcedure(self.selectionChanged,
                                                     [('int', 'item'),
                                                      ('int', 'state')],
                                                     returnType="int")
            editCommand = Utils.createMelProcedure(self.editLabelCommand,
                                                   [('string', 'item'),
                                                    ('string', 'newName')])

        self.control = cmds.treeView(
            numberOfButtons=0,
            height=100,
            selectCommand=selectCommand,
            editLabelCommand=editCommand,
            dragAndDropCommand=self.handleDragDropCommand)

        cmds.treeView(self.control, e=True, enableKeys=True)

        # list of selected IDs
        self.selectedItems = []
        self.onSelectionChanged = Signal()

        self.__selectionChanging = False
        self.__itemNameChanging = False
Esempio n. 2
0
 def __init__(self, annotation, name, value=None):
     self.annotation = annotation
     self.name = name
     self.intensityTexts = {0.0: "low", 0.33: "medium", 0.7: "high"}
     self.changeCommand = Signal()
     self.__value = Options.loadOption(name,
                                       1.0 if value is None else value)
Esempio n. 3
0
    def __init__(self):
        self.items = [] # type: list[InfluencesListEntry]
        self.mirrorMode = False

        self.mapper = None # type: InfluenceMapping
        self.currentInfluencesSelection = []

        self.onDelete = Signal("influence mapping preview: onDelete")
Esempio n. 4
0
 def __init__(self):
     '''
     :param str licensePath:
     :param Server licenseServerInterface:
     '''
     self.licensePath = None
     self.status = None
     self.changed = Signal("licenseStatus")
Esempio n. 5
0
    def __init__(self, range=(0.0, 1.0)):
        assert (len(range) == 2)

        self.model = None
        self.floatField = None
        self.slider = None
        self.onChange = Signal("floatSliderChange")
        self.onChanging = Signal("floatSliderChanging")
        self.range = range
        self.flexibleRange = False
Esempio n. 6
0
    def __init__(self, range=[0.0, 1.0]):
        assert len(range) == 2

        self.model = None
        self.floatField = None
        self.slider = None
        self.onChange = Signal()
        self.onChanging = Signal()
        self.range = range
        self.flexibleRange = False
Esempio n. 7
0
class ObservedValue(object):
    def __init__(self, name, initialValue=None):
        self.changed = Signal(name="cachedValue change (%s)" % name)
        self.value = initialValue

    def get(self):
        return self.value

    def set(self, value):
        self.value = value
        self.changed.emit()
Esempio n. 8
0
class Status:
    '''
    given licensePath, track license status.
    works both with ngstKey licenses and RLM licenses
    '''
    def __init__(self):
        '''
        :param str licensePath:
        :param Server licenseServerInterface:
        '''
        self.licensePath = None
        self.status = None
        self.changed = Signal("licenseStatus")

    def setLicensePath(self, licensePath):
        self.licensePath = licensePath
        self.recalculate()

    def setStatus(self, status):
        log.info("changing license status to %s", status)
        self.status = status
        self.changed.emit()

    def recalculate(self):
        if self.licensePath is None:
            self.licensePath = getLicensePath()

        ngstKeyLicenses = fileFormat.discoverLicenseFiles(self.licensePath)
        if len(ngstKeyLicenses) == 0:
            # no ngstkey licenses found, checking RLM
            self.setStatus(
                cmds.ngSkinToolsLicense(validateLicense=True,
                                        licensePath=self.licensePath))
        else:
            for license in ngstKeyLicenses:
                status = cmds.ngSkinToolsLicense(
                    validateLicense=True,
                    hostid=license.get('hostid'),
                    licenseKey=license.get('licensekey'),
                    licenseType=license.get('licenseType'),
                    signature=license.get('sig'))
                if status == 0:
                    self.setStatus(status)
                    break

        return self.status

    def isLicenseActive(self):
        return self.status == 0

    def getDefaultLicenseFileName(self):
        return os.path.join(self.licensePath, "ngskintools.lic")
Esempio n. 9
0
class IntensitySlider:
    def __init__(self, annotation, name, value=None):
        self.annotation = annotation
        self.name = name
        self.intensityTexts = {0.0: "low", 0.33: "medium", 0.7: "high"}
        self.changeCommand = Signal()
        self.__value = Options.loadOption(name,
                                          1.0 if value is None else value)

    def create(self):
        form = FormLayout(width=100)

        self.intensityIndicator = cmds.textField(
            width=Constants.NUMBER_FIELD_WIDTH,
            editable=False,
            annotation=self.annotation)
        self.sliderIntensity = cmds.floatSlider(min=0,
                                                max=1,
                                                step=0.05,
                                                value=self.__value,
                                                cc=self.sliderChange,
                                                annotation=self.annotation)

        form.attachForm(self.intensityIndicator, 0, None, 0, 0)
        form.attachForm(self.sliderIntensity, 2, 0, None, None)
        form.attachControl(self.sliderIntensity, self.intensityIndicator, None,
                           None, None, 0)

        self.updateIntensityDisplay()
        return form

    def getIntensity(self):
        return cmds.floatSlider(self.sliderIntensity, q=True, value=True)

    def sliderChange(self, *args):
        self.updateIntensityDisplay()
        Options.saveOption(self.name, self.getIntensity())
        self.changeCommand.emit()

    def updateIntensityDisplay(self):
        currIntensity = self.getIntensity()

        displayText = None
        displayedValue = -1
        for currValue, currText in self.intensityTexts.items():
            if currValue <= currIntensity and currValue > displayedValue:
                displayText = currText
                displayedValue = currValue

        cmds.textField(self.intensityIndicator, e=True, text=displayText)
Esempio n. 10
0
 def __init__(self, newLayerMode):
     BaseDialog.__init__(self)
     self.title = "New Layer" if newLayerMode else "Layer Properties"
     self.buttons = [self.BUTTON_OK, self.BUTTON_CANCEL]
     self.layerNameValue = ValueModel()
     self.layerOpacityValue = ValueModel()
     self.newLayerMode = newLayerMode
     self.onOpacityChange = Signal("Layer Opacity changed")
Esempio n. 11
0
class IntensitySlider:
    def __init__(self,annotation,name,value=None):
        self.annotation = annotation
        self.name = name
        self.intensityTexts = {0.0:"low",0.33:"medium",0.7:"high"}
        self.changeCommand = Signal()
        self.__value = Options.loadOption(name, 1.0 if value is None else value);
    
    def create(self):
        form = FormLayout(width=100)

        self.intensityIndicator = cmds.textField(width=Constants.NUMBER_FIELD_WIDTH,editable=False,annotation=self.annotation);
        self.sliderIntensity = cmds.floatSlider(min=0, max=1, step=0.05, value=self.__value,cc=self.sliderChange,annotation=self.annotation )

        form.attachForm(self.intensityIndicator,0,None,0,0)
        form.attachForm(self.sliderIntensity,2,0,None,None)
        form.attachControl(self.sliderIntensity,self.intensityIndicator,None,None,None,0)

        self.updateIntensityDisplay()
        return form
        
    def getIntensity(self):
        return cmds.floatSlider(self.sliderIntensity,q=True,value=True)
    
    
    def sliderChange(self,*args):
        self.updateIntensityDisplay()
        Options.saveOption(self.name, self.getIntensity())
        self.changeCommand.emit()
    
    def updateIntensityDisplay(self):
        currIntensity = self.getIntensity()

        displayText = None
        displayedValue = -1
        for currValue,currText in self.intensityTexts.items():
            if currValue<=currIntensity and currValue>displayedValue:
                displayText = currText
                displayedValue = currValue

        cmds.textField(self.intensityIndicator,e=True,text=displayText);
        
Esempio n. 12
0
    def __init__(self, allowMultiSelection=True):
        self.items = []
        selectCommand = self.selectionChanged
        editCommand = self.editLabelCommand

        self.control = cmds.treeView(
            numberOfButtons=0,
            height=100,
            selectCommand=selectCommand,
            editLabelCommand=editCommand,
            dragAndDropCommand=self.handleDragDropCommand)

        cmds.treeView(self.control, e=True, enableKeys=True)

        # list of selected IDs
        self.selectedItems = []
        self.onSelectionChanged = Signal("onSelectionChanged")

        self.__selectionChanging = False
        self.__itemNameChanging = False
Esempio n. 13
0
class ValueUIWrapper(BaseUIWrapper):
    '''
    UI wrapper for controls that have a settable/gettable value (like float field)
    '''
    def __init__(self, uiCommand=None, annotation=''):
        BaseUIWrapper.__init__(self, uiCommand, annotation)
        self.valueKeyName = 'value'
        self.changeCommand = Signal("ValueUIWrapper changeCommand")

    def getValue(self):
        return self.uiCommand(self.field, q=True, **{self.valueKeyName: True})

    def setValue(self, value):
        args = {self.valueKeyName: value}
        self.uiCommand(self.field, e=True, **args)

    def createUI(self, **kargs):
        kargs['changeCommand'] = self.fieldChanged
        BaseUIWrapper.createUI(self, **kargs)

    def fieldChanged(self, *args):
        self.changeCommand.emit()
Esempio n. 14
0
class ValueUIWrapper(BaseUIWrapper):
    '''
    UI wrapper for controls that have a settable/gettable value (like float field)
    '''
    
    def __init__(self,uiCommand=None,annotation=''):
        BaseUIWrapper.__init__(self,uiCommand,annotation)
        self.valueKeyName = 'value'
        self.changeCommand = Signal()
        
    def getValue(self):
        return self.uiCommand(self.field,q=True,**{self.valueKeyName:True})
    
    def setValue(self,value):
        args = {self.valueKeyName:value}
        self.uiCommand(self.field,e=True,**args)
        
    def createUI(self,**kargs):
        kargs['changeCommand'] = self.fieldChanged
        BaseUIWrapper.createUI(self,**kargs)

    def fieldChanged(self,*args):
        self.changeCommand.emit()
Esempio n. 15
0
 def __init__(self,allowMultiSelection=True):
     self.items = []
     selectCommand = self.selectionChanged
     editCommand = self.editLabelCommand
     
     if Utils.getMayaVersion()<Utils.MAYA2012:
         selectCommand = Utils.createMelProcedure(self.selectionChanged, [('int','item'),('int','state')],returnType="int")
         editCommand = Utils.createMelProcedure(self.editLabelCommand, [('string','item'),('string','newName')])
         
     self.control = cmds.treeView(numberOfButtons=0, height=100, selectCommand=selectCommand, editLabelCommand=editCommand,dragAndDropCommand=self.handleDragDropCommand)
     
     cmds.treeView(self.control,e=True,enableKeys=True)
     
     # list of selected IDs
     self.selectedItems = []
     self.onSelectionChanged = Signal()
     
     self.__selectionChanging = False
     self.__itemNameChanging = False
Esempio n. 16
0
 def __init__(self, name, initialValue=None):
     self.changed = Signal(name="cachedValue change (%s)" % name)
     self.value = initialValue
Esempio n. 17
0
class TreeViewIDList:
    def __init__(self, allowMultiSelection=True):
        self.items = []
        selectCommand = self.selectionChanged
        editCommand = self.editLabelCommand

        if Utils.getMayaVersion() < Utils.MAYA2012:
            selectCommand = Utils.createMelProcedure(self.selectionChanged,
                                                     [('int', 'item'),
                                                      ('int', 'state')],
                                                     returnType="int")
            editCommand = Utils.createMelProcedure(self.editLabelCommand,
                                                   [('string', 'item'),
                                                    ('string', 'newName')])

        self.control = cmds.treeView(numberOfButtons=0,
                                     height=100,
                                     selectCommand=selectCommand,
                                     editLabelCommand=editCommand)

        if Utils.getMayaVersion() >= Utils.MAYA2011:
            cmds.treeView(self.control, e=True, enableKeys=True)

        self.selectedID = None
        self.selectedItems = set()
        self.onSelectionChanged = Signal()

        self.__selectionChanging = False
        self.__itemNameChanging = False

    def setItems(self, newItems, selectedID=None):
        if selectedID is None:
            selectedID = self.getSelectedID()

        if self.items != newItems:
            self.doSetItems(newItems)

        self.items = newItems
        self.selectByID(selectedID)

    def selectionChanged(self, item, selected):
        self.__selectionChanging = True
        try:
            selected = int(selected)

            item = int(item)
            if selected:
                self.selectedItems.add(item)
                self.selectedID = item
                self.onSelectionChanged.emit()
            else:
                self.selectedItems.discard(item)

        finally:
            self.__selectionChanging = False

        return True

    def internalEditLabelCommand(self, item, newName):
        '''
        indirectly executed by treeview inplace editor;
        return non empty string to let treeView know that rename succeeded 
        '''
        return ''

    def editLabelCommand(self, item, newName):
        '''
        subclasses should override internalEditLabelCommand instead
        '''
        self.__itemNameChanging = True
        try:
            return self.internalEditLabelCommand(item, newName)
        finally:
            self.__itemNameChanging = False

    def doSetItems(self, newItems):
        if self.__selectionChanging:
            raise Exception("cannot change items while selection is changing")
        if self.__itemNameChanging:
            # raise Exception("cannot change items while item name is changing")
            # silently exit: should be no harm ignoring new items as the only thing that changed
            # is label name, and we already reflect that in UI
            return

        self.selectedID = None
        self.selectedItems.clear()

        #cmds.treeView(self.control,e=True,clearSelection=True)
        cmds.treeView(self.control, e=True, removeAll=True)
        for i in newItems:
            cmds.treeView(self.control, e=True, addItem=(i.id, ''))
            cmds.treeView(self.control,
                          e=True,
                          displayLabel=(i.id, i.displayName),
                          displayLabelSuffix=(i.id, i.suffix))

    def getSelectedID(self):
        return self.selectedID

    def getSelectedNames(self):
        return [i.name for i in self.items if (i.id in self.selectedItems)]

    def getSelectedIDs(self):
        return list(self.selectedItems)

    def selectByID(self, id):
        if self.__selectionChanging:
            return

        if self.selectedID == id:
            return

        self.selectedItems.add(id)

        for i in self.items:
            cmds.treeView(self.control, e=True, si=(i.id, int(i.id == id)))
Esempio n. 18
0
 def __init__(self,annotation,name,value=None):
     self.annotation = annotation
     self.name = name
     self.intensityTexts = {0.0:"low",0.33:"medium",0.7:"high"}
     self.changeCommand = Signal()
     self.__value = Options.loadOption(name, 1.0 if value is None else value);
Esempio n. 19
0
class TreeViewIDList(object):
    def __init__(self,allowMultiSelection=True):
        self.items = []
        selectCommand = self.selectionChanged
        editCommand = self.editLabelCommand
        
        if Utils.getMayaVersion()<Utils.MAYA2012:
            selectCommand = Utils.createMelProcedure(self.selectionChanged, [('int','item'),('int','state')],returnType="int")
            editCommand = Utils.createMelProcedure(self.editLabelCommand, [('string','item'),('string','newName')])
            
        self.control = cmds.treeView(numberOfButtons=0, height=100, selectCommand=selectCommand, editLabelCommand=editCommand,dragAndDropCommand=self.handleDragDropCommand)
        
        cmds.treeView(self.control,e=True,enableKeys=True)
        
        # list of selected IDs
        self.selectedItems = []
        self.onSelectionChanged = Signal()
        
        self.__selectionChanging = False
        self.__itemNameChanging = False
    
    def handleDragDropCommand(self,itemList,prevParents,prevIndexes,newParent,newIndexes,itemBefore,itemAfter):
        pass

    def setItems(self,newItems,selectedID):
        '''
        sets new items in the ID list;
        :param list newItems: list of IdToNameEntry
        :param selectedID: item to select in the list after setting the items 
        '''
        
        log.debug("set items: {0},selected id: {1}".format(newItems,selectedID))
            
        if self.items!=newItems:
            if self.__selectionChanging:
                raise Exception("cannot change items while selection is changing")
            if self.__itemNameChanging:
                # raise Exception("cannot change items while item name is changing")
                # silently exit: should be no harm ignoring new items as the only thing that changed
                # is label name, and we already reflect that in UI 
                return
            
            self.selectedItems = []
            
            cmds.treeView(self.control,e=True,clearSelection=True)
            cmds.treeView(self.control,e=True,removeAll=True)
            for i in newItems:
                cmds.treeView(self.control,e=True,addItem=(i.id,''))
                cmds.treeView(self.control,e=True,displayLabel=(i.id,i.displayName),displayLabelSuffix=(i.id,i.suffix))
            
        self.items = newItems
        self.selectByID(selectedID)
        
    def getIdByInternalId(self,internalId):
        '''
        given item ID (internal UI label), return actual item ID from the item.
        '''
        if internalId is None:
            return None
        
        for i in self.items:
            if i.internalId==internalId:
                return i.id
            
        raise Exception("invalid internal ID: {!r}".format(internalId))

    def selectionChanged(self,internalId,selected):
        '''
        UI event handler for when selection is changed
        there are events fired for items being unselected (``selected`` is 0)
        and for items being selected (``selected`` is 1)
        '''
        item = self.getIdByInternalId(internalId)
        
        self.__selectionChanging = True
        try:
            if selected:
                if item is not None and item not in self.selectedItems:
                    self.selectedItems.append(item)
                    self.onSelectionChanged.emit()
            else:
                if item in self.selectedItems:
                    self.selectedItems.remove(item)
                    self.onSelectionChanged.emit()
        finally:
            self.__selectionChanging = False
            
        return True
        
    
    def internalEditLabelCommand(self,item,newName):
        '''
        indirectly executed by treeview inplace editor;
        return non empty string to let treeView know that rename succeeded
        
        :return: True, if rename was successful; false otherwise. 
        '''
        return False
    
    def editLabelCommand(self,internalId,newName):
        '''
        UI event handler for when user edits item label;
        
        subclasses should override internalEditLabelCommand instead
        '''
        self.__itemNameChanging = True
        try:
            item = self.getIdByInternalId(internalId)
            renameSuccessful = self.internalEditLabelCommand(item, newName)
            if not renameSuccessful:
                return ''
            return internalId
        finally:
            self.__itemNameChanging = False
            

    def getSelectedNames(self):
        '''
        :return: names of all currently selected items 
        '''
        return [i.name for i in self.items if (i.id in self.selectedItems)]
    
    
    def getSelectedID(self):
        '''
        :return: ID of currently selected item
        '''
        return None if not self.selectedItems else self.selectedItems[-1]

    def getSelectedIDs(self):
        '''
        :return: IDs of selected items
        '''
        return self.selectedItems
        

    
    def selectByID(self,itemId):
        self.setSelectedIDs([itemId])
        
    def setSelectedIDs(self,ids):
        log.debug("selecting by ID: {0}".format(id))
        if self.__selectionChanging:
            return
        
        # remove any null items
        ids = [i for i in ids if i is not None]
        
        if self.selectedItems == ids:
            return

        self.selectedItems = ids
        
        for i in self.items:
            cmds.treeView(self.control,e=True,si=(i.internalId,int(i.id in ids)))
Esempio n. 20
0
class TreeViewIDList(object):
    def __init__(self, allowMultiSelection=True):
        self.items = []
        selectCommand = self.selectionChanged
        editCommand = self.editLabelCommand

        if Utils.getMayaVersion() < Utils.MAYA2012:
            selectCommand = Utils.createMelProcedure(self.selectionChanged,
                                                     [('int', 'item'),
                                                      ('int', 'state')],
                                                     returnType="int")
            editCommand = Utils.createMelProcedure(self.editLabelCommand,
                                                   [('string', 'item'),
                                                    ('string', 'newName')])

        self.control = cmds.treeView(
            numberOfButtons=0,
            height=100,
            selectCommand=selectCommand,
            editLabelCommand=editCommand,
            dragAndDropCommand=self.handleDragDropCommand)

        cmds.treeView(self.control, e=True, enableKeys=True)

        # list of selected IDs
        self.selectedItems = []
        self.onSelectionChanged = Signal()

        self.__selectionChanging = False
        self.__itemNameChanging = False

    def handleDragDropCommand(self, itemList, prevParents, prevIndexes,
                              newParent, newIndexes, itemBefore, itemAfter):
        pass

    def setItems(self, newItems, selectedID):
        '''
        sets new items in the ID list;
        :param list newItems: list of IdToNameEntry
        :param selectedID: item to select in the list after setting the items 
        '''

        log.debug("set items: {0},selected id: {1}".format(
            newItems, selectedID))

        if self.items != newItems:
            if self.__selectionChanging:
                raise Exception(
                    "cannot change items while selection is changing")
            if self.__itemNameChanging:
                # raise Exception("cannot change items while item name is changing")
                # silently exit: should be no harm ignoring new items as the only thing that changed
                # is label name, and we already reflect that in UI
                return

            self.selectedItems = []

            cmds.treeView(self.control, e=True, clearSelection=True)
            cmds.treeView(self.control, e=True, removeAll=True)
            for i in newItems:
                cmds.treeView(self.control, e=True, addItem=(i.id, ''))
                cmds.treeView(self.control,
                              e=True,
                              displayLabel=(i.id, i.displayName),
                              displayLabelSuffix=(i.id, i.suffix))

        self.items = newItems
        self.selectByID(selectedID)

    def getIdByInternalId(self, internalId):
        '''
        given item ID (internal UI label), return actual item ID from the item.
        '''
        if internalId is None:
            return None

        for i in self.items:
            if i.internalId == internalId:
                return i.id

        raise Exception("invalid internal ID: {!r}".format(internalId))

    def selectionChanged(self, internalId, selected):
        '''
        UI event handler for when selection is changed
        there are events fired for items being unselected (``selected`` is 0)
        and for items being selected (``selected`` is 1)
        '''
        item = self.getIdByInternalId(internalId)

        self.__selectionChanging = True
        try:
            if selected:
                if item is not None and item not in self.selectedItems:
                    self.selectedItems.append(item)
                    self.onSelectionChanged.emit()
            else:
                if item in self.selectedItems:
                    self.selectedItems.remove(item)
                    self.onSelectionChanged.emit()
        finally:
            self.__selectionChanging = False

        return True

    def internalEditLabelCommand(self, item, newName):
        '''
        indirectly executed by treeview inplace editor;
        return non empty string to let treeView know that rename succeeded
        
        :return: True, if rename was successful; false otherwise. 
        '''
        return False

    def editLabelCommand(self, internalId, newName):
        '''
        UI event handler for when user edits item label;
        
        subclasses should override internalEditLabelCommand instead
        '''
        self.__itemNameChanging = True
        try:
            item = self.getIdByInternalId(internalId)
            renameSuccessful = self.internalEditLabelCommand(item, newName)
            if not renameSuccessful:
                return ''
            return internalId
        finally:
            self.__itemNameChanging = False

    def getSelectedNames(self):
        '''
        :return: names of all currently selected items 
        '''
        return [i.name for i in self.items if (i.id in self.selectedItems)]

    def getSelectedID(self):
        '''
        :return: ID of currently selected item
        '''
        return None if not self.selectedItems else self.selectedItems[-1]

    def getSelectedIDs(self):
        '''
        :return: IDs of selected items
        '''
        return self.selectedItems

    def selectByID(self, itemId):
        self.setSelectedIDs([itemId])

    def setSelectedIDs(self, ids):
        log.debug("selecting by ID: {0}".format(id))
        if self.__selectionChanging:
            return

        # remove any null items
        ids = [i for i in ids if i is not None]

        if self.selectedItems == ids:
            return

        self.selectedItems = ids

        for i in self.items:
            cmds.treeView(self.control,
                          e=True,
                          si=(i.internalId, int(i.id in ids)))
Esempio n. 21
0
 def __init__(self, parent):
     self.parent = parent
     self.mainLayout = None
     self.filterChanged = Signal("Influence filter changed")
     self.isVisible = PersistentValueModel(
         Options.VAR_OPTION_PREFIX + "_InfluenceFilterVisible", False)
Esempio n. 22
0
class InfluenceFilterUi:
    VAR_PREFIX = 'ngSkinToolsInfluenceFilter_'

    def __init__(self, parent):
        self.parent = parent
        self.mainLayout = None
        self.filterChanged = Signal("Influence filter changed")
        self.isVisible = PersistentValueModel(
            Options.VAR_OPTION_PREFIX + "_InfluenceFilterVisible", False)

    def createUI(self, parent):
        result = group = self.mainLayout = uiWrappers.frameLayout(
            parent=parent,
            label="Influence Filter",
            marginWidth=Constants.MARGIN_SPACING_HORIZONTAL,
            marginHeight=Constants.MARGIN_SPACING_VERTICAL,
            collapsable=True,
            expandCommand=self.isVisible.save,
            collapseCommand=self.isVisible.save,
            borderStyle='etchedIn')
        cmds.frameLayout(group, e=True, collapse=self.isVisible.get())

        column = cmds.columnLayout(
            parent=group,
            adjustableColumn=1,
            rowSpacing=Constants.MARGIN_SPACING_VERTICAL)

        form = FormLayout(parent=column)

        label = cmds.text(label='Influence Filter:')
        textField = self.influenceNameFilter = TextEdit(
            annotation="Filter influence list by name")
        clearButton = cmds.button(label='clear',
                                  width=50,
                                  command=self.clearNameFilter)

        form.attachForm(label, 10, None, 0,
                        Constants.MARGIN_SPACING_HORIZONTAL)
        form.attachForm(clearButton, 10, Constants.MARGIN_SPACING_HORIZONTAL,
                        0, None)
        form.attachForm(textField, 10, None, 0, None)
        form.attachControl(textField, label, None, None, None,
                           Constants.MARGIN_SPACING_HORIZONTAL)
        form.attachControl(textField, clearButton, None,
                           Constants.MARGIN_SPACING_HORIZONTAL, None, None)

        textField.changeCommand.addHandler(self.filterChanged.emit)

        cmds.setParent(result)
        cmds.radioCollection()

        form = FormLayout(parent=column)

        self.radioAllInfluences = RadioButtonField(self.VAR_PREFIX +
                                                   "allInfluences",
                                                   defaultValue=1,
                                                   label='Show all influences')
        self.radioAllInfluences.changeCommand.addHandler(
            self.radioAllInfluencesChanged)
        self.radioActiveInfluences = RadioButtonField(
            self.VAR_PREFIX + "activeInfluences",
            defaultValue=0,
            label='Only influences with non-zero weights')
        form.attachForm(self.radioAllInfluences, 0, 0, None, 90)
        form.attachForm(self.radioActiveInfluences, None, 0, None, 90)
        form.attachControl(self.radioActiveInfluences, self.radioAllInfluences,
                           0, None, None, None)

        return result

    def clearNameFilter(self, *args):
        self.influenceNameFilter.setValue('')
        self.influenceNameFilter.changeCommand.emit()

    def radioAllInfluencesChanged(self):
        self.parent.updateInfluenceMenuValues()
        self.filterChanged.emit()

    def setVisible(self, visible):
        self.isVisible.set(visible)
        cmds.frameLayout(self.mainLayout,
                         e=True,
                         collapse=self.isVisible.get())

    def toggle(self):
        self.setVisible(not self.isVisible.get())

    def createNameFilter(self):
        '''
        returns name filter (InflFilterMatch instance) based on control values
        '''
        filter = InfluenceNameFilter()
        filter.setFilterString(self.influenceNameFilter.getValue())
        return filter
Esempio n. 23
0
 def __init__(self, uiCommand=None, annotation=''):
     BaseUIWrapper.__init__(self, uiCommand, annotation)
     self.valueKeyName = 'value'
     self.changeCommand = Signal("ValueUIWrapper changeCommand")
Esempio n. 24
0
class InfluencesMappingPreview(object):
    VAR_PREFIX = "ngSkinToolsInfluenceSourceDestinationPreview"

    def __init__(self):
        self.items = [] # type: list[InfluencesListEntry]
        self.mirrorMode = False

        self.mapper = None # type: InfluenceMapping
        self.currentInfluencesSelection = []

        self.onDelete = Signal("influence mapping preview: onDelete")

    def constructInfluenceList(self):
        self.items = []
        self.currentInfluencesSelection = []


        mapper = self.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(src):
            return src.logicalIndex not in mapper.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, 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.influencesList, e=True, removeAll=True,
                            append=[i.asLabel() for i in self.items])

    @staticmethod
    def findAssociation(itemList, source, destination, automatic):
        for i in itemList:
            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 getSelectedPairs(self):
        selection = cmds.textScrollList(self.influencesList,q=True,sii=True)

        if selection is not None:
            for i in selection:
                yield self.items[i-1]

    # noinspection PyUnusedLocal
    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)

    def createUI(self, parent):
        influencesLayout = cmds.columnLayout(parent=parent, adjustableColumn=1,
                                             rowSpacing=Constants.MARGIN_SPACING_VERTICAL)
        cmds.text(parent=influencesLayout, label="Influence mapping to be used:", align='left')
        self.influencesList = cmds.textScrollList(parent=influencesLayout, height=200, numberOfRows=5,
                                                           allowMultiSelection=True,
                                                           selectCommand=self.onInfluenceSelected,
                                                           deleteKeyCommand=lambda
                                                               *args: self.onDelete.emit())
Esempio n. 25
0
class InfluencesManualMapping(object):
    VAR_PREFIX = "ngSkinToolsInfluencesManualMapping"

    def __init__(self):
        self.mirrorMode = False
        self.getSelectedInfluences = lambda: None
        self.manualOverrides = {}
        self.manualOverridesChanged = Signal(
            "influences manual mapping: manual overrides changed")

    def createUI(self, parent):
        cmds.rowLayout(parent=parent, 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=parent, 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'
        )

    def addManualInfluenceMapping(self, source, destination):
        self.manualOverrides[
            source.
            logicalIndex] = None if destination is None else destination.logicalIndex

    def doDisconnectMapping(self):
        for mapping in self.getSelectedInfluences():
            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.manualOverridesChanged.emit()

    def doConnectMapping(self, bidirectional=True):
        selection = self.getSelectedInfluences()
        if len(selection) < 2:
            return

        if bidirectional and len(selection) != 2:
            return

        validSources = []

        for item in selection[:-1]:
            if item.isConnectedElsewhere() or item.source is None:
                continue
            validSources.append(item)

        # second selected list item
        destinationItem = selection[-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.manualOverridesChanged.emit()

    def removeSelectedManualMappings(self):
        selection = self.getSelectedInfluences()
        for item in selection:
            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.manualOverridesChanged.emit()
Esempio n. 26
0
 def __init__(self):
     self.mirrorMode = False
     self.getSelectedInfluences = lambda: None
     self.manualOverrides = {}
     self.manualOverridesChanged = Signal(
         "influences manual mapping: manual overrides changed")
Esempio n. 27
0
 def __init__(self,parent):
     self.parent = parent
     self.mainLayout = None
     self.filterChanged = Signal("Influence filter changed")
     self.isVisible = PersistentValueModel(Options.VAR_OPTION_PREFIX+"_InfluenceFilterVisible", False)
Esempio n. 28
0
 def __init__(self, ownerUI):
     self.ownerUI = ownerUI
     self.onExecuted = Signal("action executed")
     self.updateControls = []
Esempio n. 29
0
class FloatSliderField:
    """
    Similar to float slider group, only without caption
    """

    def __init__(self, range=[0.0, 1.0]):
        assert len(range) == 2

        self.model = None
        self.floatField = None
        self.slider = None
        self.onChange = Signal()
        self.onChanging = Signal()
        self.range = range
        self.flexibleRange = False

    def updateModel(self):
        if self.model is not None:
            self.model.set(self.getValue())

    def sliderChanging(self, *args):
        """
        handler for when float slider value is dragged
        """
        self.floatField.setValue(cmds.floatSlider(self.slider, q=True, value=True))
        self.updateModel()
        self.onChanging.emit()

    def sliderChanged(self, *args):
        self.updateModel()
        self.onChange.emit()

    def __updateSliderValue(self):
        value = self.floatField.getValue()
        minValue = cmds.floatSlider(self.slider, q=True, minValue=True)
        maxValue = cmds.floatSlider(self.slider, q=True, maxValue=True)
        if self.flexibleRange:
            # change min/max, if needed
            cmds.floatSlider(self.slider, e=True, minValue=min(value, minValue), maxValue=max(value, maxValue))
        else:
            # change value, if needed
            value = min(maxValue, max(value, minValue))

        cmds.floatSlider(self.slider, e=True, value=self.floatField.getValue())

    def fieldChanged(self):
        """
        handler for when float field value changes
        """
        self.__updateSliderValue()
        self.updateModel()
        self.onChange.emit()

    def create(self):
        result = self.mainLayout = cmds.rowLayout(nc=2, adjustableColumn=2)
        step = (self.range[1] - self.range[0]) / 100.0

        self.floatField = FloatField(self.model, step=step)
        if not self.flexibleRange:
            self.floatField.editUI(minValue=self.range[0], maxValue=self.range[1])

        self.floatField.changeCommand.addHandler(self.fieldChanged)
        self.slider = cmds.floatSlider(
            value=self.floatField.getValue(),
            dragCommand=self.sliderChanging,
            changeCommand=self.sliderChanged,
            step=step,
            minValue=self.range[0],
            maxValue=self.range[1],
        )

        self.__updateSliderValue()
        return result

    def setModel(self, model):
        self.floatField.setModel(model)
        self.__updateSliderValue()

    def setValue(self, value):
        self.floatField.setValue(value)
        self.__updateSliderValue()

    def getValue(self):
        return self.floatField.getValue()

    def setEnabled(self, enabled):
        cmds.layout(self.mainLayout, e=True, enable=enabled)
Esempio n. 30
0
 def __init__(self, ownerUI):
     self.ownerUI = ownerUI
     self.onExecuted = Signal("influence filter visibility changed")
     self.updateControls = []
Esempio n. 31
0
 def __init__(self,uiCommand=None,annotation=''):
     BaseUIWrapper.__init__(self,uiCommand,annotation)
     self.valueKeyName = 'value'
     self.changeCommand = Signal()
Esempio n. 32
0
class InfluenceFilterUi:
    VAR_PREFIX = 'ngSkinToolsInfluenceFilter_'

    def __init__(self,parent):
        self.parent = parent
        self.mainLayout = None
        self.filterChanged = Signal("Influence filter changed")
        self.isVisible = PersistentValueModel(Options.VAR_OPTION_PREFIX+"_InfluenceFilterVisible", False)
    
    def createUI(self,parent):
        result = group = self.mainLayout = cmds.frameLayout(parent=parent,label="Influence Filter", marginWidth=Constants.MARGIN_SPACING_HORIZONTAL,marginHeight=Constants.MARGIN_SPACING_VERTICAL, collapsable=True,
                                 expandCommand=self.isVisible.save,collapseCommand=self.isVisible.save,
                                 borderStyle='etchedIn')
        cmds.frameLayout(group,e=True,collapse = self.isVisible.get())
        
        column = cmds.columnLayout(parent=group,adjustableColumn=1,rowSpacing=Constants.MARGIN_SPACING_VERTICAL)
        
        form = FormLayout(parent=column)
        
        label=cmds.text(label='Influence Filter:')
        textField = self.influenceNameFilter = TextEdit(annotation="Filter influence list by name")
        clearButton = cmds.button(label='clear',width=50,command=self.clearNameFilter)
        
        
        form.attachForm(label, 10, None, 0, Constants.MARGIN_SPACING_HORIZONTAL)
        form.attachForm(clearButton, 10, Constants.MARGIN_SPACING_HORIZONTAL, 0, None)
        form.attachForm(textField,10,None,0,None)
        form.attachControl(textField,label,None,None,None,Constants.MARGIN_SPACING_HORIZONTAL)
        form.attachControl(textField,clearButton,None,Constants.MARGIN_SPACING_HORIZONTAL,None,None)
        
        
        textField.changeCommand.addHandler(self.filterChanged.emit)
        
        cmds.setParent(result)
        cmds.radioCollection()
        
        
        form = FormLayout(parent=column)
        
        self.radioAllInfluences = RadioButtonField(self.VAR_PREFIX+"allInfluences",defaultValue=1,label='Show all influences')
        self.radioAllInfluences.changeCommand.addHandler(self.radioAllInfluencesChanged)
        self.radioActiveInfluences = RadioButtonField(self.VAR_PREFIX+"activeInfluences",defaultValue=0,label='Only influences with non-zero weights')
        form.attachForm(self.radioAllInfluences, 0, 0, None, 90)
        form.attachForm(self.radioActiveInfluences, None, 0, None, 90)
        form.attachControl(self.radioActiveInfluences, self.radioAllInfluences, 0, None, None, None)
        
        return result
    
    def clearNameFilter(self,*args):
        self.influenceNameFilter.setValue('')
        self.influenceNameFilter.changeCommand.emit()
        
    def radioAllInfluencesChanged(self):
        self.parent.updateInfluenceMenuValues()
        self.filterChanged.emit()

        

    def setVisible(self,visible):
        self.isVisible.set(visible)
        cmds.frameLayout(self.mainLayout,e=True,collapse = self.isVisible.get())
    
    def toggle(self):
        self.setVisible(not self.isVisible.get())
        
    def createNameFilter(self):
        '''
        returns name filter (InflFilterMatch instance) based on control values
        '''
        filter = InfluenceNameFilter()
        filter.setFilterString(self.influenceNameFilter.getValue())
        return filter
Esempio n. 33
0
class FloatSliderField:
    '''
    Similar to float slider group, only without caption
    '''
    def __init__(self, range=(0.0, 1.0)):
        assert (len(range) == 2)

        self.model = None
        self.floatField = None
        self.slider = None
        self.onChange = Signal("floatSliderChange")
        self.onChanging = Signal("floatSliderChanging")
        self.range = range
        self.flexibleRange = False

    def updateModel(self):
        if self.model is not None:
            self.model.set(self.getValue())

    def sliderChanging(self, *args):
        '''
        handler for when float slider value is dragged
        '''
        self.floatField.setValue(
            cmds.floatSlider(self.slider, q=True, value=True))
        self.updateModel()
        self.onChanging.emit()

    def sliderChanged(self, *args):
        self.updateModel()
        self.onChange.emit()

    def __updateSliderValue(self):
        value = self.floatField.getValue()
        minValue = cmds.floatSlider(self.slider, q=True, minValue=True)
        maxValue = cmds.floatSlider(self.slider, q=True, maxValue=True)
        if self.flexibleRange:
            #change min/max, if needed
            cmds.floatSlider(self.slider,
                             e=True,
                             minValue=min(value, minValue),
                             maxValue=max(value, maxValue))
        else:
            #change value, if needed
            value = min(maxValue, max(value, minValue))

        cmds.floatSlider(self.slider, e=True, value=self.floatField.getValue())

    def fieldChanged(self):
        '''
        handler for when float field value changes
        '''
        self.__updateSliderValue()
        self.updateModel()
        self.onChange.emit()

    def create(self):
        result = self.mainLayout = cmds.rowLayout(nc=2, adjustableColumn=2)
        step = (self.range[1] - self.range[0]) / 100.0

        self.floatField = FloatField(self.model, step=step)
        if not self.flexibleRange:
            self.floatField.editUI(minValue=self.range[0],
                                   maxValue=self.range[1])

        self.floatField.changeCommand.addHandler(self.fieldChanged)
        self.slider = cmds.floatSlider(value=self.floatField.getValue(),
                                       dragCommand=self.sliderChanging,
                                       changeCommand=self.sliderChanged,
                                       step=step,
                                       minValue=self.range[0],
                                       maxValue=self.range[1])

        self.__updateSliderValue()
        return result

    def setModel(self, model):
        self.floatField.setModel(model)
        self.__updateSliderValue()

    def setValue(self, value):
        self.floatField.setValue(value)
        self.__updateSliderValue()

    def getValue(self):
        return self.floatField.getValue()

    def setEnabled(self, enabled):
        cmds.layout(self.mainLayout, e=True, enable=enabled)
Esempio n. 34
0
class TreeViewIDList:
    def __init__(self,allowMultiSelection=True):
        self.items = []
        selectCommand = self.selectionChanged
        editCommand = self.editLabelCommand
        
        if Utils.getMayaVersion()<Utils.MAYA2012:
            selectCommand = Utils.createMelProcedure(self.selectionChanged, [('int','item'),('int','state')],returnType="int")
            editCommand = Utils.createMelProcedure(self.editLabelCommand, [('string','item'),('string','newName')])
            
        self.control = cmds.treeView(numberOfButtons=0, height=100, selectCommand=selectCommand, editLabelCommand=editCommand)
        
        if Utils.getMayaVersion()>=Utils.MAYA2011:
            cmds.treeView(self.control,e=True,enableKeys=True)
        
        self.selectedID = None
        self.selectedItems = set()
        self.onSelectionChanged = Signal()
        
        self.__selectionChanging = False
        self.__itemNameChanging = False

    def setItems(self,newItems,selectedID=None):
        if selectedID is None:
            selectedID = self.getSelectedID()
            
        if self.items!=newItems:
            self.doSetItems(newItems)
            
        self.items = newItems
        self.selectByID(selectedID)

    def selectionChanged(self,item,selected):
        self.__selectionChanging = True
        try:
            selected = int(selected)
            
            item = int(item)
            if selected:
                self.selectedItems.add(item)
                self.selectedID = item
                self.onSelectionChanged.emit()
            else:
                self.selectedItems.discard(item)
            
        finally:
            self.__selectionChanging = False

        return True
        
    
    def internalEditLabelCommand(self,item,newName):
        '''
        indirectly executed by treeview inplace editor;
        return non empty string to let treeView know that rename succeeded 
        '''
        return ''
    
    def editLabelCommand(self,item,newName):
        '''
        subclasses should override internalEditLabelCommand instead
        '''
        self.__itemNameChanging = True
        try:
            return self.internalEditLabelCommand(item, newName)
        finally:
            self.__itemNameChanging = False
            

    def doSetItems(self,newItems):
        if self.__selectionChanging:
            raise Exception("cannot change items while selection is changing")
        if self.__itemNameChanging:
            # raise Exception("cannot change items while item name is changing")
            # silently exit: should be no harm ignoring new items as the only thing that changed
            # is label name, and we already reflect that in UI 
            return
        
        self.selectedID = None
        self.selectedItems.clear()
        
        #cmds.treeView(self.control,e=True,clearSelection=True)
        cmds.treeView(self.control,e=True,removeAll=True)
        for i in newItems:
            cmds.treeView(self.control,e=True,addItem=(i.id,''))
            cmds.treeView(self.control,e=True,displayLabel=(i.id,i.displayName),displayLabelSuffix=(i.id,i.suffix))
    
    
    def getSelectedID(self):
        return self.selectedID
    
    def getSelectedNames(self):
        return [i.name for i in self.items if (i.id in self.selectedItems)]
    
    def getSelectedIDs(self):
        return list(self.selectedItems)
        

    
    def selectByID(self,id):
        if self.__selectionChanging:
            return
        
        
        if self.selectedID==id:
            return
        
        self.selectedItems.add(id)
        
        
        for i in self.items:
            cmds.treeView(self.control,e=True,si=(i.id,int(i.id==id)))
Esempio n. 35
0
class TreeViewIDList(object):
    def __init__(self, allowMultiSelection=True):
        self.items = []
        selectCommand = self.selectionChanged
        editCommand = self.editLabelCommand

        self.control = cmds.treeView(
            numberOfButtons=0,
            height=100,
            selectCommand=selectCommand,
            editLabelCommand=editCommand,
            dragAndDropCommand=self.handleDragDropCommand)

        cmds.treeView(self.control, e=True, enableKeys=True)

        # list of selected IDs
        self.selectedItems = []
        self.onSelectionChanged = Signal("onSelectionChanged")

        self.__selectionChanging = False
        self.__itemNameChanging = False

    def handleDragDropCommand(self, itemList, prevParents, prevIndexes,
                              newParent, newIndexes, itemBefore, itemAfter):
        pass

    def setItems(self, newItems, selectedID):
        '''
        sets new items in the ID list;
        :param list newItems: list of IdToNameEntry
        :param selectedID: item to select in the list after setting the items 
        '''

        collapsedItems = set(
            [item.id for item in self.items if self.isCollapsed(item.id)])

        if self.items != newItems:
            if self.__selectionChanging:
                raise Exception(
                    "cannot change items while selection is changing")
            if self.__itemNameChanging:
                # raise Exception("cannot change items while item name is changing")
                # silently exit: should be no harm ignoring new items as the only thing that changed
                # is label name, and we already reflect that in UI
                return

            self.selectedItems = []

            cmds.treeView(self.control, e=True, clearSelection=True)
            cmds.treeView(self.control, e=True, removeAll=True)

            for i in newItems:
                cmds.treeView(
                    self.control,
                    e=True,
                    addItem=(i.id, "" if i.parent is None else str(i.parent)))
                cmds.treeView(self.control,
                              e=True,
                              displayLabel=(i.id, i.displayName),
                              displayLabelSuffix=(i.id, i.suffix),
                              expandItem=(i.id, i.id not in collapsedItems))

        self.items = newItems
        self.selectByID(selectedID)

    def selectionChanged(self, itemId, selected):
        '''
        UI event handler for when selection is changed
        there are events fired for items being unselected (``selected`` is 0)
        and for items being selected (``selected`` is 1)
        '''

        self.__selectionChanging = True
        try:
            if selected:
                if itemId is not None and itemId not in self.selectedItems:
                    self.selectedItems.append(itemId)
                    self.onSelectionChanged.emit()
            else:
                if itemId in self.selectedItems:
                    self.selectedItems.remove(itemId)
                    self.onSelectionChanged.emit()
        finally:
            self.__selectionChanging = False

        return True

    def internalEditLabelCommand(self, item, newName):
        '''
        indirectly executed by treeview inplace editor;
        return non empty string to let treeView know that rename succeeded
        
        :return: True, if rename was successful; false otherwise. 
        '''
        return False

    def editLabelCommand(self, itemId, newName):
        '''
        UI event handler for when user edits item label;
        
        subclasses should override internalEditLabelCommand instead
        '''
        self.__itemNameChanging = True
        try:
            renameSuccessful = self.internalEditLabelCommand(itemId, newName)
            if not renameSuccessful:
                return ''
            return itemId
        finally:
            self.__itemNameChanging = False

    def getSelectedNames(self):
        '''
        :return: names of all currently selected items 
        '''
        return [i.name for i in self.items if (i.id in self.selectedItems)]

    def getSelectedID(self):
        '''
        :return: ID of currently selected item
        '''
        return None if not self.selectedItems else self.selectedItems[-1]

    def getSelectedIDs(self):
        '''
        :return: IDs of selected items
        '''
        return self.selectedItems

    def selectByID(self, itemId):
        self.setSelectedIDs([itemId])

    def setSelectedIDs(self, ids):
        log.debug("selecting by IDs: {0}".format(ids))
        if self.__selectionChanging:
            return

        # remove any null items
        ids = [str(i) for i in ids if i is not None]

        if self.selectedItems == ids:
            return

        self.selectedItems = ids

        for i in self.items:
            cmds.treeView(self.control, e=True, si=(i.id, int(i.id in ids)))

    def setCollapsed(self, itemId, isCollapsed):
        cmds.treeView(self.control,
                      e=True,
                      expandItem=(itemId, not isCollapsed))

    def isCollapsed(self, itemId):
        return not cmds.treeView(self.control, q=True, isItemExpanded=itemId)