From 68e9102f7263e70c50c7f7a959ae48ba5c16280e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 17 Jun 2021 17:04:45 +0200 Subject: [PATCH 1/3] #636 - PS created loader to load reference frame from 'render' sequence Loader cannot do containerization for now as updates would be too difficult. (Customer doesn't mind.) --- .../plugins/load/load_image_from_sequence.py | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py diff --git a/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py b/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py new file mode 100644 index 00000000000..1409065d437 --- /dev/null +++ b/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py @@ -0,0 +1,90 @@ +import os +from avalon import photoshop +from avalon.vendor import qargparse + +from openpype.hosts.photoshop.plugins.load.load_image import ImageLoader + +stub = photoshop.stub() + + +class ImageFromSequenceLoader(ImageLoader): + """ Load specifing image from sequence + + Used only as quick load of reference file from a sequence. + + Plain ImageLoader picks first frame from sequence. + + Loads only existing files - currently not possible to limit loaders + to single select - multiselect. If user selects multiple repres, list + for all of them is provided, but selection is only single file. + This loader will be triggered multiple times, but selected name will + match only to proper path. + + Loader doesnt do containerization as there is currently no data model + of 'frame of rendered files' (only rendered sequence), update would be + difficult. + """ + + families = ["render"] + representations = ["*"] + options = [] + + def load(self, context, name=None, namespace=None, data=None): + if data.get("frame"): + self.fname = os.path.join(os.path.dirname(self.fname), + data["frame"]) + if not os.path.exists(self.fname): + return + + layer_name = self._get_unique_layer_name(context["asset"]["name"], + name) + with photoshop.maintained_selection(): + layer = stub.import_smart_object(self.fname, layer_name) + + self[:] = [layer] + namespace = namespace or layer_name + + return namespace + + @classmethod + def get_options(cls, repre_contexts): + """ + Returns list of files for selected 'repre_contexts'. + + It returns only files with same extension as in context as it is + expected that context points to sequence of frames. + + Returns: + (list) of qargparse.Choice + """ + files = [] + for context in repre_contexts: + fname = ImageLoader.filepath_from_context(context) + _, file_extension = os.path.splitext(fname) + + for file_name in os.listdir(os.path.dirname(fname)): + if not file_name.endswith(file_extension): + continue + files.append(file_name) + + # return selection only if there is something + if not files or len(files) <= 1: + return [] + + return [ + qargparse.Choice( + "frame", + label="Select specific file", + items=files, + default=0, + help="Which frame should be loaded?" + ) + ] + + def update(self, container, representation): + """No update possible, not containerized.""" + pass + + def remove(self, container): + """No update possible, not containerized.""" + pass From 852a2f35aa9352e3d9b3efe8b95d96c5697b7aa1 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 17 Jun 2021 18:30:15 +0200 Subject: [PATCH 2/3] #636 - un subclassed loader from sequence, created lib for reusable methods Loader from sequence didnt was dropping connection to PS after first successful import --- openpype/hosts/photoshop/plugins/lib.py | 26 +++++++++++++++ .../photoshop/plugins/load/load_image.py | 33 ++++--------------- .../plugins/load/load_image_from_sequence.py | 15 ++++++--- 3 files changed, 43 insertions(+), 31 deletions(-) create mode 100644 openpype/hosts/photoshop/plugins/lib.py diff --git a/openpype/hosts/photoshop/plugins/lib.py b/openpype/hosts/photoshop/plugins/lib.py new file mode 100644 index 00000000000..74aff061147 --- /dev/null +++ b/openpype/hosts/photoshop/plugins/lib.py @@ -0,0 +1,26 @@ +import re + + +def get_unique_layer_name(layers, asset_name, subset_name): + """ + Gets all layer names and if 'asset_name_subset_name' is present, it + increases suffix by 1 (eg. creates unique layer name - for Loader) + Args: + layers (list) of dict with layers info (name, id etc.) + asset_name (string): + subset_name (string): + + Returns: + (string): name_00X (without version) + """ + name = "{}_{}".format(asset_name, subset_name) + names = {} + for layer in layers: + layer_name = re.sub(r'_\d{3}$', '', layer.name) + if layer_name in names.keys(): + names[layer_name] = names[layer_name] + 1 + else: + names[layer_name] = 1 + occurrences = names.get(name, 0) + + return "{}_{:0>3d}".format(name, occurrences + 1) diff --git a/openpype/hosts/photoshop/plugins/load/load_image.py b/openpype/hosts/photoshop/plugins/load/load_image.py index 44cc96c96fe..d043323768e 100644 --- a/openpype/hosts/photoshop/plugins/load/load_image.py +++ b/openpype/hosts/photoshop/plugins/load/load_image.py @@ -1,7 +1,9 @@ -from avalon import api, photoshop -import os import re +from avalon import api, photoshop + +from openpype.hosts.photoshop.plugins.lib import get_unique_layer_name + stub = photoshop.stub() @@ -15,8 +17,9 @@ class ImageLoader(api.Loader): representations = ["*"] def load(self, context, name=None, namespace=None, data=None): - layer_name = self._get_unique_layer_name(context["asset"]["name"], - name) + layer_name = get_unique_layer_name(stub.get_layers(), + context["asset"]["name"], + name) with photoshop.maintained_selection(): layer = stub.import_smart_object(self.fname, layer_name) @@ -69,25 +72,3 @@ def remove(self, container): def switch(self, container, representation): self.update(container, representation) - - def _get_unique_layer_name(self, asset_name, subset_name): - """ - Gets all layer names and if 'name' is present in them, increases - suffix by 1 (eg. creates unique layer name - for Loader) - Args: - name (string): in format asset_subset - - Returns: - (string): name_00X (without version) - """ - name = "{}_{}".format(asset_name, subset_name) - names = {} - for layer in stub.get_layers(): - layer_name = re.sub(r'_\d{3}$', '', layer.name) - if layer_name in names.keys(): - names[layer_name] = names[layer_name] + 1 - else: - names[layer_name] = 1 - occurrences = names.get(name, 0) - - return "{}_{:0>3d}".format(name, occurrences + 1) diff --git a/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py b/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py index 1409065d437..09525d2791a 100644 --- a/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py +++ b/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py @@ -1,13 +1,15 @@ import os + +from avalon import api from avalon import photoshop from avalon.vendor import qargparse -from openpype.hosts.photoshop.plugins.load.load_image import ImageLoader +from openpype.hosts.photoshop.plugins.lib import get_unique_layer_name stub = photoshop.stub() -class ImageFromSequenceLoader(ImageLoader): +class ImageFromSequenceLoader(api.Loader): """ Load specifing image from sequence Used only as quick load of reference file from a sequence. @@ -36,8 +38,11 @@ def load(self, context, name=None, namespace=None, data=None): if not os.path.exists(self.fname): return - layer_name = self._get_unique_layer_name(context["asset"]["name"], - name) + stub = photoshop.stub() + layer_name = get_unique_layer_name(stub.get_layers(), + context["asset"]["name"], + name) + with photoshop.maintained_selection(): layer = stub.import_smart_object(self.fname, layer_name) @@ -59,7 +64,7 @@ def get_options(cls, repre_contexts): """ files = [] for context in repre_contexts: - fname = ImageLoader.filepath_from_context(context) + fname = ImageFromSequenceLoader.filepath_from_context(context) _, file_extension = os.path.splitext(fname) for file_name in os.listdir(os.path.dirname(fname)): From aea7d538095a794e44ce50d6e9074b7d6715e2f4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 21 Jun 2021 17:36:51 +0200 Subject: [PATCH 3/3] #636 - use new api function --- .../hosts/photoshop/plugins/load/load_image_from_sequence.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py b/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py index 09525d2791a..8704627b123 100644 --- a/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py +++ b/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py @@ -2,8 +2,10 @@ from avalon import api from avalon import photoshop +from avalon.pipeline import get_representation_path_from_context from avalon.vendor import qargparse +from openpype.lib import Anatomy from openpype.hosts.photoshop.plugins.lib import get_unique_layer_name stub = photoshop.stub() @@ -64,7 +66,7 @@ def get_options(cls, repre_contexts): """ files = [] for context in repre_contexts: - fname = ImageFromSequenceLoader.filepath_from_context(context) + fname = get_representation_path_from_context(context) _, file_extension = os.path.splitext(fname) for file_name in os.listdir(os.path.dirname(fname)): @@ -93,3 +95,4 @@ def update(self, container, representation): def remove(self, container): """No update possible, not containerized.""" pass +