How to select new generated node using Python in Maya? - python

I am trying to create an automation to copy a shape by wrapping a polygon face to another polygon shape
# This script wrap a polygon face to the targeted terrain
import maya.cmds as cmds
# select terrain first, then the face
cmds.select( 'terrain', r = True )
cmds.select( 'face', add = True )
# wrapping a polygon face to the terrain
cmds.transferAttributes( transferPositions = 1 )
# NEW Node transferAttributes1 is created, change its attribute searchMethod to 1.
cmds.setAttr( 'transferAttributes1.searchMethod' , 1 )
# transferAttributes1 is generated after execution of
# cmds.transferAttributes( transferPositions = 1 ).
# The name might be different, such as transferAttributes2, transferAttributes3, etc.
# and cmds.setAttr( 'transferAttributes1.searchMethod' , 1 ) might give errors.
My question: Is there any way to select the new transferAttributes node and pass it to cmds.setAttr()?
PS: transferAttributes*.searchMethod might work, but it will select all transferAttributes nodes.

cmds.transferAttributes will return the name of the node it creates:
cmds.select( 'terrain', r=True )
cmds.select( 'face', add=True )
new_node = cmds.transferAttributes( transferPositions=1 )[0]
cmds.setAttr( new_node +'.searchMethod' , 1 )

import maya.cmds as cmds
cmds.select( 'terrain1', r=True )
cmds.select( 'face1', add=True )
cmds.transferAttributes( transferPositions=1 )
#select every transferAttributes Node and store them in a list.
selection = cmds.ls('transferAttributes*');
#sort the list.
selection.sort()
#select the last element from the list, thus the most recent node
key = selection[-1]
cmds.setAttr( key+'.searchMethod' , 1 )

Related

In openpyxl, is there a way to apply a format without overwriting existing formats?

I've written a procedure that applies borders around a specified range. It works but is lengthy and clunky because when openpyxl applies a border it overwrites any existing borders. If not for that, I could structure it much more simply.
I would also like to write another procedure that applies a cel fill to a range, but when I tried this, it removes the borders created by this one. I also tried to include a cell format line in this code, but it is also overwritten by the borders (or overwrites them if you put it after the border 'border = Border' (... lines)
Also, whether this is possible or not, any suggestions on making this procedure more Pythonic are appreciated.
def range_border(ws, cell_range, bstyle="thin"):
rows = ws[cell_range]
numrows = len(rows)
numcols = len(rows[0])
# Borders on the corners
if numrows == 1:
if numcols == 1:
rows[0][0].border = Border(
left=Side(style=bstyle),
top=Side(style=bstyle),
bottom=Side(style=bstyle),
right=Side(style=bstyle),
)
else:
rows[0][0].border = Border(
left=Side(style=bstyle),
top=Side(style=bstyle),
bottom=Side(style=bstyle),
)
rows[0][-1].border = Border(
right=Side(style=bstyle),
top=Side(style=bstyle),
bottom=Side(style=bstyle),
)
else:
if numcols == 1:
rows[0][0].border = Border(
left=Side(style=bstyle),
top=Side(style=bstyle),
right=Side(style=bstyle),
)
rows[-1][0].border = Border(
left=Side(style=bstyle),
bottom=Side(style=bstyle),
right=Side(style=bstyle),
)
else:
rows[0][0].border = Border(left=Side(style=bstyle), top=Side(style=bstyle))
rows[0][-1].border = Border(
right=Side(style=bstyle), top=Side(style=bstyle)
)
rows[-1][0].border = Border(
left=Side(style=bstyle), bottom=Side(style=bstyle)
)
rows[-1][-1].border = Border(
right=Side(style=bstyle), bottom=Side(style=bstyle)
)
# Borders on left and right
if numrows > 2:
for row in rows[1:-1]:
if numcols == 1:
row[0].border = Border(
left=Side(style=bstyle), right=Side(style=bstyle)
)
else:
row[0].border = Border(left=Side(style=bstyle))
row[-1].border = Border(right=Side(style=bstyle))
# # Border on top and botton
if numcols > 2:
if numrows == 1:
for col in rows[0][1:-1]:
col.border = Border(top=Side(style=bstyle), bottom=Side(style=bstyle))
else:
for col in rows[0][1:-1]:
col.border = Border(top=Side(style=bstyle))
for col in rows[-1][1:-1]:
col.border = Border(bottom=Side(style=bstyle))
I'm not exactly sure what you mean by overwrite but I suggest you create the borders outside of the processing which will make the code much easier to maintain. For applying borders to ranges of cells you might want to look at the source for how merged cells are handled.

How to keep locator at the centre of parents when deleting parent constraint in python

import maya.cmds as cmds
sel = cmds.ls(sl = True)
cmds.spaceLocator(n = 'driver')
for i in sel:
cmds.parentConstraint(i, 'driver', n = 'delPs', mo = False)
#until this line, the 'driver' keeps its position at the centre of i
cmds.delete('delPs')
#after this line, the 'driver' moves to the pivot point of the last item on the selection.
I'm trying to keep locator(driver) at the centre of the selected objects after delete constraint node.Can I get some advise?
im not sure to understand your problem but here is some solutions, hope one will help you, im using math instead of constraint
from __future__ import division
import maya.cmds as cmds
# =========================
#just to illustrate an exemple selection of 10 sphere combined
import random
psph = [cmds.polySphere()[0] for i in range(4)]
for i in psph:
cmds.setAttr(i+'.t', random.uniform(0.0, 10), random.uniform(0.0, 10), random.uniform(0.0, 10))
# sph_comb = cmds.polyUnite(psph)
cmds.delete(psph , ch=True)
# ===============================
def getCentroid(sel):
obj = cmds.ls(sel, o=True)
sel = cmds.polyListComponentConversion(sel, tv=True)
sel = cmds.ls(sel, flatten=1)
if len(obj) > 1:
pos = []
for s in sel:
p = cmds.xform(s, q=1, t=1, ws=1)
pos += p
else:
pos = cmds.xform(sel, q=1, t=1, ws=1)
nb = len(sel)
myCenter = sum(pos[0::3]) / nb, sum(pos[1::3]) / nb, sum(pos[2::3]) / nb
return myCenter
# example for each sphere
sel= cmds.ls(psph)
for i in sel:
loc = cmds.spaceLocator(n = 'driver')[0]
coord = getCentroid(i)
cmds.setAttr(loc+'.t', *coord)
# for only one at the center of all sphres
sel= cmds.ls(psph)
coord = getCentroid(sel)
loc = cmds.spaceLocator()[0]
cmds.setAttr(loc+'.t', *coord)
EDIT : fix some code issue on my function getCentroid because xform can get multiple object

How to use a slider value in calculations?

I have a piece of code which generates noise. The noise is generated inside a 0.0 to 1.0 range. As long as I set a defined number the code works. If I were to allow users to select maximum range for the number with a slider it stops working.
I have a slider which replaces the value of 1.0 inside the brightness calculation. As soon as I replace 1.0 value inside Brightness with a slider generated value called noiseAttribute the code breaks. It gives no error and technically runs but just makes the object black instead of locking the color value.
import maya.cmds as cmds
import random
import functools
colorList = cmds.ls('colorSet*')
def createUI( pWindowTitle, pNoiseVerts):
windowID = 'myWindowID'
if cmds.window( windowID, exists=True ):
cmds.deleteUI(windowID )
cmds.window( windowID, title=pWindowTitle, s=False, rtf=True )
cmds.rowColumnLayout( numberOfColumns=1, columnWidth=[(1,200)])
cmds.text(label= 'Max Value Lock')
noiseAttribute = cmds.floatSliderGrp( min=0.0, max=1.0, value=1, field=True)
cmds.button( label='Noise', command=functools.partial (addNoise) )
def cancelCallback( *pArgs ):
if cmds.window( windowID, exists=True ):
cmds.deleteUI( windowID)
cmds.button( label='Cancel', command=cancelCallback )
cmds.showWindow()
def pNoiseVerts(object, noiseAttribute):
verts = range(cmds.polyEvaluate(object, vertex=True))
random.shuffle(verts)
for vertex in verts:
cmds.select(object + '.vtx[' + str(vertex) + ']')
brightness = random.uniform(0.0, noiseAttribute)
cmds.polyColorPerVertex(rgb=(brightness, brightness, brightness))
cmds.setAttr(object + '.displayColors', True)
def addNoise(noiseAttribute, *args):
if len(colorList) > 0:
cmds.delete(colorList)
objects = cmds.ls( sl=True, long=True)
if len(objects) > 0:
setList = cmds.ls('colorSet*')
result = cmds.polyColorSet ( create=True, colorSet='colorSet#')
result = cmds.polyColorPerVertex ( rgb=[0.5,0.5,0.5])
result = cmds.polyColorSet ( create=True, colorSet='colorSet#')
for object in objects:
pNoiseVerts(object, noiseAttribute)
else:
cmds.inViewMessage (amg='Message: <hl>Please select an object first</hl>.', pos='midCenter', fade=True )
createUI( 'Config', pNoiseVerts)
As mentioned before the object turns black instead of having its max color value locked.
You dont pass any arguments with your functools
Here is one of my answer on the same topic : Need Help Making Buttons to perform for loops when you input a number
How to print the value of intField in Maya python
Maya Python - Using data from UI
You can go in my history of questions, I answered a lot about functools
import maya.cmds as cmds
import random
import functools
colorList = cmds.ls('colorSet*')
def createUI(pWindowTitle):
windowID = 'myWindowID'
if cmds.window( windowID, exists=True ):
cmds.deleteUI(windowID )
cmds.window( windowID, title=pWindowTitle, s=False, rtf=True )
cmds.rowColumnLayout( numberOfColumns=1, columnWidth=[(1,200)])
cmds.text(label= 'Max Value Lock')
noiseAttribute = cmds.floatSliderGrp( min=0.0, max=1.0, value=1, field=True)
cmds.button( label='Noise', command=functools.partial(ui_addNoise, noiseAttribute) )
def cancelCallback( *pArgs ):
if cmds.window( windowID, exists=True ):
cmds.deleteUI( windowID)
cmds.button( label='Cancel', command=cancelCallback )
cmds.showWindow()
def ui_addNoise(noiseSlider, *args):
value = cmds.floatSliderGrp(noiseSlider, q=True, value=True)
addNoise(value)
def pNoiseVerts(object, value):
verts = range(cmds.polyEvaluate(object, vertex=True))
random.shuffle(verts)
for id in verts:
# you should never select things in maya, pass it as variable :
vtx = '{}.vtx[{}]'.format(object, id)
brightness = random.uniform(0.0, value)
cmds.polyColorPerVertex(vtx, rgb=(brightness, brightness, brightness))
cmds.setAttr(object + '.displayColors', True)
def addNoise(value):
if len(colorList) > 0:
cmds.delete(colorList)
objects = cmds.ls( sl=True, long=True)
if len(objects) > 0:
setList = cmds.ls('colorSet*')
result = cmds.polyColorSet ( create=True, colorSet='colorSet#')
result = cmds.polyColorPerVertex ( rgb=[0.5,0.5,0.5])
result = cmds.polyColorSet ( create=True, colorSet='colorSet#')
for object in objects:
pNoiseVerts(object, value)
else:
cmds.inViewMessage (amg='Message: <hl>Please select an object first</hl>.', pos='midCenter', fade=True )
createUI( 'Config')

Renaming and Locators positioning on Joints

I am trying to get this renaming working as the locators are being duplicated and moved to the position of the joints.
For example, if I have a thigh_jnt, knee_jnt, ankle_jnt, the created locators will be named loc_thigh_jnt, loc_knee_jnt etc
However it is not working for me, as I am getting errors such as # ValueError: No object matches name: loc_0 #
Needless to say, the locator may be created but it is not at the position of the joint.
Also, can I ask if it is possible to create the locator for all the joint? Currently it is only creating for the thigh and knee but not the ankle
import maya.cmds as cmds
def createLoc():
cmds.select( cmds.listRelatives( type = 'joint', fullPath = True, allDescendents = True ) )
cmds.select( cmds.listRelatives( parent = True, fullPath = True ) )
sel = cmds.ls ( selection = True, type = 'joint' )
if not sel :
cmds.warning( "Please select a joint / No joints in selection " )
return
locGrp = cmds.group(n="loc_Grp_#", em=True)
cmds.addAttr ( locGrp, attributeType = 'double' , longName = 'locScale' , defaultValue = 1.0 , keyable = 1 )
masterLoc = cmds.spaceLocator(n="loc_0")[0]
cmds.parent( masterLoc, locGrp )
for attr in ["scaleZ", "scaleY", "scaleX"]:
cmds.connectAttr ( locGrp + ".locScale" , "%s.%s" % ( masterLoc, attr ) )
for jnt in sel:
print jnt
coords = cmds.xform ( jnt, query = True, worldSpace = True, pivots = True )[0:3]
cmds.select( masterLoc, replace = True )
cmds.duplicate( returnRootsOnly = True , inputConnections = True )
# This is where the errors starts
#cmds.rename(str(masterLoc), ("loc_" + str(sel)))
cmds.move( coords[0], coords[1], coords[2], rotatePivotRelative = True )
Here is your code snippet with some modifications and corrections to make it work.
import maya.cmds as cmds
def createLoc():
cmds.select( cmds.listRelatives( type='joint', fullPath=True, allDescendents=True ), add=True )
cmds.select( cmds.listRelatives( parent=True, fullPath=True ), add=True )
sel = cmds.ls ( selection = True, type = 'joint' )
if not sel :
cmds.warning( "Please select a joint / No joints in selection " )
return
locGrp = cmds.group(n="loc_Grp_#", em=True)
cmds.addAttr ( locGrp, attributeType='double' , longName='locScale' , defaultValue=1.0 , keyable=1 )
masterLoc = cmds.spaceLocator(n="loc_0")[0]
cmds.parent( masterLoc, locGrp )
for attr in ["scaleZ", "scaleY", "scaleX"]:
cmds.connectAttr ( locGrp + ".locScale" , "%s.%s" % ( masterLoc, attr ) )
is_root_loop = True
loc_to_rename = masterLoc
for jnt in sel:
print jnt
coords = cmds.xform ( jnt, query=True, worldSpace=True, pivots=True )[0:3]
cmds.select( masterLoc, replace=True )
if not is_root_loop:
loc_to_rename = cmds.duplicate( returnRootsOnly=True , inputConnections=True )[0]
# No more errors!
renamed_loc = cmds.rename(str(loc_to_rename), ("loc_" + str(jnt)))
if is_root_loop:
masterLoc = renamed_loc
cmds.move( coords[0], coords[1], coords[2], rotatePivotRelative=True )
is_root_loop = False
In the first two cmds.select() calls, I added add=True flag. Without that flag, cmds.select() will assume replace=True by default. That is why your root joint was being ignored after this call.
In the for loop, the masterLoc was being duplicated N times, where N is the number of joints, thus resulting in N+1 locators (including the masterLoc). So I added the is_root_loop flag to check if the loop is running for the first time. During this run, we manipulate the masterLoc itself (without duplicating it), rename the masterLoc and store the name. From the second iteration of this loop, we use this masterLoc to duplicate and rename the duplicated locators as you had previously written.
Another change I did was storing the name of the duplicated locator
loc_to_rename = cmds.duplicate( returnRootsOnly=True , inputConnections=True )[0]
And used this to rename. That is where you were getting the errors because you were trying to rename masterLoc in every iteration.
Also, it is always important to catch the return results of commands like cmds.duplicate and cmds.rename, as the name they assign may not always be as expected, as Maya will append a number or increment the number at the end of the new name if a name clash occurs with something else in the scene.
I hope this helped!

how to make networkx shortest_path() can support self_defined weight function?

In networkx,
shortest_path(G, source=None, target=None, weight=None)
# weight/distance/cost 1 by default
can support edge proprety "weight" as operator to calculate the shortest path between two node in graph.
However, if i have other meta class attached with a node/edge, for example:
class meta( object ):
def __init__( self, weight_shift = 1 ):
self.weight_shift = weight_shift
G.add_node('A', meta_data = meta( weight_shift = 100 ) )
G.add_node('B', meta_data = meta( weight_shift = 200 ) )
G.add_node('C')
...
G.add_edge("A", "C", weight=10, meta_data = meta( weight_shift = -5 ))
G.add_edge("B", "C", weight=10, meta_data = meta( weight_shift = -10 ))
G.add_edge("A","B")
is it possible to define a function as weight parameter for shortest_path()?
def weight_sum():
...
Which can calculate the "weight" in "runtime", for example, use logic:
weight_sum = edge.weight + edge.meta.weight_shift + node_left.meta.weight_shift + node_right.meta.weight_shift
then
shortest_path(G, source="A", target="B", weight=weight_sum())
to get the shortest path?
THANKS.
It's not possible with the current implementation in NetworkX.
Two options are:
1) Before your shortest path search process all of the edges to add a new edge attribute according to your weight function. Use that attribute as the 'weight' argument to shortest_path()
2) Replace the lines like
vw_dist = dist[v] + edgedata.get(weight,1)
in the code for Dijkstra's algorithm to use your custom function for the edge weight instead of getting the 'weight' attribute.

Categories