= 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 [Hom: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 [Hom:hou.Node] objects, so you can use the convenience method [Hom: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: {{{ #!python 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 [Hom:hou.Node] for the new HDA instance. The following table lists the contents of the `kwargs` dictionary for the various event handler types: table>> tr>> th>> Key name th>> Value th>> Event Handlers tr>> td>> `node` td>> The [Hom:hou.Node] instance for the digital asset. td>> OnCreated, OnUpdated, OnInputChanged, OnNameChanged, OnDeleted tr>> td>> `type` td>> The [Hom:hou.NodeType] defined by the digital asset. td>> OnLoaded, PreFirstCreate, OnCreated, OnUpdated, OnInputChanged, OnNameChanged, OnDeleted, PostLastDelete, PythonModule tr>> td>> `old_name` td>> A string containing the the old name of the HDA instance. td>> OnNameChanged tr>> td>> `input_index` td>> The index of the input that was connected/disconnected. td>> OnInputChanged NOTE: 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: {{{ #!python def onCreated(node): hou.ui.displayMessage("You created " + node.path()) }}} you could call it from the OnCreated Python event handler with: {{{ #!python 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: {{{ #!hscript python -c "hou.node('$arg1').hdaModule().onCreated(hou.node('$arg1'))" }}} 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 [Hom:hou.Node] and [Hom:hou.Parm] objects corresponding to the parameter. So, you could write {{{ #!python kwargs['node'].hdaModule().onButtonPress() }}} to call the onButtonPress() function in the PythonModule section. NOTE: You might see the above code written as `hou.pwd().hdaModule().onButtonPress()`. Using [Hom: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. 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. Menu parameters whose contents are generated via a script can also use PythonModule functions to generate the menu. In the current version of Houdini, menu scripts cannot be written directly in Python. Instead, you must use invoke the Python code from Hscript and use Hscript to echo the menu contents. Recall that Hscript menu generation scripts output a whitespace-separate list of strings, with two strings per menu entry. Each subsequent pair of strings contain the internal name for the menu entry in the first part of the pair, and the label in the second part. 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". The following code illustrates how to use Python to generate the menu. The following functions would go in the PythonModule section: {{{ #!python def flattenMenuForHoudini(menu): """Take a sequence of pairs and return a space-separated string that Houdini understands.""" return " ".join(repr(e[0]) + " " + repr(e[1]) for e in menu) def generateMenu(): """This function is called from the hscript menu generation script.""" return flattenMenuForHoudini((('one', 'Apples'), ('two', 'Cream Pie'))) }}} This Hscript menu script calls the functions above. Note that the strreplace prevents Hscript from stripping single quotes for when the menu labels contain spaces. {{{ #!hscript echo `strreplace(pythonexprs("hou.pwd().hdaModule().generateMenu()"), "'", "\'")` }}} == 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": {{{ #!python def foo(): print "This is bar.foo()!" }}} Then, in the PythonModule section, you would write: {{{ #!python 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: {{{ #!python hou.pwd().hdaModule().bar.foo() }}}