I wrote this to help with cluster weighting using hot keys.
please use/modify at your own risk.
Happy Sculpting!
Nate
"""useful for adjusting weights on selected components via hot keys. currently works with clusters only. with skinclusters it would be more complicated to subtract weights. later i want to add support for clusters on cvs of curves or nurbssurfaces. @author Nathaniel Anozie Modify at your own risk last updated: 11/08/2017 -- initial release last updated: 11/07/2017 -- preparing for initial release last updated: 11/05/2017 -- initial work import sys sys.path.append('/Users/Nathaniel/Documents/src/python/naDeformation')#put your script path here or add to python scripts maya looks for import naNudgeWeight reload(naNudgeWeight) #this is needed to tell what is the cluster to be painted naNudgeWeight.InitSimpleWindow() #these two lines can be used as a hot key to add weight nudge = naNudgeWeight.NudgeCluster() nudge.nudge(0.1) #this can be used as a hotkey to remove weight nudge = naNudgeWeight.NudgeCluster() nudge.nudge(-0.1) #this can be used as a hotkey to smooth weight nudge = naNudgeWeight.NudgeCluster() nudge.smooth() #please note when you’re done with the tool just run naNudgeWeight.removeAllTagging() #so there is no naNudge attribute left on any clusters in scene """ import maya.cmds as cmds import maya.mel as mel from functools import partial NA_ACTIVE_CLUSTER_ATTR = 'naNudge' #for a tagging mechanism so our hot keys can work by finding active weight container def removeTagging(clusterName): if not cmds.listAttr(clusterName,ud=True): return if NA_ACTIVE_CLUSTER_ATTR in cmds.listAttr(clusterName,ud=True): cmds.deleteAttr('%s.%s' %(clusterName,NA_ACTIVE_CLUSTER_ATTR) ) def removeAllTagging(): for cl in findWeightContainers(): removeTagging(cl) def addTagging(clusterName): removeAllTagging() #only one weight container gets tag, should stay same with skin clusters cmds.addAttr(clusterName, ln= NA_ACTIVE_CLUSTER_ATTR, at="bool") def getActiveWeightContainerInScene(): """searches scene for a tagged object. in this case we find first active weight container. should be just one """ result = None for clusterName in findWeightContainers(): if not cmds.listAttr(clusterName,ud=True): continue if NA_ACTIVE_CLUSTER_ATTR in cmds.listAttr(clusterName,ud=True): result = clusterName break return result class InitSimpleWindow(object): """ this kind of maya based ui can be used for other examples inspired by capper's techart online forum posting on #acessing controls from external functions """ WINDOW_NAME = 'naNudgeWindow' WINDOW_WIDTH = 250 WINDOW_HEIGHT = 150 SCROLL_LIST_HEIGHT = 100 def __init__(self): self.weightContainerScrollList = None self.initUI() #these are a bunch of defs specific to the ui widgets used in window def getActiveCluster(self): result = None clusterSelected = cmds.textScrollList(self.weightContainerScrollList, q=True,si=True) if clusterSelected: result = clusterSelected[0] return result def selectionChange(self, *args): #print 'selectionChange event' clusterName = self.getActiveCluster() if not clusterName: return cmds.select(cmds.listConnections(clusterName)) def buttonPressedOK(self, *args): #print 'buttonPressedOK()>>>' clusterName = self.getActiveCluster() if not clusterName: print 'no weight container chosen' return print 'initializing weighting tool for cluster %s' %clusterName removeAllTagging() addTagging(clusterName) #clear selection cmds.select(clear=True) self.closeUI() def buttonPressedHelp(self, *args): msg = 'After Picking a Cluster from list and pressing ok. Use something like this for hotkeys:\n\n' msg += 'alt + --> add weight:\n' msg += 'nudge = naNudgeWeight.NudgeCluster()\n' msg += 'nudge.nudge(0.1)\n\n' msg += 'alt - --> remove weight:\n' msg += 'nudge = naNudgeWeight.NudgeCluster()\n' msg += 'nudge.nudge(-0.1)\n\n' msg += 'nudge = naNudgeWeight.NudgeCluster()\n' msg += 'alt s --> smooth weight:\n' msg += 'nudge.smooth()' cmds.confirmDialog( title='help', message=msg ) def closeUI(self): if cmds.window(self.WINDOW_NAME,exists=True): cmds.deleteUI(self.WINDOW_NAME) def initUI(self): """run this each time we want to work on a new or different cluster. it sets an attribute on active cluser. Inspired by: Mark Jackson's online ui examples """ weightContainers = findWeightContainers() if not weightContainers: print 'Nothing to do, tool requires a supported weight container in scene.\ supporting: clusters only' return #some error checking self.closeUI() cmds.window(self.WINDOW_NAME,title="naNudgeWeight v 1.0.0")#, cmds.columnLayout(w=self.WINDOW_WIDTH, adj=True) self.weightContainerScrollList = cmds.textScrollList(height = self.SCROLL_LIST_HEIGHT,\ ams=False,\ da=True,\ sc=self.selectionChange ) for wgtContainer in weightContainers: cmds.textScrollList( self.weightContainerScrollList, edit=True, append=wgtContainer) #cmds.setParent('..') cmds.button(label="OK",command= self.buttonPressedOK ) cmds.button(label="help",c=self.buttonPressedHelp) #cmds.setParent('..') cmds.showWindow(self.WINDOW_NAME) cmds.window(self.WINDOW_NAME, edit=True, widthHeight=(self.WINDOW_WIDTH,self.WINDOW_HEIGHT))#w=self.WINDOW_WIDTH class NudgeCluster(object): """ tools for nudging weight on cluster Inspired by Brian Tindall """ CLUSTER_SUPPORTED = ['cluster'] #later add skinCluster COMPONENT_SUPPORTED = ['mesh'] #later add nurbs curve cvs, nurbs surface cvs def __init__(self, cluster = None): """ needed because there could be multiple clusters acting on selected components. this will make sure we are acting on correct cluster. should be done on initialization. """ self.cluster = cluster if not cluster: print 'if dont manually initialize a cluster it will go by possible tags' print 'looking for active cluster...' activeWeightContainer = getActiveWeightContainerInScene() if activeWeightContainer and cmds.objectType(activeWeightContainer) in self.CLUSTER_SUPPORTED: print 'found active cluster %s' %activeWeightContainer self._setCluster(activeWeightContainer) def getActiveWeightContainer(self): """will be called on every hot key press. returns active weight container """ result = None for cl in findWeightContainers(): if NA_ACTIVE_CLUSTER_ATTR in cmds.listAttr(cl,ud=True): return cl return result def _setCluster(self,clusterName): self.cluster = clusterName def _getCluster(self): return self.cluster def _getWeightInfo(self): """ get current weight information on selected vertices. later could support nurbs curves/surfaces weightInfo = [{'cmp':'pPlane1.vtx[0]','wgt':0.95},{'cmp':'pPlane1.vtx[4]','wgt':0.85}] """ result = [] cluster = self._getCluster() if not cluster: return if not cmds.objectType(cluster) in self.CLUSTER_SUPPORTED: return curSel = cmds.ls(selection=True) selExpandedComponents = getExpandedComponents(curSel) ###only consider components a member of same cluster. clusterSetAll = [x for x in cmds.listConnections(cluster) if cmds.objectType(x) == 'objectSet'] if not clusterSetAll: print 'cluster is probably not of cluster type' return clusterSet = clusterSetAll[0] clusterSetShort = cmds.sets( clusterSet, query=True) clusterExpandedComponents = getExpandedComponents(clusterSetShort) if not clusterExpandedComponents: return print 'clusterExpandedComponents',clusterExpandedComponents print 'selExpandedComponents', selExpandedComponents #only consider components a member of same cluster for comp in selExpandedComponents: if comp in clusterExpandedComponents: info = {} info['cmp'] = comp info['wgt'] = getWeight(cluster,comp) result.append(info) return result def nudge(self, delta = 0.05): """ add or subtract delta from selected components """ cluster = self._getCluster() if not cluster: print 'could not find a cluster to nudge :(' return if not cmds.objectType(cluster) in self.CLUSTER_SUPPORTED: return #in case selection included components not members of our cluster it will skip them weightInfo = self._getWeightInfo() #print 'weightInfo', weightInfo for arg in weightInfo: component = arg['cmp'] currentWeight = arg['wgt'] #make sure weight between 0 and 1 newWeight = min( max( currentWeight + delta, 0 ), 1.0 ) setWeight( cluster, [component], newWeight ) def smooth(self): cluster = self._getCluster() if not cluster: print 'could not find a cluster to nudge :(' return if not cmds.objectType(cluster) in self.CLUSTER_SUPPORTED: return smoothClusterWeight( cluster ) def setWeight(cluster, component, value): """ set weight on provided components, and cluster to value. currently only supports cluster. later add skinCluster type. """ curSel = cmds.ls(selection=True) if not cmds.objectType(cluster) in ['cluster']: return #later add in other support for skinCluster if cmds.objectType(cluster) == 'cluster': for comp in component: print 'setting weight cluster:%s, component:%s, value:%d' %(cluster,comp,value ) cmds.select( comp, replace=True) cmds.percent( cluster, v = value ) #restore selection cmds.select(curSel, replace=True) def getWeight(cluster, component): """ get weight. later add support for skinCluster type. maybe add support for list of components """ result = None if not cmds.objectType(component) in ['mesh']: print 'cannot support component type %s' %(cmds.objectType(component)) return if cmds.objectType(cluster) == 'cluster': result = cmds.percent(cluster,value=True,query=True)[0] return result def smoothClusterWeight( clusterName ): """smooths weights on selected components for given cluster name inspired by Paul Molodowitch (online examples on smoothing cluster weights) """ if not cmds.objectType(clusterName)=="cluster": return curSel = cmds.ls(selection=True) #not sure of another way to smooth cluster weights mel.eval('artSetToolAndSelectAttr( "artAttrCtx", ("cluster.%s.weights"));'%clusterName) mel.eval("artAttrInitPaintableAttr;") mel.eval( "artAttrPaintOperation artAttrCtx Smooth;" ) mel.eval( "artAttrCtx -e -clear `currentCtx`;") #this bit restores our mode to object, without it we would be in paint weight mode mel.eval("selectMode -object;") mel.eval("setToolTo selectSuperContext;") cmds.select(clear=True) cmds.select(curSel,replace=True) def findWeightContainers(): return cmds.ls(type="cluster") def getExpandedComponents( sel = [] ): """ get expanded components supports cvs, vertices """ result = [] curSel = cmds.ls(selection=True) #need selection to expand cmds.select(sel,replace=True) components = cmds.filterExpand(sm = 31) #VERTS if not components: components = cmds.filterExpand(sm = 28) #CV if components: result = components #restore current selection cmds.select(curSel, replace=True) return result
Inspired by Brian Tindall's Art of moving points book