diff --git a/client/ayon_nuke/api/pipeline.py b/client/ayon_nuke/api/pipeline.py index 6bcf1d36f9..35d27a1116 100644 --- a/client/ayon_nuke/api/pipeline.py +++ b/client/ayon_nuke/api/pipeline.py @@ -17,7 +17,7 @@ from ayon_core.host.interfaces import SaveWorkfileContext -from ayon_core.lib import register_event_callback, Logger +from ayon_core.lib import Logger from ayon_core.pipeline import ( register_loader_plugin_path, register_creator_plugin_path, @@ -142,9 +142,6 @@ def install(self): register_inventory_action_path(INVENTORY_PATH) register_workfile_build_plugin_path(WORKFILE_BUILD_PATH) - # Register AYON event for workfiles loading. - register_event_callback("workio.open_file", check_inventory_versions) - def setup_ui_callbacks_and_menu(self): """Setup AYON menus.""" if not nuke.GUI: @@ -166,7 +163,6 @@ def update_context_data(self, data, changes): root_node = nuke.root() set_node_data(root_node, ROOT_DATA_KNOB, data) - def _before_workfile_save( self, save_workfile_context: SaveWorkfileContext ) -> None: @@ -226,11 +222,8 @@ def add_nuke_callbacks(project_settings: dict = None): nuke_settings = project_settings["nuke"] - # Set all workfile settings.' nuke.addOnCreate(on_root_create, nodeClass="Root") - # set checker for last versions on loaded containers - nuke.addOnScriptLoad(check_inventory_versions) - # fix ffmpeg settings on script + nuke.addOnScriptLoad(on_script_load) # set checker for last versions on loaded containers @@ -245,33 +238,40 @@ def add_nuke_callbacks(project_settings: dict = None): def on_root_create() -> None: - """Callback function for on script create.""" - # set apply all workfile settings on script load and save + """Callback function for on root node create.""" + log.info("On root create...") + # adding favorites to file browser workfile_settings = WorkfileSettings() - on_script_create_settings = ( - workfile_settings.project_settings - ["nuke"] - ["workfile_callbacks"] - ["on_script_create"] - ) + workfile_settings.set_favorites() - if on_script_create_settings["set_resolution"]: - workfile_settings.reset_resolution() + # Root created callback is also triggered on scene load because that also + # creates a new root node. So we check if current file is None to run + # logic that we only want to apply on new scene creation. + if current_file() is None: + log.info("On new file..") + on_script_create_settings = ( + workfile_settings.project_settings + ["nuke"] + ["workfile_callbacks"] + ["on_script_create"] + ) - if on_script_create_settings["set_frame_range"]: - workfile_settings.reset_frame_range_handles() + if on_script_create_settings["set_resolution"]: + workfile_settings.reset_resolution() - if on_script_create_settings["set_colorspace"]: - workfile_settings.set_colorspace() + if on_script_create_settings["set_frame_range"]: + workfile_settings.reset_frame_range_handles() - # adding favorites to file browser - workfile_settings.set_favorites() - # template builder callbacks - start_workfile_template_builder() + if on_script_create_settings["set_colorspace"]: + workfile_settings.set_colorspace() + + # template builder callbacks + start_workfile_template_builder() def on_script_load() -> None: """Callback function for on script load.""" + log.info("On script load...") # fix ffmpeg settings on script if nuke.env["LINUX"]: nuke.tcl('load ffmpegReader') @@ -298,6 +298,9 @@ def on_script_load() -> None: if on_script_load_settings["set_colorspace"]: workfile_settings.set_colorspace() + # set checker for last versions on loaded containers + check_inventory_versions() + def reload_config(): """Attempt to reload pipeline at run-time. diff --git a/client/ayon_nuke/api/workio.py b/client/ayon_nuke/api/workio.py index aec89b59ae..28b77e4b67 100644 --- a/client/ayon_nuke/api/workio.py +++ b/client/ayon_nuke/api/workio.py @@ -1,10 +1,22 @@ """Host API required Work Files tool""" import os +import contextlib import nuke import shutil from .constants import ASSIST +@contextlib.contextmanager +def _no_create_callbacks(): + """Context manager to temporarily disable `nuke.onCreate` callbacks.""" + callbacks = nuke.onCreates.copy() + try: + nuke.onCreates.clear() + yield + finally: + nuke.onCreates.update(callbacks) + + def file_extensions(): return [".nk"] @@ -21,40 +33,69 @@ def save_file(filepath): nuke.Root().setModified(False) +def _get_autosave_filepath(filepath: str) -> str | None: + """Query autosave file path for a given file path, by evaluating the + autosave name preferences with root name temporarily set to the given + file path. + + This allows us to get the autosave path for a file that is not currently + open in Nuke. + """ + root = nuke.Root() + original_name = root.name() + root["name"].setValue(filepath) + try: + autosave = nuke.toNode("preferences")["AutoSaveName"].evaluate() + if os.path.isfile(autosave): + return autosave + return None + finally: + root["name"].setValue(original_name) + + def open_file(filepath): def read_script(nuke_script): if not ASSIST: - nuke.scriptClear() + with _no_create_callbacks(): + nuke.scriptClear() nuke.scriptReadFile(nuke_script) - nuke.Root()["name"].setValue(nuke_script) - nuke.Root()["project_directory"].setValue(os.path.dirname(nuke_script)) - nuke.Root().setModified(False) + + root = nuke.Root() + root["name"].setValue(nuke_script) + root["project_directory"].setValue(os.path.dirname(nuke_script)) + root.setModified(False) + + # Above way of loading script does not trigger onCreate() nor + # onScriptLoad() callback, so we need to call it manually here + # after root name has been set so the callbacks behave similar + # to how they would if user would do File > Open or on Nuke + # launch with a comp script file. + nuke.onCreate() + nuke.onScriptLoad() else: nuke.scriptOpen(nuke_script) filepath = filepath.replace("\\", "/") - # To remain in the same window, we have to clear the script and read - # in the contents of the workfile. - # Nuke Preferences can be read after the script is read. - read_script(filepath) - - if nuke.GUI: - autosave = nuke.toNode("preferences")["AutoSaveName"].evaluate() - autosave_prmpt = "Autosave detected.\n" \ - "Would you like to load the autosave file?" # noqa - if os.path.isfile(autosave) and nuke.ask(autosave_prmpt): + # Before opening the file, see if it has an autosave file and ask the user + # if they want to load it instead. + if nuke.GUI and (autosave := _get_autosave_filepath(filepath)): + if nuke.ask( + "Autosave detected.\n" + "Would you like to load the autosave file?" + ): try: # Overwrite the filepath with autosave shutil.copy(autosave, filepath) - # Now read the (auto-saved) script again - read_script(filepath) except shutil.Error as err: nuke.message( - "Detected autosave file could not be used.\n{}" + f"Detected autosave file could not be used.\n{err}" + ) - .format(err)) + # To remain in the same window, we have to clear the script and read + # in the contents of the workfile. + read_script(filepath) return True