Skip to content

creator_node_shelves

Library to register OpenPype Creators for Houdini TAB node search menu.

This can be used to install custom houdini tools for the TAB search menu which will trigger a publish instance to be created interactively.

The Creators are automatically registered on launch of Houdini through the Houdini integration's host.install() method.

create_interactive(creator_identifier, **kwargs)

Create a Creator using its identifier interactively.

This is used by the generated shelf tools as callback when a user selects the creator from the node tab search menu.

The kwargs should be what Houdini passes to the tool create scripts context. For more information see: https://www.sidefx.com/docs/houdini/hom/tool_script.html#arguments

Parameters:

Name Type Description Default
creator_identifier str

The creator identifier of the Creator plugin to create.

required
Return

list: The created instances.

Source code in client/ayon_houdini/api/creator_node_shelves.py
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
def create_interactive(creator_identifier, **kwargs):
    """Create a Creator using its identifier interactively.

    This is used by the generated shelf tools as callback when a user selects
    the creator from the node tab search menu.

    The `kwargs` should be what Houdini passes to the tool create scripts
    context. For more information see:
    https://www.sidefx.com/docs/houdini/hom/tool_script.html#arguments

    Args:
        creator_identifier (str): The creator identifier of the Creator plugin
            to create.

    Return:
        list: The created instances.

    """
    host = registered_host()
    context = CreateContext(host)
    creator = context.manual_creators.get(creator_identifier)
    if not creator:
        raise RuntimeError("Invalid creator identifier: {}".format(
            creator_identifier)
        )

    # TODO Use Qt instead
    result, variant = hou.ui.readInput(
        "Define variant name",
        buttons=("Ok", "Cancel"),
        initial_contents=creator.get_default_variant(),
        title="Define variant",
        help="Set the variant for the publish instance",
        close_choice=1
    )

    if result == 1:
        # User interrupted
        return

    variant = variant.strip()
    if not variant:
        raise RuntimeError("Empty variant value entered.")

    # TODO: Once more elaborate unique create behavior should exist per Creator
    #   instead of per network editor area then we should move this from here
    #   to a method on the Creators for which this could be the default
    #   implementation.
    pane = stateutils.activePane(kwargs)
    is_null_created = False
    if isinstance(pane, hou.NetworkEditor):
        pwd = pane.pwd()
        project_name = context.get_current_project_name()
        folder_path = context.get_current_folder_path()
        task_name = context.get_current_task_name()
        folder_entity = ayon_api.get_folder_by_path(
            project_name, folder_path
        )
        task_entity = ayon_api.get_task_by_name(
            project_name, folder_entity["id"], task_name
        )
        product_name = creator.get_product_name(
            project_name=context.get_current_project_name(),
            folder_entity=folder_entity,
            task_entity=task_entity,
            variant=variant,
            host_name=context.host_name,
        )

        tool_fn = CATEGORY_GENERIC_TOOL.get(pwd.childTypeCategory())
        if tool_fn is not None:
            out_null = tool_fn(kwargs, "null")
            # TODO: For whatever reason the code does not continue if the
            #  user cancels the operation with escape; yet also no error seems
            #  to be raised.
            if out_null:
                out_null.setName("OUT_{}".format(product_name),
                                 unique_name=True)
                is_null_created = True

    before = context.instances_by_id.copy()

    # Create the instance
    context.create(
        creator_identifier=creator_identifier,
        variant=variant,
        pre_create_data={"use_selection": True}
    )

    # For convenience we set the new node as current since that's much more
    # familiar to the artist when creating a node interactively. However
    # we do not select it if the user used the "null" place down functionality
    # TODO Allow to disable auto-select in studio settings or user preferences
    #  allow to choose:
    #  - always select instance node
    #  - never select instance node
    #  - select null if created else select instance node (current default)
    after = context.instances_by_id
    new = set(after) - set(before)
    if new and not is_null_created:
        # Select the new instance
        for instance_id in new:
            instance = after[instance_id]
            node = hou.node(instance.get("instance_node"))
            node.setCurrent(True)

    return list(new)

install()

Install the Creator plug-ins to show in Houdini's TAB node search menu.

This function is re-entrant and can be called again to reinstall and update the node definitions. For example during development it can be useful to call it manually: >>> from ayon_houdini.api.creator_node_shelves import install >>> install()

Returns:

Name Type Description
list

List of hou.Tool instances

Source code in client/ayon_houdini/api/creator_node_shelves.py
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
def install():
    """Install the Creator plug-ins to show in Houdini's TAB node search menu.

    This function is re-entrant and can be called again to reinstall and
    update the node definitions. For example during development it can be
    useful to call it manually:
        >>> from ayon_houdini.api.creator_node_shelves import install
        >>> install()

    Returns:
        list: List of `hou.Tool` instances

    """

    host = registered_host()

    # Store the filepath on the host
    # TODO: Define a less hacky static shelf path for current houdini session
    filepath_attr = "_creator_node_shelf_filepath"
    filepath = getattr(host, filepath_attr, None)
    if filepath is None:
        f = tempfile.NamedTemporaryFile(prefix="houdini_creator_nodes_",
                                        suffix=".shelf",
                                        delete=False)
        f.close()
        filepath = f.name
        setattr(host, filepath_attr, filepath)
    elif os.path.exists(filepath):
        # Remove any existing shelf file so that we can completey regenerate
        # and update the tools file if creator identifiers change
        os.remove(filepath)

    icon = get_ayon_icon_filepath()
    tab_menu_label = os.environ.get("AYON_MENU_LABEL") or "AYON"

    # Create context only to get creator plugins, so we don't reset and only
    # populate what we need to retrieve the list of creator plugins
    create_context = CreateContext(host, reset=False)
    create_context.reset_current_context()
    create_context._reset_creator_plugins()

    log.debug("Writing OpenPype Creator nodes to shelf: {}".format(filepath))
    tools = []

    with shelves_change_block():
        for identifier, creator in create_context.manual_creators.items():

            # Allow the creator plug-in itself to override the categories
            # for where they are shown with `Creator.get_network_categories()`
            if not hasattr(creator, "get_network_categories"):
                log.debug("Creator {} has no `get_network_categories` method "
                          "and will not be added to TAB search.")
                continue

            network_categories = creator.get_network_categories()
            if not network_categories:
                continue

            key = "ayon_create.{}".format(identifier)
            log.debug(f"Registering {key}")
            script = CREATE_SCRIPT.format(identifier=identifier)
            data = {
                "script": script,
                "language": hou.scriptLanguage.Python,
                "icon": icon,
                "help": "Create Ayon publish instance for {}".format(
                    creator.label
                ),
                "help_url": None,
                "network_categories": network_categories,
                "viewer_categories": [],
                "cop_viewer_categories": [],
                "network_op_type": None,
                "viewer_op_type": None,
                "locations": [tab_menu_label]
            }
            label = "Create {}".format(creator.label)
            tool = hou.shelves.tool(key)
            if tool:
                tool.setData(**data)
                tool.setLabel(label)
            else:
                tool = hou.shelves.newTool(
                    file_path=filepath,
                    name=key,
                    label=label,
                    **data
                )

            tools.append(tool)

    # Ensure the shelf is reloaded
    hou.shelves.loadFile(filepath)

    return tools

shelves_change_block()

Write shelf changes at the end of the context.

Source code in client/ayon_houdini/api/creator_node_shelves.py
152
153
154
155
156
157
158
159
@contextlib.contextmanager
def shelves_change_block():
    """Write shelf changes at the end of the context."""
    hou.shelves.beginChangeBlock()
    try:
        yield
    finally:
        hou.shelves.endChangeBlock()