Blog

HDA Action Button Scripts

minute read

Action Button scripts can help you inject some interactivity into your HDAs inside Houdini, or generally make operations faster or simpler. You've likely used them in many nodes built into Houdini, but if you're not too familiar with building tools or working with python you've probably never tried modifying them or building your own. On this page I've added (and will update over time) the action button scripts that I have made (or stole from nodes in Houdini) and use in a lot of tools of my own.

Through the page, you'll be able to find "Action Help" and "Action Icon" text which goes into the fields below where you paste the script. These provide the tooltip and icon you see, respectively.


Group Selection

The oh-so-common group select. If you've ever selected a group from the viewport, this is the script you've used. However, I've added a bit so you can customize it more readily without needing to use the SideFX HOU reference to look up the different commands you see.

import soputils

kwargs['geometrytype'] = kwargs['node'].parmTuple('NAMEOFGROUPTYPEPARM')
# Alternatively, the above line can be
# kwargs['geometrytype'] = hou.geometryType.GEOMETRYTYPEHERE(Points, Edges, Primitives, Vertices)
kwargs['node'] = kwargs['node'].node('NAMEOFNODETOSELECTFROM')
# If you want to select from the input of the current node, you can remove the above line
kwargs['inputindex'] = 0
# kwargs['shift'] = 1 # Will force selection using groups if enabled

soputils.selectGroupParm(kwargs)
Copy Code to Clipboard
Action Help:
Select geometry from an available viewport.
Action Icon:
BUTTONS_reselect

Attribute Visualization

Generally, being able to visualize attributes can be incredibly useful. There are different ways to go about it, a few are outlined below, but a rule of thumb is that you generally want to add this kind of action button to string parameters being used to select attributes from (this goes very well with attribute selection menu scripts). What if you don't want to attach this action button to such a string parameter? Well, that is also acceptable but you need to change kwargs['attribname'] to be equal to either another parameter which IS a string for an attribute, or simply hardcode it. For example:

kwargs['attribname'] = "variant"

would make it so this action button is essentially a toggle for the attribute "variant", and I believe it would figure out the attribute class automatically. All of the Attribute Visualization scripts use the same Action Help and Icon.

Visualize Color - Attribute-As-Is

Good for vector attributes mostly. There is no remapping of color here, so a vector of (1,0,0) will be red and a float will be greyscale.

import soputils

soputils.actionToggleVisualizer(kwargs,
{ 'type': hou.viewportVisualizers.type('vis_color'),
'parms': {
    'colortype': 0
} })
Copy Code to Clipboard

Visualize Color - Remap From White to Red

Great for float attributes, not so good for vector attributes. I believe I stole this from the heightfield masking nodes.

import soputils

soputils.actionToggleVisualizer(kwargs,
{ 'type': hou.viewportVisualizers.type('vis_color'),
'parms': {
    'colortype': 'attribramped',
    'rangespec': 'min-max',
    'minscalar': 0,
    'maxscalar': 1,
    'treatasscalar': True,
    'using': 'compabs',
    'component': 0,
    'colorramp': hou.Ramp((hou.rampBasis.Linear,
                           hou.rampBasis.Linear),
                           (0, 1),
                           ((1, 1, 1), (1, 0, 0)))
} })
Copy Code to Clipboard

Complex Example: Visualize Remapped Color for Dynamically Generated (and Deleted) Attributes

In a recent tool I created controls in the UI to be able to modify masks (attributes) that were then used within it, but deleted before being output with the geometry. So first there is a wrangle inside which is doing all the creation/modification of these masks. Then operations using the masks came afterwards. I chose to create a switch that would output the unmodified geometry with the masks on it so we could visualize it. Otherwise, there was no way to see precisely what the mask looked like which isn't very artist-friendly.

To make matters more complex, the tool's operation actually works in layers and relies on a multiparm setup. The action button is within this layer setup and shows a different mask on every layer (depending on how said layer mask is defined). So in my UI, I have:

  • A toggle that each action button checks and switches to say "we're in visualizing mode" and that redirects the geometry to show the masked version instead.
  • A hidden int parameter set to the layer being visualized (used for nodes inside to show the right attribute).
  • And a check for if anything is currently being visualized to brute-force turn that off before proceeding (had issues otherwise)

So this isn't exactly something to follow as a reference but feel free to study it or modify and use it if you see it being beneficial.

import soputils
import toolutils

# set attrib to visualize and create visualizer
kwargs['attribname'] = f"__mask{kwargs['script_multiparm_index']}"
hda = kwargs["node"]
layer_num = hda.parm('vis_layer')
vis_tog = hda.parm('tog_vis')
soputils.turnOffVisualizers(
    hou.viewportVisualizers.type('vis_color'), 
    hou.viewportVisualizerCategory.Scene, 
    None, 
    toolutils.sceneViewer().curViewport())
if str(layer_num.eval()) == str(kwargs['script_multiparm_index']) and vis_tog.eval() == 1:
    layer_num.set(0)
    vis_tog.set(0)
    
else:
    layer_num.set(kwargs['script_multiparm_index'])
    vis_tog.set(1)
    soputils.actionToggleVisualizer(kwargs,
        { 'type': hou.viewportVisualizers.type('vis_color'),
        'parms': {
            'colortype': 'attribramped',
            'rangespec': 'min-max',
            'minscalar': 0,
            'maxscalar': 1,
            'treatasscalar': True,
            'using': 'compabs',
            'component': 0,
            'colorramp': hou.Ramp((hou.rampBasis.Linear,
                                   hou.rampBasis.Linear),
                                   (0, 1),
                                   ((1, 1, 1), (1, 0, 0)))
        } })
Copy Code to Clipboard
Action Help:
Toggle visualization. Ctrl-LMB: Open the visualization editor
Action Icon:
VIEW_visualization

Set Vector Parameters (Directions)

Vector direction parameters can be tricky. Sure, you can do a kind of "dumb" set by dragging numbers around, but what if there were better ways? Below I've outlined a few fairly useful action buttons I regularly add to tools to make setting direction vectors a much faster, more intuitive process.

Note: I often use multiple action buttons for any given parm. In the Action Button Tricks there is a solution for how to do this.

Simple Vector

X, Y, Z.. these are fairly straightforward vectors to enter, but I like having buttons for each vs having to enter numbers because it's a bit faster for the user. Click the action button twice and it switches to a second vector. Theoretically, you could create a list of vectors for 1 button to cycle through as well... may have to add that code later.

# Set parm to specified vector.
node = kwargs['node']
num = kwargs['script_multiparm_index']
vectorParm = node.parmTuple(f"VectorParmToSet{num}")

targetDir = (1,0,0)
if vectorParm.eval() == targetDir:
    targetDir = (-1,0,0)
for num in range(3):
    vectorParm[num].set(targetDir[num])
Copy Code to Clipboard
Action Help:
Sets the direction to be the specified vector. Press again to switch to the negative vector.
Action Button:
Needs a custom icon for the specified vector. I usually use X, Y, or Z as the icon but Houdini doesn't have those built-in believe it or not.

Action Button Tricks

Setting up Multiple Action Buttons for 1 Parameter

I often use multiple action buttons (for vector parms) in particular, but that's not very straightforward. You can do the same by doing the following:

  1. First, set an action button on the target parameter and enable the setting "Horizontally Join to Next Parameter".
  2. For each additional action button you want, add a "Data" parameter. Normally I believe these are meant for storing some kind of temporary cached data in the HDA itself but I've never used them for that purpose. Instead, disable the label, enable "Horizontally Join to Next Parameter" (unless it's the last one in the list), and set the action button on this parm as well. It should join up right next to the one that you just set.

If done correctly, you should have a parameter that looks something like this.

Setting vectors with action buttons can make setting them far more intuitive.

Finding Existing Icons to Add to Action Buttons

BUTTON_reselect? VIEW_visualization? What the heck ARE these? Well, Houdini has 1000s of icons built-in. You can find them if you want. On Windows, they exist in C:\Program Files\Side Effects Software\Houdini XX.X.XXX\houdini\config\Icons\icons.zip.

But don't do that.

There are a couple of different Icon Browser panels/tools that have been created and released online. This one by Timothée Maron is free. They will have instructions on how to install them. It's pretty straightforward, and then you'll have a panel directly in Houdini that you can use to find icons that come with Houdini itself. These icons don't have what you'd normally recognize as a path or url to their location which is odd but convenient.


What if you create your own icons to add to a tool???

The icons you see in the above parm were made that way, and that is also not very hard... there just isn't really any documentation on it (so here you go).

Storing on-disk

This is my favourite way to access icons I've created. However, it can be a touch temperamental if you don't have your env variables set up with a proper location to store your tools. I will write an article on that soon, but in the meantime, I'd recommend creating an "icons" folder in your C:\Users\XXXXX\Documents\houdiniXX.X directory.

Create your icons in your program of choice, I personally use Affinity Designer but Adobe Illustrator would be another popular one. You ideally want to export your icons as .svg files which are vectors and can scale without issue. Don't make them too detailed, remember the scale they will be when you're seeing them on screen. I usually purposely make my canvas size something like 64x64 pixels so I can have it close to the scale you'll see in Houdini.

Once exported, assuming you put them in the above folder, you can access the icons using the path:
$HOUDINI_USER_PREF_DIR/icons/nameOfIcon.svg
in your action button icon path. If you define your own env variables (ideally using a packages method, will be documenting) then you can have a path that looks more like:
$JTOOLS/icons/nameOfIcon.svg
This is ideal because if your actual tool is stored at $JTOOLS/otls/nameOfTool.hda, then the icons will automatically load as long as your tool does, so you can be confident that the icons will always exist alongside the tools.

Storing in the HDA

It is also possible to store icons directly inside the "Extra Files" of your HDA. It is recommended that as you add files, keep the filename in the section name (what gets created when you add files) so that Houdini can tell what kind of file is located there. The big plus of this approach is that the icons are guaranteed to be stored with that tool, but that is less convenient if you want to use the same icon with multiple tools. However, as I've never actually gotten this method to work, all I can do is refer you to this page to try it for yourself.