diff --git a/pype/plugins/global/load/open_imagesequence.py b/pype/plugins/global/load/open_imagesequence.py index 8cb16fc507..a910625733 100644 --- a/pype/plugins/global/load/open_imagesequence.py +++ b/pype/plugins/global/load/open_imagesequence.py @@ -18,7 +18,7 @@ def open(filepath): class PlayImageSequence(api.Loader): """Open Image Sequence with system default""" - families = ["imagesequence"] + families = ["write"] representations = ["*"] label = "Play sequence" diff --git a/pype/plugins/nuke/load/load_alembic b/pype/plugins/nuke/_load_unused/load_alembic similarity index 100% rename from pype/plugins/nuke/load/load_alembic rename to pype/plugins/nuke/_load_unused/load_alembic diff --git a/pype/plugins/nuke/load/load_backdrop b/pype/plugins/nuke/_load_unused/load_backdrop similarity index 100% rename from pype/plugins/nuke/load/load_backdrop rename to pype/plugins/nuke/_load_unused/load_backdrop diff --git a/pype/plugins/nuke/load/load_camera_abc b/pype/plugins/nuke/_load_unused/load_camera_abc similarity index 100% rename from pype/plugins/nuke/load/load_camera_abc rename to pype/plugins/nuke/_load_unused/load_camera_abc diff --git a/pype/plugins/nuke/load/load_camera_nk b/pype/plugins/nuke/_load_unused/load_camera_nk similarity index 100% rename from pype/plugins/nuke/load/load_camera_nk rename to pype/plugins/nuke/_load_unused/load_camera_nk diff --git a/pype/plugins/nuke/_load_unused/load_sequence.py b/pype/plugins/nuke/_load_unused/load_sequence.py new file mode 100644 index 0000000000..695dd0b981 --- /dev/null +++ b/pype/plugins/nuke/_load_unused/load_sequence.py @@ -0,0 +1,252 @@ +import os +import contextlib + +from avalon import api +import avalon.io as io + +from avalon.nuke import log +import nuke + + +@contextlib.contextmanager +def preserve_inputs(node, knobs): + """Preserve the node's inputs after context""" + + values = {} + for name in knobs: + try: + knob_value = node[name].vaule() + values[name] = knob_value + except ValueError: + log.warning("missing knob {} in node {}" + "{}".format(name, node['name'].value())) + + try: + yield + finally: + for name, value in values.items(): + node[name].setValue(value) + + +@contextlib.contextmanager +def preserve_trim(node): + """Preserve the relative trim of the Loader tool. + + This tries to preserve the loader's trim (trim in and trim out) after + the context by reapplying the "amount" it trims on the clip's length at + start and end. + + """ + # working script frame range + script_start = nuke.root()["start_frame"].value() + + start_at_frame = None + offset_frame = None + if node['frame_mode'].value() == "start at": + start_at_frame = node['frame'].value() + if node['frame_mode'].value() is "offset": + offset_frame = node['frame'].value() + + try: + yield + finally: + if start_at_frame: + node['frame_mode'].setValue("start at") + node['frame'].setValue(str(script_start)) + log.info("start frame of reader was set to" + "{}".format(script_start)) + + if offset_frame: + node['frame_mode'].setValue("offset") + node['frame'].setValue(str((script_start + offset_frame))) + log.info("start frame of reader was set to" + "{}".format(script_start)) + + +def loader_shift(node, frame, relative=True): + """Shift global in time by i preserving duration + + This moves the loader by i frames preserving global duration. When relative + is False it will shift the global in to the start frame. + + Args: + loader (tool): The fusion loader tool. + frame (int): The amount of frames to move. + relative (bool): When True the shift is relative, else the shift will + change the global in to frame. + + Returns: + int: The resulting relative frame change (how much it moved) + + """ + # working script frame range + script_start = nuke.root()["start_frame"].value() + + if node['frame_mode'].value() == "start at": + start_at_frame = node['frame'].value() + if node['frame_mode'].value() is "offset": + offset_frame = node['frame'].value() + + if relative: + shift = frame + else: + if start_at_frame: + shift = frame + if offset_frame: + shift = frame + offset_frame + + # Shifting global in will try to automatically compensate for the change + # in the "ClipTimeStart" and "HoldFirstFrame" inputs, so we preserve those + # input values to "just shift" the clip + with preserve_inputs(node, knobs=["file", + "first", + "last", + "originfirst", + "originlast", + "frame_mode", + "frame"]): + + # GlobalIn cannot be set past GlobalOut or vice versa + # so we must apply them in the order of the shift. + if start_at_frame: + node['frame_mode'].setValue("start at") + node['frame'].setValue(str(script_start + shift)) + if offset_frame: + node['frame_mode'].setValue("offset") + node['frame'].setValue(str(shift)) + + return int(shift) + + +class LoadSequence(api.Loader): + """Load image sequence into Nuke""" + + families = ["write"] + representations = ["*"] + + label = "Load sequence" + order = -10 + icon = "code-fork" + color = "orange" + + def load(self, context, name, namespace, data): + + from avalon.nuke import ( + containerise, + ls_img_sequence, + viewer_update_and_undo_stop + ) + log.info("here i am") + # Fallback to asset name when namespace is None + if namespace is None: + namespace = context['asset']['name'] + + # Use the first file for now + # TODO: fix path fname + file = ls_img_sequence(os.path.dirname(self.fname), one=True) + + # Create the Loader with the filename path set + with viewer_update_and_undo_stop(): + # TODO: it might be universal read to img/geo/camera + r = nuke.createNode( + "Read", + "name {}".format(self.name)) # TODO: does self.name exist? + r["file"].setValue(file['path']) + if len(file['frames']) is 1: + first = file['frames'][0][0] + last = file['frames'][0][1] + r["originfirst"].setValue(first) + r["first"].setValue(first) + r["originlast"].setValue(last) + r["last"].setValue(last) + else: + first = file['frames'][0][0] + last = file['frames'][:-1][1] + r["originfirst"].setValue(first) + r["first"].setValue(first) + r["originlast"].setValue(last) + r["last"].setValue(last) + log.warning("Missing frames in image sequence") + + # Set global in point to start frame (if in version.data) + start = context["version"]["data"].get("startFrame", None) + if start is not None: + loader_shift(r, start, relative=False) + + containerise(r, + name=name, + namespace=namespace, + context=context, + loader=self.__class__.__name__) + + def switch(self, container, representation): + self.update(container, representation) + + def update(self, container, representation): + """Update the Loader's path + + Fusion 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 ( + viewer_update_and_undo_stop, + ls_img_sequence, + update_container + ) + log.info("this i can see") + node = container["_tool"] + # TODO: prepare also for other readers img/geo/camera + assert node.Class() == "Reader", "Must be Reader" + + root = api.get_representation_path(representation) + file = ls_img_sequence(os.path.dirname(root), one=True) + + # Get start frame from version data + version = io.find_one({"type": "version", + "_id": representation["parent"]}) + start = version["data"].get("startFrame") + if start is None: + log.warning("Missing start frame for updated version" + "assuming starts at frame 0 for: " + "{} ({})".format(node['name'].value(), representation)) + start = 0 + + with viewer_update_and_undo_stop(): + + # Update the loader's path whilst preserving some values + with preserve_trim(node): + with preserve_inputs(node, + knobs=["file", + "first", + "last", + "originfirst", + "originlast", + "frame_mode", + "frame"]): + node["file"] = file["path"] + + # Set the global in to the start frame of the sequence + global_in_changed = loader_shift(node, start, relative=False) + if global_in_changed: + # Log this change to the user + log.debug("Changed '{}' global in:" + " {:d}".format(node['name'].value(), start)) + + # Update the imprinted representation + update_container( + node, + {"representation": str(representation["_id"])} + ) + + def remove(self, container): + + from avalon.nuke import viewer_update_and_undo_stop + + node = container["_tool"] + assert node.Class() == "Reader", "Must be Reader" + + with viewer_update_and_undo_stop(): + nuke.delete(node) diff --git a/pype/plugins/nuke/load/load_still b/pype/plugins/nuke/_load_unused/load_still similarity index 100% rename from pype/plugins/nuke/load/load_still rename to pype/plugins/nuke/_load_unused/load_still diff --git a/pype/plugins/nuke/load/actions.py b/pype/plugins/nuke/load/actions.py index f3b7748f01..449567987a 100644 --- a/pype/plugins/nuke/load/actions.py +++ b/pype/plugins/nuke/load/actions.py @@ -3,6 +3,9 @@ """ from avalon import api +from pype.api import Logger + +log = Logger.getLogger(__name__, "nuke") class SetFrameRangeLoader(api.Loader): @@ -10,7 +13,7 @@ class SetFrameRangeLoader(api.Loader): families = ["animation", "camera", - "imagesequence", + "write", "yeticache", "pointcache"] representations = ["*"] @@ -30,9 +33,10 @@ class SetFrameRangeLoader(api.Loader): start = version_data.get("startFrame", None) end = version_data.get("endFrame", None) + log.info("start: {}, end: {}".format(start, end)) if start is None or end is None: - print("Skipping setting frame range because start or " - "end frame data is missing..") + log.info("Skipping setting frame range because start or " + "end frame data is missing..") return lib.update_frame_range(start, end) @@ -43,7 +47,7 @@ class SetFrameRangeWithHandlesLoader(api.Loader): families = ["animation", "camera", - "imagesequence", + "write", "yeticache", "pointcache"] representations = ["*"] diff --git a/pype/plugins/nuke/load/load_sequence.py b/pype/plugins/nuke/load/load_sequence.py index 0b771a7007..ee5e93aad5 100644 --- a/pype/plugins/nuke/load/load_sequence.py +++ b/pype/plugins/nuke/load/load_sequence.py @@ -1,127 +1,18 @@ +import nuke import os import contextlib from avalon import api import avalon.io as io -from avalon.nuke import log -import nuke - - -@contextlib.contextmanager -def preserve_inputs(node, knobs): - """Preserve the node's inputs after context""" - - values = {} - for name in knobs: - try: - knob_value = node[name].vaule() - values[name] = knob_value - except ValueError: - log.warning("missing knob {} in node {}" - "{}".format(name, node['name'].value())) - - try: - yield - finally: - for name, value in values.items(): - node[name].setValue(value) - - -@contextlib.contextmanager -def preserve_trim(node): - """Preserve the relative trim of the Loader tool. - - This tries to preserve the loader's trim (trim in and trim out) after - the context by reapplying the "amount" it trims on the clip's length at - start and end. - - """ - # working script frame range - script_start = nuke.root()["start_frame"].value() - - start_at_frame = None - offset_frame = None - if node['frame_mode'].value() == "start at": - start_at_frame = node['frame'].value() - if node['frame_mode'].value() is "offset": - offset_frame = node['frame'].value() - - try: - yield - finally: - if start_at_frame: - node['frame_mode'].setValue("start at") - node['frame'].setValue(str(script_start)) - log.info("start frame of reader was set to" - "{}".format(script_start)) - - if offset_frame: - node['frame_mode'].setValue("offset") - node['frame'].setValue(str((script_start + offset_frame))) - log.info("start frame of reader was set to" - "{}".format(script_start)) - - -def loader_shift(node, frame, relative=True): - """Shift global in time by i preserving duration - - This moves the loader by i frames preserving global duration. When relative - is False it will shift the global in to the start frame. - - Args: - loader (tool): The fusion loader tool. - frame (int): The amount of frames to move. - relative (bool): When True the shift is relative, else the shift will - change the global in to frame. - - Returns: - int: The resulting relative frame change (how much it moved) - - """ - # working script frame range - script_start = nuke.root()["start_frame"].value() - - if node['frame_mode'].value() == "start at": - start_at_frame = node['frame'].value() - if node['frame_mode'].value() is "offset": - offset_frame = node['frame'].value() - - if relative: - shift = frame - else: - if start_at_frame: - shift = frame - if offset_frame: - shift = frame + offset_frame - - # Shifting global in will try to automatically compensate for the change - # in the "ClipTimeStart" and "HoldFirstFrame" inputs, so we preserve those - # input values to "just shift" the clip - with preserve_inputs(node, knobs=["file", - "first", - "last", - "originfirst", - "originlast", - "frame_mode", - "frame"]): - - # GlobalIn cannot be set past GlobalOut or vice versa - # so we must apply them in the order of the shift. - if start_at_frame: - node['frame_mode'].setValue("start at") - node['frame'].setValue(str(script_start + shift)) - if offset_frame: - node['frame_mode'].setValue("offset") - node['frame'].setValue(str(shift)) - - return int(shift) +from pype.api import Logger +log = Logger.getLogger(__name__, "nuke") class LoadSequence(api.Loader): """Load image sequence into Nuke""" - families = ["imagesequence"] + families = ["write"] representations = ["*"] label = "Load sequence" @@ -131,122 +22,121 @@ class LoadSequence(api.Loader): def load(self, context, name, namespace, data): - from avalon.nuke import ( - containerise, - ls_img_sequence, - viewer_update_and_undo_stop - ) - - # Fallback to asset name when namespace is None - if namespace is None: - namespace = context['asset']['name'] - - # Use the first file for now - # TODO: fix path fname - file = ls_img_sequence(os.path.dirname(self.fname), one=True) - - # Create the Loader with the filename path set - with viewer_update_and_undo_stop(): - # TODO: it might be universal read to img/geo/camera - r = nuke.createNode( - "Read", - "name {}".format(self.name)) # TODO: does self.name exist? - r["file"].setValue(file['path']) - if len(file['frames']) is 1: - first = file['frames'][0][0] - last = file['frames'][0][1] - r["originfirst"].setValue(first) - r["first"].setValue(first) - r["originlast"].setValue(last) - r["last"].setValue(last) - else: - first = file['frames'][0][0] - last = file['frames'][:-1][1] - r["originfirst"].setValue(first) - r["first"].setValue(first) - r["originlast"].setValue(last) - r["last"].setValue(last) - log.warning("Missing frames in image sequence") - - # Set global in point to start frame (if in version.data) - start = context["version"]["data"].get("startFrame", None) - if start is not None: - loader_shift(r, start, relative=False) - - containerise(r, - name=name, - namespace=namespace, - context=context, - loader=self.__class__.__name__) - - def switch(self, container, representation): - self.update(container, representation) - - def update(self, container, representation): - """Update the Loader's path - - Fusion 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 ( - viewer_update_and_undo_stop, - ls_img_sequence, - update_container - ) - - node = container["_tool"] - # TODO: prepare also for other readers img/geo/camera - assert node.Class() == "Reader", "Must be Reader" - - root = api.get_representation_path(representation) - file = ls_img_sequence(os.path.dirname(root), one=True) - - # Get start frame from version data - version = io.find_one({"type": "version", - "_id": representation["parent"]}) - start = version["data"].get("startFrame") - if start is None: - log.warning("Missing start frame for updated version" - "assuming starts at frame 0 for: " - "{} ({})".format(node['name'].value(), representation)) - start = 0 - - with viewer_update_and_undo_stop(): - - # Update the loader's path whilst preserving some values - with preserve_trim(node): - with preserve_inputs(node, - knobs=["file", - "first", - "last", - "originfirst", - "originlast", - "frame_mode", - "frame"]): - node["file"] = file["path"] - - # Set the global in to the start frame of the sequence - global_in_changed = loader_shift(node, start, relative=False) - if global_in_changed: - # Log this change to the user - log.debug("Changed '{}' global in:" - " {:d}".format(node['name'].value(), start)) - - # Update the imprinted representation - update_container( - node, - {"representation": str(representation["_id"])} - ) - - def remove(self, container): - - from avalon.nuke import viewer_update_and_undo_stop - - node = container["_tool"] - assert node.Class() == "Reader", "Must be Reader" - - with viewer_update_and_undo_stop(): - nuke.delete(node) + log.info("context: {}\n".format(context["representation"])) + log.info("name: {}\n".format(name)) + log.info("namespace: {}\n".format(namespace)) + log.info("data: {}\n".format(data)) + return + # # Fallback to asset name when namespace is None + # if namespace is None: + # namespace = context['asset']['name'] + # + # # Use the first file for now + # # TODO: fix path fname + # file = ls_img_sequence(os.path.dirname(self.fname), one=True) + # + # # Create the Loader with the filename path set + # with viewer_update_and_undo_stop(): + # # TODO: it might be universal read to img/geo/camera + # r = nuke.createNode( + # "Read", + # "name {}".format(self.name)) # TODO: does self.name exist? + # r["file"].setValue(file['path']) + # if len(file['frames']) is 1: + # first = file['frames'][0][0] + # last = file['frames'][0][1] + # r["originfirst"].setValue(first) + # r["first"].setValue(first) + # r["originlast"].setValue(last) + # r["last"].setValue(last) + # else: + # first = file['frames'][0][0] + # last = file['frames'][:-1][1] + # r["originfirst"].setValue(first) + # r["first"].setValue(first) + # r["originlast"].setValue(last) + # r["last"].setValue(last) + # log.warning("Missing frames in image sequence") + # + # # Set global in point to start frame (if in version.data) + # start = context["version"]["data"].get("startFrame", None) + # if start is not None: + # loader_shift(r, start, relative=False) + # + # containerise(r, + # name=name, + # namespace=namespace, + # context=context, + # loader=self.__class__.__name__) + # + # def switch(self, container, representation): + # self.update(container, representation) + # + # def update(self, container, representation): + # """Update the Loader's path + # + # Fusion 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 ( + # viewer_update_and_undo_stop, + # ls_img_sequence, + # update_container + # ) + # log.info("this i can see") + # node = container["_tool"] + # # TODO: prepare also for other readers img/geo/camera + # assert node.Class() == "Reader", "Must be Reader" + # + # root = api.get_representation_path(representation) + # file = ls_img_sequence(os.path.dirname(root), one=True) + # + # # Get start frame from version data + # version = io.find_one({"type": "version", + # "_id": representation["parent"]}) + # start = version["data"].get("startFrame") + # if start is None: + # log.warning("Missing start frame for updated version" + # "assuming starts at frame 0 for: " + # "{} ({})".format(node['name'].value(), representation)) + # start = 0 + # + # with viewer_update_and_undo_stop(): + # + # # Update the loader's path whilst preserving some values + # with preserve_trim(node): + # with preserve_inputs(node, + # knobs=["file", + # "first", + # "last", + # "originfirst", + # "originlast", + # "frame_mode", + # "frame"]): + # node["file"] = file["path"] + # + # # Set the global in to the start frame of the sequence + # global_in_changed = loader_shift(node, start, relative=False) + # if global_in_changed: + # # Log this change to the user + # log.debug("Changed '{}' global in:" + # " {:d}".format(node['name'].value(), start)) + # + # # Update the imprinted representation + # update_container( + # node, + # {"representation": str(representation["_id"])} + # ) + # + # def remove(self, container): + # + # from avalon.nuke import viewer_update_and_undo_stop + # + # node = container["_tool"] + # assert node.Class() == "Reader", "Must be Reader" + # + # with viewer_update_and_undo_stop(): + # nuke.delete(node)