Need help in executing python script !!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
I tried running the script with python tab but nothing happens.
I've placed the script into maya directory folder \Documents\maya\2020\scripts.
It's a script for creating cmuscle spline i was following along a tutorial.
any help would be much appreciated 😊
import string
import maya.cmds as cmds
def make_spline(side, description, index, ctrl_num=3, jnt_num=5, control_mid=True):
"""
create cMuscleSpline
This script is simply convert from Maya muscle's mel script
the mel script location is C:/Program Files/Autodesk/Maya2020/scripts/muscle/cMuscleSplineUI.mel, line 660 cMS_makeSpline
made some changes with the naming to fit current pipeline
add some global offset attr so user can plug into global control to do some overall control
Args:
side(str): muscle spline's side, 'l', 'r' or 'm'
description(str): muscle spline's description
index(i): muscle spline's index
ctrl_num(i): how many controls needed, default is 3
jnt_num(i): how many joints needed, default is 5
control_mid(bool): the start/end control will control in-between controls with falloff, default is True
Returns:
spline(str): cMuscleSpline node name
controls(list): spline controls
joints(list): spline joints
group(str): root group for the spline setup
"""
# make master groups
grp_main = cmds.createNode('transform',
name='grp_{}_{}MusSpline_{:03d}'.format(side, description, index))
# c muscle spline node
spline = cmds.createNode('cMuscleSpline',
name='cMuscleSpline_{}_{}MusSpline_{:03d}Shape'.format(side, description, index))
# lock label attrs
cmds.setAttr(spline + '.DISPLAY', lock=True)
cmds.setAttr(spline + '.TANGENTS', lock=True)
cmds.setAttr(spline + '.LENGTH', lock=True)
# get the transform node of spline shape and parent it under master group
transform_spline = cmds.listRelatives(spline, parent=True)[0]
cmds.parent(transform_spline, grp_main)
# set inheritsTransform to 0 to avoid double transformation
cmds.setAttr(transform_spline + '.inheritsTransform', 0)
# lock the attrs because it shouldn't move
for attr in 'trs':
for axis in 'xyz':
cmds.setAttr('{}.{}{}'.format(transform_spline, attr, axis), lock=True, keyable=False)
# connect time to drive the spline
cmds.connectAttr('time1.outTime', spline + '.inTime', force=True)
# create some attrs so user can view easily in channel box
cmds.addAttr(spline, longName='curLen', keyable=True)
cmds.connectAttr(spline + '.outLen', spline + '.curLen')
# add squash/stretch volume attr to connect with other controller
cmds.addAttr(spline, longName='squashXMult', attributeType='float', minValue=0, defaultValue=1)
cmds.addAttr(spline, longName='squashZMult', attributeType='float', minValue=0, defaultValue=1)
cmds.addAttr(spline, longName='stretchXMult', attributeType='float', minValue=0, defaultValue=1)
cmds.addAttr(spline, longName='stretchZMult', attributeType='float', minValue=0, defaultValue=1)
# add global attr so it can be easily plugged with global control
cmds.addAttr(spline, longName='jiggleMult', defaultValue=1)
cmds.addAttr(spline, longName='jiggleXMult', defaultValue=1)
cmds.addAttr(spline, longName='jiggleYMult', defaultValue=1)
cmds.addAttr(spline, longName='jiggleZMult', defaultValue=1)
cmds.addAttr(spline, longName='jiggleImpactMult', defaultValue=1)
# ctrl grp
grp_ctrls = cmds.createNode('transform',
name='grp_{}_{}MusSplineControls_{:03d}'.format(side, description, index))
cmds.parent(grp_ctrls, grp_main)
for attr in 'trs':
for axis in 'xyz':
cmds.setAttr('{}.{}{}'.format(grp_ctrls, attr, axis), lock=True, keyable=False)
# driven grp for joints
grp_driven = cmds.createNode('transform',
name='grp_{}_{}MusSplineJoints_{:03d}'.format(side, description, index))
cmds.parent(grp_driven, grp_main)
cmds.setAttr(grp_driven + '.inheritsTransform', 0)
for attr in 'trs':
for axis in 'xyz':
cmds.setAttr('{}.{}{}'.format(grp_driven, attr, axis), lock=True, keyable=False)
ctrls = []
# make controls
for i in range(ctrl_num):
ctrl_name = 'ctrl_{}_{}MusSpline{}_{:03d}'.format(side, description, string.ascii_uppercase[i], index)
# create curve
ctrl = cmds.curve(point=[[-0.5, -0.5, -0.5], [-0.5, -0.5, 0.5], [0.5, -0.5, 0.5], [0.5, -0.5, -0.5],
[-0.5, -0.5, -0.5], [-0.5, 0.5, -0.5], [0.5, 0.5, -0.5], [0.5, -0.5, -0.5],
[0.5, -0.5, 0.5], [0.5, 0.5, 0.5], [0.5, 0.5, -0.5], [0.5, 0.5, 0.5],
[-0.5, 0.5, 0.5], [-0.5, -0.5, 0.5], [-0.5, 0.5, 0.5], [-0.5, 0.5, -0.5]],
degree=1,
knot=[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0],
name=ctrl_name)
# rename curve shape
cmds.rename(cmds.listRelatives(ctrl, shapes=True)[0], ctrl_name + 'Shape')
# add to list
ctrls.append(ctrl)
# make control hierarchy
ctrl_nodes = []
for node_type in ['zero', 'driven', 'connect', 'offset']:
grp = cmds.createNode('transform', name=ctrl.replace('ctrl', node_type))
ctrl_nodes.append(grp)
# parent hierarchy
cmds.parent(ctrl, ctrl_nodes[-1])
cmds.parent(ctrl_nodes[-1], ctrl_nodes[-2])
cmds.parent(ctrl_nodes[-2], ctrl_nodes[1])
cmds.parent(ctrl_nodes[1], ctrl_nodes[0])
# store zero node to variable
zero = ctrl_nodes[0]
# place controls vertically
cmds.setAttr(zero + '.translateY', i)
# parent zero group to control group
cmds.parent(zero, grp_ctrls)
# color control
ctrl_shape = cmds.listRelatives(ctrl, shapes=True)[0]
cmds.setAttr(ctrl_shape + '.overrideEnabled', 1)
if side == 'l':
color = 28
elif side == 'r':
color = 31
else:
color = 26
cmds.setAttr(ctrl_shape + '.overrideColor', color)
# add jiggle value, make sure start and end not jiggle
if 0 < i < ctrl_num - 1:
jiggle = 1
else:
jiggle = 0
# add attrs
cmds.addAttr(ctrl, longName='tangentLength', shortName='tanlen', minValue=0, defaultValue=1, keyable=True)
cmds.addAttr(ctrl, longName='jiggle', shortName='jig', defaultValue=jiggle, minValue=0, keyable=True)
cmds.addAttr(ctrl, longName='jiggleX', shortName='jigx', defaultValue=jiggle, minValue=0, keyable=True)
cmds.addAttr(ctrl, longName='jiggleY', shortName='jigy', defaultValue=jiggle * 0.25, minValue=0, keyable=True)
cmds.addAttr(ctrl, longName='jiggleZ', shortName='jigz', defaultValue=jiggle, minValue=0, keyable=True)
cmds.addAttr(ctrl, longName='jiggleImpact', shortName='jigimp', defaultValue=jiggle * 0.5, minValue=0,
keyable=True)
cmds.addAttr(ctrl, longName='jiggleImpactStart', shortName='jigimpst', defaultValue=1000, minValue=0,
keyable=True)
cmds.addAttr(ctrl, longName='jiggleImpactStop', shortName='jigimpsp', defaultValue=0.001, minValue=0,
keyable=True)
cmds.addAttr(ctrl, longName='cycle', shortName='cyc', minValue=1, defaultValue=12, keyable=True)
cmds.addAttr(ctrl, longName='rest', shortName='rst', minValue=1, defaultValue=24, keyable=True)
# lock hide scale and vis
for attr in ['sx', 'sy', 'sz', 'v']:
cmds.setAttr('{}.{}'.format(ctrl, attr), lock=True, keyable=False)
# connect attr
cmds.connectAttr(ctrl + '.worldMatrix[0]', '{}.controlData[{}].insertMatrix'.format(spline, i))
# connect jiggle attr with global mult attr, then plug into controlData
for attr in ['jiggle', 'jiggleX', 'jiggleY', 'jiggleZ', 'jiggleImpact']:
# mult node
mult = 'mult_{}_{}MusSpline{}{}{}_{:03d}'.format(side, description, string.ascii_uppercase[i],
attr[0].upper(), attr[1:], index)
cmds.createNode('multDoubleLinear', name=mult)
cmds.connectAttr('{}.{}'.format(ctrl, attr), mult + '.input1')
cmds.connectAttr('{}.{}Mult'.format(spline, attr), mult + '.input2')
# connect output to controlData
cmds.connectAttr(mult + '.output', '{}.controlData[{}].{}'.format(spline, i, attr))
# connect other attrs directly into controlData
for attr in ['tangentLength', 'jiggleImpactStart', 'jiggleImpactStop', 'cycle', 'rest']:
cmds.connectAttr('{}.{}'.format(ctrl, attr), '{}.controlData[{}].{}'.format(spline, i, attr))
# control mid points
if control_mid:
for i in range(1, ctrl_num - 1):
# get weight value
weight = i / float(ctrl_num - 1)
# get driven group
driven = ctrls[i].replace('ctrl', 'driven')
# point constraint with start and end
pont_con = cmds.pointConstraint(ctrls[0], ctrls[-1], driven, maintainOffset=False)[0]
cmds.setAttr('{}.{}W0'.format(pont_con, ctrls[0]), 1 - weight)
cmds.setAttr('{}.{}W1'.format(pont_con, ctrls[-1]), weight)
# aim constraint dummy node to start and end, and blend to ctrl's orient
grp_aim_start = 'grp_{}_{}MusSpline{}AimStart_{:03d}'.format(side, description,
string.ascii_uppercase[i], index)
grp_aim_end = 'grp_{}_{}MusSpline{}AimEnd_{:03d}'.format(side, description,
string.ascii_uppercase[i], index)
cmds.createNode('transform', name=grp_aim_start)
cmds.createNode('transform', name=grp_aim_end)
# snap to control's position
cmds.matchTransform(grp_aim_start, grp_aim_end, driven, pos=True, rot=True)
# parent dummy groups under zero
cmds.parent(grp_aim_start, grp_aim_end, driven.replace('driven', 'zero'))
# aim constraint with start and end
aim_start = cmds.aimConstraint(ctrls[0], grp_aim_start, aimVector=[0, -1, 0], upVector=[1, 0, 0],
worldUpVector=[1, 0, 0], worldUpType='objectrotation',
worldUpObject=ctrls[0], maintainOffset=False)[0]
aim_end = cmds.aimConstraint(ctrls[-1], grp_aim_end, aimVector=[0, 1, 0], upVector=[1, 0, 0],
worldUpVector=[1, 0, 0], worldUpType='objectrotation',
worldUpObject=ctrls[-1], maintainOffset=False)[0]
# add blend attr to chose either aim with x up or z up
blend = 'blend_{}_{}MusSpline{}AimUp_{:03d}'.format(side, description,
string.ascii_uppercase[i], index)
if not cmds.objExists(blend):
cmds.createNode('blendColors', name=blend)
cmds.connectAttr(spline + '.upAxis', blend + '.blender')
cmds.setAttr(blend + '.color1', 0, 0, 1)
cmds.setAttr(blend + '.color2', 1, 0, 0)
# connect blend to aim constraint
for attr in ['upVector', 'worldUpVector']:
cmds.connectAttr(blend + '.output', '{}.{}'.format(aim_start, attr))
cmds.connectAttr(blend + '.output', '{}.{}'.format(aim_end, attr))
# connect point constraint's output to dummy groups since they are the same place with driver
for attr in ['translateX', 'translateY', 'translateZ']:
for grp in [grp_aim_start, grp_aim_end]:
cmds.connectAttr('{}.constraint{}{}'.format(pont_con, attr[0].upper(), attr[1:]),
'{}.{}'.format(grp, attr))
# orient constraint dummy groups with driver
ont_con = cmds.orientConstraint(grp_aim_start, grp_aim_end, driven, maintainOffset=False)[0]
cmds.setAttr(ont_con + '.interpType', 2)
cmds.setAttr('{}.{}W0'.format(ont_con, grp_aim_start), 1 - weight)
cmds.setAttr('{}.{}W1'.format(ont_con, grp_aim_end), weight)
# create joints
jnts = []
for i in range(jnt_num):
# get u value
u_val = i / float(jnt_num - 1)
# joint name
jnt = 'jnt_{}_{}MusSpline{:03d}_{:03d}'.format(side, description, i + 1, index)
# create joint
cmds.createNode('joint', name=jnt)
# append to jnts list
jnts.append(jnt)
# add uValue to joint
cmds.addAttr(jnt, longName='uValue', minValue=0, maxValue=1, defaultValue=u_val, keyable=True)
# parent to driven group
cmds.parent(jnt, grp_driven)
# connect attr
cmds.connectAttr(jnt + '.uValue', '{}.readData[{}].readU'.format(spline, i))
cmds.connectAttr(jnt + '.rotateOrder', '{}.readData[{}].readRotOrder'.format(spline, i))
cmds.connectAttr('{}.outputData[{}].outTranslate'.format(spline, i), jnt + '.translate')
cmds.connectAttr('{}.outputData[{}].outRotate'.format(spline, i), jnt + '.rotate')
# add scale x, z
# get scale weight, the middle point should be 1, and falloff till the start/end point
mid_num = (jnt_num - 1) * 0.5
scale_weight = 1 - (abs(mid_num - i) / float(mid_num))**2
if scale_weight != 0:
# not the start/end point
# add scale x and scale z attr to spline so user can tweak individually
cmds.addAttr(spline, longName='joint{:03d}SquashX'.format(i + 1), attributeType='float', minValue=0,
defaultValue=1 + scale_weight, keyable=True)
cmds.addAttr(spline, longName='joint{:03d}StretchX'.format(i + 1), attributeType='float', minValue=0,
defaultValue=1 - scale_weight * 0.9, keyable=True)
cmds.addAttr(spline, longName='joint{:03d}SquashZ'.format(i + 1), attributeType='float', minValue=0,
defaultValue=1 + scale_weight, keyable=True)
cmds.addAttr(spline, longName='joint{:03d}StretchZ'.format(i + 1), attributeType='float', minValue=0,
defaultValue=1 - scale_weight * 0.9, keyable=True)
# add blend node to blend squash stretch
blend_squash = 'blend_{}_{}MusSplineSquash{:03d}_{:03d}'.format(side, description, i + 1, index)
blend_stretch = 'blend_{}_{}MusSplineStretch{:03d}_{:03d}'.format(side, description, i + 1, index)
cmds.createNode('blendColors', name=blend_squash)
cmds.createNode('blendColors', name=blend_stretch)
# connect blender with spline's outPctSquash/Stretch
cmds.connectAttr(spline + '.outPctSquash', blend_squash + '.blender')
cmds.connectAttr(spline + '.outPctStretch', blend_stretch + '.blender')
cmds.setAttr(blend_squash + '.color2', 1, 1, 1)
cmds.setAttr(blend_stretch + '.color2', 1, 1, 1)
# mult node to multiply individual joint squash and stretch with global mult
mult_squash = 'mult_{}_{}MusSplineSquash{:03d}_{:03d}'.format(side, description, i + 1, index)
mult_stretch = 'mult_{}_{}MusSplineStretch{:03d}_{:03d}'.format(side, description, i + 1, index)
cmds.createNode('multiplyDivide', name=mult_squash)
cmds.createNode('multiplyDivide', name=mult_stretch)
cmds.connectAttr('{}.joint{:03d}SquashX'.format(spline, i + 1), mult_squash + '.input1X')
cmds.connectAttr('{}.joint{:03d}SquashZ'.format(spline, i + 1), mult_squash + '.input1Z')
cmds.connectAttr('{}.joint{:03d}StretchX'.format(spline, i + 1), mult_stretch + '.input1X')
cmds.connectAttr('{}.joint{:03d}StretchZ'.format(spline, i + 1), mult_stretch + '.input1Z')
cmds.connectAttr(spline + '.squashXMult', mult_squash + '.input2X')
cmds.connectAttr(spline + '.squashZMult', mult_squash + '.input2Z')
cmds.connectAttr(spline + '.stretchXMult', mult_stretch + '.input2X')
cmds.connectAttr(spline + '.stretchZMult', mult_stretch + '.input2Z')
# connect to blend colors input1X
cmds.connectAttr(mult_squash + '.output', blend_squash + '.color1')
cmds.connectAttr(mult_stretch + '.output', blend_stretch + '.color1')
# plus node to add scale together
plus = 'plus_{}_{}MusSplineSquashStretch{:03d}_{:03d}'.format(side, description, i + 1, index)
cmds.createNode('plusMinusAverage', name=plus)
cmds.connectAttr(blend_squash + '.output', plus + '.input3D[0]')
cmds.connectAttr(blend_stretch + '.output', plus + '.input3D[1]')
cmds.setAttr(plus + '.input3D[2]', -1, -1, -1)
# multiply node to mult global scale
mult_scale = 'mult_{}_{}MusSplineScale{:03d}_{:03d}'.format(side, description, i + 1, index)
cmds.createNode('multiplyDivide', name=mult_scale)
cmds.connectAttr(plus + '.output3D', mult_scale + '.input1')
cmds.connectAttr(spline + '.userScale', mult_scale + '.input2X')
cmds.connectAttr(spline + '.userScale', mult_scale + '.input2Y')
cmds.connectAttr(spline + '.userScale', mult_scale + '.input2Z')
# connect back to joint
cmds.connectAttr(mult_scale + '.outputX', jnt + '.scaleX')
cmds.connectAttr(mult_scale + '.outputZ', jnt + '.scaleZ')
cmds.connectAttr(spline + '.userScale', jnt + '.scaleY')
else:
cmds.connectAttr(spline + '.userScale', jnt + '.scaleX')
cmds.connectAttr(spline + '.userScale', jnt + '.scaleY')
cmds.connectAttr(spline + '.userScale', jnt + '.scaleZ')
# set squash stretch values
# get length
length = cmds.getAttr(spline + '.outLen')
# set default length
cmds.setAttr(spline + '.lenDefault', length)
# stretch and squash
cmds.setAttr(spline + '.lenSquash', length * 0.25)
cmds.setAttr(spline + '.lenStretch', length * 2.5)
return spline, ctrls, jnts, grp_main
def add_spline(mus_joint, joint_number=5, control_number=3, control_mid=True, mirror=True, scale_attr=None):
"""
add cMuscle spline setup to given muscle joint
Args:
mus_joint (str): muscle joint need to add cMuscle spline setup
joint_number (int): cMuscle spline joints number
control_number (int): cMuscle spline controls number
control_mid (bool): constraint the mid controller with start/end controller, default is True
mirror (bool): mirror to the right side
scale_attr (str): global scale attribute
"""
# get name parts
name_parts = mus_joint.split('_')
side = name_parts[1]
description = name_parts[2]
index = int(name_parts[3])
# get muscle info
mus_group = cmds.listRelatives(mus_joint, parent=True)[0]
end_joint = cmds.listRelatives(mus_joint, children=True, type='joint')[0]
aim_locator = 'loc_{}_{}AimTarget_{:03d}'.format(side, description, index)
grp_locator = aim_locator.replace('loc_', 'driven_')
pnt_con = cmds.listConnections(grp_locator, source=True, destination=False, plugs=False)[0]
driver_node = cmds.listConnections(pnt_con + '.target[0].targetTranslate', source=True, destination=False,
plugs=False)[0]
# get world up vector
aim_vector = [1, 0, 0]
world_up_vector = [0, 1, 0]
if side == 'r':
aim_vector = [-1, 0, 0]
world_up_vector = [0, -1, 0]
# create cMuscle spline
spline_node, ctrls, jnts, grp_spline = make_spline(side, description, index, ctrl_num=control_number,
jnt_num=joint_number, control_mid=control_mid)
# create pos group to present muscle joint's position
grp_pos = 'zero_{}_{}MusSpline_{:03d}'.format(side, description, index)
cmds.createNode('transform', name=grp_pos)
cmds.matchTransform(grp_pos, mus_joint, position=True, rotation=True)
cmds.parent(grp_pos, mus_group)
# connect with muscle joint's position
cmds.connectAttr(mus_joint + '.translate', grp_pos + '.translate')
cmds.connectAttr(mus_joint + '.rotate', grp_pos + '.rotate')
# parent to muscle joint
cmds.parent(grp_spline, grp_pos)
# snap the position
cmds.matchTransform(grp_spline, grp_pos, position=True, rotation=True)
# do a temp aim constraint to orient the group
cmds.delete(cmds.aimConstraint(end_joint, grp_spline, aimVector=[0, 1, 0], upVector=[-1, 0, 0],
worldUpType='objectrotation', worldUpObject=mus_joint,
worldUpVector=world_up_vector, maintainOffset=False))
# snap each control to its position
# snap the end control
cmds.matchTransform(ctrls[-1].replace('ctrl', 'zero'), end_joint, position=True, rotation=False)
# snap the in-between so driven node will get lesser offset value
for i in range(1, control_number - 1):
# get weight value
weight = i / float(control_number - 1)
# get zero group
zero = ctrls[i].replace('ctrl', 'zero')
# point constraint with start and end
pnt_con = cmds.pointConstraint(ctrls[0], ctrls[-1], zero, maintainOffset=False)[0]
cmds.setAttr('{}.{}W0'.format(pnt_con, ctrls[0]), 1 - weight)
cmds.setAttr('{}.{}W1'.format(pnt_con, ctrls[-1]), weight)
# delete point constraint
cmds.delete(pnt_con)
# point constraint end control driven with muscle joint's end joint
ctrl_end_driven = ctrls[-1].replace('ctrl', 'driven')
cmds.pointConstraint(aim_locator, ctrl_end_driven, maintainOffset=False)
# reverse aim set up for end control
aim_node = cmds.createNode('transform', name='grp_{}_{}EndAim_{:03d}'.format(side, description, index))
zero_aim_node = cmds.createNode('transform', name=aim_node.replace('grp', 'zero'))
cmds.parent(aim_node, zero_aim_node)
cmds.matchTransform(zero_aim_node, end_joint, position=True)
# parent to driver node
cmds.parent(zero_aim_node, driver_node)
# temp aim back to muscle group to get orientation
cmds.delete(cmds.aimConstraint(mus_joint, zero_aim_node, maintainOffset=False,
aimVector=aim_vector, upVector=world_up_vector,
worldUpType='objectrotation', worldUpVector=world_up_vector))
cmds.pointConstraint(aim_locator, aim_node, maintainOffset=False)
cmds.aimConstraint(mus_group, aim_node, maintainOffset=False, aimVector=aim_vector,
upVector=world_up_vector, worldUpType='none')
# add offset transform
offset_grp = cmds.createNode('transform', name='grp_{}_{}EndAimOffset_{:03d}'.format(side, description, index))
cmds.parent(offset_grp, aim_node)
cmds.matchTransform(offset_grp, ctrl_end_driven, position=True, rotation=True)
# orient constraint with end control's driven node
cmds.orientConstraint(offset_grp, ctrl_end_driven, maintainOffset=False)
# reset curve length and stretch squash limit
# get default curve length
crv_length = cmds.getAttr(spline_node + '.curLen')
# set as default
cmds.setAttr(spline_node + '.lenDefault', crv_length)
# set stretch and squash limit
cmds.setAttr(spline_node + '.lenStretch', crv_length * 2)
cmds.setAttr(spline_node + '.lenSquash', crv_length * 0.5)
# connect global scale
if scale_attr:
cmds.connectAttr(scale_attr, spline_node + '.userScale')
# hide muscle joints
cmds.setAttr(mus_joint + '.visibility', 0)
if mirror and side == 'l':
add_spline(mus_joint.replace('_l_', '_r_'), joint_number=joint_number, control_number=control_number,
control_mid=control_mid, mirror=False, scale_attr=scale_attr)
def mirror_muscle_spline_settings():
"""
mirror cMuscleSpline settings from left to right
"""
# get all left side muscle spline node and muscle spline controllers
spline_nodes = cmds.ls('cMuscleSpline_l_*MusSpline_???Shape')
spline_ctrls = cmds.ls('ctrl_l_*MusSpline?_???')
# loop in each node
for node in spline_nodes + spline_ctrls:
node_right = node.replace('_l_', '_r_')
# get all custom attrs
attrs = cmds.listAttr(node, userDefined=True)
for attr in attrs:
# check if it has connection or locked
node_attr = '{}.{}'.format(node, attr)
if not cmds.listConnections(node_attr, source=True, destination=False) and \
not cmds.getAttr(node_attr, lock=True):
val = cmds.getAttr(node_attr)
cmds.setAttr('{}.{}'.format(node_right, attr), val)