From 44db8cfd6dad2ce38960559dd45fea242228d059 Mon Sep 17 00:00:00 2001 From: splidje Date: Wed, 11 Feb 2026 16:01:45 +0000 Subject: [PATCH 1/6] Added setting for disabling forcing context settings (e.g. frame range) upon opening an existing Script. --- client/ayon_nuke/api/lib.py | 19 +++++++++++++++++++ client/ayon_nuke/api/pipeline.py | 17 +++++------------ client/ayon_nuke/api/plugin.py | 2 +- .../plugins/create/convert_legacy.py | 4 ++-- server/settings/general.py | 2 ++ 5 files changed, 29 insertions(+), 15 deletions(-) diff --git a/client/ayon_nuke/api/lib.py b/client/ayon_nuke/api/lib.py index a4e93c14ca..0a7c016129 100644 --- a/client/ayon_nuke/api/lib.py +++ b/client/ayon_nuke/api/lib.py @@ -819,6 +819,25 @@ def get_view_process_node(): return duplicate_node(ipn_node) +def on_create_root(): + if script_name(): + log.info("Not a new Script. Skipping setup.") + return + + log.info("New Script. Setting up ...") + + workfile_settings = WorkfileSettings() + + # Set context settings. + workfile_settings.set_context_settings() + + # adding favorites to file browser + workfile_settings.set_favorites() + + # template builder callbacks + start_workfile_template_builder() + + def on_script_load(): ''' Callback for ffmpeg support ''' diff --git a/client/ayon_nuke/api/pipeline.py b/client/ayon_nuke/api/pipeline.py index 68c524dd04..2987bb76ab 100644 --- a/client/ayon_nuke/api/pipeline.py +++ b/client/ayon_nuke/api/pipeline.py @@ -55,6 +55,7 @@ check_inventory_versions, set_avalon_knob_data, read_avalon_data, + on_create_root, on_script_load, dirmap_file_name_filter, add_scripts_menu, @@ -162,17 +163,8 @@ def add_nuke_callbacks(project_settings: dict = None): project_settings = get_current_project_settings() nuke_settings = project_settings["nuke"] - workfile_settings = WorkfileSettings() - # Set context settings. - nuke.addOnCreate( - workfile_settings.set_context_settings, nodeClass="Root") - - # adding favorites to file browser - nuke.addOnCreate(workfile_settings.set_favorites, nodeClass="Root") - - # template builder callbacks - nuke.addOnCreate(start_workfile_template_builder, nodeClass="Root") + nuke.addOnCreate(on_create_root, nodeClass="Root") # fix ffmpeg settings on script nuke.addOnScriptLoad(on_script_load) @@ -181,8 +173,9 @@ def add_nuke_callbacks(project_settings: dict = None): nuke.addOnScriptLoad(check_inventory_versions) nuke.addOnScriptSave(check_inventory_versions) - # set apply all workfile settings on script load and save - nuke.addOnScriptLoad(WorkfileSettings().set_context_settings) + if nuke_settings["general"]["set_context_settings_on_script_open"]: + # set apply all workfile settings on script load and save + nuke.addOnScriptLoad(WorkfileSettings().set_context_settings) if nuke_settings["dirmap"]["enabled"]: log.info("Added Nuke's dir-mapping callback ...") diff --git a/client/ayon_nuke/api/plugin.py b/client/ayon_nuke/api/plugin.py index f2e5355c29..0ebabecfe7 100644 --- a/client/ayon_nuke/api/plugin.py +++ b/client/ayon_nuke/api/plugin.py @@ -1465,7 +1465,7 @@ def _connect_to_above_nodes(self, node, product_name, message): self._shift_to_previous_node_and_temp(product_name, node, message) -def convert_to_valid_instaces(): +def convert_to_valid_instances(): """ Check and convert to latest publisher instances Also save as new minor version of workfile. diff --git a/client/ayon_nuke/plugins/create/convert_legacy.py b/client/ayon_nuke/plugins/create/convert_legacy.py index 65e719d15b..de743eb223 100644 --- a/client/ayon_nuke/plugins/create/convert_legacy.py +++ b/client/ayon_nuke/plugins/create/convert_legacy.py @@ -6,7 +6,7 @@ get_avalon_knob_data, NODE_TAB_NAME, ) -from ayon_nuke.api.plugin import convert_to_valid_instaces +from ayon_nuke.api.plugin import convert_to_valid_instances import nuke @@ -50,6 +50,6 @@ def find_instances(self): def convert(self): # loop all instances and convert them - convert_to_valid_instaces() + convert_to_valid_instances() # remove legacy item if all is fine self.remove_convertor_item() diff --git a/server/settings/general.py b/server/settings/general.py index d6eeb0013c..cbbffb8320 100644 --- a/server/settings/general.py +++ b/server/settings/general.py @@ -27,6 +27,8 @@ class MenuShortcut(BaseSettingsModel): class GeneralSettings(BaseSettingsModel): """Nuke general project settings.""" + set_context_settings_on_script_open: bool = SettingsField(True, title="Set Context Settings on Script Open") + menu: MenuShortcut = SettingsField( default_factory=MenuShortcut, title="Menu Shortcuts", From 08e116e1085c54025f467e1e8a036d311c48fd9c Mon Sep 17 00:00:00 2001 From: splidje Date: Thu, 12 Feb 2026 09:33:19 +0000 Subject: [PATCH 2/6] optional on create root now only for context settings. favourites and template builder back where they were. --- client/ayon_nuke/api/lib.py | 16 ++++------------ client/ayon_nuke/api/pipeline.py | 10 ++++++++-- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/client/ayon_nuke/api/lib.py b/client/ayon_nuke/api/lib.py index 0a7c016129..905ca593f1 100644 --- a/client/ayon_nuke/api/lib.py +++ b/client/ayon_nuke/api/lib.py @@ -819,23 +819,15 @@ def get_view_process_node(): return duplicate_node(ipn_node) -def on_create_root(): +def set_context_settings_if_new(): if script_name(): - log.info("Not a new Script. Skipping setup.") + log.info("Not a new Script. Skipping setting context settings.") return - log.info("New Script. Setting up ...") - - workfile_settings = WorkfileSettings() + log.info("New Script. Setting context settings ...") # Set context settings. - workfile_settings.set_context_settings() - - # adding favorites to file browser - workfile_settings.set_favorites() - - # template builder callbacks - start_workfile_template_builder() + WorkfileSettings().set_context_settings() def on_script_load(): diff --git a/client/ayon_nuke/api/pipeline.py b/client/ayon_nuke/api/pipeline.py index 2987bb76ab..6f3bc6a851 100644 --- a/client/ayon_nuke/api/pipeline.py +++ b/client/ayon_nuke/api/pipeline.py @@ -55,7 +55,7 @@ check_inventory_versions, set_avalon_knob_data, read_avalon_data, - on_create_root, + set_context_settings_if_new, on_script_load, dirmap_file_name_filter, add_scripts_menu, @@ -164,7 +164,13 @@ def add_nuke_callbacks(project_settings: dict = None): nuke_settings = project_settings["nuke"] - nuke.addOnCreate(on_create_root, nodeClass="Root") + nuke.addOnCreate(set_context_settings_if_new, nodeClass="Root") + + # adding favorites to file browser + nuke.addOnCreate(WorkfileSettings().set_favorites, nodeClass="Root") + + # template builder callbacks + nuke.addOnCreate(start_workfile_template_builder, nodeClass="Root") # fix ffmpeg settings on script nuke.addOnScriptLoad(on_script_load) From d12bc95c5e0236816be53be6b8145f0d0e11180e Mon Sep 17 00:00:00 2001 From: splidje Date: Thu, 12 Feb 2026 10:03:19 +0000 Subject: [PATCH 3/6] added new set_context_settings_on_script_open setting to defaults --- server/settings/general.py | 1 + 1 file changed, 1 insertion(+) diff --git a/server/settings/general.py b/server/settings/general.py index cbbffb8320..e4fdeeb6f9 100644 --- a/server/settings/general.py +++ b/server/settings/general.py @@ -36,6 +36,7 @@ class GeneralSettings(BaseSettingsModel): DEFAULT_GENERAL_SETTINGS = { + "set_context_settings_on_script_open": True, "menu": { "create": "ctrl+alt+c", "publish": "ctrl+alt+p", From 827bb7334c78b327c20887bc4259b15d46c4bc41 Mon Sep 17 00:00:00 2001 From: splidje Date: Mon, 16 Feb 2026 11:21:29 +0000 Subject: [PATCH 4/6] [optional_set_context_settings_on_load_script] Asks for user confirmation before setting format or frame range. Can also set a specific script to check context settings on load or not, overriding the general setting if on. --- client/ayon_nuke/api/lib.py | 144 ++++++++++++++++++++++++++----- client/ayon_nuke/api/pipeline.py | 16 ++-- client/ayon_nuke/api/workio.py | 2 + server/settings/general.py | 4 +- 4 files changed, 136 insertions(+), 30 deletions(-) diff --git a/client/ayon_nuke/api/lib.py b/client/ayon_nuke/api/lib.py index 950c36f7cf..58b45473ae 100644 --- a/client/ayon_nuke/api/lib.py +++ b/client/ayon_nuke/api/lib.py @@ -7,7 +7,7 @@ import platform import tempfile import contextlib -from collections import OrderedDict +from collections import defaultdict, OrderedDict import nuke from qtpy import QtCore, QtWidgets @@ -845,7 +845,7 @@ def get_view_process_node(): return duplicate_node(ipn_node) -def set_context_settings_if_new(): +def on_create_root_node(): if script_name(): log.info("Not a new Script. Skipping setting context settings.") return @@ -1480,6 +1480,60 @@ def create_backdrop(label="", color=None, layer=0, bdn["note_font_size"].setValue(20) return bdn +class ConfirmSetContextSettingMessageBox(QtWidgets.QDialog): + def __init__(self, change_description): + super().__init__() + layout = QtWidgets.QVBoxLayout() + self.setLayout(layout) + layout.addWidget(QtWidgets.QLabel( + "Would you like this script to have the following change:\n\n" + f"{change_description}\n\n" + "This is to match the correct settings for this context." + )) + if not ASSIST: + self._always_check_check_box = QtWidgets.QCheckBox( + "Always check this Script's context settings on load\n" + "(if switched off, can be switched back on in Project Settings AYON tab)" + ) + self._always_check_check_box.setChecked(True) + layout.addWidget( + self._always_check_check_box + ) + self._always_check_check_box.toggled.connect(self._always_check_check_box_toggled) + button_box = QtWidgets.QDialogButtonBox( + QtWidgets.QDialogButtonBox.StandardButtons.Yes| + QtWidgets.QDialogButtonBox.StandardButtons.No, + ) + layout.addWidget(button_box) + button_box.accepted.connect(self.accept) + button_box.rejected.connect(self.reject) + + def _always_check_check_box_toggled(self, is_checked): + root_node = nuke.root() + if "check_context_settings_on_load" not in root_node.knobs(): + root_node.addKnob(nuke.Boolean_Knob( + "check_context_settings_on_load", + "Check Context Settings on Load", + True, + )) + root_node["check_context_settings_on_load"].setValue(is_checked) + + @classmethod + def ask_for_confirmation(cls, message): + project_settings = get_current_project_settings() + if not project_settings["nuke"]["general"]["check_context_settings_on_script_open"]: + return False + + root_node = nuke.root() + # a specific script can be set to never check + if ( + "check_context_settings_on_load" in root_node.knobs() + and not root_node["check_context_settings_on_load"].value() + ): + return False + + return cls(message).exec() == QtWidgets.QDialog.Accepted + class WorkfileSettings(object): """ @@ -2034,7 +2088,7 @@ def set_colorspace(self): if read_clrs_inputs: self.set_reads_colorspace(read_clrs_inputs) - def reset_frame_range_handles(self): + def reset_frame_range_handles(self, requires_confirmation=False): """Set frame range to current folder.""" if "attrib" not in self._task_entity: @@ -2073,22 +2127,54 @@ def reset_frame_range_handles(self): frame_start_handle = frame_start - handle_start frame_end_handle = frame_end + handle_end - self._root_node["lock_range"].setValue(False) - self._root_node["fps"].setValue(fps) - self._root_node["first_frame"].setValue(frame_start_handle) - self._root_node["last_frame"].setValue(frame_end_handle) - self._root_node["lock_range"].setValue(True) - - # update node graph so knobs are updated - update_node_graph() - frame_range = '{0}-{1}'.format(frame_start, frame_end) - for node in nuke.allNodes(filter="Viewer"): - node['frame_range'].setValue(frame_range) - node['frame_range_lock'].setValue(True) - node['frame_range'].setValue(frame_range) - node['frame_range_lock'].setValue(True) + knob_name_current_new_value_triplets_by_node = defaultdict(list) + for knob_name_new_value_pairs, nodes in ((( + ("lock_range", False), + ("fps", fps), + ("first_frame", frame_start_handle), + ("last_frame", frame_end_handle), + ("lock_range", True), + ), (self._root_node,)), + (( + ("frame_range", frame_range), + ("frame_range_lock", True), + ), nuke.allNodes(filter="Viewer"))): + for node in nodes: + for knob_name, new_value in knob_name_new_value_pairs: + current_value = node[knob_name].value() + if current_value != new_value: + knob_name_current_new_value_triplets_by_node[node].append((knob_name, current_value, new_value)) + + if knob_name_current_new_value_triplets_by_node: + if not requires_confirmation or ConfirmSetContextSettingMessageBox.ask_for_confirmation( + "\n\n".join( + "{}:\n{}".format( + node.name(), + "\n".join( + f"{knob_name}: {current_value} -> {new_value}" for knob_name, current_value, new_value in knob_name_current_new_value_triplets + ) + ) for node, knob_name_current_new_value_triplets in knob_name_current_new_value_triplets_by_node.items() + ) + ): + was_node_graph_updated = False + for node, knob_name_current_new_value_triplets in knob_name_current_new_value_triplets_by_node.items(): + # Root node will be first. + # Based on how the code was when I found it, + # `update_node_graph` needs calling between + # root node and viewers - splidje + if not was_node_graph_updated and node != self._root_node: + # update node graph so knobs are updated + update_node_graph() + was_node_graph_updated = True + for knob_name, _, new_value in knob_name_current_new_value_triplets: + node[knob_name].setValue(new_value) + + # in case no Viewers needed knob changes + if not was_node_graph_updated: + # update node graph so knobs are updated + update_node_graph() if not ASSIST: set_node_data( @@ -2105,7 +2191,7 @@ def reset_frame_range_handles(self): "updating custom knobs..." ) - def reset_resolution(self): + def reset_resolution(self, requires_confirmation=False): """Set resolution to project resolution.""" log.info("Resetting resolution") project_name = get_current_project_name() @@ -2127,6 +2213,20 @@ def reset_resolution(self): log.error(msg) nuke.message(msg) + current_format = nuke.root()["format"].value() + if requires_confirmation and any(( + current_format.width() != format_data["width"], + current_format.height() != format_data["height"], + current_format.pixelAspect() != format_data["pixel_aspect"], + )): + if not ConfirmSetContextSettingMessageBox.ask_for_confirmation( + "Format:\n" + f"{current_format.width()}x{current_format.height()} {current_format.pixelAspect()}" + " -> " + f'{format_data["width"]}x{format_data["height"]} {format_data["pixel_aspect"]}' + ): + return + existing_format = None for format in nuke.formats(): if format_data["name"] == format.name(): @@ -2169,10 +2269,10 @@ def make_format_string(self, **kwargs): "{name}".format(**kwargs) ) - def set_context_settings(self): - self.reset_resolution() - self.reset_frame_range_handles() - # add colorspace menu item + def set_context_settings(self, requires_confirmation=False): + self.reset_resolution(requires_confirmation) + self.reset_frame_range_handles(requires_confirmation) + # todo: No need to check confirmation for color? self.set_colorspace() def set_favorites(self): diff --git a/client/ayon_nuke/api/pipeline.py b/client/ayon_nuke/api/pipeline.py index 6f3bc6a851..23611eb84e 100644 --- a/client/ayon_nuke/api/pipeline.py +++ b/client/ayon_nuke/api/pipeline.py @@ -55,7 +55,7 @@ check_inventory_versions, set_avalon_knob_data, read_avalon_data, - set_context_settings_if_new, + on_create_root_node, on_script_load, dirmap_file_name_filter, add_scripts_menu, @@ -164,10 +164,13 @@ def add_nuke_callbacks(project_settings: dict = None): nuke_settings = project_settings["nuke"] - nuke.addOnCreate(set_context_settings_if_new, nodeClass="Root") + nuke.addOnCreate(on_create_root_node, nodeClass="Root") # adding favorites to file browser - nuke.addOnCreate(WorkfileSettings().set_favorites, nodeClass="Root") + nuke.addOnCreate( + lambda: WorkfileSettings().set_favorites(), + nodeClass="Root", + ) # template builder callbacks nuke.addOnCreate(start_workfile_template_builder, nodeClass="Root") @@ -179,9 +182,10 @@ def add_nuke_callbacks(project_settings: dict = None): nuke.addOnScriptLoad(check_inventory_versions) nuke.addOnScriptSave(check_inventory_versions) - if nuke_settings["general"]["set_context_settings_on_script_open"]: - # set apply all workfile settings on script load and save - nuke.addOnScriptLoad(WorkfileSettings().set_context_settings) + # set apply all workfile settings on script load + nuke.addOnScriptLoad( + lambda: WorkfileSettings().set_context_settings(requires_confirmation=True) + ) if nuke_settings["dirmap"]["enabled"]: log.info("Added Nuke's dir-mapping callback ...") diff --git a/client/ayon_nuke/api/workio.py b/client/ayon_nuke/api/workio.py index 0a46b8043b..a985d38dc2 100644 --- a/client/ayon_nuke/api/workio.py +++ b/client/ayon_nuke/api/workio.py @@ -23,6 +23,7 @@ def save_file(filepath): def open_file(filepath): + from .lib import WorkfileSettings def read_script(nuke_script): if not ASSIST: @@ -31,6 +32,7 @@ def read_script(nuke_script): nuke.Root()["name"].setValue(nuke_script) nuke.Root()["project_directory"].setValue(os.path.dirname(nuke_script)) nuke.Root().setModified(False) + WorkfileSettings().set_context_settings(requires_confirmation=True) else: nuke.scriptOpen(nuke_script) diff --git a/server/settings/general.py b/server/settings/general.py index e4fdeeb6f9..a82346e8dd 100644 --- a/server/settings/general.py +++ b/server/settings/general.py @@ -27,7 +27,7 @@ class MenuShortcut(BaseSettingsModel): class GeneralSettings(BaseSettingsModel): """Nuke general project settings.""" - set_context_settings_on_script_open: bool = SettingsField(True, title="Set Context Settings on Script Open") + check_context_settings_on_script_open: bool = SettingsField(True, title="Check Context Settings on Script Open") menu: MenuShortcut = SettingsField( default_factory=MenuShortcut, @@ -36,7 +36,7 @@ class GeneralSettings(BaseSettingsModel): DEFAULT_GENERAL_SETTINGS = { - "set_context_settings_on_script_open": True, + "check_context_settings_on_script_open": True, "menu": { "create": "ctrl+alt+c", "publish": "ctrl+alt+p", From 0b2081bfcb0bf8de0494bf4ba2a4d597cbbc1ebc Mon Sep 17 00:00:00 2001 From: splidje Date: Mon, 16 Feb 2026 11:28:17 +0000 Subject: [PATCH 5/6] [optional_set_context_settings_on_load_script] tidied formatting a little. --- client/ayon_nuke/api/lib.py | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/client/ayon_nuke/api/lib.py b/client/ayon_nuke/api/lib.py index 58b45473ae..e9ea707096 100644 --- a/client/ayon_nuke/api/lib.py +++ b/client/ayon_nuke/api/lib.py @@ -2130,22 +2130,32 @@ def reset_frame_range_handles(self, requires_confirmation=False): frame_range = '{0}-{1}'.format(frame_start, frame_end) knob_name_current_new_value_triplets_by_node = defaultdict(list) - for knob_name_new_value_pairs, nodes in ((( - ("lock_range", False), - ("fps", fps), - ("first_frame", frame_start_handle), - ("last_frame", frame_end_handle), - ("lock_range", True), - ), (self._root_node,)), - (( - ("frame_range", frame_range), - ("frame_range_lock", True), - ), nuke.allNodes(filter="Viewer"))): + for knob_name_new_value_pairs, nodes in ( + ( + ( + ("lock_range", False), + ("fps", fps), + ("first_frame", frame_start_handle), + ("last_frame", frame_end_handle), + ("lock_range", True), + ), + (self._root_node,), + ), + ( + ( + ("frame_range", frame_range), + ("frame_range_lock", True), + ), + nuke.allNodes(filter="Viewer"), + ) + ): for node in nodes: for knob_name, new_value in knob_name_new_value_pairs: current_value = node[knob_name].value() if current_value != new_value: - knob_name_current_new_value_triplets_by_node[node].append((knob_name, current_value, new_value)) + knob_name_current_new_value_triplets_by_node[node].append( + (knob_name, current_value, new_value) + ) if knob_name_current_new_value_triplets_by_node: if not requires_confirmation or ConfirmSetContextSettingMessageBox.ask_for_confirmation( From b17af258f7bfe321d5d5ef7974aaa73add6ad203 Mon Sep 17 00:00:00 2001 From: splidje Date: Mon, 16 Feb 2026 16:03:16 +0000 Subject: [PATCH 6/6] [optional_set_context_settings_on_load_script] fixed checkbox text in confirm set context setting message box --- client/ayon_nuke/api/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_nuke/api/lib.py b/client/ayon_nuke/api/lib.py index e9ea707096..4e64dfaa47 100644 --- a/client/ayon_nuke/api/lib.py +++ b/client/ayon_nuke/api/lib.py @@ -1493,7 +1493,7 @@ def __init__(self, change_description): if not ASSIST: self._always_check_check_box = QtWidgets.QCheckBox( "Always check this Script's context settings on load\n" - "(if switched off, can be switched back on in Project Settings AYON tab)" + "(if switched off, can be switched back on in Project Settings User tab)" ) self._always_check_check_box.setChecked(True) layout.addWidget(