Houdini 20.0 Python scripting

Python script locations

Various places in Houdini where you can customize behavior using Python scripting.

On this page

General tips

  • If you need to check whether some code ran, the easiest way is to call hou.ui.displayMessage:

    import hou
    hou.ui.displayMessage("I ran! I ran so far away!")
    

    You can also use this to display the values of variables. For example, to see what’s in the kwargs global variable in an event handler:

    import hou
    hou.ui.displayMessage(repr(kwargs))
    
  • You can share code between handlers by putting it in a module on disk and calling it from the handler script. For example, the handler script might just pass the kwargs to a common function:

    import companyutils
    
    companyutils.on_created(kwargs)
    
  • Houdini has a very large, powerful (but verbose) set of APIs. Browsing through what’s available in the API reference will help you discover what’s possible with scripting.

  • In general, Houdini looks for “scripts” (usually callback scripts) on disk under HOUDINIPATH/scripts/, while modules you would import are under HOUDINIPATH/scripts/python/ or HOUDINIPATH/pythonX.Ylibs.

How to refer to code in other locations in your scripts

Python files on disk

In the current .hip file’s session module

Use hou.session to refer to the current file’s session module.

In the current node’s Python module

You can create a default Python module in a custom node type to hold code related to the node type. Then elsewhere you can get a reference to the node type’s Python module using hou.OpNode.hdaModule (or hou.NodeType.hdaModule).

For example, in a button parameter’s callback script or asset event script:

# In a callback script, kwargs["node"] contains a reference to the current node
nodeutils = kwargs["node"].hdaModule()

nodeutils.my_function()

Startup scripts

Houdini will look for these directories/files in the directories specified on the Houdini path.

pythonX.Ylibs/pythonrc.py

Houdini will find any files matching this pattern in the Houdini path and run them at startup. Houdini runs this script (if it exists) very early in the startup sequence, before the UI is available and before assets are loaded. If you don’t need the UI or assets, or if you want to configure the UI or asset loading before it starts, you can use this script. Otherwise, use ready.py or uiready.py instead.

For example, $HOUDINI_USER_PREF_DIR/pythonX.Ylibs/pythonrc.py for your personal script.

pythonX.Ylibs/ready.py

Houdini will find any files matching this pattern in the Houdini path and run them at startup after all non-graphical components of Houdini has been loaded.

For example, $HOUDINI_USER_PREF_DIR/pythonX.Ylibs/ready.py for your personal script, or $HSITE/houdiniX.Y/pythonX.Ylibs/ready.py for a studio-wide script.

Note

The difference between pythonrc.py and ready.py is that pythonrc.py executes before HDAs are loaded and ready.py executes after HDAs are loaded.

pythonX.Ylibs/uiready.py

Houdini will find any files matching this pattern in the Houdini path and run them after the UI is ready. You should use this script to set up interactive since when it runs the UI and UI scripting is available, and Houdini has loaded . Houdini only runs this script (if it exists) in interactive sessions. If your code needs assets to be loaded, but you want it available in both Houdini, batch mode, and hython, use ready.py instead.

For example, $HOUDINI_USER_PREF_DIR/pythonX.Ylibs/uiready.py for your personal script.

scripts/123.py

Houdini runs this script when it is started without a scene (.hip) file. Houdini will only run the first 123.py script it finds in the path. This is useful for customizing the empty scene, for example if you want to start every scene with a default lighting rig.

This is the Python equivalent of the 123.cmd Hscript file. If 123.cmd and 123.py both exist, Houdini will only run 123.py.

Note

Only Houdini FX runs 123.py. Houdini Core runs houdinicore.py instead of 123.py on startup. Both 123.py and houdinicore.py serve the same purpose but for different products.

scripts/456.py

Houdini runs this script whenever a scene file is loaded (including when Houdini starts up with a scene file).

This is the Python equivalent of the 456.cmd Hscript file.

scripts/category/ nodename_eventtype.py

See node event scripts below.

Note

You need to restart Houdini to have it recognize new scripts. However, you can change existing scripts without needing to restart Houdini.

Run scripts before and/or after saving the scene (.hip) file

Houdini lets you run scripts before and/or after a save. This can be useful to update asset management and source control tools whenever a save occurs.

  • Before Houdini saves the scene file, it will run HOUDINIPATH/scripts/beforescenesave.py if it exists.

  • After Houdini attempts to save the scene file, it will run HOUDINIPATH/scripts/afterscenesave.py if it exists.

Tip

These scripts are useful because they're automatically global (they run for any scene file you work on), and they're easy to set up. On the other hand, they're difficult to set up programmatically, and they only relate to saving.

The more general/fine-grained way to react to scene file events is to set up a scene file event handler.

The scripts run in a context containing a global dictionary named kwargs. This dictionary contains the following items:

file (str)

The path to the scene file will be saved to. (In beforescenesave.py, a file may or may not already exist there).

autosave (bool)

Contains True if this save was triggered by the autosave timer. If this is False, this is a “regular” save caused by the user.

success (bool)

(afterscenesave.py only) Contains True if Houdini was able to save to the file.

Note

Houdini might not actually be able to save the file at the given path (for example, if the user doesn’t have the proper permissions), so the file still may not exist when afterscenesave.py runs, or may not contain updated data. You should check kwargs["success"] in the script before you assume the file contains the saved data.

The following example automatically “stages” the file for commit in git when it’s saved:

# afterscenesave.py
import subprocess

# Only run the command if the save succeeded and it's
# not an autosave
if kwargs["success"] and not kwargs["autosave"]:
    # Pass the scene file path to the git command
    subprocess.call("git", "add", kwargs["file"])

Scene file event callbacks

You can register a callback function that Houdini will call whenever a the scene file changes. Houdini calls the function with one argument. The argument is a hou.hipFileEventType object representing the type of event that occurred.

Instead of subscribing to a specific event, the script will be called for every event, and you need to check the event type to see if it’s an event you're interested in. This is usually more convenient for scene events, since you will often share code between reactions to several events.

In the script, you can use the functions in the hou.hipFile module to get information about the current scene file. Each file operation (New file, Open file, Import file, Save) has corresponding “Before” and “After” event types, so you can read the scene file’s values before and/or after the change.

For example, to run some code every time a new scene file is loaded:

def scene_was_loaded(event_type):
    if event_type == hou.hipFileEventType.AfterLoad:
        print("The user loaded", hou.hipFile.path())

Parameter expressions

Session module

Houdini keeps a Python module associated with the scene file. You can edit it using Window ▸ Python source editor. This gives you a convenient place to store functions, classes, and objects specific to the scene file, and automatically have them saved and loaded with the scene file.

When the scene file is loaded (or when you edit the source code), the module source code is evaluated and the module is available to other Python code as hou.session.

For example, you write a log_deletion() function in the Python source editor, then call it from an asset deletion callback like this:

hou.session.log_deletion()

You can programmatically modify the hou.session source code using the hou.appendSessionModuleSource() function. The source code is saved with the scene file.

Note

Any dynamic changes you make the the module object once it’s loaded into memory (for example hou.session.x = "foo") are not saved with the scene file. Only the module source code is saved with the scene file.

Asset modules

Each digital asset type has a Python module, similar to the scene file’s hou.session module. You can use this module to store functions, classes, and objects specific to the asset type.

The module source code is stored in a section of the asset named PythonModule (on the Extra files tab of the type properties window). You can create this section manually, or Houdini will create it automatically if you create a “Python Module” event handler on the Scripts tab.

In the HDA module’s code, you can get a reference to the HDA’s node type using:

nodetype = kwargs["type"]

You can access the module using the hou.NodeType.hdaModule method (if you have a NodeType object) or the hou.OpNode.hdaModule method (if you nave a Node object). For example, to call a myfunc function on the myasset object type:

hou.nodeType(hou.objNodeTypeCategory(), "myasset").hdaModule().my_function()

Python expressions on nodes inside the asset definition can access the PythonModule through relative references. For example, a Python parameter expression on a node in the asset’s definition subnetwork could use hou.node("..").hdaModule().foo().

If you have so much Python code inside an asset that you want to organize it into multiple modules, you can create other HDA sections to store those modules. For example, you could create a FooModule section containing Python code. Then in the main PythonModule section, use the following code to import the contents of FooModule as foo:

import toolutils
foo = toolutils.createModuleFromSection('bar', kwargs['type'], 'FooModule')

Note

New to Houdini 18.0, the createModuleFromSection function expects the code in the HDA section to have Python 3 style print statements.

In general this means that print statements in the HDA section must have the arguments enclosed in parentheses.

For example

print("Hello world!")

instead of

print "Hello world!"

Modules on disk

Houdini automatically adds any $HOUDINI_PATH/pythonX.Xlibs directories to the Python path, meaning packages and modules inside those directories can be imported in Python code.

To make packages and modules in other locations available to python, use the $PYTHONPATH environment variable, or modify the sys.path list in Python.

For example, to import the module in $HOUDINI_USER_PREF_DIR/pythonX.Ylibs/mystuff.py:

import mystuff

mystuff.my_function()

If you are trying to keep your script to a single line (for example, in a parameter expression or callback), you can use Python’s built-in __import__ function to return the module and immediately access its attributes:

__import__("mystuff").my_function()

Digital asset event handlers

You can write Digital asset event handlers in Python. After you create an event handler on the Scripts page of the type properties window (for example “On Created”), make sure Edit as is set to “Python”.

The event script can access the event parameters in a global dictionary variable called kwargs. For example, to get the hou.OpNode object involved in the event, use kwargs["node"].

See how to reference embedded files for information on how to refer to the embedded scripts wherever Houdini expects a filename.

Tip

These handlers let you set up scripts that run when an event happens for a specific node type. You can also set up general asset event handlers which run when any asset or asset library is loaded or unloaded.

Note

The default language when creating a handler in the type properties interface is now Python, however, if you create a handler section through scripting, its default language will still be HScript.

To set the default language of an asset section to Python through script, use the HDADefinition.setExtraFileOption method to set the option EventName/IsPython to True:

on_created_body = """
from my_studio import register_stats
register_stats.createdSpecialNode()
"""

# Get a reference to the asset definition
hdadef = hou.nodeType("mystudio::Sop/flamingo::2.0").definition()
# Create the event handler section on the asset
hdadef.addSection('OnCreated', on_created_body)
# Set the "IsPython" option on the section
hdadef.setExtraFileOption("OnCreated/IsPython", True)

Event types

The following events can trigger scripts.

Before First Create (PreFirstCreate)

Runs when the first instance of a node type is created in a scene. This includes when Houdini is creating nodes as it loads a scene file from disk.

For example, if a scene has no geometry objects, when you add one, this would trigger the PreFirstCreate handler for the geometry object type. If you then added another geometry object, the event would not trigger.

This can be useful for setting up an environment necessary for one or more instances of the asset to work. For example, copying texture maps to required locations, or setting environment variables.

  • In the script you can check if the node is being created because Houdini is loading a file using the hou.hipFile.isLoadingHipFile function.

  • The user cannot undo the actions performed in this script. You may want to include corresponding code in a PostLastDelete handler (see below) to undo changes made by this script.

On Created (OnCreated)

Runs after the user creates a new instance of a node type (not when a scene file loads, see OnLoaded below).

You can use this, for example, to make changes to a node that Houdini will automatically save (for example, add spare parameters).

On Loaded (OnLoaded)

Runs after an instance of a node type is created as Houdini loads a scene file from disk (not when a user creates a node in the network editor, see OnCreated above). This also runs when pasting nodes from the clipboard.

This does not run when loading the node as part of the contents of another asset. If you need to do something when a node inside an asset loads, you must put that code in the asset’s load handler.

This runs on each node after all nodes are loaded, so the script will see the complete scene.

On Updated (OnUpdated)

Runs for each instance of an asset when the asset updates because the shared definition changed.

On Deleted (OnDeleted)

Runs before an instance of a node type is deleted (while the node still exists). This includes when Houdini is “unloading” the node because the user is starting a new file, opening a file, or quitting.

  • In the script you can check if the node is being deleted because Houdini is unloading the scene using the hou.hipFile.isShuttingDown function (despite the name, this function returns True when Houdini is starting a new file or opening a file as well as when it is quitting).

  • Unrelated to this event handler, each node instance can have its own individual “deletion script”. The user can edit this (HScript command language only) script in the parameter editor using ▸ Edit deletion script. The OnDeleted handler runs before the node’s individual “deletion script” if it exists.

After Last Delete (PostLastDelete)

Runs after the last instance of a node type is deleted in a scene. This includes when Houdini is “unloading” the node because the user is starting a new file, opening a file, or quitting.

This can be useful to clean up changes made in a PreFirstCreate script (see above).

  • The user cannot undo actions performed in this script.

  • In the script you can check if the node is being deleted because Houdini is unloading the scene using the hou.hipFile.isShuttingDown function (despite the name, this function returns True when Houdini is starting a new file or opening a file as well as when it is quitting).

On Input Changed (OnInputChanged)

Runs when an input on a node of this type is connected, disconnected, or switched. The script can use kwargs["input_index"] to get the number (starting from 0) of the input that changed.

On Name Changed (OnNameChanged)

Runs after the user changes the name of a node of this type. The script can use kwargs["old_name"] to get the previous name string.

This can be useful, for example, if you are somehow indexing nodes by their names or paths in some external storage, to keep the external index up-to-date.

On Install (OnInstall)

Runs when this node type is installed into the session.

Note that saving changes to an HDA node type triggers the On Uninstall and On Install scripts since the old HDA node type is uninstalled first before the new HDA node type with the latest changes is installed.

On Uninstall (OnUninstall)

Runs when this node type is uninstalled from the session.

Note that saving changes to an HDA node type triggers the On Uninstall and On Install scripts since the old HDA node type is uninstalled first before the new HDA node type with the latest changes is installed.

Sync Node Version (SyncNodeVersion)

This is part of the older versioning system, allowing you to write an “upgrade” handler that can automatically convert old versions of a node to the latest version. This system may be useful for small-scale, backwards-compatible changes such as adding new parameters. See the two types of asset versioning for more information.

This runs when an asset instance loads and notices the asset’s Version field has changed since the scene file was saved. It runs after a node’s parameters are loaded and resolved, but before the OnLoaded or OnUpdated event handlers.

The script can get the old and current version strings in kwargs["old_version"] and kwargs["current_version"].

The handler then does the work of changing the node (using the reference in kwargs["node"]) to match the current version. This means you must update the script every time you update the asset.

(You can trigger a node’s upgrade script manually using hou.OpNode.syncNodeVersionIfNeeded.)

Script variables

The handler scripts run in an environment with a global kwargs dictionary. The following table shows what items are available in the dictionary for the various event types:

Key

Value

Events

node

A hou.OpNode reference to the node instance

OnCreated, OnUpdated, OnInputChanged, OnNameChanged, OnDeleted, SyncNodeVersion

type

A hou.NodeType reference to the node type

OnLoaded, PreFirstCreate, OnCreated, OnUpdated, OnInputChanged, OnNameChanged, OnDeleted, PostLastDelete, UpgradeLoadedParms, PythonModule

old_name

A string containing the old name of the HDA instance.

OnNameChanged

input_index

The index of the input that was connected/disconnected.

OnInputChanged

current_version

The current HDA version string.

SyncNodeVersion

old_version

The HDA version string that the node was last saved with.

SyncNodeVersion

Node event handler files

You can put event handler scripts in files in the Houdini path instead of/in addition to embedding them in assets. To respond to an event on a digital asset, you probably just want to use the built-in event handler support in the type properties window. However, using script files can have the following benefits:

  • These on-disk event scripts work for both assets and built-in node types.

  • You can install “global” event handler files that run for any node type.

Houdini looks for files matching the following patterns:

  • Files matching the pattern HOUDINIPATH/scripts/category/nodename_event.py (for example, $HOUDINI_USER_PREF_DIR/scripts/obj/geo_OnCreated.py) will run when the given event type occurs to a node of the given type.

    For complex node type names with namespaces and/or versions, replace any :: separators in the name with single hyphens.

    For example, if the node type name is edu.toronto3d::foo::2.0, you would use a filename like edu.toronto3d-foo-2.0_OnCreate.py.

  • Files matching the pattern HOUDINIPATH/scripts/event.py run when the given event type occurs to any node.

Note

The script path uses the “directory” form for node categories, not the “table” form. For example, obj not Object, sop not Sop, out not Rop. These different forms are an unfortunate historical inconsistency in Houdini.

An event script file will run in an environment with a built-in kwargs global dictionary variable. To get the hou.OpNode object for the newly created node in the script, kwargs["node"]. See script variables above for information on the items available in the dictionary.

Note

Houdini will also find files matching HOUDINIPATH/scripts/category/nodename.py (no event suffix, for example $HOUDINI_USER_PREF_DIR/scripts/obj/geo.py). This is a factory node’s “creation script”, which is (confusingly) not the same as a node’s OnCreated handler.

Old documentation or tutorials may refer to this type of file as a way to run code when a node is created. However, this does not work with digital assets. You should now use an OnCreated event script instead. Consider the “creation script” an internal implementation detail subject to change without notice.

Individual node event handlers

You can set up event handlers on individual node instances. Unlike asset events, which trigger when the event happens for any instance of the asset, these handlers only run when an event happens to the specific node you set them up on.

See hou.OpNode.addEventCallback for how to set up an event handler on a node. See hou.nodeEventType for the types of events you can react to.

General asset event handlers

You can set up handlers for changes to asset libraries. For example, when an asset is created or deleted, or an asset library is installed or uninstalled.

See hou.hda.addEventCallback for how to set up an asset library event handler. See hou.hdaEventType for the types of events you can react to.

Shelf tool scripts

You write the script that runs when the user clicks a shelf tool in Python. See how to write a tool script for more information.

Parameter callback scripts

You can write Python scripts that are called whenever the value of a parameter on a node changes.

If the default callback language is HScript, remember to change it to Python.

The event script can access the event parameters in a global dictionary variable called kwargs. For example, to get the hou.OpNode object containing the parameter, use kwargs["node"], and to get the hou.Parm object of the parameter, use kwargs["parm"].

Tip

The most convenient way to implement a callback script is to write one line of Python that calls the “real” callback function in your asset’s Python module and passes it the kwargs dictionary.

For example, you can make a my_callback function in the asset module that takes the dictionary of options and a hou.OpNode object representing the current node instance. Then set the parameter’s Python Callback line to call the function from the module using the following:

hou.pwd().hm().my_callback(kwargs, hou.pwd())

(hou.pwd() returns the node which is currently cooking. hou.OpNode.hm returns an asset’s Python module.)

The following table lists the contents of the kwargs dictionary for parameter callbacks:

parm

The hou.Parm object whose callback script was invoked.

node

The hou.OpNode object containing the parameter.

parm_name

The name of the hou.Parm. This value is the same as kwargs['parm'].name().

script_parm

The name of the hou.ParmTuple containing the parameter. This is the same as kwargs['parm'].tuple().name(). This key is script_parm instead of something more meaningful for backwards compatibility with Hscript.

script_value

The value of the parameter. This entry is the same as kwargs['parm'].eval().

script_value0, script_value1, …

The values of the parameter tuple. These entries are the same as [p.eval() for p in kwargs['parm'].tuple()].

script_multiparm_index

The number of the multiparm, if the parameter is an instance of a multiparm (otherwise -1). Note that the “First Instance” parameter on the multiparm’s folder block list determines where to start numbering multiparms. Typically, the first multiparm is numbered 1, the second 2, and so on.

script_multiparm_nesting

If this parameter is not an instance of a multiparm, this value is 0. If it is a multiparm instance but the instance is not contained inside other multiparm instances, the value is 1. Otherwise, if the multiparm instance is nested inside one level of multiparm instances it returns the number of nesting levels.

script_multiparm_index2, …, script_multiparm_indexN

These values are only available if script_multiparm_nesting is 2 or higher. These values correspond to the multiparm instance numbers of the outer multiparm instances. For example, if multiparm instance 3 is nested inside instance 5 which is inside instance 4, script_multiparm_index will be 3, script_multiparm_index2 will be 5, and script_multiparm_index3 will be 4.

Parameter menu scripts

You can use Python to generate dynamic parameter menus.

Tip

Houdini runs this script whenever the user opens the menu, and when the parameter is evaluated, so make it as simple and fast as possible.

If a Python menu script contains only one line, Houdini evaluates it as a Python expression. If it contains multiple lines, Houdini evaluates it as a function body and you must use return to return the list. This behavior is the same as for Python parameter expressions.

The script must return a flat list of strings, where the even items are tokens and the odd items are labels. For example:

["1", "Light", "50", "Medium", "100", "Heavy"]

Tip

You're probably used to working with pairs of values in Python using tuples, like this:

pairs = [("1", "Light"), ("50", "Medium"), ("100", "Heavy")]

You can work that way when generating and working with the list, and then convert to the “flat” list Houdini requires at the end using this trick:

return [item for sublist in pairs for item in sublist]
# -> ["1", "Light", "50", "Medium", "100", "Heavy"]

For example, to generate a menu of Object-level digital assets using Python:

result = []
for tool in hou.shelves.tools().values():
    node_type_name = tool.toolMenuOpType(hou.paneTabType.NetworkEditor)
    if (node_type_name.startswith("Object/") and
            'Digital Assets' in tool.toolMenuLocations()):
        result.append(tool.name())
        result.append(tool.label())
return result

In an asset, you can put the code that generates the menu into a function in the asset’s PythonModule section (for example, a generate_menu function) and call that function in the menu script hou.pwd().hdaModule().generate_menu().

The menu script can access the calling parameter in a global dictionary variable called kwargs. For example, to get the hou.OpNode object containing the parameter, use kwargs["node"], and to get the hou.Parm object of the parameter, use kwargs["parm"].

The following table lists the contents of the kwargs dictionary for parameter callbacks:

parm

The hou.Parm object whose callback script was invoked.

node

The hou.OpNode object containing the parameter.

script_multiparm_index

The number of the multiparm, if the parameter is an instance of a multiparm (otherwise -1). Note that the “First Instance” parameter on the multiparm’s folder block list determines where to start numbering multiparms. Typically, the first multiparm is numbered 1, the second 2, and so on.

script_multiparm_nesting

If this parameter is not an instance of a multiparm, this value is 0. If it is a multiparm instance but the instance is not contained inside other multiparm instances, the value is 1. Otherwise, if the multiparm instance is nested inside one level of multiparm instances it returns the number of nesting levels.

script_multiparm_index2, …, script_multiparm_indexN

These values are only available if script_multiparm_nesting is 2 or higher. These values correspond to the multiparm instance numbers of the outer multiparm instances. For example, if multiparm instance 3 is nested inside instance 5 which is inside instance 4, script_multiparm_index will be 3, script_multiparm_index2 will be 5, and script_multiparm_index3 will be 4.

Scripting button/icon strip parameters

You can create “button strip” and “icon strip” parameters (either on an asset or as spare parameters). You can use these to provide a strip of clickable action buttons, or a strip of mutually exclusive options you can select from, or a strip of independent options you can individually turn on or off.

  • When you create a strip parameter, you set whether it is “Normal” (mutually exclusive: only one item can be selected at a time) or “Toggle” (each item can be turned on or off individually).

  • Reading the value of a “Normal” strip is straightforward. hou.Parm.eval will return the index of the selected item (starting from 0).

    To translate the index into the corresponding token you set up for each item in the menu, you can get the list of tokens from the parameter template and then index into that list:

    def get_selected_token(parm):
        # Read which item is currently selected
        selected = parm.eval()
    
        # Get the list of menu tokens from the parameter template
        tokens = parm.parmTemplate().menuItems()
    
        # Return the token corresponding to the selected item
        return tokens[selected]
    
    # In a parameter callback script...
    token = get_selected_token(kwargs["parm"])
    
  • Reading the value of a “Toggle” strip returns a bit field . This is a number where each “bit” in the number’s binary representation encodes the on/off state of a button in the strip.

    If you're not a hardcore programmer that probably sounds very technical, but all you need are some utility functions like the ones below to translate a bit field into a more usable form.

    # The general form to test if the "bit" at position n is "on"
    # in a number is:
    # number & (1 << n)
    
    def strip_to_tokens(parm):
        # Takes a reference to a button/icon strip parameter
        # instance and returns a list of tokens corresponding
        # to the buttons that are on
    
        bitfield = parm.eval()
        tokens = parm.parmTemplate().menuItems()
        return [token for n, token in enumerate(tokens) if bitfield & (1 << n)]
    
    def is_n_selected(bitfield, n):
        # Returns True if the nth position (starting at zero)
        # is "on" in the bitfield
    
        return bitfield & (1 << n)
    
    def bitfield_to_list(bitfield, size=32):
        # Takes a bitfield and returns a list of booleans
        # (True or False) indicating whether each position
        # is "on" or "off" in the bitfield.
        # ("size" is the number of buttons in the strip.)
    
        return [bitfield & (1 << n) for n in range(size)]
    
  • Working with the value of a “toggle” strip in an expression would be extremely difficult. If for some reason you need the settings of a toggle strip to drive an expression, a workaround would be give the strip a callback script that sets a simple value on a hidden parameter based on the strip settings. Then reference the hidden parameter in the expression instead of the strip.

  • To make a button/icon strip that acts more like a toolbar of action items, make a “Toggle” strip, then give it a callback script that reads which item was turned on, performs some action based on that, and then immediately turns the option off again.

    For example:

    import math
    
    # A button strip callback strip that makes the buttons act like
    # action buttons. This script assumes the menu type is set to "Toggle". 
    
    # Read which button was turned on
    parm = kwargs["parm"]
    bitfield = parm.eval()
    # Only one item in the bitfield should be selected, so the base-2
    # log of the bitfield should give us the index of the selected button
    selected_index = int(math.log(bitfield, 2))
    
    # Do something here based on which button was clicked
    
    # Clear the selected button. Setting the parameter's bitfield value
    # to zero turns off every button.
    parm.set(0)
    

Key-value parameter button scripts

You can use Python to generate a new key-value pair for a key-value parameter when the user clicks a button. This script can present an interface for the user to choose a preset (for example, using hou.ui.selectFromList or hou.ui.selectFromTree). It must return a (key, value) tuple.

If the value in the field is one line, it is treated as a Python expression and evaluated. If it has more than one line, it is treated as if it was the body of a function and must use a return statement at the end to return a value. This is consistent with other parameter callbacks.

  • The script can return return non-string values in the tuple. Houdini will convert them to strings.

  • The script runs in an environment containing a kwargs global dictionary variable. kwargs["node"] contains a hou.OpNode for the node. kwargs["parm"] contains a hou.Parm reference for the key-value parameter.

  • If the user cancels choosing, the script should return empty strings in the tuple ("", "").

  • We recommend you define a function to handle the callback in the asset’s Python module.

    For example, you can put this function in the asset’s Python module:

    def choose_env_variable():
        """
        Lets the user choose an environment variable name and returns
        the name and value to the key-value parameter.
        """
    
        # Grab the environment variable dictionary from the OS
        import os
        names = os.environ.keys()
        # Use a Houdini UI function to ask the user to choose from the list
        results = hou.ui.selectFromList(names)
        if not results:
            # The user didn't choose anything
            return ("", "")
        # We only support choosing one item; if the user picked more than
        # one item, just use the first
        chosen_index = results[0]
        # hou.ui.selectFromList() returns the number of the chosen item;
        # translate that back into a name
        chosen_name = names[chosen_index]
        # Return a tuple of the variable name and the value of that variable
        return (chosen_name, os.environ[chosen_name])
    

    Then you could call it using this in the Chooser callback field:

    kwargs["node"].hdaModule().choose_env_variable()
    

    If the callback function needs the node and/or parameter reference, you can pass them to the function:

    kwargs["node"].hm().choose_env_variable(kwargs["node"], kwargs["parm"])
    
  • Note that the callback does not need to interactively ask the user to choose something. You could have a button that adds a key-value pair based on current conditions.

    For example, you could put this in the Chooser callback field:

    (hou.frame(), hou.time())
    

    When a user clicked the button, this Python expression would evaluate to a tuple containing the current frame number and current time.

Background processes

You can set up Python code to execute “in the background” while the user is working.

  • You can use hou.ui.addEventLoopCallback to set up a function that Houdini calls whenever the UI is not busy.

    The callback function will be called very often (around 20 times a second). The function should be very fast to run so it doesn’t slow down the UI.

  • You can use a Qt QTimer object to schedule code to run in the future. That code can itself set up another QTimer if you want some code to execute at a certain interval.

Tips

  • You can use hou.findFile() to search for filenames in the Houdini path.

  • To read content from an embedded file inside an asset, use hou.HDADefinition.sections to get a dictionary of section names to HDASection objects, then use hou.HDASection.contents to get the section contents.

    For example, to read the contents of an embedded file named example in the my_asset object:

    objects = hou.nodeTypeCategories()["Object"]
    my_asset = objects.nodeTypes()["my_asset"]
    my_asset_def = my_asset.definition()
    section = my_asset_def.sections()["example"]
    contents = section.contents()
    

    As a shortcut, you can use Houdini’s support for reading asset sections as if they were files using an opdef: path:

    content = hou.readFile("opdef:Object/my_asset?example")
    
  • To access Python variables in the global scope from hou.session, import __main__. For example, if x is a variable in the global scope, you can access it as __main__.x.

    You can use this approach to access Python global variables from parameter expressions, HDA Python modules, Python button callbacks, shelf scripts, and so on.

See also

Python scripting

Getting started

Next steps

Reference

  • hou

    Module containing all the sub-modules, classes, and functions to access Houdini.

Guru level

Python viewer states

Python viewer handles

Plugin types