A list of different menu scripts which make HDAs easier to use in Houdini. Note that you need to change the Menu Script Language to Python vs HScript for all of these. Updated as I create them.
Group Selection
Fairly straightforward. Generates a menu with the existing groups of a particular type. There are a couple variations of the code depending on if you want to use a different parameter to control the type being selected (which is how the group node works).
Attribute Selection
Simple Attribute Selection (Point Attributes)
The only modification needed is to select the type of attributes to find. Options listed here.
node = kwargs['node']
input = node.input(0)
if input:
geo = input.geometry()
menu = geo.generateAttribMenu(attrib_type=hou.attribType.Point)
return menu
else:
return ["", "No Input Connected"]
Copy Code to Clipboard
Full Attribute Selection
By default, the below script will select Point, Prim, and Vertex Attributes. The datatypes (options listed here) vary, as do the min and max sizes in order to demonstrate different ways to change or extend the code.
node = kwargs['node']
input = node.input(0)
if input:
geo = input.geometry()
menu = list(geo.generateAttribMenu(attrib_type=hou.attribType.Point, data_type=hou.attribData.Int, min_size=3))
menu += list(geo.generateAttribMenu(attrib_type=hou.attribType.Prim, data_type=hou.attribData.String, max_size=1))
menu += list(geo.generateAttribMenu(attrib_type=hou.attribType.Vertex, data_type=hou.attribData.Float, min_size=2, max_size=3))
return menu
else:
return ["", "No Input Connected"]
Copy Code to Clipboard
Float Attribute Selection with Separators
Similar to above, the below script will select Point, Prim, and Vertex Attributes. It only selects floats up to a size of 3, but puts a separator between different classes in a fairly elegant way.
from functools import reduce
node = kwargs['node']
input = node.input(0)
if input:
geo = input.geometry()
sep = ["_separator_", "_separator_"]
pt_menu = list(geo.generateAttribMenu(attrib_type=hou.attribType.Point, data_type=hou.attribData.Float, max_size=3))
prim_menu = list(geo.generateAttribMenu(attrib_type=hou.attribType.Prim, data_type=hou.attribData.Float, max_size=3))
vtx_menu = list(geo.generateAttribMenu(attrib_type=hou.attribType.Vertex, data_type=hou.attribData.Float, max_size=3))
menu2 = [pt_menu, prim_menu, vtx_menu]
menu = reduce(lambda a,b:a+sep+b,menu2)
return menu
else:
return ["", "No Input Connected"]
Copy Code to Clipboard
Attribute Values as Menu
Once in a while, you need to create a menu of the values of a specific attribute. An example could be if you want certain transform attributes to only apply to say, the polygons with their variant attribute set to "dog". The expectation in the below code is that you have a string attribute (called "targetAttrib" in this case) being used to say which attribute to target, but you could also hard-set it by typing
for example.
node = kwargs["node"]
attribName = node.parm('targetAttrib').evalAsString()
input = node.input(0)
menu = []
if node:
geo = input.geometry()
findAttrib = geo.findPointAttrib(attribName)
if findAttrib:
type = geo.findPointAttrib(attribName).dataType()
list_menu = None
if type == hou.attribData.String:
list_menu = geo.pointStringAttribValues(attribName)
elif type == hou.attribData.Int:
list_menu = geo.pointIntAttribValues(attribName)
list_menu = list(dict.fromkeys(list_menu))
list_menu = sorted(list_menu)
for value in list_menu:
if value not in menu:
menu.append(str(value))
menu.append(str(value))
return menu
Copy Code to Clipboard
List all Parameters on a node (Int and Float only)
Setting up Multiple Action Buttons for 1 Parameter
This splits parameter names so they appear in an order designed to find "random" controls first. Sorts by parameters that have the following in their names, in this order: seed -> iter -> parameters with size 1 -> parameters with size 3.
FURTHERMORE, it is designed to work inside of a multiparm so you will see kwargs['script_multiparm_index'] which represents the mparm number. It also adds separators into the menu if you don't know how to do that.
def allParmTemplates(group_or_folder):
"""
Creates a generator (iterator) I can use to access ALL parmTemplates in a folder, including the folders.
"""
for parm_template in group_or_folder.parmTemplates():
yield parm_template
# Note that we don't want to return parm templates inside multiparm
# blocks, so we verify that the folder parm template is actually
# for a folder.
if parm_template.type() == hou.parmTemplateType.Folder and parm_template.isActualFolder():
for sub_parm_template in allParmTemplates(parm_template):
yield sub_parm_template
hda = hou.pwd()
node = hda.parm(f"node_var{kwargs['script_multiparm_index']}").evalAsNode()
seeds = []
iters = []
parms = []
vectors = []
if node:
for parm_template in allParmTemplates(node.parmTemplateGroup()):
if parm_template.type() in [hou.parmTemplateType.Int, hou.parmTemplateType.Float]:
if parm_template.numComponents() > 1:
parm_name = f"{parm_template.name()} "
if parm_template.namingScheme() == hou.parmNamingScheme.Base1:
parm_name += "123 (Choose One)"
if parm_template.namingScheme() == hou.parmNamingScheme.XYZW:
parm_name += "xyz (Choose One)"
if parm_template.namingScheme() == hou.parmNamingScheme.XYWH:
parm_name += "xywh (Choose One)"
if parm_template.namingScheme() == hou.parmNamingScheme.UVW:
parm_name += "uvw (Choose One)"
if parm_template.namingScheme() == hou.parmNamingScheme.RGBA:
parm_name += "rgba (Choose One)"
vectors.append(parm_name)
vectors.append(parm_name)
else:
if parm_template.name().find("seed") != -1:
seeds.append(parm_template.name())
seeds.append(parm_template.name())
elif parm_template.name().find("iter") != -1:
iters.append(parm_template.name())
iters.append(parm_template.name())
else:
parms.append(parm_template.name())
parms.append(parm_template.name())
if len(seeds) > 0 and len(iters+parms+vectors) > 0:
seeds.append("_separator_")
seeds.append("_separator_")
if len(iters) > 0 and len(parms+vectors) > 0:
iters.append("_separator_")
iters.append("_separator_")
if len(parms) > 0 and len(vectors) > 0:
parms.append("_separator_")
parms.append("_separator_")
return seeds+iters+parms+vectors
else:
return ("Node is invalid", "Node is invalid")
Copy Code to Clipboard