Setting HDA tab submenu name via python?

   1752   3   2
User Avatar
Member
59 posts
Joined: April 2006
Offline
I am writing a script that creates an OTL entirely programmatically via Python, and I need it to fill out a number of the fields that a user would ordinarily fill out using the Type Properties editor when creating an OTL interactively. Specifically, I need to set the tab submenu name string.

By exploring an existing OTL I can see that when the Interactive/Shelf Tools/Context/TAB Submenu Path field of the Type Properties editor is set to something custom (i.e., not the default "Digital Asset"), the corresponding hou.HDADefinition object contains a hou.HDASection called "Tools.shelf", and its contents is a string of XML containing "<toolSubmenu>MyTools</toolSubmenu>" amidst all the other XML tags.

But a brand new OTL has no such section. What is the method for setting this field programmatically in Python? I don't think reverse engineering the XML text is a good approach, and I can't imagine SideFX expects us to do that in any case. But I can't find anything in the API docs that describes how to set any of the options in the "Interactive" portion of the Type Properties editor for an OTL in general, or the tab submenu string in particular.
User Avatar
Member
1919 posts
Joined: Nov. 2006
Online
I believe the default tool stuff (and probably other stuff now too since there's more interactivey things) is handled automagically via the Type Properties dialog on the first save and there is no good way of doing this still as I recall from having to do the same thing many many years ago.

I dug up some old code and it goes something like below. This code also supported copying assets and renaming them or something so it might do more than necessary for your case:

import defaulttools

def create_default_tool(asset_definition):
    node_type = asset_definition.nodeType()

    category_name = node_type.category().name().lower()

    default_tool_name = hou.shelves.defaultToolName(
        category_name,
        node_type.name()
    )

    # Try to find an existing tool of this name.  When we copy
    # the hda and it has a default, internal tool, that tool
    # gets copied too.  We need to delete it before we can
    # create and embed it into the asset.
    existing_tool = hou.shelves.tools().get(default_tool_name)
    if existing_tool:
        existing_tool.destroy()

    # Construct a temp .shelf file path.
    temp_file_path = os.path.join(tempfile.gettempdir(), "temp.shelf")

    # Create a temporary tool.
    temp_tool = defaulttools.createDefaultHDATool(
        temp_file_path,
        node_type,
        default_tool_name
    )

    # Set the HDA tool variables to the tool.
    defaulttools.setHDAToolVariables(temp_tool, asset_definition)

    # Grab the contents of the tool file.
    contents = hou.readFile(temp_file_path)

    # Store it in the asset definition.
    asset_definition.addSection("Tools.shelf", contents)

    # Look for a tool named "$HDA_DEFAULT_TOOL".
    default_tool = hou.shelves.tools().get("$HDA_DEFAULT_TOOL")

    # If it exists we need to destroy it or it will cause problems.
    if default_tool is not None:
        default_tool.destroy()

    # Remove the temp file.
    os.remove(temp_file_path)
Graham Thompson, Technical Artist @ Rockstar Games
User Avatar
Member
59 posts
Joined: April 2006
Offline
Wow. That's a lot of code to just set the TAB submenu name! I appreciate you sharing the code. I will give it a try. Thank you very much!
User Avatar
Member
30 posts
Joined: May 2019
Offline
For completeness, here is my implementation for creating new, and editing an existing tool.

The tab menu is like a regular shelf. There are several places where these shelves are saved: in the houdini session, in the default directory, and possibly in other directories that are in HOUDINI_PATH

Initially, when the HDA is created, it doesn't have a tool defined. Instead there is a tool in the houdini session.
Not sure at which point the default tool is created. Later when you refer to a definition, it will have a tool with the default name defined.

The tab menu path is the tool's
tool.toolMenuLocations()
.

Also this is undocumented, but you can specify the location in createDefaultHDATool, note the locations arg:
defaulttools.createDefaultHDATool(
        temp_file_path,
        node_type,
        default_tool_name,
        locations=(menu,),
    )


def create_default_tool(definition: hou.HDADefinition,
                        menu: str):
    """ Create a new default tool for this definition.
    """
    node_type = definition.nodeType()

    # category_name = node_type.category().name().lower()

    default_tool_name = hou.shelves.defaultToolName(menu, node_type.name())

    # Try to find an existing tool of this name.  When we copy
    # the hda and it has a default, internal tool, that tool
    # gets copied too.  We need to delete it before we can
    # create and embed it into the asset.
    existing_tool = hou.shelves.tools().get(default_tool_name)
    if existing_tool:
        existing_tool.destroy()

    # Construct a temp .shelf file path.
    temp_file_path = os.path.join(tempfile.gettempdir(), "temp.shelf")

    # Create a temporary tool.
    temp_tool = defaulttools.createDefaultHDATool(
        temp_file_path,
        node_type,
        default_tool_name,
        locations=(menu,),
    )

    # Set the HDA tool variables to the tool.
    defaulttools.setHDAToolVariables(temp_tool, definition)

    # Grab the contents of the tool file.
    contents = hou.readFile(temp_file_path)

    # Store it in the asset definition.
    definition.addSection("Tools.shelf", contents)

    # Look for a tool named "$HDA_DEFAULT_TOOL".
    default_tool = hou.shelves.tools().get("$HDA_DEFAULT_TOOL")

    # If it exists we need to destroy it or it will cause problems.
    if default_tool is not None:
        default_tool.destroy()

    # Remove the temp file.
    os.remove(temp_file_path)


def set_default_tool_location(definition: hou.HDADefinition,
                              menu: str):
    """ Set default location on existing tool.
    """
    tool = (definition.tools().get(
        hou.shelves.defaultToolName(
            definition.nodeTypeCategory().name(),
            definition.nodeTypeName())))

    if tool is None:
        raise ValueError("No shelf tool found for definition {}".format(definition))

    print(tool.setToolLocations((menu,)))


def get_menu_location(definition, default):
    tool = (definition.tools().get(
        hou.shelves.defaultToolName(
            definition.nodeTypeCategory().name(),
            definition.nodeTypeName())))

    if tool is None:
        return default

    return tool.toolMenuLocations()
  • Quick Links