def __init__(self,windowName): log.debug("creating main window") BaseToolWindow.__init__(self,windowName) self.windowTitle = self.createWindowTitle() self.mainTabLayout = None self.tabs = [] # layer target UI - compound for layers list/no layer data ui self.targetUI = None self.actions = None self.preferedWidth = PersistentValueModel('ngSkinToolsMainWindow_preferedWidth', 400); self.preferedHeight = PersistentValueModel('ngSkinToolsMainWindow_preferedHeight',400); self.preferedTop = PersistentValueModel('ngSkinToolsMainWindow_preferedTop'); self.preferedLeft = PersistentValueModel('ngSkinToolsMainWindow_preferedLeft'); self.preferedFloating = PersistentValueModel('ngSkinToolsMainWindow_preferedFloating',False) self.useUserPrefSize = False self.defaultWidth = self.preferedWidth.get() self.defaultHeight = self.preferedHeight.get() self.sizeable = True
def uniqueClientId(): ''' returns a unique ID for this installation. randomly generated at first run. ''' model = PersistentValueModel(Options.VAR_OPTION_PREFIX+"updateCheckUniqueClientId", None) if model.get()==None: model.set(generateUniqueClientId()) return model.get()
def createWindow(self): ''' creates main GUI window and it's contents ''' BaseToolWindow.createWindow(self) self.targetUI = TargetDataDisplay() self.actions = MainUiActions(self.windowName) self.mainMenu = MainMenu() self.mainMenu.create() # putting tabs in a from targetUiLayout is needed to workaround maya2011 # bug with an additional empty tab appearing otherwise # form = FormLayout(parent=self.windowName) # targetUiLayout = self.targetUI.create(form) # form.attachForm(targetUiLayout, 0, Constants.MARGIN_SPACING_HORIZONTAL,None,Constants.MARGIN_SPACING_HORIZONTAL) # # self.mainTabLayout = cmds.tabLayout(childResizable=True,parent=form,scrollable=False,innerMarginWidth=3) # form.attachControl(self.mainTabLayout, targetUiLayout, Constants.MARGIN_SPACING_VERTICAL, None,None,None) # form.attachForm(self.mainTabLayout, None, 0,0,0) self.splitPosition = PersistentValueModel( name="ngSkinTools_mainWindow_splitPosition", defaultValue=50) def updateSplitPosition(*args): size = cmds.paneLayout(horizontalSplit, q=True, paneSize=True) # returns (widht, height, width, height) self.splitPosition.set(size[1]) horizontalSplit = cmds.paneLayout( configuration="horizontal2", width=100, height=200, separatorMovedCommand=updateSplitPosition) if Utils.getMayaVersion() >= Utils.MAYA2011: cmds.paneLayout(horizontalSplit, e=True, staticHeightPane=2) cmds.paneLayout(horizontalSplit, e=True, paneSize=(1, 100, self.splitPosition.get())) cmds.paneLayout(horizontalSplit, e=True, paneSize=(2, 100, 100 - self.splitPosition.get())) targetUiLayout = self.targetUI.create(horizontalSplit) self.mainTabLayout = cmds.tabLayout(childResizable=True, parent=horizontalSplit, scrollable=False, innerMarginWidth=3) self.tabPaint = self.addTab(TabPaint()) self.tabMirror = self.addTab(TabMirror()) self.tabRelax = self.addTab(TabSkinRelax()) self.tabAssignWeights = self.addTab(TabAssignWeights()) self.tabSettings = self.addTab(TabSettings()) self.actions.updateEnabledAll()
def __init__(self,model=None,uiCommand=None,defaultValue=None,annotation=''): ValueUIWrapper.__init__(self,uiCommand=uiCommand,annotation=annotation) if (isinstance(model, ValueModel)): self.model = model elif model is not None: self.model = PersistentValueModel(model, defaultValue=defaultValue) else: self.model = None
def __init__(self): BaseTab.__init__(self) self.controls.brushShapeButtons = [] self.intensityReplace = PersistentValueModel(self.VAR_PREFIX+'intensityReplace',defaultValue=1.0) self.intensityAdd = PersistentValueModel(self.VAR_PREFIX+'intensityAdd',defaultValue=.1) self.intensityScale = PersistentValueModel(self.VAR_PREFIX+'intensityScale',defaultValue=.95) self.intensitySmooth = PersistentValueModel(self.VAR_PREFIX+'intensitySmooth',defaultValue=.5) self.brushShape = PersistentValueModel(self.VAR_PREFIX+'brushShape',defaultValue=1)
def __init__(self, windowName): log.debug("creating main window") BaseToolWindow.__init__(self, windowName) self.windowTitle = self.createWindowTitle() self.mainTabLayout = None self.tabs = [] # layer target UI - compound for layers list/no layer data ui self.targetUI = None self.actions = None self.preferedWidth = PersistentValueModel( 'ngSkinToolsMainWindow_preferedWidth', 400) self.preferedHeight = PersistentValueModel( 'ngSkinToolsMainWindow_preferedHeight', 400) self.preferedTop = PersistentValueModel( 'ngSkinToolsMainWindow_preferedTop') self.preferedLeft = PersistentValueModel( 'ngSkinToolsMainWindow_preferedLeft') self.preferedFloating = PersistentValueModel( 'ngSkinToolsMainWindow_preferedFloating', False) self.useUserPrefSize = False self.defaultWidth = self.preferedWidth.get() self.defaultHeight = self.preferedHeight.get() self.sizeable = True
def createWindow(self): ''' creates main GUI window and it's contents ''' BaseToolWindow.createWindow(self) self.targetUI = TargetDataDisplay() self.actions = MainUiActions(self.windowName) self.mainMenu = MainMenu() self.mainMenu.create(); # putting tabs in a from targetUiLayout is needed to workaround maya2011 # bug with an additional empty tab appearing otherwise # form = FormLayout(parent=self.windowName) # targetUiLayout = self.targetUI.create(form) # form.attachForm(targetUiLayout, 0, Constants.MARGIN_SPACING_HORIZONTAL,None,Constants.MARGIN_SPACING_HORIZONTAL) # # self.mainTabLayout = cmds.tabLayout(childResizable=True,parent=form,scrollable=False,innerMarginWidth=3) # form.attachControl(self.mainTabLayout, targetUiLayout, Constants.MARGIN_SPACING_VERTICAL, None,None,None) # form.attachForm(self.mainTabLayout, None, 0,0,0) self.splitPosition = PersistentValueModel(name="ngSkinTools_mainWindow_splitPosition", defaultValue=50) def updateSplitPosition(*args): size = cmds.paneLayout(horizontalSplit,q=True,paneSize=True) # returns (widht, height, width, height) self.splitPosition.set(size[1]) horizontalSplit = cmds.paneLayout(configuration="horizontal2",width=100,height=200,separatorMovedCommand=updateSplitPosition) if Utils.getMayaVersion()>=Utils.MAYA2011: cmds.paneLayout(horizontalSplit,e=True,staticHeightPane=2) cmds.paneLayout(horizontalSplit,e=True,paneSize=(1,100,self.splitPosition.get())) cmds.paneLayout(horizontalSplit,e=True,paneSize=(2,100,100-self.splitPosition.get())) targetUiLayout = self.targetUI.create(horizontalSplit) self.mainTabLayout = cmds.tabLayout(childResizable=True,parent=horizontalSplit,scrollable=False,innerMarginWidth=3) self.tabPaint = self.addTab(TabPaint()) self.tabMirror = self.addTab(TabMirror()) self.tabRelax = self.addTab(TabSkinRelax()) self.tabAssignWeights = self.addTab(TabAssignWeights()) self.tabSettings = self.addTab(TabSettings()) self.actions.updateEnabledAll()
def uniqueClientId(): ''' returns a unique ID for this installation. randomly generated at first run. ''' model = PersistentValueModel(Options.VAR_OPTION_PREFIX+"updateCheckUniqueClientId", None) if model.get() is None: model.set(generateUniqueClientId()) return model.get()
class MainWindow(BaseToolWindow): WINDOW_NAME = 'ngSkinToolsMainWindow' WINDOW_DEFAULT_WIDTH = 400 WINDOW_DEFAULT_HEIGHT = 500 @staticmethod @Utils.visualErrorHandling def open(): ''' just a shortcut method to construct and display main window ''' window = MainWindow.getInstance() window.showWindow() # don't know where to fit this in, it's just an utility warning for those trying to run # this on a different maya version if Utils.getMayaVersion() == Utils.MAYAUNSUPPORTEDVERSION: Utils.displayError('unsupported Maya version detected.') Utils.silentCheckForUpdates() return window @staticmethod def getInstance(): ''' returns instance of a main window; returned value is only valid while window is opened. ''' return BaseToolWindow.getWindowInstance(MainWindow.WINDOW_NAME, MainWindow) def __init__(self, windowName): log.debug("creating main window") BaseToolWindow.__init__(self, windowName) self.windowTitle = self.createWindowTitle() self.mainTabLayout = None self.tabs = [] # layer target UI - compound for layers list/no layer data ui self.targetUI = None self.actions = None self.defaultWidth = MainWindow.WINDOW_DEFAULT_WIDTH self.defaultHeight = MainWindow.WINDOW_DEFAULT_HEIGHT self.sizeable = True def createWindowTitle(self): ''' creates main window title ''' return Version.getReleaseName() def createWindow(self): ''' creates main GUI window and it's contents ''' BaseToolWindow.createWindow(self) self.targetUI = TargetDataDisplay() self.actions = MainUiActions(self.windowName) self.mainMenu = MainMenu() self.mainMenu.create() # putting tabs in a from targetUiLayout is needed to workaround maya2011 # bug with an additional empty tab appearing otherwise # form = FormLayout(parent=self.windowName) # targetUiLayout = self.targetUI.create(form) # form.attachForm(targetUiLayout, 0, Constants.MARGIN_SPACING_HORIZONTAL,None,Constants.MARGIN_SPACING_HORIZONTAL) # # self.mainTabLayout = cmds.tabLayout(childResizable=True,parent=form,scrollable=False,innerMarginWidth=3) # form.attachControl(self.mainTabLayout, targetUiLayout, Constants.MARGIN_SPACING_VERTICAL, None,None,None) # form.attachForm(self.mainTabLayout, None, 0,0,0) self.splitPosition = PersistentValueModel( name="ngSkinTools_mainWindow_splitPosition", defaultValue=50) def updateSplitPosition(*args): size = cmds.paneLayout(horizontalSplit, q=True, paneSize=True) # returns (widht, height, width, height) self.splitPosition.set(size[1]) horizontalSplit = cmds.paneLayout( configuration="horizontal2", width=100, height=200, separatorMovedCommand=updateSplitPosition) if Utils.getMayaVersion() >= Utils.MAYA2011: cmds.paneLayout(horizontalSplit, e=True, staticHeightPane=2) cmds.paneLayout(horizontalSplit, e=True, paneSize=(1, 100, self.splitPosition.get())) cmds.paneLayout(horizontalSplit, e=True, paneSize=(2, 100, 100 - self.splitPosition.get())) targetUiLayout = self.targetUI.create(horizontalSplit) self.mainTabLayout = cmds.tabLayout(childResizable=True, parent=horizontalSplit, scrollable=False, innerMarginWidth=3) self.tabPaint = self.addTab(TabPaint()) self.tabMirror = self.addTab(TabMirror()) self.tabRelax = self.addTab(TabSkinRelax()) self.tabAssignWeights = self.addTab(TabAssignWeights()) self.tabSettings = self.addTab(TabSettings()) self.actions.updateEnabledAll() def addTab(self, tab): ''' adds tab object to tab UI, creating it's ui and attaching to main window ''' cmds.setParent(self.mainTabLayout) layout = tab.createUI(self.mainTabLayout) cmds.tabLayout(self.mainTabLayout, edit=True, tabLabel=((layout, tab.getTitle()))) tab.parentWindow = self self.tabs.append(tab) return tab def findTab(self, tabClass): for i in self.tabs: if isinstance(i, tabClass): return i return None
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
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
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)
class TabPaint(BaseTab): TOOL_PAINT = 'ngSkinToolsLayerPaintCtx' VAR_PREFIX = 'ngSkinToolsPaintTab_' def __init__(self): BaseTab.__init__(self) self.controls.brushShapeButtons = [] self.intensityReplace = PersistentValueModel(self.VAR_PREFIX+'intensityReplace',defaultValue=1.0) self.intensityAdd = PersistentValueModel(self.VAR_PREFIX+'intensityAdd',defaultValue=.1) self.intensityScale = PersistentValueModel(self.VAR_PREFIX+'intensityScale',defaultValue=.95) self.intensitySmooth = PersistentValueModel(self.VAR_PREFIX+'intensitySmooth',defaultValue=.5) self.brushShape = PersistentValueModel(self.VAR_PREFIX+'brushShape',defaultValue=1) def updateHighlight(self,includeInfluence=True): ''' updates highlight - if painting, adds current influence to highlight ''' if cmds.currentCtx()!=self.TOOL_PAINT: return if not LayerDataModel.getInstance().layerDataAvailable: return newHighlightItems = [] if includeInfluence: currentInfluencePath = LayerDataModel.getInstance().mll.getPaintTargetPath(); if currentInfluencePath: newHighlightItems.append(currentInfluencePath) SelectHelper.replaceHighlight(newHighlightItems) def paintCtxSetupProcedure(self,toolContext): cmds.ngSkinLayer(colorDisplayNode=1) cmds.ngSkinLayer(displayUpdate=True,paintingMode=1) self.updateHighlight() def paintCtxCleanupProcedure(self,toolContext): cmds.ngSkinLayer(colorDisplayNode=0) cmds.ngSkinLayer(displayUpdate=True,paintingMode=0) self.updateHighlight(False) def doStartPaint(self): if not cmds.artUserPaintCtx(self.TOOL_PAINT,exists=True): cmds.artUserPaintCtx(self.TOOL_PAINT); cmds.artUserPaintCtx(self.TOOL_PAINT,e=True, tsc=Utils.createMelProcedure(self.paintCtxSetupProcedure, [('string','toolContext')]), toolCleanupCmd=Utils.createMelProcedure(self.paintCtxCleanupProcedure, [('string','toolContext')]), initializeCmd="ngLayerPaintCtxInitialize", finalizeCmd="ngLayerPaintCtxFinalize", setValueCommand="ngLayerPaintCtxSetValue", getValueCommand="ngLayerPaintCtxGetValue", whichTool="userPaint", fullpaths = True ) self.configurePaintValues() cmds.setToolTo(self.TOOL_PAINT); def doFlood(self): self.configurePaintValues() cmds.ngSkinLayer(paintFlood=True) def execPaint(self,*args): if self.isPainting(): self.doFlood() else: self.doStartPaint() def isPainting(self): return cmds.currentCtx()==self.TOOL_PAINT def updateToTool(self): ''' update controls to current tool ''' isPainting = self.isPainting() cmds.control(self.cmdLayout.innerLayout,e=True,enable=isPainting) cmds.button(self.cmdLayout.buttons[1],e=True,label="Flood" if isPainting else "Paint") if (isPainting): self.controls.brushRadiusSlider.setValue(cmds.artUserPaintCtx(self.TOOL_PAINT,q=True,radius=True)) self.controls.brushRadiusSlider.setEnabled(isPainting) layersAvailable = LayerDataModel.getInstance().layerDataAvailable cmds.layout(self.cmdLayout.buttonForm,e=True,enable=layersAvailable) def getPaintModeValues(self): ''' returns artUserPaintCtx value for selectedattroper option ''' if self.controls.paintModeAdd.getValue(): return PaintMode.PAINTMODE_ADD,self.intensityAdd if self.controls.paintModeScale.getValue(): return PaintMode.PAINTMODE_SCALE,self.intensityScale if self.controls.paintModeSmooth.getValue(): return PaintMode.PAINTMODE_SMOOTH,self.intensitySmooth return PaintMode.PAINTMODE_REPLACE,self.intensityReplace def configurePaintValues(self): ''' sets paint tool values from UI ''' oper,pvalue = self.getPaintModeValues() cmds.ngSkinLayer(paintOperation=oper,paintIntensity=pvalue.get()) if cmds.artUserPaintCtx(self.TOOL_PAINT,exists=True): # internally, use maya's "replace" brush with intensity of 1.0 cmds.artUserPaintCtx(self.TOOL_PAINT,e=True, selectedattroper='absolute', value=1.0, opacity=1.0, brushfeedback=False, accopacity=False, stampProfile=self.getSelectedBrushShape()) self.updateToTool() def storeIntensitySettings(self): ''' stores intensity settings plugin-side ''' cmds.ngSkinLayer(paintOperation=PaintMode.PAINTMODE_REPLACE,paintIntensity=self.intensityReplace.get()) cmds.ngSkinLayer(paintOperation=PaintMode.PAINTMODE_ADD,paintIntensity=self.intensityAdd.get()) cmds.ngSkinLayer(paintOperation=PaintMode.PAINTMODE_SCALE,paintIntensity=self.intensityScale.get()) cmds.ngSkinLayer(paintOperation=PaintMode.PAINTMODE_SMOOTH,paintIntensity=self.intensitySmooth.get()) def changeBrushRadius(self): if cmds.artUserPaintCtx(self.TOOL_PAINT,exists=True): cmds.artUserPaintCtx(self.TOOL_PAINT,e=True, radius=self.controls.brushRadiusSlider.getValue()) def getSelectedBrushShape(self): for button,shape in zip(self.controls.brushShapeButtons,["gaussian","poly","solid","square"]): if cmds.symbolCheckBox(button,q=True,value=True): return shape return "solid" def selectIntensityModel(self): ''' selects the right model to edit into intensity slider (each brush mode has it's own intensity variable) ''' _, pvalue = self.getPaintModeValues() self.controls.intensitySlider.setModel(pvalue) def paintValuesChanged(self): ''' brush UI change handler, called when user select new brush shape or intensity value ''' self.selectIntensityModel() self.configurePaintValues() def brushButtonClicked(self,clickedIndex): ''' handler for brush button click ''' self.brushShape.set(clickedIndex) for index,button in enumerate(self.controls.brushShapeButtons): cmds.symbolCheckBox(button,e=True,value=index==clickedIndex) self.paintValuesChanged() def createBrushShapeButtons(self): class ButtonClickHandler: def __init__(self,number,parent): self.number=number self.parent=parent def __call__(self,*args): self.parent.brushButtonClicked(self.number) icons = ['circleGaus.png','circlePoly.png','circleSolid.png','rect.png'] for index,i in enumerate(icons): btn = cmds.symbolCheckBox(w=33,h=36,i=i,changeCommand=ButtonClickHandler(index,self),value=index==self.brushShape.get()) self.controls.brushShapeButtons.append(btn) def createBrushSettingsGroup(self,parent): group = self.createUIGroup(parent, 'Brush Settings') self.createTitledRow(group, 'Brush Shape') brushWidth = 35; cmds.rowLayout(nc=4,cw4=[brushWidth,brushWidth,brushWidth,brushWidth]) self.createBrushShapeButtons() cmds.setParent("..") def innerLayout(): return cmds.rowColumnLayout( numberOfColumns=2,columnWidth=[(1,100),(2,100)]) self.createTitledRow(group, 'Mode',innerContentConstructor=innerLayout) cmds.radioCollection() for index,i in enumerate(['Replace','Add','Scale','Smooth']): ctrl = self.controls.__dict__['paintMode'+i] = RadioButtonField(self.VAR_PREFIX+'paintMode'+i,defaultValue=1 if index==0 else 0,label=i) ctrl.changeCommand.addHandler(self.paintValuesChanged) self.controls.intensitySlider = FloatSliderField() self.controls.intensitySlider.flexibleRange = True self.createTitledRow(group, 'Intensity',self.controls.intensitySlider.create) self.controls.intensitySlider.onChange.addHandler(self.paintValuesChanged) self.controls.brushRadiusSlider = FloatSliderField(range=[0,30]) self.controls.brushRadiusSlider.flexibleRange = True self.createTitledRow(group, 'Brush Radius',self.controls.brushRadiusSlider.create) self.controls.brushRadiusSlider.onChange.addHandler(self.changeBrushRadius) def createUI(self,parent): from ngSkinTools.ui.mainwindow import MainWindow LayerEvents.currentLayerChanged.addHandler(self.updateHighlight, parent) LayerEvents.currentInfluenceChanged.addHandler(self.updateHighlight,parent) LayerEvents.layerAvailabilityChanged.addHandler(self.updateToTool,parent) MayaEvents.nodeSelectionChanged.addHandler(self.updateToTool, parent) MayaEvents.toolChanged.addHandler(self.updateToTool,parent) self.setTitle('Paint') def commandButtons(): yield ('Mirror',MainWindow.getInstance().actions.mirrorWeights,'') yield ('Paint',self.execPaint,'') self.cmdLayout = self.createCommandLayout(commandButtons(), SkinToolsDocs.UI_TAB_PAINT) self.createBrushSettingsGroup(self.cmdLayout.innerLayout) self.storeIntensitySettings() self.selectIntensityModel() self.configurePaintValues() self.updateToTool() return self.cmdLayout.outerLayout
class MainWindow(BaseToolWindow): WINDOW_NAME = 'ngSkinToolsMainWindow' DOCK_NAME = 'ngSkinToolsMainWindow_dock' @staticmethod @Utils.visualErrorHandling def open(): ''' just a shortcut method to construct and display main window ''' window = MainWindow.getInstance() if cmds.control(MainWindow.DOCK_NAME,q=True,exists=True): cmds.control(MainWindow.DOCK_NAME,e=True,visible=True) else: cmds.dockControl(MainWindow.DOCK_NAME,l=window.createWindowTitle(),content=MainWindow.WINDOW_NAME, area='right',allowedArea=['right', 'left'], width=window.preferedWidth.get(), floating=window.preferedFloating.get(), visibleChangeCommand=window.visibilityChanged) if window.preferedFloating.get(): cmds.window(MainWindow.DOCK_NAME,e=True, topEdge=window.preferedTop.get(),leftEdge=window.preferedLeft.get(), w=window.preferedWidth.get(),h=window.preferedHeight.get()) Utils.silentCheckForUpdates() # bring tab to front; evaluate lazily as sometimes UI can show other errors and this command somehow fails cmds.evalDeferred(lambda *args: cmds.dockControl(MainWindow.DOCK_NAME,e=True,r=True)); # a bit of a fake, but can't find a better place for an infrequent save LayerEvents.layerAvailabilityChanged.addHandler(window.savePrefs, MainWindow.DOCK_NAME) return window def visibilityChanged(self,*args): hidden = cmds.control(MainWindow.DOCK_NAME,q=True,isObscured=1) if hidden: self.savePrefs() def savePrefs(self): if cmds.dockControl(MainWindow.DOCK_NAME,exists=True): self.preferedFloating.set(cmds.dockControl(MainWindow.DOCK_NAME,q=True,floating=True)) self.preferedWidth.set(cmds.dockControl(MainWindow.DOCK_NAME,q=True,w=True)) if cmds.window(MainWindow.DOCK_NAME,exists=True): self.preferedWidth.set(cmds.window(MainWindow.DOCK_NAME,q=True,w=True)) self.preferedHeight.set(cmds.window(MainWindow.DOCK_NAME,q=True,h=True)) self.preferedTop.set(cmds.window(MainWindow.DOCK_NAME,q=True,topEdge=True)) self.preferedLeft.set(cmds.window(MainWindow.DOCK_NAME,q=True,leftEdge=True)) @staticmethod def getInstance(): ''' returns instance of a main window; returned value is only valid while window is opened. :rtype: MainWindow ''' return BaseToolWindow.getWindowInstance(MainWindow.WINDOW_NAME,MainWindow) def __init__(self,windowName): log.debug("creating main window") BaseToolWindow.__init__(self,windowName) self.windowTitle = self.createWindowTitle() self.mainTabLayout = None self.tabs = [] # layer target UI - compound for layers list/no layer data ui self.targetUI = None self.actions = None self.preferedWidth = PersistentValueModel('ngSkinToolsMainWindow_preferedWidth', 400); self.preferedHeight = PersistentValueModel('ngSkinToolsMainWindow_preferedHeight',400); self.preferedTop = PersistentValueModel('ngSkinToolsMainWindow_preferedTop'); self.preferedLeft = PersistentValueModel('ngSkinToolsMainWindow_preferedLeft'); self.preferedFloating = PersistentValueModel('ngSkinToolsMainWindow_preferedFloating',False) self.useUserPrefSize = False self.defaultWidth = self.preferedWidth.get() self.defaultHeight = self.preferedHeight.get() self.sizeable = True def createWindowTitle(self): ''' creates main window title ''' return Version.getReleaseName() def createWindow(self): ''' creates main GUI window and it's contents ''' BaseToolWindow.createWindow(self) self.targetUI = TargetDataDisplay() self.actions = MainUiActions(self.windowName) self.mainMenu = MainMenu() self.mainMenu.create(); # putting tabs in a from targetUiLayout is needed to workaround maya2011 # bug with an additional empty tab appearing otherwise self.splitPosition = PersistentValueModel(name="ngSkinTools_mainWindow_splitPosition", defaultValue=50) def updateSplitPosition(*args): size = cmds.paneLayout(horizontalSplit,q=True,paneSize=True) # returns (widht, height, width, height) self.splitPosition.set(int(size[1])) horizontalSplit = cmds.paneLayout(configuration="horizontal2",width=100,height=200,separatorMovedCommand=updateSplitPosition) cmds.paneLayout(horizontalSplit,e=True,staticHeightPane=2) cmds.paneLayout(horizontalSplit,e=True,paneSize=(1,100,self.splitPosition.get())) cmds.paneLayout(horizontalSplit,e=True,paneSize=(2,100,100-self.splitPosition.get())) targetUiLayout = self.targetUI.create(horizontalSplit) self.mainTabLayout = cmds.tabLayout(childResizable=True,parent=horizontalSplit,scrollable=False,innerMarginWidth=3) self.tabPaint = self.addTab(TabPaint()) self.tabMirror = self.addTab(TabMirror()) self.tabRelax = self.addTab(TabSkinRelax()) self.tabAssignWeights = self.addTab(TabAssignWeights()) self.tabSettings = self.addTab(TabSettings()) self.actions.updateEnabledAll() def addTab(self,tab): ''' adds tab object to tab UI, creating it's ui and attaching to main window ''' cmds.setParent(self.mainTabLayout) layout = tab.createUI(self.mainTabLayout) cmds.tabLayout( self.mainTabLayout, edit=True, tabLabel=((layout, tab.getTitle()))); tab.parentWindow = self self.tabs.append(tab) return tab def findTab(self,tabClass): for i in self.tabs: if isinstance(i,tabClass): return i return None
class TabPaint(BaseTab): TOOL_PAINT = 'ngSkinToolsLayerPaintCtx' VAR_PREFIX = 'ngSkinToolsPaintTab_' PAINTMODE_REPLACE = 1 PAINTMODE_ADD = 2 PAINTMODE_SCALE = 3 PAINTMODE_SMOOTH = 4 def __init__(self): BaseTab.__init__(self) self.controls.brushShapeButtons = [] self.intensityReplace = PersistentValueModel(self.VAR_PREFIX+'intensityReplace',defaultValue=1.0) self.intensityAdd = PersistentValueModel(self.VAR_PREFIX+'intensityAdd',defaultValue=.1) self.intensityScale = PersistentValueModel(self.VAR_PREFIX+'intensityScale',defaultValue=.95) self.intensitySmooth = PersistentValueModel(self.VAR_PREFIX+'intensitySmooth',defaultValue=.5) self.brushShape = PersistentValueModel(self.VAR_PREFIX+'brushShape',defaultValue=1) def updateHighlight(self,includeInfluence=True): ''' updates highlight - if painting, adds current influence to highlight ''' if cmds.currentCtx()!=self.TOOL_PAINT: return if not LayerDataModel.getInstance().layerDataAvailable: return newHighlightItems = [] if includeInfluence: currentInfluence = cmds.ngSkinLayer(q=True,ci=True) # returns influence path, if current paint target has any if (len(currentInfluence)>1): newHighlightItems.append(currentInfluence[1]) SelectHelper.replaceHighlight(newHighlightItems) def paintCtxSetupProcedure(self,toolContext): cmds.ngSkinLayer(colorDisplayNode=1) cmds.ngSkinLayer(displayUpdate=True,paintingMode=1) self.updateHighlight() def paintCtxCleanupProcedure(self,toolContext): cmds.ngSkinLayer(colorDisplayNode=0) cmds.ngSkinLayer(displayUpdate=True,paintingMode=0) self.updateHighlight(False) def doStartPaint(self): if not cmds.artUserPaintCtx(self.TOOL_PAINT,exists=True): cmds.artUserPaintCtx(self.TOOL_PAINT); cmds.artUserPaintCtx(self.TOOL_PAINT,e=True, tsc=Utils.createMelProcedure(self.paintCtxSetupProcedure, [('string','toolContext')]), toolCleanupCmd=Utils.createMelProcedure(self.paintCtxCleanupProcedure, [('string','toolContext')]), initializeCmd="ngLayerPaintCtxInitialize", finalizeCmd="ngLayerPaintCtxFinalize", setValueCommand="ngLayerPaintCtxSetValue", getValueCommand="ngLayerPaintCtxGetValue", whichTool="userPaint", fullpaths = True ) self.configurePaintValues() cmds.setToolTo(self.TOOL_PAINT); def doFlood(self): self.configurePaintValues() cmds.ngSkinLayer(paintFlood=True) def execPaint(self,*args): if self.isPainting(): self.doFlood() else: self.doStartPaint() def isPainting(self): return cmds.currentCtx()==self.TOOL_PAINT def updateToTool(self): ''' update controls to current tool ''' isPainting = self.isPainting() cmds.control(self.cmdLayout.innerLayout,e=True,enable=isPainting) cmds.button(self.cmdLayout.buttons[1],e=True,label="Flood" if isPainting else "Paint") if (isPainting): self.controls.brushRadiusSlider.setValue(cmds.artUserPaintCtx(self.TOOL_PAINT,q=True,radius=True)) self.controls.brushRadiusSlider.setEnabled(isPainting) layersAvailable = LayerDataModel.getInstance().layerDataAvailable cmds.layout(self.cmdLayout.buttonForm,e=True,enable=layersAvailable) def getPaintModeValues(self): ''' returns artUserPaintCtx value for selectedattroper option ''' if self.controls.paintModeAdd.getValue(): return self.PAINTMODE_ADD,self.intensityAdd if self.controls.paintModeScale.getValue(): return self.PAINTMODE_SCALE,self.intensityScale if self.controls.paintModeSmooth.getValue(): return self.PAINTMODE_SMOOTH,self.intensitySmooth return self.PAINTMODE_REPLACE,self.intensityReplace def configurePaintValues(self): ''' sets paint tool values from UI ''' oper,pvalue = self.getPaintModeValues() cmds.ngSkinLayer(paintOperation=oper,paintIntensity=pvalue.get()) if cmds.artUserPaintCtx(self.TOOL_PAINT,exists=True): # internally, use maya's "replace" brush with intensity of 1.0 cmds.artUserPaintCtx(self.TOOL_PAINT,e=True, selectedattroper='absolute', value=1.0, opacity=1.0, brushfeedback=False, accopacity=False, stampProfile=self.getSelectedBrushShape()) self.updateToTool() def storeIntensitySettings(self): ''' stores intensity settings plugin-side ''' cmds.ngSkinLayer(paintOperation=self.PAINTMODE_REPLACE,paintIntensity=self.intensityReplace.get()) cmds.ngSkinLayer(paintOperation=self.PAINTMODE_ADD,paintIntensity=self.intensityAdd.get()) cmds.ngSkinLayer(paintOperation=self.PAINTMODE_SCALE,paintIntensity=self.intensityScale.get()) cmds.ngSkinLayer(paintOperation=self.PAINTMODE_SMOOTH,paintIntensity=self.intensitySmooth.get()) def changeBrushRadius(self): if cmds.artUserPaintCtx(self.TOOL_PAINT,exists=True): cmds.artUserPaintCtx(self.TOOL_PAINT,e=True, radius=self.controls.brushRadiusSlider.getValue()) def getSelectedBrushShape(self): for button,shape in zip(self.controls.brushShapeButtons,["gaussian","poly","solid","square"]): if cmds.symbolCheckBox(button,q=True,value=True): return shape return "solid" def selectIntensityModel(self): ''' selects the right model to edit into intensity slider (each brush mode has it's own intensity variable) ''' _, pvalue = self.getPaintModeValues() self.controls.intensitySlider.setModel(pvalue) def paintValuesChanged(self): ''' brush UI change handler, called when user select new brush shape or intensity value ''' self.selectIntensityModel() self.configurePaintValues() def brushButtonClicked(self,clickedIndex): ''' handler for brush button click ''' self.brushShape.set(clickedIndex) for index,button in enumerate(self.controls.brushShapeButtons): cmds.symbolCheckBox(button,e=True,value=index==clickedIndex) self.paintValuesChanged() def createBrushShapeButtons(self): class ButtonClickHandler: def __init__(self,number,parent): self.number=number self.parent=parent def __call__(self,*args): self.parent.brushButtonClicked(self.number) newIcons = ['circleGaus.png','circlePoly.png','circleSolid.png','rect.png'] oldIcons = ['circleGaus.xpm','circlePoly.xpm','circleSolid.xpm','rect.xpm'] icons = newIcons if Utils.getMayaVersion()>=Utils.MAYA2011 else oldIcons for index,i in enumerate(icons): btn = cmds.symbolCheckBox(w=33,h=36,i=i,changeCommand=ButtonClickHandler(index,self),value=index==self.brushShape.get()) self.controls.brushShapeButtons.append(btn) def createBrushSettingsGroup(self,parent): group = self.createUIGroup(parent, 'Brush Settings') self.createTitledRow(group, 'Brush Shape') brushWidth = 35; cmds.rowLayout(nc=4,cw4=[brushWidth,brushWidth,brushWidth,brushWidth]) self.createBrushShapeButtons() cmds.setParent("..") def innerLayout(): return cmds.rowColumnLayout( numberOfColumns=2,columnWidth=[(1,100),(2,100)]) self.createTitledRow(group, 'Mode',innerContentConstructor=innerLayout) cmds.radioCollection() for index,i in enumerate(['Replace','Add','Scale','Smooth']): ctrl = self.controls.__dict__['paintMode'+i] = RadioButtonField(self.VAR_PREFIX+'paintMode'+i,defaultValue=1 if index==0 else 0,label=i) ctrl.changeCommand.addHandler(self.paintValuesChanged) self.controls.intensitySlider = FloatSliderField() self.createTitledRow(group, 'Intensity',self.controls.intensitySlider.create) self.controls.intensitySlider.onChange.addHandler(self.paintValuesChanged) self.controls.brushRadiusSlider = FloatSliderField(range=[0,30]) self.controls.brushRadiusSlider.flexibleRange = True self.createTitledRow(group, 'Brush Radius',self.controls.brushRadiusSlider.create) self.controls.brushRadiusSlider.onChange.addHandler(self.changeBrushRadius) def createUI(self,parent): from ngSkinTools.ui.mainwindow import MainWindow LayerEvents.currentLayerChanged.addHandler(self.updateHighlight, parent) LayerEvents.currentInfluenceChanged.addHandler(self.updateHighlight,parent) LayerEvents.layerAvailabilityChanged.addHandler(self.updateToTool,parent) MayaEvents.nodeSelectionChanged.addHandler(self.updateToTool, parent) MayaEvents.toolChanged.addHandler(self.updateToTool,parent) self.setTitle('Paint') def commandButtons(): yield ('Mirror',MainWindow.getInstance().actions.mirrorWeights,'') yield ('Paint',self.execPaint,'') self.cmdLayout = self.createCommandLayout(commandButtons(), SkinToolsDocs.MIRRORWEIGHTS_INTERFACE) self.createBrushSettingsGroup(self.cmdLayout.innerLayout) self.storeIntensitySettings() self.selectIntensityModel() self.configurePaintValues() self.updateToTool() return self.cmdLayout.outerLayout
class ModelUIWrapper(ValueUIWrapper): ''' value wrapper that uses persistent value storage (model) to store it's value ''' def __init__(self,model=None,uiCommand=None,defaultValue=None,annotation=''): ValueUIWrapper.__init__(self,uiCommand=uiCommand,annotation=annotation) if (isinstance(model, ValueModel)): self.model = model elif model is not None: self.model = PersistentValueModel(model, defaultValue=defaultValue) else: self.model = None def createUI(self,**kargs): ValueUIWrapper.createUI(self,**kargs) self.updateUI() def updateModel(self): ''' updates model with current control value ''' if self.model is not None: self.model.set(self.getValue()) def updateUI(self): ''' updates UI to current model value ''' if self.model is not None: try: self.editUI(**{self.valueKeyName:self.model.get()}) except: pass def setValue(self,value): ''' saves value after setting it through code ''' ValueUIWrapper.setValue(self, value) self.updateModel(); def getModelValue(self): if self.model is not None: return self.model.get() return self.getValue() def setModel(self,pValue): if self.model==pValue: return assert (isinstance(pValue,ValueModel)) self.model = pValue ValueUIWrapper.setValue(self, pValue.get()) def fieldChanged(self,*args): ''' updates data storage when field changes ''' self.updateModel(); ValueUIWrapper.fieldChanged(self,*args)
class MainWindow(BaseToolWindow): WINDOW_NAME = 'ngSkinToolsMainWindow' DOCK_NAME = 'ngSkinToolsMainWindow_dock' @staticmethod @Utils.visualErrorHandling def open(): ''' just a shortcut method to construct and display main window ''' window = MainWindow.getInstance() if cmds.control(MainWindow.DOCK_NAME, q=True, exists=True): cmds.control(MainWindow.DOCK_NAME, e=True, visible=True) else: cmds.dockControl(MainWindow.DOCK_NAME, l=window.createWindowTitle(), content=MainWindow.WINDOW_NAME, area='right', allowedArea=['right', 'left'], width=window.preferedWidth.get(), floating=window.preferedFloating.get(), visibleChangeCommand=window.visibilityChanged) if window.preferedFloating.get(): cmds.window(MainWindow.DOCK_NAME, e=True, topEdge=window.preferedTop.get(), leftEdge=window.preferedLeft.get(), w=window.preferedWidth.get(), h=window.preferedHeight.get()) Utils.silentCheckForUpdates() # bring tab to front; evaluate lazily as sometimes UI can show other errors and this command somehow fails cmds.evalDeferred(lambda *args: cmds.dockControl( MainWindow.DOCK_NAME, e=True, r=True)) # a bit of a fake, but can't find a better place for an infrequent save LayerEvents.layerAvailabilityChanged.addHandler( window.savePrefs, MainWindow.DOCK_NAME) return window def visibilityChanged(self, *args): hidden = cmds.control(MainWindow.DOCK_NAME, q=True, isObscured=1) if hidden: self.savePrefs() def savePrefs(self): if cmds.dockControl(MainWindow.DOCK_NAME, exists=True): self.preferedFloating.set( cmds.dockControl(MainWindow.DOCK_NAME, q=True, floating=True)) self.preferedWidth.set( cmds.dockControl(MainWindow.DOCK_NAME, q=True, w=True)) if cmds.window(MainWindow.DOCK_NAME, exists=True): self.preferedWidth.set( cmds.window(MainWindow.DOCK_NAME, q=True, w=True)) self.preferedHeight.set( cmds.window(MainWindow.DOCK_NAME, q=True, h=True)) self.preferedTop.set( cmds.window(MainWindow.DOCK_NAME, q=True, topEdge=True)) self.preferedLeft.set( cmds.window(MainWindow.DOCK_NAME, q=True, leftEdge=True)) @staticmethod def getInstance(): ''' returns instance of a main window; returned value is only valid while window is opened. ''' return BaseToolWindow.getWindowInstance(MainWindow.WINDOW_NAME, MainWindow) def __init__(self, windowName): log.debug("creating main window") BaseToolWindow.__init__(self, windowName) self.windowTitle = self.createWindowTitle() self.mainTabLayout = None self.tabs = [] # layer target UI - compound for layers list/no layer data ui self.targetUI = None self.actions = None self.preferedWidth = PersistentValueModel( 'ngSkinToolsMainWindow_preferedWidth', 400) self.preferedHeight = PersistentValueModel( 'ngSkinToolsMainWindow_preferedHeight', 400) self.preferedTop = PersistentValueModel( 'ngSkinToolsMainWindow_preferedTop') self.preferedLeft = PersistentValueModel( 'ngSkinToolsMainWindow_preferedLeft') self.preferedFloating = PersistentValueModel( 'ngSkinToolsMainWindow_preferedFloating', False) self.useUserPrefSize = False self.defaultWidth = self.preferedWidth.get() self.defaultHeight = self.preferedHeight.get() self.sizeable = True def createWindowTitle(self): ''' creates main window title ''' return Version.getReleaseName() def createWindow(self): ''' creates main GUI window and it's contents ''' BaseToolWindow.createWindow(self) self.targetUI = TargetDataDisplay() self.actions = MainUiActions(self.windowName) self.mainMenu = MainMenu() self.mainMenu.create() # putting tabs in a from targetUiLayout is needed to workaround maya2011 # bug with an additional empty tab appearing otherwise self.splitPosition = PersistentValueModel( name="ngSkinTools_mainWindow_splitPosition", defaultValue=50) def updateSplitPosition(*args): size = cmds.paneLayout(horizontalSplit, q=True, paneSize=True) # returns (widht, height, width, height) self.splitPosition.set(size[1]) horizontalSplit = cmds.paneLayout( configuration="horizontal2", width=100, height=200, separatorMovedCommand=updateSplitPosition) if Utils.getMayaVersion() >= Utils.MAYA2011: cmds.paneLayout(horizontalSplit, e=True, staticHeightPane=2) cmds.paneLayout(horizontalSplit, e=True, paneSize=(1, 100, self.splitPosition.get())) cmds.paneLayout(horizontalSplit, e=True, paneSize=(2, 100, 100 - self.splitPosition.get())) targetUiLayout = self.targetUI.create(horizontalSplit) self.mainTabLayout = cmds.tabLayout(childResizable=True, parent=horizontalSplit, scrollable=False, innerMarginWidth=3) self.tabPaint = self.addTab(TabPaint()) self.tabMirror = self.addTab(TabMirror()) self.tabRelax = self.addTab(TabSkinRelax()) self.tabAssignWeights = self.addTab(TabAssignWeights()) self.tabSettings = self.addTab(TabSettings()) self.actions.updateEnabledAll() def addTab(self, tab): ''' adds tab object to tab UI, creating it's ui and attaching to main window ''' cmds.setParent(self.mainTabLayout) layout = tab.createUI(self.mainTabLayout) cmds.tabLayout(self.mainTabLayout, edit=True, tabLabel=((layout, tab.getTitle()))) tab.parentWindow = self self.tabs.append(tab) return tab def findTab(self, tabClass): for i in self.tabs: if isinstance(i, tabClass): return i return None
class ModelUIWrapper(ValueUIWrapper): ''' value wrapper that uses persistent value storage (model) to store it's value ''' def __init__(self, model=None, uiCommand=None, defaultValue=None, annotation=''): ValueUIWrapper.__init__(self, uiCommand=uiCommand, annotation=annotation) if (isinstance(model, ValueModel)): self.model = model elif model is not None: self.model = PersistentValueModel(model, defaultValue=defaultValue) else: self.model = None def createUI(self, **kargs): ValueUIWrapper.createUI(self, **kargs) self.updateUI() def updateModel(self): ''' updates model with current control value ''' if self.model is not None: self.model.set(self.getValue()) def updateUI(self): ''' updates UI to current model value ''' if self.model is not None: try: self.editUI(**{self.valueKeyName: self.model.get()}) except: pass def setValue(self, value): ''' saves value after setting it through code ''' ValueUIWrapper.setValue(self, value) self.updateModel() def getModelValue(self): if self.model is not None: return self.model.get() return self.getValue() def setModel(self, pValue): if self.model == pValue: return assert (isinstance(pValue, ValueModel)) self.model = pValue ValueUIWrapper.setValue(self, pValue.get()) def fieldChanged(self, *args): ''' updates data storage when field changes ''' self.updateModel() ValueUIWrapper.fieldChanged(self, *args)
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)
# and conditions defined by EULA. # # A copy of EULA can be found in file 'LICENSE.txt', which is part # of this source code package. # import os from maya import cmds from ngSkinTools.license import fileFormat from ngSkinTools.log import getLogger from ngSkinTools.ui.events import Signal from ngSkinTools.ui.options import PersistentValueModel from ngSkinTools.utils import Utils savedLicensePath = PersistentValueModel("ngSkinToolsOption_licensePath") log = getLogger("license") class LicenseType: RLM = "rlm-ez" NGSTKEY = "ngstKey" class Status: ''' given licensePath, track license status. works both with ngstKey licenses and RLM licenses ''' def __init__(self):
def __init__(self): BaseTab.__init__(self) self.parentWindow = None # type: MainWindow self.controls.brushShapeButtons = [] self.intensityReplace = PersistentValueModel(self.VAR_PREFIX + 'intensityReplace', defaultValue=1.0) self.intensityAdd = PersistentValueModel(self.VAR_PREFIX + 'intensityAdd', defaultValue=.1) self.intensityScale = PersistentValueModel(self.VAR_PREFIX + 'intensityScale', defaultValue=.95) self.intensitySmooth = PersistentValueModel(self.VAR_PREFIX + 'intensitySmooth', defaultValue=.5) self.intensitySharpen = PersistentValueModel(self.VAR_PREFIX + 'intensitySharpen', defaultValue=.3) self.brushShape = PersistentValueModel(self.VAR_PREFIX + 'brushShape', defaultValue=1)
class TabPaint(BaseTab): TOOL_PAINT = 'ngSkinToolsLayerPaintCtx' VAR_PREFIX = 'ngSkinToolsPaintTab_' def __init__(self): BaseTab.__init__(self) self.parentWindow = None # type: MainWindow self.controls.brushShapeButtons = [] self.intensityReplace = PersistentValueModel(self.VAR_PREFIX + 'intensityReplace', defaultValue=1.0) self.intensityAdd = PersistentValueModel(self.VAR_PREFIX + 'intensityAdd', defaultValue=.1) self.intensityScale = PersistentValueModel(self.VAR_PREFIX + 'intensityScale', defaultValue=.95) self.intensitySmooth = PersistentValueModel(self.VAR_PREFIX + 'intensitySmooth', defaultValue=.5) self.intensitySharpen = PersistentValueModel(self.VAR_PREFIX + 'intensitySharpen', defaultValue=.3) self.brushShape = PersistentValueModel(self.VAR_PREFIX + 'brushShape', defaultValue=1) def updateHighlight(self, includeInfluence=True): ''' updates highlight - if painting, adds current influence to highlight ''' if cmds.currentCtx() != self.TOOL_PAINT: return if not LayerDataModel.getInstance().layerDataAvailable: return newHighlightItems = [] if includeInfluence: currentInfluencePath = LayerDataModel.getInstance( ).mll.getPaintTargetPath() if currentInfluencePath: newHighlightItems.append(currentInfluencePath) SelectHelper.replaceHighlight(newHighlightItems) def updateColorDisplaySettings(self): influenceWeightsMode = self.controls.inflDisplay.getValue() if cmds.objExists(self.colorDisplayNode): cmds.setAttr(self.colorDisplayNode + ".influenceWeightsMode", influenceWeightsMode) def paintCtxSetupProcedure(self, toolContext): # add and configure display nodes self.colorDisplayNode = cmds.ngSkinLayer(colorDisplayNode=1) self.updateColorDisplaySettings() cmds.ngSkinLayer(displayUpdate=True, paintingMode=1) self.updateHighlight() def paintCtxCleanupProcedure(self, toolContext): if cmds.objExists(self.colorDisplayNode): cmds.delete(self.colorDisplayNode) cmds.ngSkinLayer(displayUpdate=True, paintingMode=0) self.updateHighlight(False) def doStartPaint(self): if not cmds.artUserPaintCtx(self.TOOL_PAINT, exists=True): cmds.artUserPaintCtx(self.TOOL_PAINT, whichTool="userPaint", fullpaths=True) cmds.artUserPaintCtx( self.TOOL_PAINT, e=True, tsc=Utils.createMelProcedure(self.paintCtxSetupProcedure, [('string', 'toolContext')]), toolCleanupCmd=Utils.createMelProcedure( self.paintCtxCleanupProcedure, [('string', 'toolContext')]), initializeCmd=Utils.createMelProcedure(ngLayerPaintCtxInitialize, [('string', 'mesh')], returnType='string'), finalizeCmd="ngLayerPaintCtxFinalize", setValueCommand="ngLayerPaintCtxSetValue", getValueCommand="ngLayerPaintCtxGetValue", ) self.configurePaintValues() cmds.setToolTo(self.TOOL_PAINT) def doFlood(self): self.configurePaintValues() cmds.ngSkinLayer(paintFlood=True) def execPaint(self, *args): if self.isPainting(): self.doFlood() else: self.doStartPaint() def isPainting(self): return cmds.currentCtx() == self.TOOL_PAINT def updateToTool(self): ''' update controls to current tool ''' isPainting = self.isPainting() cmds.layout(self.cmdLayout.innerLayout, e=True, enable=isPainting) cmds.button(self.cmdLayout.buttons[1], e=True, label="Flood" if isPainting else "Paint") if (isPainting): self.controls.brushRadiusSlider.setValue( cmds.artUserPaintCtx(self.TOOL_PAINT, q=True, radius=True)) self.controls.brushRadiusSlider.setEnabled(isPainting) layersAvailable = LayerDataModel.getInstance().layerDataAvailable cmds.layout(self.cmdLayout.buttonForm, e=True, enable=layersAvailable) def getPaintModeValues(self): ''' returns artUserPaintCtx value for selectedattroper option ''' if self.controls.paintModeAdd.getValue(): return PaintMode.PAINTMODE_ADD, self.intensityAdd if self.controls.paintModeScale.getValue(): return PaintMode.PAINTMODE_SCALE, self.intensityScale if self.controls.paintModeSmooth.getValue(): return PaintMode.PAINTMODE_SMOOTH, self.intensitySmooth if self.controls.paintModeSharpen.getValue(): return PaintMode.PAINTMODE_SHARPEN, self.intensitySharpen return PaintMode.PAINTMODE_REPLACE, self.intensityReplace def configurePaintValues(self): ''' sets paint tool values from UI ''' oper, pvalue = self.getPaintModeValues() cmds.ngSkinLayer(paintOperation=oper, paintIntensity=pvalue.get(), paintMirror=self.controls.mirroredMode.isChecked()) from ngSkinTools import selectionState if self.controls.mirroredMode.isChecked( ) and selectionState.getLayersAvailable(): self.parentWindow.tabMirror.influenceMappingConfiguration.updateSelectionsInfluenceMapping( ) MllInterface().setPaintOptionRedistributeWeight( self.controls.redistributeWeightSetting.isChecked()) if cmds.artUserPaintCtx(self.TOOL_PAINT, exists=True): # internally, use maya's "replace" brush with intensity of 1.0 cmds.artUserPaintCtx( self.TOOL_PAINT, e=True, selectedattroper='absolute', value=1.0, opacity=1.0, brushfeedback=False, accopacity=False, usepressure=self.controls.stylusPressureOnOff.isChecked(), stampProfile=self.getSelectedBrushShape()) if self.controls.stylusPressureOnOff.isChecked(): try: cmds.artUserPaintCtx(self.TOOL_PAINT, e=True, mappressure=self.controls. stylusPressureMode.getSelectedText()) except: pass self.updateToTool() def storeIntensitySettings(self): ''' stores intensity settings plugin-side ''' cmds.ngSkinLayer(paintOperation=PaintMode.PAINTMODE_REPLACE, paintIntensity=self.intensityReplace.get()) cmds.ngSkinLayer(paintOperation=PaintMode.PAINTMODE_ADD, paintIntensity=self.intensityAdd.get()) cmds.ngSkinLayer(paintOperation=PaintMode.PAINTMODE_SCALE, paintIntensity=self.intensityScale.get()) cmds.ngSkinLayer(paintOperation=PaintMode.PAINTMODE_SMOOTH, paintIntensity=self.intensitySmooth.get()) cmds.ngSkinLayer(paintOperation=PaintMode.PAINTMODE_SHARPEN, paintIntensity=self.intensitySharpen.get()) def changeBrushRadius(self): if cmds.artUserPaintCtx(self.TOOL_PAINT, exists=True): cmds.artUserPaintCtx( self.TOOL_PAINT, e=True, radius=self.controls.brushRadiusSlider.getValue()) def getSelectedBrushShape(self): for button, shape in zip(self.controls.brushShapeButtons, ["gaussian", "poly", "solid", "square"]): if cmds.symbolCheckBox(button, q=True, value=True): return shape return "solid" def selectIntensityModel(self): ''' selects the right model to edit into intensity slider (each brush mode has it's own intensity variable) ''' _, pvalue = self.getPaintModeValues() self.controls.intensitySlider.setModel(pvalue) def paintValuesChanged(self): ''' brush UI change handler, called when user select new brush shape or intensity value ''' self.selectIntensityModel() self.configurePaintValues() def brushButtonClicked(self, clickedIndex): ''' handler for brush button click ''' self.brushShape.set(clickedIndex) for index, button in enumerate(self.controls.brushShapeButtons): cmds.symbolCheckBox(button, e=True, value=index == clickedIndex) self.paintValuesChanged() def createBrushShapeButtons(self): class ButtonClickHandler: def __init__(self, number, parent): self.number = number self.parent = parent def __call__(self, *args): self.parent.brushButtonClicked(self.number) icons = [ 'circleGaus.png', 'circlePoly.png', 'circleSolid.png', 'rect.png' ] for index, i in enumerate(icons): btn = cmds.symbolCheckBox(w=33, h=36, i=i, changeCommand=ButtonClickHandler( index, self), value=index == self.brushShape.get()) self.controls.brushShapeButtons.append(btn) def addPaidAnnotation(self, control, licenseActive): annotation = cmds.control(control, q=True, annotation=True) text = " (paid feature)" if licenseActive: if annotation.endswith(text): cmds.control(control, e=True, annotation=annotation[:-len(text)]) else: if not annotation.endswith(text): cmds.control(control, e=True, annotation=annotation + text) def licenseStatusChanged(self): from ngSkinTools.license import license self.controls.mirroredMode.setEnabled(license.status.isLicenseActive()) self.addPaidAnnotation(self.controls.mirroredMode, license.status.isLicenseActive()) def createBrushSettingsGroup(self, parent): group = self.createUIGroup(parent, 'Brush Settings') self.createTitledRow(group, 'Brush Shape') brushWidth = 35 cmds.rowLayout(nc=4, cw4=[brushWidth, brushWidth, brushWidth, brushWidth]) self.createBrushShapeButtons() cmds.setParent("..") def innerLayout(): return cmds.rowColumnLayout(numberOfColumns=2, columnWidth=[(1, 100), (2, 100)]) self.createTitledRow(group, 'Mode', innerContentConstructor=innerLayout) cmds.radioCollection() for index, i in enumerate( ['Replace', 'Add', 'Scale', 'Smooth', 'Sharpen']): ctrl = self.controls.__dict__['paintMode' + i] = RadioButtonField( self.VAR_PREFIX + 'paintMode' + i, defaultValue=1 if index == 0 else 0, label=i) ctrl.changeCommand.addHandler(self.paintValuesChanged) self.createTitledRow(group, title=None) self.controls.mirroredMode = CheckBoxField( self.VAR_PREFIX + 'mirroredMode', label="Interactive mirror", annotation= 'Paint stroke is mirrored on the other side. Needs initialized mirror', defaultValue=0) self.controls.mirroredMode.changeCommand.addHandler( self.paintValuesChanged) self.controls.redistributeWeightSetting = CheckBoxField( self.VAR_PREFIX + 'redistributeWeight', label="Distribute removed weights", annotation= 'When reducing weight on current influence, share that weight to other influences', defaultValue=1) self.controls.redistributeWeightSetting.changeCommand.addHandler( self.paintValuesChanged) self.controls.intensitySlider = FloatSliderField() self.controls.intensitySlider.flexibleRange = True self.createTitledRow(group, 'Intensity', self.controls.intensitySlider.create) self.controls.intensitySlider.onChange.addHandler( self.paintValuesChanged) self.controls.brushRadiusSlider = FloatSliderField(range=[0, 30]) self.controls.brushRadiusSlider.flexibleRange = True self.createTitledRow(group, 'Brush Radius', self.controls.brushRadiusSlider.create) self.controls.brushRadiusSlider.onChange.addHandler( self.changeBrushRadius) def createDisplaySettingsGroup(self, parent): group = self.createUIGroup(parent, 'Display Settings') self.createTitledRow(group, 'Influence Display') inflDisplay = DropDownField(self.VAR_PREFIX + 'influenceDisplaySetting') inflDisplay.addOption("All influences, multiple colors") inflDisplay.addOption("Current influence, grayscale gradient") inflDisplay.changeCommand.addHandler(self.updateColorDisplaySettings, parent) self.controls.inflDisplay = inflDisplay def createTabletSettingsGroup(self, parent): group = self.createUIGroup(parent, 'Stylus Pressure') titledRow.create(group, title=None) self.controls.stylusPressureOnOff = CheckBoxField( self.VAR_PREFIX + 'stylusPressureOnOff', label="Use stylus pressure", annotation='Turn stylus pressure on/off', defaultValue=1) self.controls.stylusPressureOnOff.changeCommand.addHandler( self.configurePaintValues, ownerUI=parent) titledRow.createFixed(group, title="Pressure mapping") mode = DropDownField(self.VAR_PREFIX + 'stylesPressureMapping') mode.addOption("Opacity") mode.addOption("Radius") mode.addOption("Both") mode.changeCommand.addHandler(self.configurePaintValues, ownerUI=parent) self.controls.stylusPressureMode = mode def createUI(self, parent): from ngSkinTools.ui.mainwindow import MainWindow LayerEvents.currentLayerChanged.addHandler(self.updateHighlight, parent) LayerEvents.currentInfluenceChanged.addHandler(self.updateHighlight, parent) LayerEvents.layerAvailabilityChanged.addHandler( self.updateToTool, parent) MayaEvents.nodeSelectionChanged.addHandler(self.updateToTool, parent) MayaEvents.toolChanged.addHandler(self.updateToTool, parent) license.status.changed.addHandler(self.licenseStatusChanged, parent) self.setTitle('Paint') def commandButtons(): yield ('Mirror', MainWindow.getInstance().actions.mirrorWeights, '') yield ('Paint', self.execPaint, '') self.cmdLayout = self.createCommandLayout(commandButtons(), SkinToolsDocs.UI_TAB_PAINT) self.createBrushSettingsGroup(self.cmdLayout.innerLayout) self.createDisplaySettingsGroup(self.cmdLayout.innerLayout) self.createTabletSettingsGroup(self.cmdLayout.innerLayout) def fullUpdate(): self.storeIntensitySettings() self.selectIntensityModel() self.configurePaintValues() self.updateToTool() self.licenseStatusChanged() # force update on UI create cmds.evalDeferred(fullUpdate) return self.cmdLayout.outerLayout
class MainWindow(BaseToolWindow): WINDOW_NAME = 'ngSkinToolsMainWindow' WINDOW_DEFAULT_WIDTH = 400; WINDOW_DEFAULT_HEIGHT = 500; @staticmethod @Utils.visualErrorHandling def open(): ''' just a shortcut method to construct and display main window ''' window = MainWindow.getInstance() window.showWindow() # don't know where to fit this in, it's just an utility warning for those trying to run # this on a different maya version if Utils.getMayaVersion()==Utils.MAYAUNSUPPORTEDVERSION: Utils.displayError('unsupported Maya version detected.') Utils.silentCheckForUpdates() return window @staticmethod def getInstance(): ''' returns instance of a main window; returned value is only valid while window is opened. ''' return BaseToolWindow.getWindowInstance(MainWindow.WINDOW_NAME,MainWindow) def __init__(self,windowName): log.debug("creating main window") BaseToolWindow.__init__(self,windowName) self.windowTitle = self.createWindowTitle() self.mainTabLayout = None self.tabs = [] # layer target UI - compound for layers list/no layer data ui self.targetUI = None self.actions = None self.defaultWidth = MainWindow.WINDOW_DEFAULT_WIDTH self.defaultHeight = MainWindow.WINDOW_DEFAULT_HEIGHT self.sizeable = True def createWindowTitle(self): ''' creates main window title ''' return Version.getReleaseName() def createWindow(self): ''' creates main GUI window and it's contents ''' BaseToolWindow.createWindow(self) self.targetUI = TargetDataDisplay() self.actions = MainUiActions(self.windowName) self.mainMenu = MainMenu() self.mainMenu.create(); # putting tabs in a from targetUiLayout is needed to workaround maya2011 # bug with an additional empty tab appearing otherwise # form = FormLayout(parent=self.windowName) # targetUiLayout = self.targetUI.create(form) # form.attachForm(targetUiLayout, 0, Constants.MARGIN_SPACING_HORIZONTAL,None,Constants.MARGIN_SPACING_HORIZONTAL) # # self.mainTabLayout = cmds.tabLayout(childResizable=True,parent=form,scrollable=False,innerMarginWidth=3) # form.attachControl(self.mainTabLayout, targetUiLayout, Constants.MARGIN_SPACING_VERTICAL, None,None,None) # form.attachForm(self.mainTabLayout, None, 0,0,0) self.splitPosition = PersistentValueModel(name="ngSkinTools_mainWindow_splitPosition", defaultValue=50) def updateSplitPosition(*args): size = cmds.paneLayout(horizontalSplit,q=True,paneSize=True) # returns (widht, height, width, height) self.splitPosition.set(size[1]) horizontalSplit = cmds.paneLayout(configuration="horizontal2",width=100,height=200,separatorMovedCommand=updateSplitPosition) if Utils.getMayaVersion()>=Utils.MAYA2011: cmds.paneLayout(horizontalSplit,e=True,staticHeightPane=2) cmds.paneLayout(horizontalSplit,e=True,paneSize=(1,100,self.splitPosition.get())) cmds.paneLayout(horizontalSplit,e=True,paneSize=(2,100,100-self.splitPosition.get())) targetUiLayout = self.targetUI.create(horizontalSplit) self.mainTabLayout = cmds.tabLayout(childResizable=True,parent=horizontalSplit,scrollable=False,innerMarginWidth=3) self.tabPaint = self.addTab(TabPaint()) self.tabMirror = self.addTab(TabMirror()) self.tabRelax = self.addTab(TabSkinRelax()) self.tabAssignWeights = self.addTab(TabAssignWeights()) self.tabSettings = self.addTab(TabSettings()) self.actions.updateEnabledAll() def addTab(self,tab): ''' adds tab object to tab UI, creating it's ui and attaching to main window ''' cmds.setParent(self.mainTabLayout) layout = tab.createUI(self.mainTabLayout) cmds.tabLayout( self.mainTabLayout, edit=True, tabLabel=((layout, tab.getTitle()))); tab.parentWindow = self self.tabs.append(tab) return tab def findTab(self,tabClass): for i in self.tabs: if isinstance(i,tabClass): return i return None