diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index 62f3a3c3ff..0a5f772346 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -48,20 +48,15 @@ from openpype.pipeline import ( get_current_asset_name, ) from openpype.pipeline.context_tools import ( - get_current_project_asset, get_custom_workfile_template_from_session ) -from openpype.pipeline.colorspace import ( - get_imageio_config -) +from openpype.pipeline.colorspace import get_imageio_config from openpype.pipeline.workfile import BuildWorkfile from . import gizmo_menu from .constants import ASSIST -from .workio import ( - save_file, - open_file -) +from .workio import save_file +from .utils import get_node_outputs log = Logger.get_logger(__name__) @@ -2802,7 +2797,7 @@ def find_free_space_to_paste_nodes( @contextlib.contextmanager -def maintained_selection(): +def maintained_selection(exclude_nodes=None): """Maintain selection during context Example: @@ -2811,7 +2806,12 @@ def maintained_selection(): >>> print(node["selected"].value()) False """ + if exclude_nodes: + for node in exclude_nodes: + node["selected"].setValue(False) + previous_selection = nuke.selectedNodes() + try: yield finally: @@ -2823,6 +2823,51 @@ def maintained_selection(): select_nodes(previous_selection) +@contextlib.contextmanager +def swap_node_with_dependency(old_node, new_node): + """ Swap node with dependency + + Swap node with dependency and reconnect all inputs and outputs. + It removes old node. + + Arguments: + old_node (nuke.Node): node to be replaced + new_node (nuke.Node): node to replace with + + Example: + >>> old_node_name = old_node["name"].value() + >>> print(old_node_name) + old_node_name_01 + >>> with swap_node_with_dependency(old_node, new_node) as node_name: + ... new_node["name"].setValue(node_name) + >>> print(new_node["name"].value()) + old_node_name_01 + """ + # preserve position + xpos, ypos = old_node.xpos(), old_node.ypos() + # preserve selection after all is done + outputs = get_node_outputs(old_node) + inputs = old_node.dependencies() + node_name = old_node["name"].value() + + try: + nuke.delete(old_node) + + yield node_name + finally: + + # Reconnect inputs + for i, node in enumerate(inputs): + new_node.setInput(i, node) + # Reconnect outputs + if outputs: + for n, pipes in outputs.items(): + for i in pipes: + n.setInput(i, new_node) + # return to original position + new_node.setXYpos(xpos, ypos) + + def reset_selection(): """Deselect all selected nodes""" for node in nuke.selectedNodes(): @@ -2920,13 +2965,13 @@ def process_workfile_builder(): "workfile_builder", {}) # get settings - createfv_on = workfile_builder.get("create_first_version") or None + create_fv_on = workfile_builder.get("create_first_version") or None builder_on = workfile_builder.get("builder_on_start") or None last_workfile_path = os.environ.get("AVALON_LAST_WORKFILE") # generate first version in file not existing and feature is enabled - if createfv_on and not os.path.exists(last_workfile_path): + if create_fv_on and not os.path.exists(last_workfile_path): # get custom template path if any custom_template_path = get_custom_workfile_template_from_session( project_settings=project_settings diff --git a/openpype/hosts/nuke/plugins/load/load_gizmo.py b/openpype/hosts/nuke/plugins/load/load_gizmo.py index 5d028fc2db..23cf4d7741 100644 --- a/openpype/hosts/nuke/plugins/load/load_gizmo.py +++ b/openpype/hosts/nuke/plugins/load/load_gizmo.py @@ -12,7 +12,8 @@ from openpype.pipeline import ( from openpype.hosts.nuke.api.lib import ( maintained_selection, get_avalon_knob_data, - set_avalon_knob_data + set_avalon_knob_data, + swap_node_with_dependency, ) from openpype.hosts.nuke.api import ( containerise, @@ -45,7 +46,7 @@ class LoadGizmo(load.LoaderPlugin): data (dict): compulsory attribute > not used Returns: - nuke node: containerised nuke node object + nuke node: containerized nuke node object """ # get main variables @@ -83,12 +84,12 @@ class LoadGizmo(load.LoaderPlugin): # add group from nk nuke.nodePaste(file) - GN = nuke.selectedNode() + group_node = nuke.selectedNode() - GN["name"].setValue(object_name) + group_node["name"].setValue(object_name) return containerise( - node=GN, + node=group_node, name=name, namespace=namespace, context=context, @@ -110,7 +111,7 @@ class LoadGizmo(load.LoaderPlugin): version_doc = get_version_by_id(project_name, representation["parent"]) # get corresponding node - GN = nuke.toNode(container['objectName']) + group_node = nuke.toNode(container['objectName']) file = get_representation_path(representation).replace("\\", "/") name = container['name'] @@ -135,22 +136,24 @@ class LoadGizmo(load.LoaderPlugin): for k in add_keys: data_imprint.update({k: version_data[k]}) + # capture pipeline metadata + avalon_data = get_avalon_knob_data(group_node) + # adding nodes to node graph # just in case we are in group lets jump out of it nuke.endGroup() - with maintained_selection(): - xpos = GN.xpos() - ypos = GN.ypos() - avalon_data = get_avalon_knob_data(GN) - nuke.delete(GN) - # add group from nk + with maintained_selection([group_node]): + # insert nuke script to the script nuke.nodePaste(file) - - GN = nuke.selectedNode() - set_avalon_knob_data(GN, avalon_data) - GN.setXYpos(xpos, ypos) - GN["name"].setValue(object_name) + # convert imported to selected node + new_group_node = nuke.selectedNode() + # swap nodes with maintained connections + with swap_node_with_dependency( + group_node, new_group_node) as node_name: + new_group_node["name"].setValue(node_name) + # set updated pipeline metadata + set_avalon_knob_data(new_group_node, avalon_data) last_version_doc = get_last_version_by_subset_id( project_name, version_doc["parent"], fields=["_id"] @@ -161,11 +164,12 @@ class LoadGizmo(load.LoaderPlugin): color_value = self.node_color else: color_value = "0xd88467ff" - GN["tile_color"].setValue(int(color_value, 16)) + + new_group_node["tile_color"].setValue(int(color_value, 16)) self.log.info("updated to version: {}".format(version_doc.get("name"))) - return update_container(GN, data_imprint) + return update_container(new_group_node, data_imprint) def switch(self, container, representation): self.update(container, representation) diff --git a/openpype/hosts/nuke/plugins/load/load_gizmo_ip.py b/openpype/hosts/nuke/plugins/load/load_gizmo_ip.py index ba2de3d05d..ce0a1615f1 100644 --- a/openpype/hosts/nuke/plugins/load/load_gizmo_ip.py +++ b/openpype/hosts/nuke/plugins/load/load_gizmo_ip.py @@ -14,7 +14,8 @@ from openpype.hosts.nuke.api.lib import ( maintained_selection, create_backdrop, get_avalon_knob_data, - set_avalon_knob_data + set_avalon_knob_data, + swap_node_with_dependency, ) from openpype.hosts.nuke.api import ( containerise, @@ -47,7 +48,7 @@ class LoadGizmoInputProcess(load.LoaderPlugin): data (dict): compulsory attribute > not used Returns: - nuke node: containerised nuke node object + nuke node: containerized nuke node object """ # get main variables @@ -85,17 +86,17 @@ class LoadGizmoInputProcess(load.LoaderPlugin): # add group from nk nuke.nodePaste(file) - GN = nuke.selectedNode() + group_node = nuke.selectedNode() - GN["name"].setValue(object_name) + group_node["name"].setValue(object_name) # try to place it under Viewer1 - if not self.connect_active_viewer(GN): - nuke.delete(GN) + if not self.connect_active_viewer(group_node): + nuke.delete(group_node) return return containerise( - node=GN, + node=group_node, name=name, namespace=namespace, context=context, @@ -117,7 +118,7 @@ class LoadGizmoInputProcess(load.LoaderPlugin): version_doc = get_version_by_id(project_name, representation["parent"]) # get corresponding node - GN = nuke.toNode(container['objectName']) + group_node = nuke.toNode(container['objectName']) file = get_representation_path(representation).replace("\\", "/") name = container['name'] @@ -142,22 +143,24 @@ class LoadGizmoInputProcess(load.LoaderPlugin): for k in add_keys: data_imprint.update({k: version_data[k]}) + # capture pipeline metadata + avalon_data = get_avalon_knob_data(group_node) + # adding nodes to node graph # just in case we are in group lets jump out of it nuke.endGroup() - with maintained_selection(): - xpos = GN.xpos() - ypos = GN.ypos() - avalon_data = get_avalon_knob_data(GN) - nuke.delete(GN) - # add group from nk + with maintained_selection([group_node]): + # insert nuke script to the script nuke.nodePaste(file) - - GN = nuke.selectedNode() - set_avalon_knob_data(GN, avalon_data) - GN.setXYpos(xpos, ypos) - GN["name"].setValue(object_name) + # convert imported to selected node + new_group_node = nuke.selectedNode() + # swap nodes with maintained connections + with swap_node_with_dependency( + group_node, new_group_node) as node_name: + new_group_node["name"].setValue(node_name) + # set updated pipeline metadata + set_avalon_knob_data(new_group_node, avalon_data) last_version_doc = get_last_version_by_subset_id( project_name, version_doc["parent"], fields=["_id"] @@ -168,11 +171,11 @@ class LoadGizmoInputProcess(load.LoaderPlugin): color_value = self.node_color else: color_value = "0xd88467ff" - GN["tile_color"].setValue(int(color_value, 16)) + new_group_node["tile_color"].setValue(int(color_value, 16)) self.log.info("updated to version: {}".format(version_doc.get("name"))) - return update_container(GN, data_imprint) + return update_container(new_group_node, data_imprint) def connect_active_viewer(self, group_node): """