#
# Produced by:
# Graham Thompson
# captainhammy@gmail.com
# www.captainhammy.com
#
# Name: parmutils.py
#
# Comments: Functions dealing with parameters.
#
# Version: 1.0
#
# Compatibility: Houdini 11.0
#
import hou
import os
import re
def copyParmValues(source_node, target_node):
""" Copy parameter values of the source node to those of the target node
if a parameter with the same name exists.
"""
for parm_to_copy in source_node.parms():
parm_template = parm_to_copy.parmTemplate()
# Skip folder parms.
if isinstance(parm_template, hou.FolderSetParmTemplate):
continue
parm_to_copy_to = target_node.parm(parm_to_copy.name())
# If the parameter on the target node does not exist, skip this parm.
if parm_to_copy_to is None:
continue
# If we have keys/expressions we need to copy them all.
if parm_to_copy.keyframes():
# Copy all hou.Keyframe objects.
for key in parm_to_copy.keyframes():
parm_to_copy_to.setKeyframe(key)
else:
# If the parameter is a string copy the raw string.
if isinstance(parm_template, hou.StringParmTemplate):
parm_to_copy_to.set(parm_to_copy.unexpandedString())
# Copy the raw value.
else:
parm_to_copy_to.set(parm_to_copy.eval())
def linkParmsTo(source_node, target_node):
""" Link the parms of the source node to those of the target node if
a parameter with the same name exists.
"""
for parm_to_link in source_node.parms():
# Skip folder parms.
if isinstance(parm_to_link.parmTemplate(), hou.FolderSetParmTemplate):
continue
parm_to_link_to = target_node.parm(parm_to_link.name())
# If the target parm exists
if parm_to_link_to is not None:
parm_to_link.set(parm_to_link_to)
def copySpareParms(source_node, target_node):
""" Copy spare parameters from the source node to the target node. """
spare_parms = [parm_tuple for parm_tuple in source_node.parms()
if parm_tuple.isSpare()]
for parm_tuple in spare_parms:
template = parm_tuple.parmTemplate()
if isinstance(template, hou.FolderSetParmTemplate):
continue
folders = parm_tuple[0].containingFolders()
target_node.addSpareParmTuple(template, folders, True)
def copyParameterLayout(source_node, target_node):
""" Copy the entire parameter layout from the source node to the target_node. """
parm_template_group = source_node.parmTemplateGroup()
target_node.setParmTemplateGroup(parm_template_group)
def hardenAssetParameters(node):
""" Convert any spare parameters on an asset node to asset parameters. """
# Get the node type definition. It will be None if the node is
# not an asset.
asset_definition = node.type().definition()
# Make sure the node is a digital asset.
if asset_definition is None:
raise hou.OperationFailed("Node must be a digital asset.")
# Get the parameter template group from the node.
parm_template_group = node.parmTemplateGroup()
# Set the parameter template on the asset to be the one from
# the node. This converts all spare parameters to normal
# asset parameters.
asset_definition.setParmTemplateGroup(parm_template_group)
def copyAndReplace(source_node):
""" Replace a node with a new instance and copy the parameters."""
# Create a new instance of the node.
new_node = source_node.parent().createNode(source_node.type().name())
# If the node is selected, set the new one selected.
if source_node.isSelected():
new_node.setSelected(True, False)
# Copy the parms.
parm_template_group = source_node.parmTemplateGroup()
new_node.setParmTemplateGroup(parm_template_group)
# Match the position and color.
new_node.setPosition(source_node.position())
new_node.setColor(source_node.color())
# Process inputs.
input_connections = source_node.inputConnections()
for connection in input_connections:
in_idx = connection.inputIndex()
out_idx = connection.outputIndex()
input_node = connection.inputNode()
new_node.setInput(in_idx, input_node, out_idx)
# Process outputs.
output_connections = source_node.outputConnections()
for connection in output_connections:
in_idx = connection.inputIndex()
out_idx = connection.outputIndex()
output_node = connection.outputNode()
output_node.setInput(in_idx, new_node, out_idx)
#TODO: Per context flag handling.
# If this is a Sop, look at setting the Render flag.
if isinstance(new_node, hou.SopNode):
new_node.setRenderFlag(source_node.isRenderFlagSet())
new_node.setDisplayFlag(source_node.isDisplayFlagSet())
name = source_node.name()
source_node.destroy()
new_node.setName(name)
def parmValueDictionary(node):
""" Get a dictionary of all the parameters and corresponding
values.
"""
parm_dict = {}
for parm in node.parms():
if not parm.keyframes():
parm_dict[parm.name()] = parm.eval()
return parm_dict
def parmExpressionDictionary(node):
""" Get a dictionary of all the parameters with expressions
and their corresponding values.
"""
parm_dict = {}
for parm in node.parms():
if parm.keyframes():
parm_dict[parm.name()] = parm.keyframes()[0].expression()
return parm_dict
def displayParmInDialog(parm_or_parm_tuple):
""" Force the parameter dialog to display this parameter. """
if isinstance(parm_or_parm_tuple, hou.ParmTuple):
parm_or_parm_tuple = parm_or_parm_tuple[0]
for tpl, idx in zip(parm_or_parm_tuple.containingFolderSetParmTuples(),
parm_or_parm_tuple.containingFolderIndices()):
tpl[0].set(idx)
def defaultValue(parm_or_parm_tuple):
""" Get the default value of a parameter. """
# Handle a hou.Parm object.
if isinstance(parm_or_parm_tuple, hou.Parm):
# Get the parm tuple so we can access the names of all
# the parms in the group.
parm_tuple = parm_or_parm_tuple.tuple()
# Get a list of the parm names.
parm_names = [parm.name() for parm in parm_tuple]
# Find the index of our parm in the tuple.
idx = parm_names.index(parm_or_parm_tuple.name())
# Use the index to get the correct default value and return it.
return parm_tuple.parmTemplate().defaultValue()[idx]
else:
return parm_or_parm_tuple.parmTemplate().defaultValue()
def isDefaultValue(parm_or_parm_tuple):
""" Determine if the parameter is currently set to the default value. """
default = defaultValue(parm_or_parm_tuple)
return parm_or_parm_tuple.eval() == default
def applyFunctionToParms(parm_list, function, args=()):
""" Apply a function with specific args to each parameter in the list.
The hou.Parm object is inserted into the beginning of the args list.
"""
for parm in parm_list:
function.__call__((parm,) + args)
def linkParmToParent(parm):
""" Link a parameter to a parameter of the same name on the parent node
if the parameter exists.
"""
parent = parm.node().parent()
parm_node = parm.node()
parent_parm = parent.parm("%s_%s" % (parm_node.name(), parm.name()))
if parent_parm is None:
parent_parm = parent.parm(parm.name())
if parent_parm:
parm.set(parent_parm)
def promoteToParent(parm_tuple):
""" Promote a parameter to the parent node. """
parent = parm_tuple.node().parent()
promoteToNode(parm_tuple, parent)
def promoteToNode(source_tuple, target_node=None):
""" Promote a parameter to a specific node as a spare parameter.
A new parameter is created with the name nodename_parmname.
Promoting the "t" parameter from a node called "xform1" will
create a new parameter "xform1_t".
"""
# Get the source node.
source_node = source_tuple.node()
source_node_name = source_node.name()
if target_node is None:
# If the ui module isn't available then we can't choose a
# node so we will throw an exception.
if not hou.isUIAvailable():
raise hou.OperationFailed("Please specify a target node.")
# Try to select a node.
target_node_path = hou.ui.selectNode()
if target_node_path is None:
return
target_node = hou.node(target_node_path)
# Get the parameter template and change the name and label.
template = source_tuple.parmTemplate()
# Change the parm name and label to include the source node name.
parm_name = "%s_%s" % (source_node_name, source_tuple.name())
parm_label = "%s %s" % (source_node_name, template.label())
template.setName(parm_name)
template.setLabel(parm_label)
# Get the current value.
parm_value = source_tuple.eval()
target_tuple = target_node.addSpareParmTuple(template)
# Link the parms.
for parm, target in zip(source_tuple, target_tuple):
parm.set(target)
# Restore the parm value.
target_tuple.set(parm_value)
# Clean up the spare parameter if the linked node is destroyed.
def cleanParm(**kwargs):
try:
target_node.removeSpareParmTuple(target_tuple)
except hou.ObjectWasDeleted:
pass
# Clean up the channel reference if the control node is destroyed.
def cleanRefs(**kwargs):
try:
value = target_tuple.eval()
source_tuple.revertToDefaults()
source_tuple.set(value)
except hou.ObjectWasDeleted:
pass
# Add event callbacks.
source_node.addEventCallback((hou.nodeEventType.BeingDeleted,), cleanParm)
target_node.addEventCallback((hou.nodeEventType.BeingDeleted,), cleanRefs)
def isParmDriven(parm):
""" Test to see if a parameter is driven by an animation
channel, expression or CHOP override.
"""
if parm.keyframes() or parm.overrideTrack():
return True
else:
return False
def getMultiParmInstanceNumber(parm):
""" Get the instance number from a multi parm parameter. """
# Parameter needs to be a multi parm instance.
if not parm.isMultiParmInstance():
raise hou.OperationFailed("Parameter is not a multi parm parameter.")
# Use a regular expression to extract the number from the parameter.
result = re.search("[0-9]*$", parm.name())
# Convert the result to an integer and return it.
return int(result.group())
def getCurrentMenuLabel(parm):
""" Returns the label of the current menu item. """
# Get the template for the parameter.
parm_template = parm.parmTemplate()
# If this is a regular menu parameter then we just use the value
# of the parameter to index into the list of menu labels.
if isinstance(parm_template, hou.MenuParmTemplate):
return parm_template.menuLabels()[parm.eval()]
# If this parameter at is a string parm then we can't just use
# the value as the index.
elif isinstance(parm_template, hou.StringParmTemplate):
# Get the possible string values.
menu_items = parm_template.menuItems()
# Get the current string value.
value = parm.evalAsString()
# Use the index of the current value in the possible values
# to index into the labels.
return parm_template.menuLabels()[menu_items.index(value)]
# Not a valid parameter type so we throw an exception.
else:
raise hou.OperationFailed("Parmeter is not a menu type.")
def driveByRamp(scriptargs, driver_expr="$F/$RFEND"):
""" Drive a parameter's value(s) using a ramp.
Single parameters are driven by a Float ramp while multiple valued
parameters are driven by a Color ramp.
Only Float and Integer parameters can be driven by a ramp by default
but more types can be added.
scriptargs: The kwargs dictionary generated for a parameter interaction
by the PARMmenu.xml file.
driver_expr: The default expression to drive the ramp position.
"""
# Parameter types that can be driven by a ramp.
allowable_types = (hou.parmTemplateType.Float,
hou.parmTemplateType.Int,
)
# Get the parameters.
parms = scriptargs['parms']
# If there are more then 3 parms, ie Vector4 then we need to clamp the number to
# 3 since the most parameters we can drive is 3 with the Color ramp.
num_parms = len(parms)
if num_parms > 3:
num_parms = 3
# Get the parameter or tuple and the correct ramp type.
if num_parms == 1:
parm = parms[0]
ramp_type = hou.rampParmType.Float
else:
parm = parms[0].tuple()
ramp_type = hou.rampParmType.Color
# Get the parameter type.
parm_template_type = parm.parmTemplate().type()
# If the parameter isn't in the allowable types we display a message and abort.
if parm_template_type not in allowable_types:
hou.ui.displayMessage("Cannot drive %s parameter type using a ramp."
% parm_template_type.name(),
severity=hou.severityType.Error)
return
# Get the node we are working on.
node = parm.node()
# Create the parameter template for the ramp parm.
ramp_name = "%s_ramp" % parm.name()
ramp_label = "%s Ramp" % parm.description()
ramp_template = hou.RampParmTemplate(ramp_name, ramp_label, ramp_type)
# Create the parameter template for the ramp position.
position_name = "%s_position" % ramp_name
position_label = "%s Position" % ramp_label
position_template = hou.FloatParmTemplate(position_name, position_label,
1, default_value=([0]), min=0, max=1,
min_is_strict=True, max_is_strict=True)
# Add the parms to the node.
position_parm = node.addSpareParmTuple(position_template)
ramp_parm = node.addSpareParmTuple(ramp_template)
# Set an expression for the position driver.
position_parm[0].setExpression(driver_expr)
# If it is just a single parm then set an expression to point to the ramp.
if isinstance(parm, hou.Parm):
parm.setExpression('chramp("%s", ch("%s"), 0)' % (ramp_name, position_name))
# If there are multiple parameters then set expressions to the correct indices
# of the ramp.
else:
for p, idx in zip(parm, range(num_parms)):
p.setExpression('chramp("%s", ch("%s"), %s)' % (ramp_parm.name(),
position_name, idx))
def removeRampDriver(scriptargs):
""" Remove the ramp that is driving a parameter. """
# Get the parameters.
parms = scriptargs['parms']
# If there are more then 3 parms, ie Vector4 then we need to clamp the
# number to 3 since the most parameters we could have drove is 3 with
# the Color ramp. This ensures that if we have 4 parms that we don't
# delete any expression in the last parm.
num_parms = len(parms)
if num_parms > 3:
num_parms = 3
# Get the parameter or tuple and the correct ramp type.
if num_parms == 1:
parm = parms[0]
else:
parm = parms[0].tuple()
parm_name = parm.name()
node = parm.node()
# Get the names of the parms to remove.
ramp_name = "%s_ramp" % parm.name()
position_name = "%s_position" % ramp_name
# Get the parameters to remove.
ramp_parm = node.parmTuple(ramp_name)
ramp_position_parm = node.parmTuple(position_name)
# Delete all the expressions.
if isinstance(parm, hou.Parm):
parm.deleteAllKeyframes()
else:
for p, idx in zip(parm, range(num_parms)):
p.deleteAllKeyframes()
# Remove the parameters.
node.removeSpareParmTuple(ramp_parm)
node.removeSpareParmTuple(ramp_position_parm)