HOM
Storing Python Scripts in digital assets
Python Module per Digital Asset
Each Houdini Digital Asset (HDA) may have its own Python module. This module corresponds to the asset type, and is independent of the number of instances of the asset. Typically, one would use this module to store functions, classes, constants, etc. to do with the HDA.
The HDA’s Python module is stored in the PythonModule section. To create this module, create a “Python Module” event handler in the Scripts page of the Operator Type Properties dialog.
Because the module is associated with the HDA’s node type, you can access the Python module object via hou.NodeType.hdaModule. For example, you could write hou.nodeType(hou.objNodeTypeCategory(), 'hdaname').hdaModule() to access the module for the hdaname HDA. Most of the time, however, you're dealing with hou.Node objects, so you can use the convenience method hou.Node.hdaModule. If n is a Node object, calling n.hdaModule() is equivalent to calling n.type().hdaModule().
The return value of hdaModule() behaves like a Python module. So, if the PythonModule section contains:
def foo(): print "hello"and /obj/hda1 is an instance of that HDA, you could write
hou.node('/obj/hda1').hdaModule().foo().
Python Event Handlers
HDA event handlers can be written in Hscript or Python. Simply change the “Edit as” menu to tell Houdini which language the code is in.
Parameters to Python event handlers are passed in via a global dictionary variable named kwargs, similar to how they're passed into shelf tool scripts. For example, in an Hscript OnCreated event handler you would use $arg1 to get the path to the new instance of the HDA. In a Python OnCreated handler, you would use kwargs['node'] to get the hou.Node for the new HDA instance.
The following table lists the contents of the kwargs dictionary for the various event handler types:
|
Key name |
Value |
Event Handlers |
|---|---|---|
|
|
The hou.Node instance for the digital asset. |
OnCreated, OnUpdated, OnInputChanged, OnNameChanged, OnDeleted |
|
|
The hou.NodeType defined by the digital asset. |
OnLoaded, PreFirstCreate, OnCreated, OnUpdated, OnInputChanged, OnNameChanged, OnDeleted, PostLastDelete, PythonModule |
|
|
A string containing the the old name of the HDA instance. |
OnNameChanged |
|
|
The index of the input that was connected/disconnected. |
OnInputChanged |
Note that kwargs['type'] is also available in the PythonModule section.
Accessing the Python Module
There are a number of places where you will commonly call functions in the PythonModule, including Python and Hscript event handlers, parameters in nodes inside the HDA, HDA button callbacks, and menu generation scripts.
To call a function in PythonModule from a Python event handler, simply use kwargs['node'] to access the node and from the node you can access the module. For example, if you had the following function in the PythonModule section:
def onCreated(node): hou.ui.displayMessage("You created " + node.path())you could call it from the OnCreated Python event handler with:
kwargs['node'].hdaModule().onCreated(kwargs['node'])You could also call this method from an Hscript version of the OnCreated section using the python Hscript command:
python -c "hou.node('$arg1').hdaModule().onCreated(hou.node('$arg1'))"
Parameter Callback Scripts
When Houdini runs a Python parameter callback script, it also passes its arguments via a kwargs dictionary. The most useful values in this dictionary are kwargs['node'] and kwargs['parm'], containing the hou.Node and hou.Parm objects corresponding to the parameter. So, you could write
kwargs['node'].hdaModule().onButtonPress()to call the onButtonPress() function in the PythonModule section.
You might see the above code written as hou.pwd().hdaModule().onButtonPress(). Using hou.pwd, or hou.node('.'), is also a valid way to access the PythonModule section, since Houdini changes the current node to the HDA instance when it invokes a parameter callback.
The keys in the kwargs dictionary have the same names as the variables available to Hscript callback scripts, except that it contains the extra keys node and parm. This dictionary contains the following entries:
|
parm |
The hou.Parm object whose callback script was invoked. |
|
node |
The hou.Node object containing the parameter. |
|
parm_name |
The name of the hou.Parm. This value is the same as |
|
script_parm |
The name of the hou.ParmTuple containing the parameter. This value is the same as |
|
script_value |
The value of the parameter. This entry is the same as |
|
script_value0, script_value1, … |
The values of the parameter tuple. These entries are the same as |
|
script_multiparm_index |
The number of the multiparm, if the parameter is an instance of a multiparm. 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. If this parameter is not an instance of a multiparm, this value is -1. |
|
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 |
Parameter Expressions
Python expressions in nodes inside the HDA can easily access the PythonModule via relative references. For example, if “/obj/hda1” is an instance of the HDA and “/obj/hda1/geo1” is a node inside it, a Python expression on one of geo1’s parameters could contain, for example, hou.node('..').hdaModule().bar() to call the bar() function in PythonModule.
Parameter Menu Scripts
Menu parameters whose contents are generated via a script can also use PythonModule functions to generate the menu. You can write these menu scripts using either Python or Hscript. In Hscript, these scripts output a whitespace-separated list of strings, with two strings per menu entry. Each pair of strings contains the internal name for the menu entry in the first part of the pair and the label in the second part. In Python you write code that evaluates to a sequence of strings. For example, ['one', 'Apples', 'two', 'Cream Pie'] would correspond to two menu entries with internal names “one” and “two” and labels “Apples” and “Cream Pie”.
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. This behavior is the same for Python expressions in parameters.
The following code illustrates how to use Python to generate a menu of Object-level digital asset names:
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
Note that you can also put the code that generates the menu into a function in the PythonModule section (e.g. a generateMenu function). You would then call that function with hou.pwd().hdaModule().generateMenu().
Node Initialization Scripts
Houdini supports both Hscript and Python versions of node initialization scripts. Hscript initialization scripts have always been supported. For example, when you put down a geometry object, Houdini searches $HOUDINI_PATH for Hscript script files named scripts/obj/geo.cmd and runs the first one it finds.
These initialization scripts may also be written in Python, and Houdini will also search for scripts/obj/geo.py and run it if it finds it. If Houdini finds both an Hscript (.cmd) version and a Python (.py) version of a node initialization script, it will run the Python version.
Python node initialization scripts can use the kwargs dictionary. It contains one entry: kwargs['node'] contains the hou.Node object that was just created.
Multiple Python Modules in a Digital Asset
If you want to break up your Python code into multiple modules, you can use other HDA sections to store those modules. For example, suppose you want to create a (sub)module named bar. You could put the following code in an HDA section named “bar_PythonModule”:
def foo(): print "This is bar.foo()!"
Then, in the PythonModule section, you would write:
import toolutils bar = toolutils.createModuleFromSection( 'bar', kwargs['type'], 'bar_PythonModule')
bar now appears as a submodule of hdaModule(). So, for example, you could write the following in a button callback to call the foo function:
hou.pwd().hdaModule().bar.foo()