Is APEX animate state extensible?

   413   4   2
User Avatar
Member
714 posts
Joined: Aug. 2019
Offline
For example, if I'd like to add a new panel similar to this:



But with some buttons to invoke my custom scripts. Is it possible to do so without directly changing the python code shipped with Houdini?

Attachments:
Enter_a_filename.png (219.7 KB)

User Avatar
Staff
47 posts
Joined: March 2020
Offline
Hey! More support will come in the future but you can extend those parameter windows through the use of shelf tools.
Here's an example script that will add a new tab and a button.

import hou
import apex
from apex.ui.animationhudwidgets import TAB_WIDGET

sv = hou.ui.paneTabOfType(hou.paneTabType.SceneViewer)

# Send a state command to retrieve the viewer state object
kwargs = {}
sv.runStateCommand('getState', kwargs)
state = kwargs["state"]

# Get the Parms window
hud_window = state.hud_channel_panel.hud_window

# Add the tab
TAB_NAME = "scripts"
hud_window.addTab(TAB_WIDGET, TAB_NAME, "Scripts")

# Create the callback function
def helloWorld():
    print("Hello World!")

# The hud window's class is defined in `$HFS/houdini/python3.13libs/statehud/window.py`.
# All the functions that bind parameters to the window start with `bind`.
hud_window.bindButton(
    name = "hello_world",
    label = "Hello World",
    callback = helloWorld,
    tab_widget_name = TAB_WIDGET,
    tab_name = TAB_NAME,
    label_width = 70
)
Edited by jonathangardner - March 23, 2026 12:58:35
User Avatar
Member
714 posts
Joined: Aug. 2019
Offline
Thank you for the answer!

How do I do actual animation related things in the callback function though? (e.g. change a channel prim's curve, move an abstract control, etc...) Are these things available from the state?
Edited by raincole - March 23, 2026 15:20:53
User Avatar
Staff
47 posts
Joined: March 2020
Offline
The etc.. bit scares me because that is a much bigger question that will need a lot more documentation than we have ready.
There will be a proper guide for extending the state in the future but I can give you a couple of sample functions for the specific examples you gave.


Setting a control's parameters

import apex
from apex.control_2 import getParmsForControls, controlRigPath

def setControlTranslation(state, ctrl_path, frame, value=hou.Vector3(0,0,0)):
  # Get the animation scene from the state
  scene = state.scene

  # Get the rig path from the control path then grab the rig's control manager from the scene data
  # The control path is f"{rig_path}/{control_name}"
  rig_path = controlRigPath(ctrl_path)
  rig_control_manager = scene.getData(f"{rig_path}/control_manager")

  # Get the control mapping
  ctrl_mapping = rig_control_manager.getControlMapping(ctrl_path)

  # If the control's 't' parameter is not set, return early.
  # The control mapping also contains entries for r, s, x, and y
  if ctrl_mapping.t == "":
    return False
  
  # Get the rig from the scene's data
  rig = scene.getData(rig_path)
  if rig is None:
      return False
  
  # Set the new value for the t parameter into the rig's parameter dictionary
  rig.graph_parms[ctrl_mapping.t] = value
  
  # Grab the animation binding for the rig and set a key for the parameter
  binding = scene.getData(f"{rig_path}/animbinding")
  binding.setKeysFromDict(scene, frame, pattern=ctrl_mapping.t, force_key=True)
  
  # Run the scene callbacks on the viewer state to update the geometries
  state.runSceneCallbacks()
  return True


Updating the channel primitives for a rig

import apex
from apex.control_2 import getParmsForControls, controlRigPath

def updateRigChannelPrims(state, rig_path):
  # Get the animation scene from the state
  scene = state.scene
  
  # Grab the animation binding for the rig
  binding = scene.getData(f"{rig_path}/animbinding")

  # Grab the path in the scene data for the geometry containing the given rigs channels on the current animation layer
  channels_path = binding.activeLayerGeoPath()

  # Grab a copy of the channel primitives from the scene
  channels_geo = scene.getData(channels_path).freeze()

  #
  # Make your alterations to the geometry here.
  # Each primitive on this geometry is a channel primitive matching the parameter names from the rig.
  # Vector parameters on rig have 3 associated channel primitives, one for each component.
  #

  # Set the new channel primitives back into the scene
  scene.getData(channels_path, channels_geo)
  
  # Run the scene callbacks on the viewer state to update the geometries
  state.runSceneCallbacks()
  return True


Please note that neither of these examples set add undo blocks. Those need to be custom made.

A few other things you may find useful:
# The path for the latest selected control in the viewer state (the one that shows up in the channels widget).
state.primary_control

# All of the currently selected controls
state.control_paths

# Sets the list of selected controls with a pick modifier.
state._selectControls(...)

# Set keys or pending values for each of the currently selected controls.
state.setKeysOrPending(...)
User Avatar
Member
714 posts
Joined: Aug. 2019
Offline
Thank you very much for the answer again! I'll try it out.

Just one more clarifying question: you said

Please note that neither of these examples set add undo blocks. Those need to be custom made.

It means that I have to write an undo class that can be passed into hou.undos.add(), right.
Edited by raincole - March 23, 2026 23:08:47
  • Quick Links