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
HOUDINI_PATH/scripts/
, while modules you would import are underHOUDINI_PATH/scripts/python/
orHOUDINI_PATH/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.
|
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 For example, |
|
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, Note The difference between |
|
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 For example, |
|
Houdini runs this script when it is started without a scene ( This is the Python equivalent of the Note Only Houdini FX runs |
|
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 |
|
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
HOUDINI_PATH/scripts/beforescenesave.py
if it exists. -
After Houdini attempts to save the scene file, it will run
HOUDINI_PATH/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')
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 ( |
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 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.
|
On Created ( |
Runs after the user creates a new instance of a node type (not when a scene file loads, see You can use this, for example, to make changes to a node that Houdini will automatically save (for example, add spare parameters). |
On Loaded ( |
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 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 ( |
Runs for each instance of an asset when the asset updates because the shared definition changed. |
On Deleted ( |
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.
|
After Last Delete ( |
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
|
On Input Changed ( |
Runs when an input on a node of this type is connected, disconnected, or switched. The script can use |
On Name Changed ( |
Runs after the user changes the name of a node of this type. The script can use 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 ( |
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 ( |
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 ( |
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 The script can get the old and current version strings in The handler then does the work of changing the node (using the reference in (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 |
---|---|---|
|
A hou.OpNode reference to the node instance |
|
|
A hou.NodeType reference to the node type |
|
|
A string containing the old name of the HDA instance. |
|
|
The index of the input that was connected/disconnected. |
|
|
The current HDA version string. |
|
|
The HDA version string that the node was last saved with. |
|
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
HOUDINI_PATH/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 likeedu.toronto3d-foo-2.0_OnCreated.py
. -
Files matching the pattern
HOUDINI_PATH/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 HOUDINI_PATH/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:
|
The hou.Parm object whose callback script was invoked. |
|
The hou.OpNode object containing the parameter. |
|
The name of the hou.Parm. This value is the same as |
|
The name of the hou.ParmTuple containing the parameter. This is the same as |
|
The value of the parameter. This entry is the same as |
|
The values of the parameter tuple. These entries are the same as |
|
The number of the multiparm, if the parameter is an instance of a multiparm (otherwise |
|
If this parameter is not an instance of a multiparm, this value is |
|
These values are only available if |
Parameter menu scripts ¶
Scripting button/icon strip parameters ¶
Key-value parameter button scripts ¶
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 anotherQTimer
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 themy_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, ifx
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 |