|On this page|
You can write a script that acts like a tool (that is, uses the current selection or asks the user for a selection, or lets the user place something in the viewer), or a simple action that’s performed immediately when the user clicks the item and doesn’t require any interaction.
You may not need to write a script at all, if all you want is a shelf item that creates a certain node/asset. When you drag a node onto the shelf, Houdini automatically creates a shelf item with a generic script to create that type of node. See how to customize the shelf for more information.
While running shelf scripts Houdini is still "Live". This is most noticeable if you acquire DOP Objects and then change the DOP Network. After changing the DOP Network, the current frame will recook and invalidate your DOP Objects.
To avoid recooking the current frame, you can call hou.setSimulationEnabled() to disable the simulation for the duration of your operation. Make sure you record the initial simulation state (hou.simulationEnabled()) and restore it, however.
The tool script can access extra information in a global dictionary variable called
kwargs. For example, to check if the user selected the tool in a viewport, use
Houdini’s shelf uses a convention where clicking a tool with the ⌃ Ctrl key held turns the tool into an immediate action. For example, the Camera tool on the Lights and Cameras shelf tab lets you place a camera in the scene, but ctrl-clicking it immediately creates a camera from the current view, instead of asking you to place the camera. Similarly, ctrl-clicking the Sphere tool on the Create shelf tab immediately places the sphere at the origin instead of asking you to place it.
If you want to use this convention in your own tool, you can use the value of
kwargs["ctrlclick"] to see if the user ctrl-clicked your tool.
When Houdini calls your script, it sets a special dictionary variable named
kwargs in the script’s context. This dictionary contains useful information. You can see its contents by creating a tool and giving it the following script…
# Convert kwargs to a string and pass it # to the displayMessage function. hou.ui.displayMessage(str(kwargs))
While you're debugging a script, you can display alert windows using…
The default shelf items created when you drag nodes onto the shelf are generated by the code in
$HFS/houdini/python2.7libs/defaulttools.pyand generally just call
xxxtoolutils.genericTool(). You may want to look at these scripts to learn how to write a simple tool script.
$HFS/houdini/python2.5libs/defaulttools.pycontain some useful high-level functions that might make writing tool scripts easier.
View the scripts of stock items on the shelf, such as the Delete action on the Modify shelf tab, for useful techniques and bits of code.
To get the scene viewer pane object…
import toolutils scene_view = toolutils.sceneViewer()
To get the current viewport in the active viewer:
To get the network location of the scene viewer:
import toolutils scene_viewer = toolutils.sceneViewer() path = scene_viewer.pwd()
Detecting what network context the tool is called in
You might want to make a tool that works inside two or more different network types. For example, the Delete action on the Modify shelf tab works at both the object level, where it deletes the selected object, and at the geometry level, where it creates a Blast surface node.
There are a few ways you could write this part of your script. One is to follow the code used by the Delete action’s script:
import toolutils # Find out current context. # Returns the active pane when the tool/action was called. # If there is not an active pane when the tool/action is called, # this returns None. active_pane = toolutils.activePane(kwargs) # If the pane is a context viewer, treat it like a scene viewer. if active_pane is not None and active_pane.type() == hou.paneTabType.ContextViewer: active_pane = active_pane.sceneViewer() # If there is no active pane, or the active pane is not a # scene viewer, raise an error. if active_pane is None or active_pane.type() != hou.paneTabType.SceneViewer: raise hou.Error("The tool was not invoked in the scene viewer.") # Get the network context of the viewer. child_type = active_pane.pwd().childTypeCategory() if child_type == hou.objNodeTypeCategory(): ... elif child_type == hou.sopNodeTypeCategory(): ... elif child_type == hou.dopNodeTypeCategory(): ... elif child_type == hou.popNodeTypeCategory(): ...
Getting a selection
You can use methods on the scene viewer pane object to get a selection. If the current selection matches the selection type you're asking for, the method will return immediately. Otherwise, it will prompt the user for a new selection.
import toolutils # The toolutils.selectionPrompt() utility function # gets the standard prompt string for a given node type. # For example, this... prompt = toolutils.selectionPrompt(hou.dopNodeTypeCategory()) # will return something like # "Select dynamic objects. Press Enter to accept selection." # The selectXXX methods of the hou.SceneViewer class # prompt the user for a selection in the viewer. # The argument is the text of the prompt that appears in # the status line. # The result is usually a tuple of nodes, but the # selectGeometry() method returns a GeometrySelection object. scene_viewer = toolutils.sceneViewer() selected_objects = scene_viewer.selectObjects(prompt)
If you try to test the scene_viewer.selectXXX methods in the Python Shell window, it may seem to freeze or not do anything. This is because the prompt only appears when the mouse is over the viewer.
If you're writing your own custom prompt text, remember to tell the user to press Enter to finish the selection.
Limiting which objects the user can select
The hou.SceneViewer.selectObjects() method has an optional
allowed_types keyword argument that lets you supply a list of object type names. If you supply this argument, the method will only accept selections of these types, and during interactive selection the user won’t be able to select objects that aren’t of the allowed type(s).
The list of allowed types uses the internal names of nodes. To see a node type’s internal name, right-click an instance of the node type in the network editor and choose Type properties. At the top of the window the internal name is listed after Operator type:.
For example, to ask for a camera selection…
# Note: internal name of the Camera object is "cam" selected_objects = scene_viewer.selectObjects("Select a camera and press Enter", allowed_types = ["cam"])
Prompting the user for a position, multiple positions, or a path
To ask the user for a location, as when you place new geometry using the tools on the Create shelf tab:
import toolutils # The selectPositions() method of the hou.SceneViewer object lets # you prompt the user for one or more positions (using the # number_of_positions keyword), optionally connecting multiple # positions (for example when prompting for a path), displaying # a bounding box (when prompting to place geometry), etc. # The result is a tuple of Vector3 objects. scene_viewer = toolutils.sceneViewer() positions = scene_viewer.selectPositions(prompt='Click to specify a position', number_of_positions = 1, connect_positions = True, show_coordinates = True, bbox = BoundingBox(), position_type = positionType.WorldSpace, icon = None, label = None)
Manipulating the current viewport
To get the current viewport…
import toolutils # Get the scene viewer scene_view = toolutils.sceneViewer() # Get the current viewport viewport = scene_view.curViewport()
# Get the viewport's settings object settings = viewport.settings()
Among the most useful methods on the settings object are
setViewTransform(), which get and set the viewport’s transformation matrix respectively. You can
To set the viewport to look through a camera, use…
To get the camera node that a viewport is currently looking through…
# Returns None if not looking through a camera viewport.settings().camera()
There’s a special method on viewports to copy their view to a camera or light, so you can copy the ctrl-click behavior of the standard tools on the Lights and Cameras shelf tab…
Modifying node parameters
To modify the current selection, first get the selection as shown above. If the user already has an appropriate selection, you’ll get that, or Houdini will prompt her for a selection.
If you want to make an item that always modifies the same node, instead of the currently selected one (for example, a button that toggles the Activate parameter on an important node), just use the standard function to get a node by name…
my_cam = hou.node("/obj/cam1")
To modify a node’s parameters…
# Get a list of all parameters on a node all_parms = my_cam.parms() # Get the current value of a parameter current_lookat = my_cam.parm("lookat").get() # Set the value of a parameter my_cam.parm("lookat").set("/obj/torus1")
The argument to the
Node.parm() method must be the internal name of the parameter. To find out the internal name of a parameter, hover over its label in the parameter editor. The internal name appears in a tooltip.