diff --git a/client/ayon_houdini/api/lib.py b/client/ayon_houdini/api/lib.py index dd89d2a922..a7ab8acf9c 100644 --- a/client/ayon_houdini/api/lib.py +++ b/client/ayon_houdini/api/lib.py @@ -21,6 +21,9 @@ from ayon_core.pipeline.create import CreateContext from ayon_core.pipeline.template_data import get_template_data from ayon_core.pipeline.context_tools import get_current_task_entity +from ayon_core.pipeline.workfile.workfile_template_builder import ( + TemplateProfileNotFound +) from ayon_core.tools.utils import PopupUpdateKeys, SimplePopup from ayon_core.tools.utils.host_tools import get_tool_by_name @@ -1389,3 +1392,15 @@ def prompt_reset_context(): update_content_on_context_change() dialog.deleteLater() + + +def start_workfile_template_builder(): + from .workfile_template_builder import ( + build_workfile_template + ) + + log.info("Starting workfile template builder...") + try: + build_workfile_template(workfile_creation_enabled=True) + except TemplateProfileNotFound: + log.warning("Template profile not found. Skipping...") diff --git a/client/ayon_houdini/api/pipeline.py b/client/ayon_houdini/api/pipeline.py index 3f2fe17f18..b0d1c388e6 100644 --- a/client/ayon_houdini/api/pipeline.py +++ b/client/ayon_houdini/api/pipeline.py @@ -79,12 +79,10 @@ def install(self): self._has_been_setup = True - # Set folder settings for the empty scene directly after launch of - # Houdini so it initializes into the correct scene FPS, - # Frame Range, etc. - # TODO: make sure this doesn't trigger when - # opening with last workfile. - _set_context_settings() + # Manually call on_new callback as it doesn't get called when AYON + # launches for the first time on a context, only when going to + # File -> New + on_new() if not IS_HEADLESS: import hdefereval # noqa, hdefereval is only available in ui mode @@ -414,6 +412,7 @@ def _enforce_start_frame(): if hou.isUIAvailable(): import hdefereval + hdefereval.executeDeferred(lib.start_workfile_template_builder) hdefereval.executeDeferred(_enforce_start_frame) else: # Run without execute deferred when no UI is available because diff --git a/client/ayon_houdini/api/workfile_template_builder.py b/client/ayon_houdini/api/workfile_template_builder.py index a3f95f16cf..678cd47d5e 100644 --- a/client/ayon_houdini/api/workfile_template_builder.py +++ b/client/ayon_houdini/api/workfile_template_builder.py @@ -1,18 +1,9 @@ -import os - import hou -from ayon_core.lib import ( - StringTemplate, - filter_profiles, -) -from ayon_core.pipeline import registered_host, Anatomy +from ayon_core.pipeline import registered_host from ayon_core.pipeline.workfile.workfile_template_builder import ( AbstractTemplateBuilder, - PlaceholderPlugin, - TemplateProfileNotFound, - TemplateLoadFailed, - TemplateNotFound + PlaceholderPlugin ) from ayon_core.tools.workfile_template_build import ( WorkfileBuildPlaceholderDialog, @@ -29,121 +20,20 @@ class HoudiniTemplateBuilder(AbstractTemplateBuilder): """Concrete implementation of AbstractTemplateBuilder for Houdini""" - - def get_template_preset(self): - """Unified way how template preset is received using settings. - Method is dependent on '_get_build_profiles' which should return filter - profiles to resolve path to a template. Default implementation looks - into host settings: - - 'project_settings/{host name}/templated_workfile_build/profiles' + def resolve_template_path(self, path, fill_data): + """Allows additional resolving over the template path using custom + integration methods, like Houdini's expand string functionality. - Returns: - dict: Dictionary with `path`, `keep_placeholder` and - `create_first_version` settings from the template preset - for current context. - - Raises: - TemplateProfileNotFound: When profiles are not filled. - TemplateLoadFailed: Profile was found but path is not set. - TemplateNotFound: Path was set but file does not exist. + This only works with ayon-core 0.4.5+ """ + # use default template data formatting + path = super().resolve_template_path(path, fill_data) - host_name = self.host_name - project_name = self.project_name - task_name = self.current_task_name - task_type = self.current_task_type - - build_profiles = self._get_build_profiles() - profile = filter_profiles( - build_profiles, - { - "task_types": task_type, - "task_names": task_name - } - ) - - if not profile: - raise TemplateProfileNotFound(( - "No matching profile found for task '{}' of type '{}' " - "with host '{}'" - ).format(task_name, task_type, host_name)) - - path = profile["path"] - - # switch to remove placeholders after they are used - keep_placeholder = profile.get("keep_placeholder") - create_first_version = profile.get("create_first_version") - - # backward compatibility, since default is True - if keep_placeholder is None: - keep_placeholder = True - - if not path: - raise TemplateLoadFailed(( - "Template path is not set.\n" - "Path need to be set in {}\\Template Workfile Build " - "Settings\\Profiles" - ).format(host_name.title())) - - # Try fill path with environments and anatomy roots - anatomy = Anatomy(project_name) - fill_data = { - key: value - for key, value in os.environ.items() - } - - fill_data["root"] = anatomy.roots - fill_data["project"] = { - "name": project_name, - "code": anatomy.project_code, - } - - result = StringTemplate.format_template(path, fill_data) - if result.solved: - path = result.normalized() - - # I copied the whole thing because I wanted to add some - # Houdini specific code here + # escape backslashes for `expandString` and expand houdini vars + path = path.replace("\\", "\\\\") path = hou.text.expandString(path) - - if path and os.path.exists(path): - self.log.info("Found template at: '{}'".format(path)) - return { - "path": path, - "keep_placeholder": keep_placeholder, - "create_first_version": create_first_version - } - - solved_path = None - while True: - try: - solved_path = anatomy.path_remapper(path) - except KeyError as missing_key: - raise KeyError( - "Could not solve key '{}' in template path '{}'".format( - missing_key, path)) - - if solved_path is None: - solved_path = path - if solved_path == path: - break - path = solved_path - - solved_path = os.path.normpath(solved_path) - if not os.path.exists(solved_path): - raise TemplateNotFound( - "Template found in AYON settings for task '{}' with host " - "'{}' does not exists. (Not found : {})".format( - task_name, host_name, solved_path)) - - self.log.info("Found template at: '{}'".format(solved_path)) - - return { - "path": solved_path, - "keep_placeholder": keep_placeholder, - "create_first_version": create_first_version - } + return path def import_template(self, path): """Import template into current scene. @@ -239,10 +129,10 @@ def delete_placeholder(self, placeholder): placeholder_node.destroy() -def build_workfile_template(*args): +def build_workfile_template(*args, **kwargs): # NOTE Should we inform users that they'll lose unsaved changes ? builder = HoudiniTemplateBuilder(registered_host()) - builder.build_template() + builder.build_template(*args, **kwargs) def update_workfile_template(*args):