Houdini 20.0 Python scripting

Python states supporting undo

How to make actions within your custom state undo-able.

On this page


Houdini automatically generates items on the undo stack for many scripted changes, such as editing the value of a parameter. However, these automatic undo items are quite fine grained.

You can specify what goes onto the undo stack in your state using the hou.undos module and methods on the SceneViewer.

Preventing scripted actions from appearing on the undo stack

Sometimes you might want to prevent scripted actions from being added to the undo stack. For example, your asset might have hidden parameters that only exist to support your custom state interaction. You don’t want changes to these hidden parameters to clog the undo stack.

Wrap the actions you don’t want on the undo stack in a hou.undos.disabler() manager:

with hou.undos.disabler():

Wrapping a series of user interactions in an undo block

In a custom state, you will often perform scripted actions across many function calls that you want to wrap up into a single undo-able action.

For example, in a custom Python state for an asset you might continuously change a parameter on the node as the user drags the mouse in the viewer. You would want each drag to be a single undo-able action, but by default every single parameter value change within a drag will be an undoable item, making it tedious and annoying to undo an entire drag.

When the user starts performing an action which you want to be undo-able, call hou.SceneViewer.beginStateUndo. The beginStateUndo() method takes a string argument which is the label you want to appear to the user for the undo-able action.

When the action is finished, call hou.SceneViewer.endStateUndo. All scripting between the two calls is bundled into a single undo-able action on the undo stack.

class DrawPoints(object):    
    # ...

    def _start(self):
        if not self._pressed:
            self.scene_viewer.beginStateUndo("Add point")
            self._index = self._insert_point()
        self._pressed = True

    def _finish(self):
        if self._pressed:
        self._pressed = False

    def onMouseEvent(self, kwargs):
        node = self._node = kwargs["node"]
        ui_event = kwargs["ui_event"]
        device = ui_event.device()
        origin, direction = ui_event.ray()

        # Find intersection with geometry or ground
        intersected = -1
        inputs = node.inputs()
        # Only try intersecting geometry if this node has input
        if inputs and inputs[0]:
            geometry = inputs[0].geometry()
            intersected, position, _, _ = stateutils.sopGeometryIntersection(geometry, origin, direction)
        if intersected < 0:
            position = stateutils.cplaneIntersection(self.scene_viewer, origin, direction)

        # Create/move point if LMB is down
        if device.isLeftButton():
            self._set(self._index, position)

    def onInterrupt(self, kwargs):

    # ...

Wrapping serial statements in an undo block

Within a single function, you will often perform a series of scripted actions that you want to appear to the user as one undo-able action. For example, in a tool script the creation of a new SOP node might involve several steps, including creating a container object, wiring the node into an existing network, moving the node in the network editor layout, and so on.

Wrap the actions you want to group together as one undo-able action in a hou.undos.group() manager. The group() function takes a string argument which is the label you want to appear to the user for the undo-able action:

with hou.undos.group("Creation of My Asset"):
    # Create a new container object for the asset
    container = hou.node("/obj").createNode("geo")

Python scripting

Getting started

Next steps


  • hou

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

Guru level

Python viewer states

Python viewer handles

Plugin types