diff --git a/openpype/hosts/nuke/api/lib.py b/openpype/hosts/nuke/api/lib.py index 90c19707d9..9959ed79ea 100644 --- a/openpype/hosts/nuke/api/lib.py +++ b/openpype/hosts/nuke/api/lib.py @@ -64,6 +64,8 @@ EXCLUDED_KNOB_TYPE_ON_READ = ( # if value is not an empty string.) ) JSON_PREFIX = "JSON:::" +ROOT_DATA_KNOB = "publish_context" +INSTANCE_DATA_KNOB = "publish_instance" class Context: @@ -95,22 +97,32 @@ def get_main_window(): return Context.main_window -def write_create_data(node, knobname, data): - knob_value = JSON_PREFIX + json.dumps(data) +def write_node_data(node, knobname, data): + # if exists then update data if knobname in node.knobs(): - knob = node[knobname] - knob.setValue(knob_value) + log.debug("Updating knobname `{}` on node `{}`".format( + knobname, node.name() + )) + update_node_data(node, knobname, data) return + log.debug("Creating knobname `{}` on node `{}`".format( + knobname, node.name() + )) + # else create new + knob_value = JSON_PREFIX + json.dumps(data) knob = nuke.String_Knob(knobname) knob.setValue(knob_value) knob.setFlag(nuke.INVISIBLE) node.addKnob(knob) -def read_create_data(node, knobname): +def read_node_data(node, knobname): + if knobname not in node.knobs(): - log.debug("get knob: {}".format(node[knobname])) + log.warnig("Knobname `{}` does not exist on node `{}`".format( + knobname, node.name() + )) return rawdata = node[knobname].getValue() @@ -124,6 +136,14 @@ def read_create_data(node, knobname): return +def update_node_data(node, knobname, data): + knob = node[knobname] + node_data = read_node_data(node, knobname) + node_data.update(data) + knob_value = JSON_PREFIX + json.dumps(node_data) + knob.setValue(knob_value) + + class Knobby(object): """[DEPRICATED] For creating knob which it's type isn't mapped in `create_knobs` @@ -2168,11 +2188,14 @@ class WorkfileSettings(object): node['frame_range_lock'].setValue(True) # adding handle_start/end to root avalon knob - if not set_avalon_knob_data(self._root_node, { - "handleStart": int(handle_start), - "handleEnd": int(handle_end) - }): - log.warning("Cannot set Avalon knob to Root node!") + write_node_data( + self._root_node, + INSTANCE_DATA_KNOB, + { + "handleStart": int(handle_start), + "handleEnd": int(handle_end) + } + ) def reset_resolution(self): """Set resolution to project resolution.""" @@ -2295,7 +2318,6 @@ def get_write_node_template_attr(node): subset=avalon_knob_data["subset"] ) - # collecting correct data correct_data = OrderedDict() @@ -2347,10 +2369,11 @@ def get_dependent_nodes(nodes): def find_free_space_to_paste_nodes( - nodes, - group=nuke.root(), - direction="right", - offset=300): + nodes, + group=nuke.root(), + direction="right", + offset=300 +): """ For getting coordinates in DAG (node graph) for placing new nodes diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index 392573e1f6..a18d3b54e7 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -34,6 +34,7 @@ from openpype.tools.utils import host_tools from .command import viewer_update_and_undo_stop from .lib import ( Context, + ROOT_DATA_KNOB, get_main_window, add_publish_knob, WorkfileSettings, @@ -47,8 +48,8 @@ from .lib import ( dirmap_file_name_filter, add_scripts_menu, add_scripts_gizmo, - read_create_data, - write_create_data + read_node_data, + write_node_data ) from .lib_template_builder import ( create_placeholder, update_placeholder @@ -136,11 +137,11 @@ class NukeHost( def get_context_data(self): root_node = nuke.root() - return read_create_data(root_node, "publish_context") + return read_node_data(root_node, ROOT_DATA_KNOB) def update_context_data(self, data, changes): root_node = nuke.root() - write_create_data(root_node, "publish_context", data) + write_node_data(root_node, ROOT_DATA_KNOB, data) def add_nuke_callbacks(): @@ -492,7 +493,7 @@ def ls(): yield container -def list_instances(): +def list_instances(creator_id=None): """List all created instances to publish from current workfile. For SubsetManager @@ -514,7 +515,7 @@ def list_instances(): # get data from avalon knob avalon_knob_data = get_avalon_knob_data( - node, ["avalon:", "ak:"]) + node) if not avalon_knob_data: continue @@ -522,8 +523,13 @@ def list_instances(): if avalon_knob_data["id"] != "pyblish.avalon.instance": continue + if creator_id and avalon_knob_data["identifier"] != creator_id: + continue + # add node name - avalon_knob_data["name"] = node.name() + avalon_knob_data.update({ + "instance_node": node + }) instances.append(avalon_knob_data) diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index 37ce03dc55..302d6c5cd7 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -1,15 +1,23 @@ +import nuke + import os +import sys +import six import random import string from collections import OrderedDict from abc import abstractmethod - -import nuke +from abc import ( + ABCMeta +) from openpype.api import get_current_project_settings from openpype.pipeline import ( LegacyCreator, LoaderPlugin, + CreatorError, + Creator as NewCreator, + CreatedInstance ) from .lib import ( Knobby, @@ -21,6 +29,115 @@ from .lib import ( set_node_knobs_from_settings, get_view_process_node ) +from .pipeline import ( + list_instances +) + + +class OpenPypeCreatorError(CreatorError): + pass + + +@six.add_metaclass(ABCMeta) +class NukeCreator(NewCreator): + selected_nodes = [] + + def _create_instance_node( + self, + node_name, + knobs=None, + parent=None, + node_type="NoOp" + ): + """Create node representing instance. + + Arguments: + node_name (str): Name of the new node. + knobs (OrderedDict): knobs name and values + parent (str): Name of the parent node. + node_type (str, optional): Type of the node. + + Returns: + nuke.Node: Newly created instance node. + + """ + node_knobs = knobs or {} + + # set parent node + parent_node = nuke.root() + if parent: + parent_node = nuke.toNode(parent) + + with parent_node: + created_node = nuke.createNode(node_type) + created_node["name"].setValue(node_name) + + for key, values in node_knobs.items(): + if key in created_node.knobs(): + created_node["key"].setValue(values) + + return created_node + + # def create(self, subset_name, instance_data, pre_create_data): + # try: + # if pre_create_data.get("use_selection"): + # self.selected_nodes = nuke.selectedNodes() + + # # Get the node type and remove it from the data, not needed + # _node_type = instance_data.pop("node_type", None) + # if _node_type is None: + # _node_type = "NoOp" + + # instance_node = self._create_instance_node( + # subset_name, node_type=_node_type, pre_create_data) + + # instance_data["instance_node"] = instance_node.path() + + # instance = CreatedInstance( + # self.family, + # subset_name, + # instance_data, + # self) + # self._add_instance_to_context(instance) + # imprint(instance_node, instance.data_to_store()) + # return instance + + # except hou.Error as er: + # six.reraise( + # OpenPypeCreatorError, + # OpenPypeCreatorError("Creator error: {}".format(er)), + # sys.exc_info()[2]) + + # def collect_instances(self): + # for instance_data in list_instances(creator_id=self.identifier): + # created_instance = CreatedInstance.from_existing( + # instance_data, self + # ) + # self._add_instance_to_context(created_instance) + + # def update_instances(self, update_list): + # for created_inst, _changes in update_list: + # instance_node = created_inst.pop("instance_node", None) + # current_data = created_inst.data + + # imprint( + # instance_node, + # { + # key: value[1] for key, value in _changes.items() + # if current_data.get(key) != value[1] + # }, + # update=True + # ) + + # def remove_instances(self, instances): + # for instance in instances: + # remove_instance(instance) + # self._remove_instance_from_context(instance) + + # def get_pre_create_attr_defs(self): + # return [ + # BoolDef("use_selection", label="Use selection") + # ] class OpenPypeCreator(LegacyCreator):