Constraint Switcher

When dealing with multiple constraints such as a character picking up an object and passing it to another character, it is a pain keeping track of the constraints and which goes to where. Let alone switching between them.

This script assists with this situation by creating a dropdown switcher box.

Copy the code below and add it to a shelf icon.

To use:

# Constraint Switcher
# Created By Jason Dixon.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# GNU General Public License for more details.
# simplify constraint setup

import functools
import collections
import maya.cmds as cmds

class Node(object):
""" Generic node """
node = ""
in_attr = ""
out_attr = ""
def connect_in(s, val):
cmds.connectAttr(val, s.node + s.in_attr, f=True)
return s

def connect_out(s, val):
cmds.connectAttr(s.node + s.out_attr, val, f=True)
return s

class Condition(Node):
""" Create a condition node """
def __init__(s):
s.node = cmds.shadingNode("condition", au = True)
cmds.setAttr("%s.ctr" % s.node, 1) #set TRUE to be 1
cmds.setAttr("%s.cfr" % s.node, 0) #set FALSE to be 0
s.in_attr = ".ft"
s.out_attr = ".ocr"

def set_value(s, value):
cmds.setAttr("%s.secondTerm" % s.node, value)
return s

class Clamp(Node):
""" Create a clamp node """
def __init__(s):
s.node = cmds.shadingNode("clamp", au = True)
cmds.setAttr("%s.mxr" % s.node, 1) #set MAX to be 1
s.in_attr = ".ipr"
s.out_attr = ".opr"

def get_pairBlend(obj):
""" Retrieve pairBlend nodes from obj """
return set(cmds.listConnections(obj, type="pairBlend", exactType=True) or [])

def get_constraints(obj):
""" Get attached constraints """
return set(cmds.listConnections(obj, s=False, type="constraint") or [])

def main(selection):
""" Create constraint switcher """
err = None
for sel in selection: # Run on multiple objects
if "transform" != cmds.objectType(sel): # Basic validation
print "%s not transform. Skipping." % sel

constraints = get_constraints(sel)
if not constraints:
print "%s has no constraints. Skipping." % sel

for at in (a + b for a in ("translate", "rotate") for b in ("X", "Y", "Z")):
if not cmds.keyframe(sel, at=at, q=True): # Add keyframe if none to force pairBlend
cmds.setKeyframe(sel, at=at)
except RuntimeError:

options = collections.defaultdict(set) # Options for switching
for const in constraints:
blend = get_pairBlend(const) # Start by following blend parents
for b in blend:
blend_attr = cmds.listConnections("%s.w" % b, p=True, d=False)
if blend_attr: # blend attribute on object
for weight in cmds.listAttr(const, sn=True, st="w*", k=True) or []: # Get contraint weights
attr = ".".join((const, weight))
name = cmds.attributeName(attr, l=True)[:-2] # Pull target name from attribute

if options: # Build our selector
opt_names = sorted(list(set(options.keys())))
opt_names.insert(0, opt_names.pop(opt_names.index("nothing"))) # Move "nothing" to the front
keyframe = functools.partial(cmds.keyframe, sel, at="atch")

if cmds.objExists("%s.atch" % sel): # Check if switcher already exists
old_keys = keyframe(q=True) # backup existing keys
old_opt = cmds.addAttr("%s.atch" % sel, q=True, en=True).split(":") # backup existing options

if old_keys: # we have something to update
print "Updating existing keyframes..."
buff = {}
for opt in old_opt:
try: # Map old keys to new keys
buff[old_opt.index(opt)] = opt_names.index(opt)
except ValueError:
buff[old_opt.index(opt)] = None

for key in old_keys:
old_val = keyframe(t=(key,key), q=True, vc=True)[0]
new_val = buff[int(old_val)]
if new_val is None: # Constraint was removed. Remove keys too
cmds.cutKey(sel, at="atch", cl=True, t=(key,key))
keyframe(e=True, t=(key,key), vc=new_val)

cmds.addAttr("%s.atch" % sel, e=True, en=":".join(opt_names)) # Update switcher
else: # Create switcher
cmds.addAttr(ln="attach_to",sn="atch", k=True, at="enum", en=":".join(opt_names))

for i, opt in enumerate(opt_names): # Connect up our switcher
if i: # Set up constraints
for link in options[opt]:
Condition().set_value(i).connect_in("%s.atch" % sel).connect_out(link)
else: # Set up blend node
for link in options[opt]:
Clamp().connect_in("%s.atch" % sel).connect_out(link)
if err:
cmds.undo(), r=True)