From a57fbd8f407cc115bb64674173116440378a9fe6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 1 Sep 2020 15:02:53 +0200 Subject: [PATCH] feat(nuke): adding image loader --- pype/plugins/nuke/load/load_image.py | 233 ++++++++++++++++++++++++ pype/plugins/nuke/load/load_sequence.py | 18 +- 2 files changed, 242 insertions(+), 9 deletions(-) create mode 100644 pype/plugins/nuke/load/load_image.py diff --git a/pype/plugins/nuke/load/load_image.py b/pype/plugins/nuke/load/load_image.py new file mode 100644 index 0000000000..377d52aa14 --- /dev/null +++ b/pype/plugins/nuke/load/load_image.py @@ -0,0 +1,233 @@ +import re +import nuke + +from avalon.vendor import qargparse +from avalon import api, io + +from pype.hosts.nuke import presets + + +class LoadImage(api.Loader): + """Load still image into Nuke""" + + families = [ + "render2d", "source", "plate", + "render", "prerender", "review", + "image" + ] + representations = ["exr", "dpx", "jpg", "jpeg", "png", "psd"] + + label = "Load Image" + order = -10 + icon = "image" + color = "white" + + options = [ + qargparse.Integer( + "frame_number", + label="Frame Number", + default=int(nuke.root()["first_frame"].getValue()), + min=1, + max=999999, + help="What frame is reading from?" + ) + ] + + def load(self, context, name, namespace, options): + from avalon.nuke import ( + containerise, + viewer_update_and_undo_stop + ) + self.log.info("__ options: `{}`".format(options)) + frame_number = options.get("frame_number", 1) + + version = context['version'] + version_data = version.get("data", {}) + repr_id = context["representation"]["_id"] + + self.log.info("version_data: {}\n".format(version_data)) + self.log.debug( + "Representation id `{}` ".format(repr_id)) + + last = first = int(frame_number) + + # Fallback to asset name when namespace is None + if namespace is None: + namespace = context['asset']['name'] + + file = self.fname + + if not file: + repr_id = context["representation"]["_id"] + self.log.warning( + "Representation id `{}` is failing to load".format(repr_id)) + return + + file = file.replace("\\", "/") + + repr_cont = context["representation"]["context"] + frame = repr_cont.get("frame") + if frame: + padding = len(frame) + file = file.replace( + frame, + format(frame_number, "0{}".format(padding))) + + read_name = "Read_{0}_{1}_{2}".format( + repr_cont["asset"], + repr_cont["subset"], + repr_cont["representation"]) + + # Create the Loader with the filename path set + with viewer_update_and_undo_stop(): + r = nuke.createNode( + "Read", + "name {}".format(read_name)) + r["file"].setValue(file) + + # Set colorspace defined in version data + colorspace = context["version"]["data"].get("colorspace") + if colorspace: + r["colorspace"].setValue(str(colorspace)) + + # load nuke presets for Read's colorspace + read_clrs_presets = presets.get_colorspace_preset().get( + "nuke", {}).get("read", {}) + + # check if any colorspace presets for read is mathing + preset_clrsp = next((read_clrs_presets[k] + for k in read_clrs_presets + if bool(re.search(k, file))), + None) + if preset_clrsp is not None: + r["colorspace"].setValue(str(preset_clrsp)) + + r["origfirst"].setValue(first) + r["first"].setValue(first) + r["origlast"].setValue(last) + r["last"].setValue(last) + + # add additional metadata from the version to imprint Avalon knob + add_keys = ["source", "colorspace", "author", "fps", "version"] + + data_imprint = { + "frameStart": first, + "frameEnd": last + } + for k in add_keys: + if k == 'version': + data_imprint.update({k: context["version"]['name']}) + else: + data_imprint.update( + {k: context["version"]['data'].get(k, str(None))}) + + data_imprint.update({"objectName": read_name}) + + r["tile_color"].setValue(int("0x4ecd25ff", 16)) + + return containerise(r, + name=name, + namespace=namespace, + context=context, + loader=self.__class__.__name__, + data=data_imprint) + + def switch(self, container, representation): + self.update(container, representation) + + 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: + + """ + + from avalon.nuke import ( + update_container + ) + + node = nuke.toNode(container["objectName"]) + frame_number = node["first"].value() + + assert node.Class() == "Read", "Must be Read" + + repr_cont = representation["context"] + + file = api.get_representation_path(representation) + + if not file: + repr_id = representation["_id"] + self.log.warning( + "Representation id `{}` is failing to load".format(repr_id)) + return + + file = file.replace("\\", "/") + + frame = repr_cont.get("frame") + if frame: + padding = len(frame) + file = file.replace( + frame, + format(frame_number, "0{}".format(padding))) + + # Get start frame from version data + version = io.find_one({ + "type": "version", + "_id": representation["parent"] + }) + + # get all versions in list + versions = io.find({ + "type": "version", + "parent": version["parent"] + }).distinct('name') + + max_version = max(versions) + + version_data = version.get("data", {}) + + last = first = int(frame_number) + + # Set the global in to the start frame of the sequence + node["origfirst"].setValue(first) + node["first"].setValue(first) + node["origlast"].setValue(last) + node["last"].setValue(last) + + updated_dict = {} + updated_dict.update({ + "representation": str(representation["_id"]), + "frameStart": str(first), + "frameEnd": str(last), + "version": str(version.get("name")), + "colorspace": version_data.get("colorspace"), + "source": version_data.get("source"), + "fps": str(version_data.get("fps")), + "author": version_data.get("author"), + "outputDir": version_data.get("outputDir"), + }) + + # change color of node + if version.get("name") not in [max_version]: + node["tile_color"].setValue(int("0xd84f20ff", 16)) + else: + node["tile_color"].setValue(int("0x4ecd25ff", 16)) + + # Update the imprinted representation + update_container( + node, + updated_dict + ) + self.log.info("udated to version: {}".format(version.get("name"))) + + def remove(self, container): + + from avalon.nuke import viewer_update_and_undo_stop + + node = nuke.toNode(container['objectName']) + assert node.Class() == "Read", "Must be Read" + + with viewer_update_and_undo_stop(): + nuke.delete(node) diff --git a/pype/plugins/nuke/load/load_sequence.py b/pype/plugins/nuke/load/load_sequence.py index 601e28c7c1..c5ce288540 100644 --- a/pype/plugins/nuke/load/load_sequence.py +++ b/pype/plugins/nuke/load/load_sequence.py @@ -120,12 +120,12 @@ class LoadSequence(api.Loader): if "#" not in file: frame = repr_cont.get("frame") padding = len(frame) - file = file.replace(frame, "#"*padding) + file = file.replace(frame, "#" * padding) read_name = "Read_{0}_{1}_{2}".format( - repr_cont["asset"], - repr_cont["subset"], - repr_cont["representation"]) + repr_cont["asset"], + repr_cont["subset"], + repr_cont["representation"]) # Create the Loader with the filename path set with viewer_update_and_undo_stop(): @@ -250,7 +250,7 @@ class LoadSequence(api.Loader): if "#" not in file: frame = repr_cont.get("frame") padding = len(frame) - file = file.replace(frame, "#"*padding) + file = file.replace(frame, "#" * padding) # Get start frame from version data version = io.find_one({ @@ -276,10 +276,10 @@ class LoadSequence(api.Loader): last = version_data.get("frameEnd") if first is None: - self.log.warning("Missing start frame for updated version" - "assuming starts at frame 0 for: " - "{} ({})".format( - node['name'].value(), representation)) + self.log.warning( + "Missing start frame for updated version" + "assuming starts at frame 0 for: " + "{} ({})".format(node['name'].value(), representation)) first = 0 first -= self.handle_start