From 444071e2d05f36dcc467cc76b7cad4172017e3a4 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 21 Nov 2019 10:22:54 +0100 Subject: [PATCH 01/26] fix(nuke): not necessary to do it on all write nodes --- pype/nuke/lib.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pype/nuke/lib.py b/pype/nuke/lib.py index 157af9019d..0079a23266 100644 --- a/pype/nuke/lib.py +++ b/pype/nuke/lib.py @@ -105,6 +105,10 @@ def writes_version_sync(): for each in nuke.allNodes(): if each.Class() == 'Write': + # check if the node is avalon tracked + if "AvalonTab" not in each.knobs(): + continue + avalon_knob_data = avalon.nuke.get_avalon_knob_data( each, ['avalon:', 'ak:']) From 74b1be0da35fc160094eda38600ca9e5f1ae6b50 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 21 Nov 2019 13:17:48 +0100 Subject: [PATCH 02/26] fix(nuke): code is redundant --- pype/plugins/nuke/create/create_write.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pype/plugins/nuke/create/create_write.py b/pype/plugins/nuke/create/create_write.py index a8c9c932da..c885631794 100644 --- a/pype/plugins/nuke/create/create_write.py +++ b/pype/plugins/nuke/create/create_write.py @@ -24,8 +24,6 @@ class CreateWriteRender(plugin.PypeCreator): def __init__(self, *args, **kwargs): super(CreateWriteRender, self).__init__(*args, **kwargs) - self.name = self.data["subset"] - data = OrderedDict() data["family"] = self.family From 3fbe8b6f455009177dc38d141fa11240e1708a16 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 21 Nov 2019 18:09:22 +0100 Subject: [PATCH 03/26] feat(nuke): update head info in create backdrop --- pype/plugins/nuke/create/create_backdrop.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pype/plugins/nuke/create/create_backdrop.py b/pype/plugins/nuke/create/create_backdrop.py index b5600e8b37..237f9ac8f0 100644 --- a/pype/plugins/nuke/create/create_backdrop.py +++ b/pype/plugins/nuke/create/create_backdrop.py @@ -4,13 +4,12 @@ from avalon.nuke.pipeline import Creator class CreateBackdrop(Creator): """Add Publishable Backdrop""" - name = "backdrop" - label = "Backdrop" - family = "group" - icon = "cube" + name = "nukenodes" + label = "Create Backdrop" + family = "nukenodes" + icon = "file-archive-o" defaults = ["Main"] def __init__(self, *args, **kwargs): super(CreateBackdrop, self).__init__(*args, **kwargs) - return From df41c92f44cf6b5f434c9d8fb7c0378f4b43d4ae Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 21 Nov 2019 19:06:00 +0100 Subject: [PATCH 04/26] feat(nuke): include only nodes with subset knob --- pype/plugins/nuke/publish/collect_instances.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/pype/plugins/nuke/publish/collect_instances.py b/pype/plugins/nuke/publish/collect_instances.py index 2500f3fca5..f83f4f5e9a 100644 --- a/pype/plugins/nuke/publish/collect_instances.py +++ b/pype/plugins/nuke/publish/collect_instances.py @@ -23,15 +23,13 @@ class CollectNukeInstances(pyblish.api.ContextPlugin): instances = [] # creating instances per write node - self.log.debug("nuke.allNodes(): {}".format(nuke.allNodes())) - for node in nuke.allNodes(): - try: - if node["disable"].value(): - continue - except Exception as E: - self.log.warning(E) - continue + # gets only nodes with subset knob + nodes = [n for n in nuke.allNodes() + if get_avalon_knob_data(n, + ["avalon:", "ak:"]).get("subset")] + # creating instances per write node + for node in nodes: # get data from avalon knob self.log.debug("node[name]: {}".format(node['name'].value())) avalon_knob_data = get_avalon_knob_data(node) From 2bf235ece19f98bcbe8b932946cc6ea58f91214b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 21 Nov 2019 19:07:04 +0100 Subject: [PATCH 05/26] clean(nuke): remove unused lines --- pype/plugins/nuke/publish/collect_instances.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pype/plugins/nuke/publish/collect_instances.py b/pype/plugins/nuke/publish/collect_instances.py index f83f4f5e9a..c901d2c161 100644 --- a/pype/plugins/nuke/publish/collect_instances.py +++ b/pype/plugins/nuke/publish/collect_instances.py @@ -18,10 +18,8 @@ class CollectNukeInstances(pyblish.api.ContextPlugin): asset_data = io.find_one({"type": "asset", "name": api.Session["AVALON_ASSET"]}) - self.log.debug("asset_data: {}".format(asset_data["data"])) instances = [] - # creating instances per write node # gets only nodes with subset knob nodes = [n for n in nuke.allNodes() From a87d7cf054a088df41d907e5ce8462802d3a0b74 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 21 Nov 2019 19:08:34 +0100 Subject: [PATCH 06/26] feat(nuke): fixing family/class detection - render families only for render group node - any group will be accepted too - disable nodes are not included --- .../plugins/nuke/publish/collect_instances.py | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/pype/plugins/nuke/publish/collect_instances.py b/pype/plugins/nuke/publish/collect_instances.py index c901d2c161..ea8c885d9a 100644 --- a/pype/plugins/nuke/publish/collect_instances.py +++ b/pype/plugins/nuke/publish/collect_instances.py @@ -40,6 +40,14 @@ class CollectNukeInstances(pyblish.api.ContextPlugin): if avalon_knob_data["id"] != "pyblish.avalon.instance": continue + # establish families + family = avalon_knob_data["family"] + families = list() + + # except disabled nodes but exclude backdrops in test + if ("nukenodes" not in family) and (node["disable"].value()): + continue + subset = avalon_knob_data.get( "subset", None) or node["name"].value() @@ -49,26 +57,28 @@ class CollectNukeInstances(pyblish.api.ContextPlugin): # Add all nodes in group instances. if node.Class() == "Group": + # only alter families for render family + if ("render" in family): + # check if node is not disabled + families.append(avalon_knob_data["families"]) + if node["render"].value(): + self.log.info("flagged for render") + add_family = "render.local" + # dealing with local/farm rendering + if node["render_farm"].value(): + self.log.info("adding render farm family") + add_family = "render.farm" + instance.data["transfer"] = False + families.append(add_family) + else: + # add family into families + families.insert(0, family) + node.begin() for i in nuke.allNodes(): instance.append(i) node.end() - family = avalon_knob_data["family"] - families = [avalon_knob_data["families"]] - if node["render"].value(): - self.log.info("flagged for render") - add_family = "render.local" - # dealing with local/farm rendering - if node["render_farm"].value(): - self.log.info("adding render farm family") - add_family = "render.farm" - instance.data["transfer"] = False - families.append(add_family) - else: - # add family into families - families.insert(0, family) - instance.data.update({ "subset": subset, "asset": os.environ["AVALON_ASSET"], From c4292d3add70c3ea1353a78afea4ca82b6e4e925 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 21 Nov 2019 19:56:12 +0100 Subject: [PATCH 07/26] feat(nuke): adding collect backdrop plugin --- .../plugins/nuke/publish/collect_nukenodes.py | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 pype/plugins/nuke/publish/collect_nukenodes.py diff --git a/pype/plugins/nuke/publish/collect_nukenodes.py b/pype/plugins/nuke/publish/collect_nukenodes.py new file mode 100644 index 0000000000..ba5a28c44a --- /dev/null +++ b/pype/plugins/nuke/publish/collect_nukenodes.py @@ -0,0 +1,34 @@ +import pyblish.api +import nuke + +class CollectBackdrops(pyblish.api.InstancePlugin): + """Collect Backdrop instance from rendered frames + """ + + order = pyblish.api.CollectorOrder + 0.3 + label = "Collect Backdrop" + hosts = ["nuke"] + families = ["nukenodes"] + + def process(self, instance): + + bckn = instance[0] + + left = bckn.xpos() + top = bckn.ypos() + right = left + bckn['bdwidth'].value() + bottom = top + bckn['bdheight'].value() + + inNodes = [] + for node in nuke.allNodes(): + if node.Class() == "Viewer": + continue + + if (node.xpos() > left) \ + and (node.xpos() + node.screenWidth() < right) \ + and (node.ypos() > top) \ + and (node.ypos() + node.screenHeight() < bottom): + inNodes.append(node) + + self.log.info("Backdrop content collected: `{}`".format(inNodes)) + self.log.info("Backdrop instance collected: `{}`".format(instance)) From a2f81fbdb02593bd216c05e531952a5c3238e6a2 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 22 Nov 2019 11:50:31 +0100 Subject: [PATCH 08/26] feat(nuke): adding label to collect backdrop --- ...llect_nukenodes.py => collect_backdrop.py} | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) rename pype/plugins/nuke/publish/{collect_nukenodes.py => collect_backdrop.py} (64%) diff --git a/pype/plugins/nuke/publish/collect_nukenodes.py b/pype/plugins/nuke/publish/collect_backdrop.py similarity index 64% rename from pype/plugins/nuke/publish/collect_nukenodes.py rename to pype/plugins/nuke/publish/collect_backdrop.py index ba5a28c44a..d88a702a84 100644 --- a/pype/plugins/nuke/publish/collect_nukenodes.py +++ b/pype/plugins/nuke/publish/collect_backdrop.py @@ -1,11 +1,13 @@ import pyblish.api import nuke + +@pyblish.api.log class CollectBackdrops(pyblish.api.InstancePlugin): - """Collect Backdrop instance from rendered frames + """Collect Backdrop node instance and its content """ - order = pyblish.api.CollectorOrder + 0.3 + order = pyblish.api.CollectorOrder + 0.22 label = "Collect Backdrop" hosts = ["nuke"] families = ["nukenodes"] @@ -14,21 +16,30 @@ class CollectBackdrops(pyblish.api.InstancePlugin): bckn = instance[0] + # define size of the backdrop left = bckn.xpos() top = bckn.ypos() right = left + bckn['bdwidth'].value() bottom = top + bckn['bdheight'].value() - inNodes = [] + # iterate all nodes for node in nuke.allNodes(): + + # exclude viewer if node.Class() == "Viewer": continue + # find all related nodes if (node.xpos() > left) \ and (node.xpos() + node.screenWidth() < right) \ and (node.ypos() > top) \ and (node.ypos() + node.screenHeight() < bottom): - inNodes.append(node) - self.log.info("Backdrop content collected: `{}`".format(inNodes)) + # add contained nodes to instance's node list + instance.append(node) + + instance.data["label"] = "{0} ({1} nodes)".format( + bckn.name(), len(instance)-1) + + self.log.info("Backdrop content collected: `{}`".format(instance[:])) self.log.info("Backdrop instance collected: `{}`".format(instance)) From 351c7ed95f4ef71d1d434df04126baab26c2411f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 22 Nov 2019 11:51:12 +0100 Subject: [PATCH 09/26] feat(nuke): adding validate backdrop checking if there are nodes above backdrop --- .../plugins/nuke/publish/validate_backdrop.py | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 pype/plugins/nuke/publish/validate_backdrop.py diff --git a/pype/plugins/nuke/publish/validate_backdrop.py b/pype/plugins/nuke/publish/validate_backdrop.py new file mode 100644 index 0000000000..59c4e96b37 --- /dev/null +++ b/pype/plugins/nuke/publish/validate_backdrop.py @@ -0,0 +1,64 @@ +import pyblish +from avalon.nuke import lib as anlib +import nuke + + +class SelectCenterInNodeGraph(pyblish.api.Action): + """ + Centering failed instance node in node grap + """ + + label = "Center node in node graph" + icon = "wrench" + on = "failed" + + def process(self, context, plugin): + + # Get the errored instances + failed = [] + for result in context.data["results"]: + if (result["error"] is not None and result["instance"] is not None + and result["instance"] not in failed): + failed.append(result["instance"]) + + # Apply pyblish.logic to get the instances for the plug-in + instances = pyblish.api.instances_by_plugin(failed, plugin) + + all_xC = list() + all_yC = list() + + # maintain selection + with anlib.maintained_selection(): + # collect all failed nodes xpos and ypos + for instance in instances: + bdn = instance[0] + xC = bdn.xpos() + bdn.screenWidth()/2 + yC = bdn.ypos() + bdn.screenHeight()/2 + + all_xC.append(xC) + all_yC.append(yC) + + self.log.info("all_xC: `{}`".format(all_xC)) + self.log.info("all_yC: `{}`".format(all_yC)) + + # zoom to nodes in node graph + nuke.zoom(2, [min(all_xC), min(all_yC)]) + + +@pyblish.api.log +class ValidateBackdrop(pyblish.api.InstancePlugin): + """Validate amount of nodes on backdrop node in case user + forgoten to add nodes above the publishing backdrop node""" + + order = pyblish.api.ValidatorOrder + optional = True + families = ["nukenodes"] + label = "Validate Backdrop" + hosts = ["nuke"] + actions = [SelectCenterInNodeGraph] + + def process(self, instance): + + msg = "No content on backdrop node: \"{}\"".format( + instance.data["name"]) + assert len(instance) > 1, msg From 443f47d240637322a9ea80c8077c78ffce9af2a6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Sat, 23 Nov 2019 23:43:48 +0100 Subject: [PATCH 10/26] feat(nuke): adding `nukenodes` family --- pype/nuke/__init__.py | 3 ++- pype/plugins/global/publish/integrate_new.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pype/nuke/__init__.py b/pype/nuke/__init__.py index b7dbf69510..a7e63bf06d 100644 --- a/pype/nuke/__init__.py +++ b/pype/nuke/__init__.py @@ -112,7 +112,8 @@ def install(): # Disable all families except for the ones we explicitly want to see family_states = [ "write", - "review" + "review", + "nukenodes" ] avalon.data["familiesStateDefault"] = False diff --git a/pype/plugins/global/publish/integrate_new.py b/pype/plugins/global/publish/integrate_new.py index 64f6dd5015..10df19b953 100644 --- a/pype/plugins/global/publish/integrate_new.py +++ b/pype/plugins/global/publish/integrate_new.py @@ -70,7 +70,8 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): "lut", "audio", "yetiRig", - "yeticache" + "yeticache", + "nukenodes" ] exclude_families = ["clip"] From d21e98aea977303b25342105b45476003a936647 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Sat, 23 Nov 2019 23:44:43 +0100 Subject: [PATCH 11/26] feat(nuke): adding method for getting dependency for list of nodes --- pype/nuke/lib.py | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/pype/nuke/lib.py b/pype/nuke/lib.py index 0079a23266..816a7d5116 100644 --- a/pype/nuke/lib.py +++ b/pype/nuke/lib.py @@ -108,7 +108,7 @@ def writes_version_sync(): # check if the node is avalon tracked if "AvalonTab" not in each.knobs(): continue - + avalon_knob_data = avalon.nuke.get_avalon_knob_data( each, ['avalon:', 'ak:']) @@ -1194,3 +1194,39 @@ class BuildWorkfile(WorkfileSettings): def position_up(self, multiply=1): self.ypos -= (self.ypos_size * multiply) + self.ypos_gap + +def get_dependent_nodes(nodes): + """Get all dependent nodes connected to the list of nodes. + + Looking for connections outside of the nodes in incoming argument. + + Arguments: + nodes (list): list of nuke.Node objects + + Returns: + connections_in: dictionary of nodes and its dependencies + connections_out: dictionary of nodes and its dependency + """ + + connections_in = dict() + connections_out = dict() + node_names = [n.name() for n in nodes] + for node in nodes: + inputs = node.dependencies() + outputs = node.dependent() + # collect all inputs outside + test_in = [(i, n) for i, n in enumerate(inputs) + if n.name() not in node_names] + if test_in: + connections_in.update({ + node: test_in + }) + # collect all outputs outside + test_out = [i for i in outputs if i.name() not in node_names] + if test_out: + # only one dependent node is allowed + connections_out.update({ + node: test_out[-1] + }) + + return connections_in, connections_out From 641406c08846603cc829fa0669c00f35f8a79b81 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Sat, 23 Nov 2019 23:45:37 +0100 Subject: [PATCH 12/26] feat(nuke): adding `nukenodes` family for loading precoms --- pype/plugins/nuke/load/load_script_precomp.py | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/pype/plugins/nuke/load/load_script_precomp.py b/pype/plugins/nuke/load/load_script_precomp.py index e84e23a890..310157f099 100644 --- a/pype/plugins/nuke/load/load_script_precomp.py +++ b/pype/plugins/nuke/load/load_script_precomp.py @@ -7,7 +7,7 @@ class LinkAsGroup(api.Loader): """Copy the published file to be pasted at the desired location""" representations = ["nk"] - families = ["workfile"] + families = ["workfile", "nukenodes"] label = "Load Precomp" order = 0 @@ -63,8 +63,6 @@ class LinkAsGroup(api.Loader): colorspace = context["version"]["data"].get("colorspace", None) self.log.info("colorspace: {}\n".format(colorspace)) - # ['version', 'file', 'reading', 'output', 'useOutput'] - P["name"].setValue("{}_{}".format(name, namespace)) P["useOutput"].setValue(True) @@ -74,14 +72,15 @@ class LinkAsGroup(api.Loader): if n.Class() == "Group" if get_avalon_knob_data(n)] - # create panel for selecting output - panel_choices = " ".join(writes) - panel_label = "Select write node for output" - p = nuke.Panel("Select Write Node") - p.addEnumerationPulldown( - panel_label, panel_choices) - p.show() - P["output"].setValue(p.value(panel_label)) + if writes: + # create panel for selecting output + panel_choices = " ".join(writes) + panel_label = "Select write node for output" + p = nuke.Panel("Select Write Node") + p.addEnumerationPulldown( + panel_label, panel_choices) + p.show() + P["output"].setValue(p.value(panel_label)) P["tile_color"].setValue(0xff0ff0ff) From c8a1bd151f9d23c3a432d86dd58b54db97af0f77 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Sat, 23 Nov 2019 23:46:29 +0100 Subject: [PATCH 13/26] feat(nuke): collecting | validating | extracting backdrop --- pype/plugins/nuke/publish/collect_backdrop.py | 41 ++++++- pype/plugins/nuke/publish/extract_backdrop.py | 103 ++++++++++++++++++ .../plugins/nuke/publish/validate_backdrop.py | 9 +- 3 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 pype/plugins/nuke/publish/extract_backdrop.py diff --git a/pype/plugins/nuke/publish/collect_backdrop.py b/pype/plugins/nuke/publish/collect_backdrop.py index d88a702a84..39402f5352 100644 --- a/pype/plugins/nuke/publish/collect_backdrop.py +++ b/pype/plugins/nuke/publish/collect_backdrop.py @@ -1,7 +1,8 @@ import pyblish.api +import pype.api as pype +from pype.nuke import lib as pnlib import nuke - @pyblish.api.log class CollectBackdrops(pyblish.api.InstancePlugin): """Collect Backdrop node instance and its content @@ -38,8 +39,46 @@ class CollectBackdrops(pyblish.api.InstancePlugin): # add contained nodes to instance's node list instance.append(node) + # get all connections from outside of backdrop + nodes = instance[1:] + connections_in, connections_out = pnlib.get_dependent_nodes(nodes) + instance.data["connections_in"] = connections_in + instance.data["connections_out"] = connections_out + + # make label nicer instance.data["label"] = "{0} ({1} nodes)".format( bckn.name(), len(instance)-1) + instance.data["families"].append(instance.data["family"]) + + # Get frame range + handle_start = instance.context.data["handleStart"] + handle_end = instance.context.data["handleEnd"] + first_frame = int(nuke.root()["first_frame"].getValue()) + last_frame = int(nuke.root()["last_frame"].getValue()) + + # get version + version = pype.get_version_from_path(nuke.root().name()) + instance.data['version'] = version + + # Add version data to instance + version_data = { + "handles": handle_start, + "handleStart": handle_start, + "handleEnd": handle_end, + "frameStart": first_frame + handle_start, + "frameEnd": last_frame - handle_end, + "version": int(version), + "families": [instance.data["family"]] + instance.data["families"], + "subset": instance.data["subset"], + "fps": instance.context.data["fps"] + } + + instance.data.update({ + "versionData": version_data, + "frameStart": first_frame, + "frameEnd": last_frame, + "subsetGroup": "backdrops" + }) self.log.info("Backdrop content collected: `{}`".format(instance[:])) self.log.info("Backdrop instance collected: `{}`".format(instance)) diff --git a/pype/plugins/nuke/publish/extract_backdrop.py b/pype/plugins/nuke/publish/extract_backdrop.py new file mode 100644 index 0000000000..7b01b5deac --- /dev/null +++ b/pype/plugins/nuke/publish/extract_backdrop.py @@ -0,0 +1,103 @@ +import pyblish.api +from avalon.nuke import lib as anlib +from pype.nuke import lib as pnlib +import nuke +import os +import pype +reload(pnlib) + +class ExtractBackdropNode(pype.api.Extractor): + """Extracting content of backdrop nodes + + Will create nuke script only with containing nodes. + Also it will solve Input and Output nodes. + + """ + + order = pyblish.api.ExtractorOrder + label = "Extract Backdrop" + hosts = ["nuke"] + families = ["nukenodes"] + + def process(self, instance): + tmp_nodes = list() + nodes = instance[1:] + # Define extract output file path + stagingdir = self.staging_dir(instance) + filename = "{0}.nk".format(instance.name) + path = os.path.join(stagingdir, filename) + + # maintain selection + with anlib.maintained_selection(): + # all connections outside of backdrop + connections_in = instance.data["connections_in"] + connections_out = instance.data["connections_out"] + self.log.debug("_ connections_in: `{}`".format(connections_in)) + self.log.debug("_ connections_out: `{}`".format(connections_out)) + + # create input nodes and name them as passing node (*_INP) + for n, inputs in connections_in.items(): + for i, input in inputs: + inpn = nuke.createNode("Input") + inpn["name"].setValue("{}_{}_INP".format(n.name(), i)) + n.setInput(i, inpn) + inpn.setXYpos(input.xpos(), input.ypos()) + nodes.append(inpn) + tmp_nodes.append(inpn) + + anlib.reset_selection() + + # connect output node + for n, output in connections_out.items(): + opn = nuke.createNode("Output") + self.log.info(n.name()) + self.log.info(output.name()) + output.setInput( + next((i for i, d in enumerate(output.dependencies()) + if d.name() in n.name()), 0), opn) + opn.setInput(0, n) + opn.autoplace() + nodes.append(opn) + tmp_nodes.append(opn) + anlib.reset_selection() + + # select nodes to copy + anlib.reset_selection() + anlib.select_nodes(nodes) + # create tmp nk file + # save file to the path + nuke.nodeCopy(path) + + # Clean up + for tn in tmp_nodes: + nuke.delete(tn) + + # restore original connections + # reconnect input node + for n, inputs in connections_in.items(): + for i, input in inputs: + n.setInput(i, input) + + # reconnect output node + for n, output in connections_out.items(): + output.setInput( + next((i for i, d in enumerate(output.dependencies()) + if d.name() in n.name()), 0), n) + + if "representations" not in instance.data: + instance.data["representations"] = [] + + # create representation + representation = { + 'name': 'nk', + 'ext': 'nk', + 'files': filename, + "stagingDir": stagingdir + } + instance.data["representations"].append(representation) + + self.log.info("Extracted instance '{}' to: {}".format( + instance.name, path)) + + self.log.info("Data {}".format( + instance.data)) diff --git a/pype/plugins/nuke/publish/validate_backdrop.py b/pype/plugins/nuke/publish/validate_backdrop.py index 59c4e96b37..cf2d56087d 100644 --- a/pype/plugins/nuke/publish/validate_backdrop.py +++ b/pype/plugins/nuke/publish/validate_backdrop.py @@ -58,7 +58,12 @@ class ValidateBackdrop(pyblish.api.InstancePlugin): actions = [SelectCenterInNodeGraph] def process(self, instance): + connections_out = instance.data["connections_out"] - msg = "No content on backdrop node: \"{}\"".format( + msg_multiple_outputs = "Only one outcoming connection from \"{}\" is allowed".format( instance.data["name"]) - assert len(instance) > 1, msg + assert len(connections_out.keys()) <= 1, msg_multiple_outputs + + msg_no_content = "No content on backdrop node: \"{}\"".format( + instance.data["name"]) + assert len(instance) > 1, msg_no_content From fc6733f582c427a4d197a518a683f7a02c6811ff Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Sun, 24 Nov 2019 20:35:55 +0100 Subject: [PATCH 14/26] feat(nuke): adding utils.py for nuke specific operations - bake_gizmos_recursively - get_node_outputs - is_node_gizmo - gizmo_is_nuke_default --- pype/nuke/utils.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 pype/nuke/utils.py diff --git a/pype/nuke/utils.py b/pype/nuke/utils.py new file mode 100644 index 0000000000..5250e80f25 --- /dev/null +++ b/pype/nuke/utils.py @@ -0,0 +1,64 @@ +import os +import nuke +from avalon.nuke import lib as anlib + + +def get_node_outputs(node): + ''' + Return a dictionary of the nodes and pipes that are connected to node + ''' + depDict = {} + dependencies = node.dependent(nuke.INPUTS | nuke.HIDDEN_INPUTS) + for d in dependencies: + depDict[d] = [] + for i in range(d.inputs()): + if d.input(i) == node: + depDict[d].append(i) + return depDict + + +def is_node_gizmo(node): + ''' + return True if node is gizmo + ''' + return 'gizmo_file' in node.knobs() + + +def gizmo_is_nuke_default(gizmo): + '''Check if gizmo is in default install path''' + plugDir = os.path.join(os.path.dirname( + nuke.env['ExecutablePath']), 'plugins') + return gizmo.filename().startswith(plugDir) + + +def bake_gizmos_recursively(in_group=nuke.Root()): + """Converting a gizmo to group + + Argumets: + is_group (nuke.Node)[optonal]: group node or all nodes + """ + # preserve selection after all is done + with anlib.maintained_selection(): + # jump to the group + with in_group: + for node in nuke.allNodes(): + if is_node_gizmo(node) and not gizmo_is_nuke_default(node): + with node: + outputs = get_node_outputs(node) + group = node.makeGroup() + # Reconnect inputs and outputs if any + if outputs: + for n, pipes in outputs.items(): + for i in pipes: + n.setInput(i, group) + for i in range(node.inputs()): + group.setInput(i, node.input(i)) + # set node position and name + group.setXYpos(node.xpos(), node.ypos()) + name = node.name() + nuke.delete(node) + group.setName(name) + node = group + + if node.Class() == "Group": + bake_gizmos_recursively(node) From fe7e3580d6b8d01b65b8d922b09c1e5d1205b864 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Sun, 24 Nov 2019 20:41:21 +0100 Subject: [PATCH 15/26] fix(nuke): converting camelcase to snakecase --- pype/nuke/utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pype/nuke/utils.py b/pype/nuke/utils.py index 5250e80f25..7583221696 100644 --- a/pype/nuke/utils.py +++ b/pype/nuke/utils.py @@ -7,14 +7,14 @@ def get_node_outputs(node): ''' Return a dictionary of the nodes and pipes that are connected to node ''' - depDict = {} + dep_dict = {} dependencies = node.dependent(nuke.INPUTS | nuke.HIDDEN_INPUTS) for d in dependencies: - depDict[d] = [] + dep_dict[d] = [] for i in range(d.inputs()): if d.input(i) == node: - depDict[d].append(i) - return depDict + dep_dict[d].append(i) + return dep_dict def is_node_gizmo(node): @@ -26,9 +26,9 @@ def is_node_gizmo(node): def gizmo_is_nuke_default(gizmo): '''Check if gizmo is in default install path''' - plugDir = os.path.join(os.path.dirname( + plug_dir = os.path.join(os.path.dirname( nuke.env['ExecutablePath']), 'plugins') - return gizmo.filename().startswith(plugDir) + return gizmo.filename().startswith(plug_dir) def bake_gizmos_recursively(in_group=nuke.Root()): From 498ba601b689a775de521c0d72a605b434583446 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 25 Nov 2019 00:53:06 +0100 Subject: [PATCH 16/26] feat(nuke): adding CreateGizmo plugin --- pype/plugins/nuke/create/create_gizmo.py | 79 ++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 pype/plugins/nuke/create/create_gizmo.py diff --git a/pype/plugins/nuke/create/create_gizmo.py b/pype/plugins/nuke/create/create_gizmo.py new file mode 100644 index 0000000000..41229862e3 --- /dev/null +++ b/pype/plugins/nuke/create/create_gizmo.py @@ -0,0 +1,79 @@ +from avalon.nuke.pipeline import Creator +from avalon.nuke import lib as anlib +import nuke +import nukescripts + +class CreateGizmo(Creator): + """Add Publishable "gizmo" group + + The name is symbolically gizmo as presumably + it is something familiar to nuke users as group of nodes + distributed downstream in workflow + """ + + name = "gizmo" + label = "Gizmo" + family = "gizmo" + icon = "file-archive-o" + defaults = ["ViewerInput", "Lut", "Effect"] + + def __init__(self, *args, **kwargs): + super(CreateGizmo, self).__init__(*args, **kwargs) + self.nodes = nuke.selectedNodes() + self.node_color = "0x7533c1ff" + return + + def process(self): + if (self.options or {}).get("useSelection"): + nodes = self.nodes + self.log.info(len(nodes)) + if len(nodes) == 1: + anlib.select_nodes(nodes) + node = nodes[-1] + # check if Group node + if node.Class() in "Group": + node["name"].setValue("{}_GZM".format(self.name)) + node["tile_color"].setValue(int(self.node_color, 16)) + return anlib.imprint(node, self.data) + else: + nuke.message("Please select a group node " + "you wish to publish as the gizmo") + + if len(nodes) >= 2: + anlib.select_nodes(nodes) + nuke.makeGroup() + gizmo_node = nuke.selectedNode() + gizmo_node["name"].setValue("{}_GZM".format(self.name)) + gizmo_node["tile_color"].setValue(int(self.node_color, 16)) + + # add sticky node wit guide + with gizmo_node: + sticky = nuke.createNode("StickyNote") + sticky["label"].setValue( + "Add following:\n- set Input" + " nodes\n- set one Output1\n" + "- create User knobs on the group") + + # add avalon knobs + return anlib.imprint(gizmo_node, self.data) + + else: + nuke.message("Please select nodes you " + "wish to add to the gizmo") + return + else: + with anlib.maintained_selection(): + gizmo_node = nuke.createNode("Group") + gizmo_node["name"].setValue("{}_GZM".format(self.name)) + gizmo_node["tile_color"].setValue(int(self.node_color, 16)) + + # add sticky node wit guide + with gizmo_node: + sticky = nuke.createNode("StickyNote") + sticky["label"].setValue( + "Add following:\n- add Input" + " nodes\n- add one Output1\n" + "- create User knobs on the group") + + # add avalon knobs + return anlib.imprint(gizmo_node, self.data) From e07a95480ba9fb7f0f90a2810dbf16fbf39054ff Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 25 Nov 2019 00:55:32 +0100 Subject: [PATCH 17/26] feat(nuke): adding `gizmo` family --- pype/nuke/__init__.py | 3 ++- pype/plugins/global/publish/integrate_new.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pype/nuke/__init__.py b/pype/nuke/__init__.py index b7dbf69510..aa746ea872 100644 --- a/pype/nuke/__init__.py +++ b/pype/nuke/__init__.py @@ -112,7 +112,8 @@ def install(): # Disable all families except for the ones we explicitly want to see family_states = [ "write", - "review" + "review", + "gizmo" ] avalon.data["familiesStateDefault"] = False diff --git a/pype/plugins/global/publish/integrate_new.py b/pype/plugins/global/publish/integrate_new.py index 64f6dd5015..f1b3a69d1e 100644 --- a/pype/plugins/global/publish/integrate_new.py +++ b/pype/plugins/global/publish/integrate_new.py @@ -70,7 +70,8 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): "lut", "audio", "yetiRig", - "yeticache" + "yeticache", + "gizmo" ] exclude_families = ["clip"] From 1077a02e9296635646dd3e3890561a156b8e4cab Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 25 Nov 2019 15:09:52 +0100 Subject: [PATCH 18/26] feat(nuke): adding process() to create backdrop --- pype/plugins/nuke/create/create_backdrop.py | 37 ++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/pype/plugins/nuke/create/create_backdrop.py b/pype/plugins/nuke/create/create_backdrop.py index 237f9ac8f0..767e92b592 100644 --- a/pype/plugins/nuke/create/create_backdrop.py +++ b/pype/plugins/nuke/create/create_backdrop.py @@ -1,5 +1,6 @@ from avalon.nuke.pipeline import Creator - +from avalon.nuke import lib as anlib +import nuke class CreateBackdrop(Creator): """Add Publishable Backdrop""" @@ -12,4 +13,38 @@ class CreateBackdrop(Creator): def __init__(self, *args, **kwargs): super(CreateBackdrop, self).__init__(*args, **kwargs) + self.nodes = nuke.selectedNodes() + self.node_color = "0xdfea5dff" return + + def process(self): + from nukescripts import autoBackdrop + nodes = list() + if (self.options or {}).get("useSelection"): + nodes = self.nodes + + if len(nodes) >= 1: + anlib.select_nodes(nodes) + bckd_node = autoBackdrop() + bckd_node["name"].setValue("{}_BDN".format(self.name)) + bckd_node["tile_color"].setValue(int(self.node_color, 16)) + bckd_node["note_font_size"].setValue(24) + bckd_node["label"].setValue("[{}]".format(self.name)) + # add avalon knobs + instance = anlib.imprint(bckd_node, self.data) + + return instance + else: + nuke.message("Please select nodes you " + "wish to add to a container") + return + else: + bckd_node = autoBackdrop() + bckd_node["name"].setValue("{}_BDN".format(self.name)) + bckd_node["tile_color"].setValue(int(self.node_color, 16)) + bckd_node["note_font_size"].setValue(24) + bckd_node["label"].setValue("[{}]".format(self.name)) + # add avalon knobs + instance = anlib.imprint(bckd_node, self.data) + + return instance From 3ed91886b49749a453951ef6795d3c6396c2581a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 25 Nov 2019 20:53:49 +0100 Subject: [PATCH 19/26] fix(global): integrate_new - families missing comma - subsetGroup were not integrating properly --- pype/plugins/global/publish/integrate_new.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pype/plugins/global/publish/integrate_new.py b/pype/plugins/global/publish/integrate_new.py index 63c22a1f46..80e11c0624 100644 --- a/pype/plugins/global/publish/integrate_new.py +++ b/pype/plugins/global/publish/integrate_new.py @@ -62,7 +62,6 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): "render", "imagesequence", "review", - "render", "rendersetup", "rig", "plate", @@ -71,7 +70,7 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): "audio", "yetiRig", "yeticache", - "nukenodes" + "nukenodes", "gizmo" ] exclude_families = ["clip"] @@ -528,10 +527,11 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): subset["data"].update( {"subsetGroup": instance.data.get("subsetGroup")} ) + self.log.info("__ subset.data: {}".format(subset["data"])) io.update_many({ 'type': 'subset', '_id': io.ObjectId(subset["_id"]) - }, {'$set': subset["data"]} + }, {'$set': {'data': subset["data"]}} ) return subset From 3e2635f3b9d7abcaec7f56ea03a1d9837f5f8fc4 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 25 Nov 2019 20:54:30 +0100 Subject: [PATCH 20/26] fix(nuke): collect legacy write was overwriting family on all groups --- pype/plugins/nuke/publish/collect_legacy_write.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pype/plugins/nuke/publish/collect_legacy_write.py b/pype/plugins/nuke/publish/collect_legacy_write.py index 74280b743a..cfb0798434 100644 --- a/pype/plugins/nuke/publish/collect_legacy_write.py +++ b/pype/plugins/nuke/publish/collect_legacy_write.py @@ -24,7 +24,8 @@ class CollectWriteLegacy(pyblish.api.InstancePlugin): self.log.info("render") return - instance.data.update( - {"family": "write.legacy", - "families": []} - ) + if "render" in node.knobs(): + instance.data.update( + {"family": "write.legacy", + "families": []} + ) From 28a048b57d9ddf4352d85e6d322cd24b4e80f2d5 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 25 Nov 2019 21:04:27 +0100 Subject: [PATCH 21/26] feat(nuke): adding Collect | Validate | Extract Gizmo family --- pype/plugins/nuke/publish/collect_gizmo.py | 56 ++++++++++++ pype/plugins/nuke/publish/extract_gizmo.py | 95 +++++++++++++++++++++ pype/plugins/nuke/publish/validate_gizmo.py | 58 +++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 pype/plugins/nuke/publish/collect_gizmo.py create mode 100644 pype/plugins/nuke/publish/extract_gizmo.py create mode 100644 pype/plugins/nuke/publish/validate_gizmo.py diff --git a/pype/plugins/nuke/publish/collect_gizmo.py b/pype/plugins/nuke/publish/collect_gizmo.py new file mode 100644 index 0000000000..ada3400dfc --- /dev/null +++ b/pype/plugins/nuke/publish/collect_gizmo.py @@ -0,0 +1,56 @@ +import pyblish.api +import pype.api as pype +import nuke + + +@pyblish.api.log +class CollectGizmo(pyblish.api.InstancePlugin): + """Collect Gizmo (group) node instance and its content + """ + + order = pyblish.api.CollectorOrder + 0.22 + label = "Collect Gizmo (Group)" + hosts = ["nuke"] + families = ["gizmo"] + + def process(self, instance): + + grpn = instance[0] + + # add family to familiess + instance.data["families"].insert(0, instance.data["family"]) + # make label nicer + instance.data["label"] = "{0} ({1} nodes)".format( + grpn.name(), len(instance) - 1) + + # Get frame range + handle_start = instance.context.data["handleStart"] + handle_end = instance.context.data["handleEnd"] + first_frame = int(nuke.root()["first_frame"].getValue()) + last_frame = int(nuke.root()["last_frame"].getValue()) + + # get version + version = pype.get_version_from_path(nuke.root().name()) + instance.data['version'] = version + + # Add version data to instance + version_data = { + "handles": handle_start, + "handleStart": handle_start, + "handleEnd": handle_end, + "frameStart": first_frame + handle_start, + "frameEnd": last_frame - handle_end, + "version": int(version), + "families": [instance.data["family"]] + instance.data["families"], + "subset": instance.data["subset"], + "fps": instance.context.data["fps"] + } + + instance.data.update({ + "versionData": version_data, + "frameStart": first_frame, + "frameEnd": last_frame, + "subsetGroup": "gizmos" + }) + self.log.info("Gizmo content collected: `{}`".format(instance[:])) + self.log.info("Gizmo instance collected: `{}`".format(instance)) diff --git a/pype/plugins/nuke/publish/extract_gizmo.py b/pype/plugins/nuke/publish/extract_gizmo.py new file mode 100644 index 0000000000..36ef1d464c --- /dev/null +++ b/pype/plugins/nuke/publish/extract_gizmo.py @@ -0,0 +1,95 @@ +import pyblish.api +from avalon.nuke import lib as anlib +from pype.nuke import lib as pnlib +from pype.nuke import utils as pnutils +import nuke +import os +import pype + + +class ExtractGizmo(pype.api.Extractor): + """Extracting Gizmo (Group) node + + Will create nuke script only with the Gizmo node. + """ + + order = pyblish.api.ExtractorOrder + label = "Extract Gizmo (Group)" + hosts = ["nuke"] + families = ["gizmo"] + + def process(self, instance): + tmp_nodes = list() + orig_grpn = instance[0] + # Define extract output file path + stagingdir = self.staging_dir(instance) + filename = "{0}.nk".format(instance.name) + path = os.path.join(stagingdir, filename) + + # maintain selection + with anlib.maintained_selection(): + orig_grpn_name = orig_grpn.name() + tmp_grpn_name = orig_grpn_name + "_tmp" + # select original group node + anlib.select_nodes([orig_grpn]) + + # copy to clipboard + nuke.nodeCopy("%clipboard%") + + # reset selection to none + anlib.reset_selection() + + # paste clipboard + nuke.nodePaste("%clipboard%") + + # assign pasted node + copy_grpn = nuke.selectedNode() + copy_grpn.setXYpos((orig_grpn.xpos() + 120), orig_grpn.ypos()) + + # convert gizmos to groups + pnutils.bake_gizmos_recursively(copy_grpn) + + # remove avalonknobs + knobs = copy_grpn.knobs() + avalon_knobs = [k for k in knobs.keys() + for ak in ["avalon:", "ak:"] + if ak in k] + avalon_knobs.append("publish") + for ak in avalon_knobs: + copy_grpn.removeKnob(knobs[ak]) + + # add to temporary nodes + tmp_nodes.append(copy_grpn) + + # swap names + orig_grpn.setName(tmp_grpn_name) + copy_grpn.setName(orig_grpn_name) + + # create tmp nk file + # save file to the path + nuke.nodeCopy(path) + + # Clean up + for tn in tmp_nodes: + nuke.delete(tn) + + # rename back to original + orig_grpn.setName(orig_grpn_name) + + if "representations" not in instance.data: + instance.data["representations"] = [] + + # create representation + representation = { + 'name': 'gizmo', + 'ext': 'nk', + 'files': filename, + "stagingDir": stagingdir + } + instance.data["representations"].append(representation) + + self.log.info("Extracted instance '{}' to: {}".format( + instance.name, path)) + + self.log.info("Data {}".format( + instance.data)) diff --git a/pype/plugins/nuke/publish/validate_gizmo.py b/pype/plugins/nuke/publish/validate_gizmo.py new file mode 100644 index 0000000000..9c94ea88ef --- /dev/null +++ b/pype/plugins/nuke/publish/validate_gizmo.py @@ -0,0 +1,58 @@ +import pyblish +from avalon.nuke import lib as anlib +import nuke + + +class OpenFailedGroupNode(pyblish.api.Action): + """ + Centering failed instance node in node grap + """ + + label = "Open Gizmo in Node Graph" + icon = "wrench" + on = "failed" + + def process(self, context, plugin): + + # Get the errored instances + failed = [] + for result in context.data["results"]: + if (result["error"] is not None and result["instance"] is not None + and result["instance"] not in failed): + failed.append(result["instance"]) + + # Apply pyblish.logic to get the instances for the plug-in + instances = pyblish.api.instances_by_plugin(failed, plugin) + + # maintain selection + with anlib.maintained_selection(): + # collect all failed nodes xpos and ypos + for instance in instances: + grpn = instance[0] + nuke.showDag(grpn) + + +@pyblish.api.log +class ValidateGizmo(pyblish.api.InstancePlugin): + """Validate amount of output nodes in gizmo (group) node""" + + order = pyblish.api.ValidatorOrder + optional = True + families = ["gizmo"] + label = "Validate Gizmo (Group)" + hosts = ["nuke"] + actions = [OpenFailedGroupNode] + + def process(self, instance): + grpn = instance[0] + + with grpn: + connections_out = nuke.allNodes('Output') + msg_multiple_outputs = "Only one outcoming connection from " + "\"{}\" is allowed".format(instance.data["name"]) + assert len(connections_out) <= 1, msg_multiple_outputs + + connections_in = nuke.allNodes('Input') + msg_missing_inputs = "At least one Input node has to be used in: " + "\"{}\"".format(instance.data["name"]) + assert len(connections_in) >= 1, msg_missing_inputs From 7b9530613ddefaf14f193ea5bae6721a650dcf89 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 26 Nov 2019 00:19:41 +0100 Subject: [PATCH 22/26] fix(global): integrate_new `subsetGroup` improvement --- pype/plugins/global/publish/integrate_new.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pype/plugins/global/publish/integrate_new.py b/pype/plugins/global/publish/integrate_new.py index 80e11c0624..f82df891f2 100644 --- a/pype/plugins/global/publish/integrate_new.py +++ b/pype/plugins/global/publish/integrate_new.py @@ -524,14 +524,11 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): # add group if available if instance.data.get("subsetGroup"): - subset["data"].update( - {"subsetGroup": instance.data.get("subsetGroup")} - ) - self.log.info("__ subset.data: {}".format(subset["data"])) io.update_many({ 'type': 'subset', '_id': io.ObjectId(subset["_id"]) - }, {'$set': {'data': subset["data"]}} + }, {'$set': {'data.subsetGroup': + instance.data.get('subsetGroup')}} ) return subset From 5fd691efe3607dec70247041a3c112893100b325 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 26 Nov 2019 00:20:04 +0100 Subject: [PATCH 23/26] feat(nuke): adding load gizmo as Imput Process --- pype/plugins/nuke/load/load_gizmo_ip.py | 239 ++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 pype/plugins/nuke/load/load_gizmo_ip.py diff --git a/pype/plugins/nuke/load/load_gizmo_ip.py b/pype/plugins/nuke/load/load_gizmo_ip.py new file mode 100644 index 0000000000..0d78c14214 --- /dev/null +++ b/pype/plugins/nuke/load/load_gizmo_ip.py @@ -0,0 +1,239 @@ +from avalon import api, style, io +import nuke +from pype.nuke import lib as pnlib +from avalon.nuke import lib as anlib +from avalon.nuke import containerise, update_container + + +class LoadGizmoInputProcess(api.Loader): + """Loading colorspace soft effect exported from nukestudio""" + + representations = ["gizmo"] + families = ["gizmo"] + + label = "Load Gizmo - Input Process" + order = 0 + icon = "eye" + color = style.colors.alert + node_color = "0x7533c1ff" + + def load(self, context, name, namespace, data): + """ + Loading function to get Gizmo as Input Process on viewer + + Arguments: + context (dict): context of version + name (str): name of the version + namespace (str): asset name + data (dict): compulsory attribute > not used + + Returns: + nuke node: containerised nuke node object + """ + + # get main variables + version = context['version'] + version_data = version.get("data", {}) + vname = version.get("name", None) + first = version_data.get("frameStart", None) + last = version_data.get("frameEnd", None) + namespace = namespace or context['asset']['name'] + colorspace = version_data.get("colorspace", None) + object_name = "{}_{}".format(name, namespace) + + # prepare data for imprinting + # add additional metadata from the version to imprint to Avalon knob + add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd", + "source", "author", "fps"] + + data_imprint = {"frameStart": first, + "frameEnd": last, + "version": vname, + "colorspaceInput": colorspace, + "objectName": object_name} + + for k in add_keys: + data_imprint.update({k: version_data[k]}) + + # getting file path + file = self.fname.replace("\\", "/") + + # adding nodes to node graph + # just in case we are in group lets jump out of it + nuke.endGroup() + + with anlib.maintained_selection(): + # add group from nk + nuke.nodePaste(file) + + GN = nuke.selectedNode() + + GN["name"].setValue(object_name) + + # try to place it under Viewer1 + if not self.connect_active_viewer(GN): + nuke.delete(GN) + return + + return containerise( + node=GN, + name=name, + namespace=namespace, + context=context, + loader=self.__class__.__name__, + data=data_imprint) + + def update(self, container, representation): + """Update the Loader's path + + Nuke automatically tries to reset some variables when changing + the loader's path to a new file. These automatic changes are to its + inputs: + + """ + + # get main variables + # Get version from io + version = io.find_one({ + "type": "version", + "_id": representation["parent"] + }) + # get corresponding node + GN = nuke.toNode(container['objectName']) + + file = api.get_representation_path(representation).replace("\\", "/") + context = representation["context"] + name = container['name'] + version_data = version.get("data", {}) + vname = version.get("name", None) + first = version_data.get("frameStart", None) + last = version_data.get("frameEnd", None) + namespace = container['namespace'] + colorspace = version_data.get("colorspace", None) + object_name = "{}_{}".format(name, namespace) + + add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd", + "source", "author", "fps"] + + data_imprint = {"representation": str(representation["_id"]), + "frameStart": first, + "frameEnd": last, + "version": vname, + "colorspaceInput": colorspace, + "objectName": object_name} + + for k in add_keys: + data_imprint.update({k: version_data[k]}) + + # adding nodes to node graph + # just in case we are in group lets jump out of it + nuke.endGroup() + + with anlib.maintained_selection(): + xpos = GN.xpos() + ypos = GN.ypos() + avalon_data = anlib.get_avalon_knob_data(GN) + nuke.delete(GN) + # add group from nk + nuke.nodePaste(file) + + GN = nuke.selectedNode() + anlib.set_avalon_knob_data(GN, avalon_data) + GN.setXYpos(xpos, ypos) + GN["name"].setValue(object_name) + + # get all versions in list + versions = io.find({ + "type": "version", + "parent": version["parent"] + }).distinct('name') + + max_version = max(versions) + + # change color of node + if version.get("name") not in [max_version]: + GN["tile_color"].setValue(int("0xd88467ff", 16)) + else: + GN["tile_color"].setValue(int(self.node_color, 16)) + + self.log.info("udated to version: {}".format(version.get("name"))) + + return update_container(GN, data_imprint) + + def connect_active_viewer(self, group_node): + """ + Finds Active viewer and + place the node under it, also adds + name of group into Input Process of the viewer + + Arguments: + group_node (nuke node): nuke group node object + + """ + group_node_name = group_node["name"].value() + + viewer = [n for n in nuke.allNodes() if "Viewer1" in n["name"].value()] + if len(viewer) > 0: + viewer = viewer[0] + else: + self.log.error("Please create Viewer node before you " + "run this action again") + return None + + # get coordinates of Viewer1 + xpos = viewer["xpos"].value() + ypos = viewer["ypos"].value() + + ypos += 150 + + viewer["ypos"].setValue(ypos) + + # set coordinates to group node + group_node["xpos"].setValue(xpos) + group_node["ypos"].setValue(ypos + 50) + + # add group node name to Viewer Input Process + viewer["input_process_node"].setValue(group_node_name) + + # put backdrop under + pnlib.create_backdrop(label="Input Process", layer=2, + nodes=[viewer, group_node], color="0x7c7faaff") + + return True + + def get_item(self, data, trackIndex, subTrackIndex): + return {key: val for key, val in data.items() + if subTrackIndex == val["subTrackIndex"] + if trackIndex == val["trackIndex"]} + + def byteify(self, input): + """ + Converts unicode strings to strings + It goes trought all dictionary + + Arguments: + input (dict/str): input + + Returns: + dict: with fixed values and keys + + """ + + if isinstance(input, dict): + return {self.byteify(key): self.byteify(value) + for key, value in input.iteritems()} + elif isinstance(input, list): + return [self.byteify(element) for element in input] + elif isinstance(input, unicode): + return input.encode('utf-8') + else: + return input + + def switch(self, container, representation): + self.update(container, representation) + + def remove(self, container): + from avalon.nuke import viewer_update_and_undo_stop + node = nuke.toNode(container['objectName']) + with viewer_update_and_undo_stop(): + nuke.delete(node) From b5abaecdfe4e69e748b065a52eba308af393dd85 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 26 Nov 2019 00:20:21 +0100 Subject: [PATCH 24/26] feat(nuke): adding colorspace to version data --- pype/plugins/nuke/publish/collect_gizmo.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pype/plugins/nuke/publish/collect_gizmo.py b/pype/plugins/nuke/publish/collect_gizmo.py index ada3400dfc..87f4cf8264 100644 --- a/pype/plugins/nuke/publish/collect_gizmo.py +++ b/pype/plugins/nuke/publish/collect_gizmo.py @@ -40,6 +40,7 @@ class CollectGizmo(pyblish.api.InstancePlugin): "handleEnd": handle_end, "frameStart": first_frame + handle_start, "frameEnd": last_frame - handle_end, + "colorspace": nuke.root().knob('workingSpaceLUT').value(), "version": int(version), "families": [instance.data["family"]] + instance.data["families"], "subset": instance.data["subset"], From d346f7e37f5065ed81fd89ba178bfb167eae53a0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 5 Dec 2019 17:21:18 +0100 Subject: [PATCH 25/26] feat(nuke): removing subsetgroup --- pype/plugins/nuke/publish/collect_backdrop.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pype/plugins/nuke/publish/collect_backdrop.py b/pype/plugins/nuke/publish/collect_backdrop.py index 39402f5352..d98a20aee0 100644 --- a/pype/plugins/nuke/publish/collect_backdrop.py +++ b/pype/plugins/nuke/publish/collect_backdrop.py @@ -77,8 +77,7 @@ class CollectBackdrops(pyblish.api.InstancePlugin): instance.data.update({ "versionData": version_data, "frameStart": first_frame, - "frameEnd": last_frame, - "subsetGroup": "backdrops" + "frameEnd": last_frame }) self.log.info("Backdrop content collected: `{}`".format(instance[:])) self.log.info("Backdrop instance collected: `{}`".format(instance)) From 90f6d2e2daddaf6150e1d5b2a3927099d776d224 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 5 Dec 2019 17:24:45 +0100 Subject: [PATCH 26/26] feat(nuke): gizmo remove subsetgroup --- pype/plugins/nuke/publish/collect_gizmo.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pype/plugins/nuke/publish/collect_gizmo.py b/pype/plugins/nuke/publish/collect_gizmo.py index 87f4cf8264..11e8c17a3f 100644 --- a/pype/plugins/nuke/publish/collect_gizmo.py +++ b/pype/plugins/nuke/publish/collect_gizmo.py @@ -50,8 +50,7 @@ class CollectGizmo(pyblish.api.InstancePlugin): instance.data.update({ "versionData": version_data, "frameStart": first_frame, - "frameEnd": last_frame, - "subsetGroup": "gizmos" + "frameEnd": last_frame }) self.log.info("Gizmo content collected: `{}`".format(instance[:])) self.log.info("Gizmo instance collected: `{}`".format(instance))