From c5d737faad0ee755b9b967eb529e0185291e1077 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 24 Feb 2022 10:47:57 +0100 Subject: [PATCH 01/28] Draft implementation of Update all to latest button --- openpype/tools/sceneinventory/window.py | 47 +++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/openpype/tools/sceneinventory/window.py b/openpype/tools/sceneinventory/window.py index e363a99d07..a05d820ec6 100644 --- a/openpype/tools/sceneinventory/window.py +++ b/openpype/tools/sceneinventory/window.py @@ -1,5 +1,6 @@ import os import sys +import logging from Qt import QtWidgets, QtCore from avalon.vendor import qtawesome @@ -20,6 +21,9 @@ from .model import ( ) from .view import SceneInvetoryView +from ..utils.lib import iter_model_rows + +log = logging.getLogger(__name__) module = sys.modules[__name__] module.window = None @@ -54,6 +58,10 @@ class SceneInventoryWindow(QtWidgets.QDialog): outdated_only_checkbox.setToolTip("Show outdated files only") outdated_only_checkbox.setChecked(False) + icon = qtawesome.icon("fa.arrow-up", color="white") + update_all_button = QtWidgets.QPushButton(self) + update_all_button.setIcon(icon) + icon = qtawesome.icon("fa.refresh", color="white") refresh_button = QtWidgets.QPushButton(self) refresh_button.setIcon(icon) @@ -62,6 +70,7 @@ class SceneInventoryWindow(QtWidgets.QDialog): control_layout.addWidget(filter_label) control_layout.addWidget(text_filter) control_layout.addWidget(outdated_only_checkbox) + control_layout.addWidget(update_all_button) control_layout.addWidget(refresh_button) # endregion control @@ -102,7 +111,9 @@ class SceneInventoryWindow(QtWidgets.QDialog): ) view.data_changed.connect(self.refresh) refresh_button.clicked.connect(self.refresh) + update_all_button.clicked.connect(self._on_update_all) + self._update_all_button = update_all_button self._outdated_only_checkbox = outdated_only_checkbox self._view = view self._model = model @@ -158,6 +169,42 @@ class SceneInventoryWindow(QtWidgets.QDialog): self._outdated_only_checkbox.isChecked() ) + def _on_update_all(self): + """Update all items that are currently 'outdated' in the view""" + + # Get all items from outdated groups + outdated_items = [] + for index in iter_model_rows(self._model, + column=0, + include_root=False): + item = index.data(self._model.ItemRole) + + if not item.get("isGroupNode"): + continue + + # Only the group nodes contain the "highest_version" data and as + # such we find only the groups and take its children. + if not self._model.outdated(item): + continue + + # Collect all children which we want to update + children = item.children() + outdated_items.extend(children) + + if not outdated_items: + log.info("Nothing to update.") + return + + # Trigger update to latest + # Logic copied from SceneInventoryView._build_item_menu_for_selection + for item in outdated_items: + try: + api.update(item, -1) + except AssertionError: + self._show_version_error_dialog(None, [item]) + log.warning("Update failed", exc_info=True) + self._view.data_changed.emit() + def show(root=None, debug=False, parent=None, items=None): """Display Scene Inventory GUI From 0b9e669d3b6421dceefd96e87aed81166708ba52 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 24 Feb 2022 10:49:28 +0100 Subject: [PATCH 02/28] Fix typos in class name and functions --- openpype/tools/sceneinventory/view.py | 4 ++-- openpype/tools/sceneinventory/window.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openpype/tools/sceneinventory/view.py b/openpype/tools/sceneinventory/view.py index 80f26a881d..2ae8c95be4 100644 --- a/openpype/tools/sceneinventory/view.py +++ b/openpype/tools/sceneinventory/view.py @@ -20,12 +20,12 @@ DEFAULT_COLOR = "#fb9c15" log = logging.getLogger("SceneInventory") -class SceneInvetoryView(QtWidgets.QTreeView): +class SceneInventoryView(QtWidgets.QTreeView): data_changed = QtCore.Signal() hierarchy_view_changed = QtCore.Signal(bool) def __init__(self, parent=None): - super(SceneInvetoryView, self).__init__(parent=parent) + super(SceneInventoryView, self).__init__(parent=parent) # view settings self.setIndentation(12) diff --git a/openpype/tools/sceneinventory/window.py b/openpype/tools/sceneinventory/window.py index a05d820ec6..d92c1f00d4 100644 --- a/openpype/tools/sceneinventory/window.py +++ b/openpype/tools/sceneinventory/window.py @@ -19,7 +19,7 @@ from .model import ( InventoryModel, FilterProxyModel ) -from .view import SceneInvetoryView +from .view import SceneInventoryView from ..utils.lib import iter_model_rows @@ -82,7 +82,7 @@ class SceneInventoryWindow(QtWidgets.QDialog): proxy.setDynamicSortFilter(True) proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) - view = SceneInvetoryView(self) + view = SceneInventoryView(self) view.setModel(proxy) # set some nice default widths for the view @@ -107,7 +107,7 @@ class SceneInventoryWindow(QtWidgets.QDialog): self._on_outdated_state_change ) view.hierarchy_view_changed.connect( - self._on_hiearchy_view_change + self._on_hierarchy_view_change ) view.data_changed.connect(self.refresh) refresh_button.clicked.connect(self.refresh) @@ -157,7 +157,7 @@ class SceneInventoryWindow(QtWidgets.QDialog): kwargs["selected"] = self._view._selected self._model.refresh(**kwargs) - def _on_hiearchy_view_change(self, enabled): + def _on_hierarchy_view_change(self, enabled): self._proxy.set_hierarchy_view(enabled) self._model.set_hierarchy_view(enabled) From 430f0428a2dd72e3b2d8502741302247cebdd95b Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 24 Feb 2022 10:52:15 +0100 Subject: [PATCH 03/28] Add tooltips --- openpype/tools/sceneinventory/window.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/tools/sceneinventory/window.py b/openpype/tools/sceneinventory/window.py index d92c1f00d4..d9d34dbb08 100644 --- a/openpype/tools/sceneinventory/window.py +++ b/openpype/tools/sceneinventory/window.py @@ -60,10 +60,12 @@ class SceneInventoryWindow(QtWidgets.QDialog): icon = qtawesome.icon("fa.arrow-up", color="white") update_all_button = QtWidgets.QPushButton(self) + update_all_button.setToolTip("Update all outdated to latest version") update_all_button.setIcon(icon) icon = qtawesome.icon("fa.refresh", color="white") refresh_button = QtWidgets.QPushButton(self) + update_all_button.setToolTip("Refresh") refresh_button.setIcon(icon) control_layout = QtWidgets.QHBoxLayout() From 02d3a5fa5764da970102229052ca6654581ccd0e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 25 Feb 2022 12:30:14 +0100 Subject: [PATCH 04/28] nuke: add reformat settings for baking mov presets publish plugin --- .../defaults/project_settings/nuke.json | 25 ++++++++++++- .../schemas/schema_nuke_publish.json | 35 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index 5a819e6904..238d21d43a 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -122,7 +122,30 @@ "viewer_process_override": "", "bake_viewer_process": true, "bake_viewer_input_process": true, - "add_tags": [] + "add_tags": [], + "reformat_node_add": false, + "reformat_node_config": [ + { + "name": "type", + "value": "to format" + }, + { + "name": "format", + "value": "HD_1080" + }, + { + "name": "filter", + "value": "Lanczos6" + }, + { + "name": "black_outside", + "value": "true" + }, + { + "name": "pbb", + "value": "false" + } + ] } } }, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json index 39390f355a..81e5d2cc3f 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json @@ -226,6 +226,41 @@ "label": "Add additional tags to representations", "type": "list", "object_type": "text" + }, + { + "type": "separator" + }, + { + "type": "boolean", + "key": "reformat_node_add", + "label": "Add Reformat Node" + }, + { + "type": "collapsible-wrap", + "label": "Reformat Node Knobs", + "collapsible": true, + "collapsed": false, + "children": [ + { + "type": "list", + "key": "reformat_node_config", + "object_type": { + "type": "dict", + "children": [ + { + "type": "text", + "key": "name", + "label": "Knob Name" + }, + { + "type": "text", + "key": "value", + "label": "Knob Value" + } + ] + } + } + ] } ] } From 15d4047b1fc76acec4a84633ec5621eaec8261c3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 25 Feb 2022 13:59:08 +0100 Subject: [PATCH 05/28] Nuke: adding reformat to bake mov worfklow procedure --- openpype/hosts/nuke/api/plugin.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index fd754203d4..79413bab6c 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -446,6 +446,8 @@ class ExporterReviewMov(ExporterReview): return path def generate_mov(self, farm=False, **kwargs): + reformat_node_add = kwargs["reformat_node_add"] + reformat_node_config = kwargs["reformat_node_config"] bake_viewer_process = kwargs["bake_viewer_process"] bake_viewer_input_process_node = kwargs[ "bake_viewer_input_process"] @@ -483,6 +485,25 @@ class ExporterReviewMov(ExporterReview): self.previous_node = r_node self.log.debug("Read... `{}`".format(self._temp_nodes[subset])) + # add reformat node + if reformat_node_add: + rf_node = nuke.createNode("Reformat") + for kn_conf in reformat_node_config: + k_name = str(kn_conf["name"]) + k_value = str(kn_conf["value"]) + if k_value == "true": + k_value = True + if k_value == "false": + k_value = False + rf_node[k_name].setValue(k_value) + + # connect + rf_node.setInput(0, self.previous_node) + self._temp_nodes[subset].append(rf_node) + self.previous_node = rf_node + self.log.debug( + "Reformat... `{}`".format(self._temp_nodes[subset])) + # only create colorspace baking if toggled on if bake_viewer_process: if bake_viewer_input_process_node: @@ -555,7 +576,7 @@ class ExporterReviewMov(ExporterReview): self.log.debug("Representation... `{}`".format(self.data)) - self.clean_nodes(subset) + # self.clean_nodes(subset) nuke.scriptSave() return self.data From 33cd5af26a897ffc1901617d2955a8af6e03878e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 25 Feb 2022 17:24:51 +0100 Subject: [PATCH 06/28] nuke: reverse clearing disable --- openpype/hosts/nuke/api/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index 79413bab6c..67c5203cda 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -576,7 +576,7 @@ class ExporterReviewMov(ExporterReview): self.log.debug("Representation... `{}`".format(self.data)) - # self.clean_nodes(subset) + self.clean_nodes(subset) nuke.scriptSave() return self.data From 9ab32bd634382a0f877b45487d7e89676f2f86e6 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 28 Feb 2022 11:39:14 +0100 Subject: [PATCH 07/28] Added possibility to create texture subset from dynamically parsed group names --- .../plugins/publish/collect_texture.py | 81 +++++++++++++------ 1 file changed, 57 insertions(+), 24 deletions(-) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py b/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py index 596a8ccfd2..8cf36fd489 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py @@ -147,6 +147,13 @@ class CollectTextures(pyblish.api.ContextPlugin): } resource_files[workfile_subset].append(item) + formatting_data = self._get_parsed_groups( + repre_file, + self.input_naming_patterns["textures"], + self.input_naming_groups["textures"], + self.color_space + ) + if ext in self.texture_extensions: c_space = self._get_color_space( repre_file, @@ -167,13 +174,15 @@ class CollectTextures(pyblish.api.ContextPlugin): self.color_space ) - formatting_data = { + explicit_data = { "color_space": c_space or '', # None throws exception "channel": channel or '', "shader": shader or '', "subset": parsed_subset or '' } + formatting_data.update(explicit_data) + fill_pairs = prepare_template_data(formatting_data) subset = format_template_with_optional_keys( fill_pairs, self.texture_subset_template) @@ -320,13 +329,14 @@ class CollectTextures(pyblish.api.ContextPlugin): """ asset_name = "NOT_AVAIL" - return self._parse(name, input_naming_patterns, input_naming_groups, - color_spaces, 'asset') or asset_name + return (self._parse_key(name, input_naming_patterns, + input_naming_groups, color_spaces, 'asset') or + asset_name) def _get_version(self, name, input_naming_patterns, input_naming_groups, color_spaces): - found = self._parse(name, input_naming_patterns, input_naming_groups, - color_spaces, 'version') + found = self._parse_key(name, input_naming_patterns, + input_naming_groups, color_spaces, 'version') if found: return found.replace('v', '') @@ -336,8 +346,8 @@ class CollectTextures(pyblish.api.ContextPlugin): def _get_udim(self, name, input_naming_patterns, input_naming_groups, color_spaces): """Parses from 'name' udim value.""" - found = self._parse(name, input_naming_patterns, input_naming_groups, - color_spaces, 'udim') + found = self._parse_key(name, input_naming_patterns, + input_naming_groups, color_spaces, 'udim') if found: return found @@ -375,8 +385,8 @@ class CollectTextures(pyblish.api.ContextPlugin): Unknown format of channel name and color spaces >> cs are known list - 'color_space' used as a placeholder """ - found = self._parse(name, input_naming_patterns, input_naming_groups, - color_spaces, 'shader') + found = self._parse_key(name, input_naming_patterns, + input_naming_groups, color_spaces, 'shader') if found: return found @@ -389,15 +399,15 @@ class CollectTextures(pyblish.api.ContextPlugin): Unknown format of channel name and color spaces >> cs are known list - 'color_space' used as a placeholder """ - found = self._parse(name, input_naming_patterns, input_naming_groups, - color_spaces, 'channel') + found = self._parse_key(name, input_naming_patterns, + input_naming_groups, color_spaces, 'channel') if found: return found self.log.warning("Didn't find channel in {}".format(name)) - def _parse(self, name, input_naming_patterns, input_naming_groups, - color_spaces, key): + def _parse_key(self, name, input_naming_patterns, input_naming_groups, + color_spaces, key): """Universal way to parse 'name' with configurable regex groups. Args: @@ -411,23 +421,46 @@ class CollectTextures(pyblish.api.ContextPlugin): Raises: ValueError - if broken 'input_naming_groups' """ + parsed_groups = self._get_parsed_groups(name, + input_naming_patterns, + input_naming_groups, + color_spaces) + + try: + parsed_value = parsed_groups[key] + return parsed_value + except IndexError: + msg = ("input_naming_groups must " + + "have '{}' key".format(key)) + raise ValueError(msg) + + def _get_parsed_groups(self, name, input_naming_patterns, + input_naming_groups, color_spaces): + """Universal way to parse 'name' with configurable regex groups. + + Args: + name (str): workfile name or texture name + input_naming_patterns (list): + [workfile_pattern] or [texture_pattern] + input_naming_groups (list) + ordinal position of regex groups matching to input_naming.. + color_spaces (list) - predefined color spaces + + Returns: + (dict) {group_name:parsed_value} + """ for input_pattern in input_naming_patterns: for cs in color_spaces: pattern = input_pattern.replace('{color_space}', cs) regex_result = re.findall(pattern, name) if regex_result: - idx = list(input_naming_groups).index(key) - if idx < 0: - msg = "input_naming_groups must " +\ - "have '{}' key".format(key) - raise ValueError(msg) + if len(regex_result[0]) == len(input_naming_groups): + return dict(zip(input_naming_groups, regex_result[0])) + else: + self.log.warning("No of parsed groups doesn't match " + "no of group labels") - try: - parsed_value = regex_result[0][idx] - return parsed_value - except IndexError: - self.log.warning("Wrong index, probably " - "wrong name {}".format(name)) + return {} def _update_representations(self, upd_representations): """Frames dont have sense for textures, add collected udims instead.""" From 52a5d13ca74df5626b46197e4616715c5b6eaa99 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 28 Feb 2022 13:50:07 +0100 Subject: [PATCH 08/28] Added possibility to create workfile subset from dynamically parsed group names --- .../plugins/publish/collect_texture.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py b/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py index 8cf36fd489..e441218ca7 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py @@ -81,14 +81,10 @@ class CollectTextures(pyblish.api.ContextPlugin): parsed_subset = instance.data["subset"].replace( instance.data["family"], '') - fill_pairs = { + explicit_data = { "subset": parsed_subset } - fill_pairs = prepare_template_data(fill_pairs) - workfile_subset = format_template_with_optional_keys( - fill_pairs, self.workfile_subset_template) - processed_instance = False for repre in instance.data["representations"]: ext = repre["ext"].replace('.', '') @@ -102,6 +98,18 @@ class CollectTextures(pyblish.api.ContextPlugin): if ext in self.main_workfile_extensions or \ ext in self.other_workfile_extensions: + formatting_data = self._get_parsed_groups( + repre_file, + self.input_naming_patterns["workfile"], + self.input_naming_groups["workfile"], + self.color_space + ) + + formatting_data.update(explicit_data) + fill_pairs = prepare_template_data(formatting_data) + workfile_subset = format_template_with_optional_keys( + fill_pairs, self.workfile_subset_template) + asset_build = self._get_asset_build( repre_file, self.input_naming_patterns["workfile"], From 4740616310b90bfbbb78ac4360648a8e41571794 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 2 Mar 2022 15:51:07 +0100 Subject: [PATCH 09/28] nuke: rework reformat knob defining --- .../defaults/project_settings/nuke.json | 9 +- .../schemas/schema_nuke_publish.json | 92 +++++++++++++++++-- 2 files changed, 91 insertions(+), 10 deletions(-) diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index 238d21d43a..e30296d0ad 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -126,24 +126,29 @@ "reformat_node_add": false, "reformat_node_config": [ { + "type": "string", "name": "type", "value": "to format" }, { + "type": "string", "name": "format", "value": "HD_1080" }, { + "type": "string", "name": "filter", "value": "Lanczos6" }, { + "type": "bool", "name": "black_outside", - "value": "true" + "value": true }, { + "type": "bool", "name": "pbb", - "value": "false" + "value": false } ] } diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json index 81e5d2cc3f..f53c53c2f8 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json @@ -245,17 +245,93 @@ "type": "list", "key": "reformat_node_config", "object_type": { - "type": "dict", - "children": [ + "type": "dict-conditional", + "enum_key": "type", + "enum_label": "Type", + "enum_children": [ { - "type": "text", - "key": "name", - "label": "Knob Name" + "key": "string", + "label": "String", + "children": [ + { + "type": "text", + "key": "name", + "label": "Name" + }, + { + "type": "text", + "key": "value", + "label": "Value" + } + ] }, { - "type": "text", - "key": "value", - "label": "Knob Value" + "key": "bool", + "label": "Boolean", + "children": [ + { + "type": "text", + "key": "name", + "label": "Name" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] + }, + { + "key": "number", + "label": "Number", + "children": [ + { + "type": "text", + "key": "name", + "label": "Name" + }, + { + "type": "list-strict", + "key": "value", + "label": "Value", + "object_types": [ + { + "type": "number", + "key": "number", + "decimal": 4 + } + ] + } + + ] + }, + { + "key": "list_numbers", + "label": "2 Numbers", + "children": [ + { + "type": "text", + "key": "name", + "label": "Name" + }, + { + "type": "list-strict", + "key": "value", + "label": "Value", + "object_types": [ + { + "type": "number", + "key": "x", + "decimal": 4 + }, + { + "type": "number", + "key": "y", + "decimal": 4 + } + ] + } + ] } ] } From c70015cbe675453107edb846f4f257cdb3ff1761 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 2 Mar 2022 16:34:13 +0100 Subject: [PATCH 10/28] nuke: connect api to new reformat config settings --- openpype/hosts/nuke/api/plugin.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index 67c5203cda..5cc1db41c7 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -489,12 +489,14 @@ class ExporterReviewMov(ExporterReview): if reformat_node_add: rf_node = nuke.createNode("Reformat") for kn_conf in reformat_node_config: + _type = kn_conf["type"] k_name = str(kn_conf["name"]) - k_value = str(kn_conf["value"]) - if k_value == "true": - k_value = True - if k_value == "false": - k_value = False + k_value = kn_conf["value"] + + # to remove unicode as nuke doesn't like it + if _type == "string": + k_value = str(kn_conf["value"]) + rf_node[k_name].setValue(k_value) # connect From 17eaec1f5be0d8841da88403db5bcf6355f7fd84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Wed, 2 Mar 2022 17:12:46 +0100 Subject: [PATCH 11/28] quick fix crypto --- openpype/plugins/publish/extract_jpeg_exr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_jpeg_exr.py b/openpype/plugins/publish/extract_jpeg_exr.py index d80b7bb9c3..99feadcc0b 100644 --- a/openpype/plugins/publish/extract_jpeg_exr.py +++ b/openpype/plugins/publish/extract_jpeg_exr.py @@ -34,7 +34,7 @@ class ExtractJpegEXR(pyblish.api.InstancePlugin): self.log.info("subset {}".format(instance.data['subset'])) # skip crypto passes. - if 'crypto' in instance.data['subset']: + if 'crypto' in instance.data['subset'].lower(): self.log.info("Skipping crypto passes.") return From bdd4e088b73c12988a48a7db916f81914c321346 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 2 Mar 2022 22:24:23 +0100 Subject: [PATCH 12/28] added todo --- openpype/plugins/publish/extract_jpeg_exr.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/plugins/publish/extract_jpeg_exr.py b/openpype/plugins/publish/extract_jpeg_exr.py index 99feadcc0b..468ed96199 100644 --- a/openpype/plugins/publish/extract_jpeg_exr.py +++ b/openpype/plugins/publish/extract_jpeg_exr.py @@ -34,6 +34,11 @@ class ExtractJpegEXR(pyblish.api.InstancePlugin): self.log.info("subset {}".format(instance.data['subset'])) # skip crypto passes. + # TODO: This is just a quick fix and has its own side-effects - it is + # affecting every subset name with `crypto` in its name. + # This must be solved properly, maybe using tags on + # representation that can be determined much earlier and + # with better precision. if 'crypto' in instance.data['subset'].lower(): self.log.info("Skipping crypto passes.") return From cc4894d899696d9f3ef80e6b9e5c21473ae610b9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 3 Mar 2022 11:19:28 +0100 Subject: [PATCH 13/28] fix value changes --- openpype/settings/entities/dict_conditional.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openpype/settings/entities/dict_conditional.py b/openpype/settings/entities/dict_conditional.py index 963fd406ed..19f326aea7 100644 --- a/openpype/settings/entities/dict_conditional.py +++ b/openpype/settings/entities/dict_conditional.py @@ -584,8 +584,9 @@ class DictConditionalEntity(ItemEntity): self.enum_entity.update_default_value(enum_value) for children_by_key in self.non_gui_children.values(): + value_copy = copy.deepcopy(value) for key, child_obj in children_by_key.items(): - child_value = value.get(key, NOT_SET) + child_value = value_copy.get(key, NOT_SET) child_obj.update_default_value(child_value) def update_studio_value(self, value): @@ -620,8 +621,9 @@ class DictConditionalEntity(ItemEntity): self.enum_entity.update_studio_value(enum_value) for children_by_key in self.non_gui_children.values(): + value_copy = copy.deepcopy(value) for key, child_obj in children_by_key.items(): - child_value = value.get(key, NOT_SET) + child_value = value_copy.get(key, NOT_SET) child_obj.update_studio_value(child_value) def update_project_value(self, value): @@ -656,8 +658,9 @@ class DictConditionalEntity(ItemEntity): self.enum_entity.update_project_value(enum_value) for children_by_key in self.non_gui_children.values(): + value_copy = copy.deepcopy(value) for key, child_obj in children_by_key.items(): - child_value = value.get(key, NOT_SET) + child_value = value_copy.get(key, NOT_SET) child_obj.update_project_value(child_value) def _discard_changes(self, on_change_trigger): From 522770a1605297be693856d50bf6ef4ae1060c49 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 3 Mar 2022 16:07:22 +0100 Subject: [PATCH 14/28] nuke: adding `reformated` tag to differentiate repre for extract review --- openpype/hosts/nuke/api/plugin.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index 5cc1db41c7..3e61caedf9 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -487,6 +487,9 @@ class ExporterReviewMov(ExporterReview): # add reformat node if reformat_node_add: + # append reformated tag + add_tags.append("reformated") + rf_node = nuke.createNode("Reformat") for kn_conf in reformat_node_config: _type = kn_conf["type"] From ff6fecdc5dbc04f583c11d5735eb4ea7116752dd Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 3 Mar 2022 16:07:58 +0100 Subject: [PATCH 15/28] global: adding `reformated` tag exception into extract review --- openpype/plugins/publish/extract_review.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index 5f286a53e6..b4a5117959 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -1171,6 +1171,9 @@ class ExtractReview(pyblish.api.InstancePlugin): self.log.debug("input_width: `{}`".format(input_width)) self.log.debug("input_height: `{}`".format(input_height)) + reformat_in_baking = bool("reformated" in new_repre["tags"]) + self.log.debug("reformat_in_baking: `{}`".format(reformat_in_baking)) + # Use instance resolution if output definition has not set it. if output_width is None or output_height is None: output_width = temp_data["resolution_width"] @@ -1182,6 +1185,17 @@ class ExtractReview(pyblish.api.InstancePlugin): output_width = input_width output_height = input_height + if reformat_in_baking: + self.log.debug(( + "Using resolution from input. It is already " + "reformated from baking process" + )) + output_width = input_width + output_height = input_height + pixel_aspect = 1 + new_repre["resolutionWidth"] = input_width + new_repre["resolutionHeight"] = input_height + output_width = int(output_width) output_height = int(output_height) From 2b470aeacca5dcab37ecc5aa92e455437b165970 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 11:01:13 +0100 Subject: [PATCH 16/28] copied functions related to change of context --- openpype/lib/avalon_context.py | 155 +++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index 1e8d21852b..9f6a9f9cdc 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -644,6 +644,161 @@ def get_workdir( ) +def template_data_from_session(session): + """ Return dictionary with template from session keys. + + Args: + session (dict, Optional): The Session to use. If not provided use the + currently active global Session. + Returns: + dict: All available data from session. + """ + from avalon import io + + if session is None: + session = avalon.api.Session + + project_name = session["AVALON_PROJECT"] + project_doc = io._database[project_name].find_one({"type": "project"}) + asset_doc = io._database[project_name].find_one({ + "type": "asset", + "name": session["AVALON_ASSET"] + }) + task_name = session["AVALON_TASK"] + host_name = session["AVALON_APP"] + return get_workdir_data(project_doc, asset_doc, task_name, host_name) + + +def compute_session_changes( + session, task=None, asset=None, app=None, template_key=None +): + """Compute the changes for a Session object on asset, task or app switch + + This does *NOT* update the Session object, but returns the changes + required for a valid update of the Session. + + Args: + session (dict): The initial session to compute changes to. + This is required for computing the full Work Directory, as that + also depends on the values that haven't changed. + task (str, Optional): Name of task to switch to. + asset (str or dict, Optional): Name of asset to switch to. + You can also directly provide the Asset dictionary as returned + from the database to avoid an additional query. (optimization) + app (str, Optional): Name of app to switch to. + + Returns: + dict: The required changes in the Session dictionary. + + """ + changes = dict() + + # If no changes, return directly + if not any([task, asset, app]): + return changes + + # Get asset document and asset + asset_document = None + asset_tasks = None + if isinstance(asset, dict): + # Assume asset database document + asset_document = asset + asset_tasks = asset_document.get("data", {}).get("tasks") + asset = asset["name"] + + if not asset_document or not asset_tasks: + from avalon import io + + # Assume asset name + asset_document = io.find_one( + { + "name": asset, + "type": "asset" + }, + {"data.tasks": True} + ) + assert asset_document, "Asset must exist" + + # Detect any changes compared session + mapping = { + "AVALON_ASSET": asset, + "AVALON_TASK": task, + "AVALON_APP": app, + } + changes = { + key: value + for key, value in mapping.items() + if value and value != session.get(key) + } + if not changes: + return changes + + # Compute work directory (with the temporary changed session so far) + _session = session.copy() + _session.update(changes) + + changes["AVALON_WORKDIR"] = get_workdir_from_session(_session) + + return changes + + +def get_workdir_from_session(session, template_key=None): + project_name = session["AVALON_PROJECT"] + host_name = session["AVALON_APP"] + anatomy = Anatomy(project_name) + template_data = template_data_from_session(session) + anatomy_filled = anatomy.format(template_data) + + if not template_key: + task_type = template_data["task"]["type"] + template_key = get_workfile_template_key( + task_type, + host_name, + project_name=project_name + ) + return anatomy_filled[template_key]["folder"] + + +def update_current_task(task=None, asset=None, app=None, template_key=None): + """Update active Session to a new task work area. + + This updates the live Session to a different `asset`, `task` or `app`. + + Args: + task (str): The task to set. + asset (str): The asset to set. + app (str): The app to set. + + Returns: + dict: The changed key, values in the current Session. + + """ + import avalon.api + from avalon.pipeline import emit + + changes = compute_session_changes( + avalon.api.Session, + task=task, + asset=asset, + app=app, + template_key=template_key + ) + + # Update the Session and environments. Pop from environments all keys with + # value set to None. + for key, value in changes.items(): + avalon.api.Session[key] = value + if value is None: + os.environ.pop(key, None) + else: + os.environ[key] = value + + # Emit session change + emit("taskChanged", changes.copy()) + + return changes + + @with_avalon def get_workfile_doc(asset_id, task_name, filename, dbcon=None): """Return workfile document for entered context. From 81d8e4d4ccd3668fca5f3a4f54e932131691d96d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 11:01:30 +0100 Subject: [PATCH 17/28] use change context function in workfiles tool --- openpype/tools/workfiles/app.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/openpype/tools/workfiles/app.py b/openpype/tools/workfiles/app.py index 3a772a038c..aece7bfb4f 100644 --- a/openpype/tools/workfiles/app.py +++ b/openpype/tools/workfiles/app.py @@ -29,6 +29,10 @@ from openpype.lib import ( create_workdir_extra_folders, get_system_general_anatomy_data ) +from openpype.lib.avalon_context import ( + update_current_task, + compute_session_changes +) from .model import FilesModel from .view import FilesView @@ -667,7 +671,7 @@ class FilesWidget(QtWidgets.QWidget): session["AVALON_APP"], project_name=session["AVALON_PROJECT"] ) - changes = pipeline.compute_session_changes( + changes = compute_session_changes( session, asset=self._get_asset_doc(), task=self._task_name, @@ -681,7 +685,7 @@ class FilesWidget(QtWidgets.QWidget): """Enter the asset and task session currently selected""" session = api.Session.copy() - changes = pipeline.compute_session_changes( + changes = compute_session_changes( session, asset=self._get_asset_doc(), task=self._task_name, @@ -692,7 +696,7 @@ class FilesWidget(QtWidgets.QWidget): # to avoid any unwanted Task Changed callbacks to be triggered. return - api.update_current_task( + update_current_task( asset=self._get_asset_doc(), task=self._task_name, template_key=self.template_key From 8f88e7b9250d541c2428e479150f9a78db664125 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 11:01:42 +0100 Subject: [PATCH 18/28] modifid assetcreator imports --- openpype/tools/assetcreator/app.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openpype/tools/assetcreator/app.py b/openpype/tools/assetcreator/app.py index 1d332d647e..60ef31e859 100644 --- a/openpype/tools/assetcreator/app.py +++ b/openpype/tools/assetcreator/app.py @@ -4,9 +4,11 @@ from subprocess import Popen import ftrack_api from Qt import QtWidgets, QtCore +from openpype import style from openpype.api import get_current_project_settings +from openpype.lib.avalon_context import update_current_task from openpype.tools.utils.lib import qt_app_context -from avalon import io, api, style, schema +from avalon import io, api, schema from . import widget, model module = sys.modules[__name__] @@ -463,12 +465,12 @@ class Window(QtWidgets.QDialog): return task_name = task_model.itemData(index)[0] try: - api.update_current_task(task=task_name, asset=asset_name) + update_current_task(task=task_name, asset=asset_name) self.open_app() finally: if origin_task is not None and origin_asset is not None: - api.update_current_task( + update_current_task( task=origin_task, asset=origin_asset ) From 59c5c464ccd3960dbf2e5260024d42c2c58436eb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 11:02:19 +0100 Subject: [PATCH 19/28] change how fusion calcuates workdir path --- openpype/hosts/fusion/scripts/fusion_switch_shot.py | 9 +++------ openpype/hosts/fusion/utility_scripts/switch_ui.py | 13 +++---------- openpype/scripts/fusion_switch_shot.py | 9 ++++----- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/openpype/hosts/fusion/scripts/fusion_switch_shot.py b/openpype/hosts/fusion/scripts/fusion_switch_shot.py index 9dd8a351e4..ed6a06bb34 100644 --- a/openpype/hosts/fusion/scripts/fusion_switch_shot.py +++ b/openpype/hosts/fusion/scripts/fusion_switch_shot.py @@ -5,11 +5,12 @@ import logging # Pipeline imports import avalon.api -from avalon import io, pipeline +from avalon import io from openpype.lib import version_up from openpype.hosts.fusion import api from openpype.hosts.fusion.api import lib +from openpype.lib.avalon_context import get_workdir_from_session log = logging.getLogger("Update Slap Comp") @@ -46,12 +47,8 @@ def _format_version_folder(folder): def _get_work_folder(session): """Convenience function to get the work folder path of the current asset""" - # Get new filename, create path based on asset and work template - template_work = self._project["config"]["template"]["work"] - work_path = pipeline._format_work_template(template_work, session) - - return os.path.normpath(work_path) + return get_workdir_from_session(session) def _get_fusion_instance(): diff --git a/openpype/hosts/fusion/utility_scripts/switch_ui.py b/openpype/hosts/fusion/utility_scripts/switch_ui.py index fe324d9a41..854c2fd415 100644 --- a/openpype/hosts/fusion/utility_scripts/switch_ui.py +++ b/openpype/hosts/fusion/utility_scripts/switch_ui.py @@ -5,11 +5,12 @@ import logging from Qt import QtWidgets, QtCore import avalon.api -from avalon import io, pipeline +from avalon import io from avalon.vendor import qtawesome as qta from openpype import style from openpype.hosts.fusion import api +from openpype.lib.avalon_context import get_workdir_from_session log = logging.getLogger("Fusion Switch Shot") @@ -158,15 +159,7 @@ class App(QtWidgets.QWidget): switch_shot.switch(asset_name=asset, filepath=file_name, new=True) def _get_context_directory(self): - - project = io.find_one({"type": "project", - "name": avalon.api.Session["AVALON_PROJECT"]}, - projection={"config": True}) - - template = project["config"]["template"]["work"] - dir = pipeline._format_work_template(template, avalon.api.Session) - - return dir + return get_workdir_from_session(avalon.api.Session) def collect_slap_comps(self, directory): items = glob.glob("{}/*.comp".format(directory)) diff --git a/openpype/scripts/fusion_switch_shot.py b/openpype/scripts/fusion_switch_shot.py index 26f5356336..a8ac6812b5 100644 --- a/openpype/scripts/fusion_switch_shot.py +++ b/openpype/scripts/fusion_switch_shot.py @@ -4,13 +4,15 @@ import sys import logging # Pipeline imports -from avalon import api, io, pipeline +from avalon import api, io import avalon.fusion # Config imports import openpype.lib as pype import openpype.hosts.fusion.lib as fusion_lib +from openpype.lib.avalon_context import get_workdir_from_session + log = logging.getLogger("Update Slap Comp") self = sys.modules[__name__] @@ -48,10 +50,7 @@ def _get_work_folder(session): """Convenience function to get the work folder path of the current asset""" # Get new filename, create path based on asset and work template - template_work = self._project["config"]["template"]["work"] - work_path = pipeline._format_work_template(template_work, session) - - return os.path.normpath(work_path) + return get_workdir_from_session(session) def _get_fusion_instance(): From 598882113129e80e1619f7d1c9b256b27847b64e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 11:26:26 +0100 Subject: [PATCH 20/28] reduced functions in fusion --- openpype/hosts/fusion/scripts/fusion_switch_shot.py | 10 ++-------- openpype/hosts/fusion/utility_scripts/switch_ui.py | 5 +---- openpype/lib/avalon_context.py | 9 +++++++-- openpype/scripts/fusion_switch_shot.py | 11 ++--------- 4 files changed, 12 insertions(+), 23 deletions(-) diff --git a/openpype/hosts/fusion/scripts/fusion_switch_shot.py b/openpype/hosts/fusion/scripts/fusion_switch_shot.py index ed6a06bb34..ca7efb9136 100644 --- a/openpype/hosts/fusion/scripts/fusion_switch_shot.py +++ b/openpype/hosts/fusion/scripts/fusion_switch_shot.py @@ -45,12 +45,6 @@ def _format_version_folder(folder): return version_folder -def _get_work_folder(session): - """Convenience function to get the work folder path of the current asset""" - # Get new filename, create path based on asset and work template - return get_workdir_from_session(session) - - def _get_fusion_instance(): fusion = getattr(sys.modules["__main__"], "fusion", None) if fusion is None: @@ -69,7 +63,7 @@ def _format_filepath(session): asset = session["AVALON_ASSET"] # Save updated slap comp - work_path = _get_work_folder(session) + work_path = get_workdir_from_session(session) walk_to_dir = os.path.join(work_path, "scenes", "slapcomp") slapcomp_dir = os.path.abspath(walk_to_dir) @@ -109,7 +103,7 @@ def _update_savers(comp, session): None """ - new_work = _get_work_folder(session) + new_work = get_workdir_from_session(session) renders = os.path.join(new_work, "renders") version_folder = _format_version_folder(renders) renders_version = os.path.join(renders, version_folder) diff --git a/openpype/hosts/fusion/utility_scripts/switch_ui.py b/openpype/hosts/fusion/utility_scripts/switch_ui.py index 854c2fd415..afb39f7041 100644 --- a/openpype/hosts/fusion/utility_scripts/switch_ui.py +++ b/openpype/hosts/fusion/utility_scripts/switch_ui.py @@ -124,7 +124,7 @@ class App(QtWidgets.QWidget): def _on_open_from_dir(self): - start_dir = self._get_context_directory() + start_dir = get_workdir_from_session() comp_file, _ = QtWidgets.QFileDialog.getOpenFileName( self, "Choose comp", start_dir) @@ -158,9 +158,6 @@ class App(QtWidgets.QWidget): import colorbleed.scripts.fusion_switch_shot as switch_shot switch_shot.switch(asset_name=asset, filepath=file_name, new=True) - def _get_context_directory(self): - return get_workdir_from_session(avalon.api.Session) - def collect_slap_comps(self, directory): items = glob.glob("{}/*.comp".format(directory)) return items diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index 9f6a9f9cdc..0bfd3f6de0 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -644,7 +644,7 @@ def get_workdir( ) -def template_data_from_session(session): +def template_data_from_session(session=None): """ Return dictionary with template from session keys. Args: @@ -654,6 +654,7 @@ def template_data_from_session(session): dict: All available data from session. """ from avalon import io + import avalon.api if session is None: session = avalon.api.Session @@ -742,7 +743,11 @@ def compute_session_changes( return changes -def get_workdir_from_session(session, template_key=None): +def get_workdir_from_session(session=None, template_key=None): + import avalon.api + + if session is None: + session = avalon.api.Session project_name = session["AVALON_PROJECT"] host_name = session["AVALON_APP"] anatomy = Anatomy(project_name) diff --git a/openpype/scripts/fusion_switch_shot.py b/openpype/scripts/fusion_switch_shot.py index a8ac6812b5..6db8ff36a8 100644 --- a/openpype/scripts/fusion_switch_shot.py +++ b/openpype/scripts/fusion_switch_shot.py @@ -46,13 +46,6 @@ def _format_version_folder(folder): return version_folder -def _get_work_folder(session): - """Convenience function to get the work folder path of the current asset""" - - # Get new filename, create path based on asset and work template - return get_workdir_from_session(session) - - def _get_fusion_instance(): fusion = getattr(sys.modules["__main__"], "fusion", None) if fusion is None: @@ -71,7 +64,7 @@ def _format_filepath(session): asset = session["AVALON_ASSET"] # Save updated slap comp - work_path = _get_work_folder(session) + work_path = get_workdir_from_session(session) walk_to_dir = os.path.join(work_path, "scenes", "slapcomp") slapcomp_dir = os.path.abspath(walk_to_dir) @@ -102,7 +95,7 @@ def _update_savers(comp, session): None """ - new_work = _get_work_folder(session) + new_work = get_workdir_from_session(session) renders = os.path.join(new_work, "renders") version_folder = _format_version_folder(renders) renders_version = os.path.join(renders, version_folder) From 37cba59fb2172ba14e101c3eacbc028c2026f203 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 4 Mar 2022 11:46:35 +0100 Subject: [PATCH 21/28] nuke: settings adding default states --- .../projects_schema/schemas/schema_nuke_publish.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json index f53c53c2f8..4c94801796 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json @@ -233,7 +233,8 @@ { "type": "boolean", "key": "reformat_node_add", - "label": "Add Reformat Node" + "label": "Add Reformat Node", + "default": false }, { "type": "collapsible-wrap", @@ -298,6 +299,7 @@ { "type": "number", "key": "number", + "default": 1, "decimal": 4 } ] @@ -322,11 +324,13 @@ { "type": "number", "key": "x", + "default": 1, "decimal": 4 }, { "type": "number", "key": "y", + "default": 1, "decimal": 4 } ] From 882a17b04a164c833de3bdbca06a1af5505eaf78 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 4 Mar 2022 11:50:36 +0100 Subject: [PATCH 22/28] Move update all logic to from window to view --- openpype/tools/sceneinventory/view.py | 37 +++++++++++++++++++++++++ openpype/tools/sceneinventory/window.py | 37 +------------------------ 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/openpype/tools/sceneinventory/view.py b/openpype/tools/sceneinventory/view.py index 1ed3c9fcb6..ec48b10e47 100644 --- a/openpype/tools/sceneinventory/view.py +++ b/openpype/tools/sceneinventory/view.py @@ -796,3 +796,40 @@ class SceneInventoryView(QtWidgets.QTreeView): ).format(version_str) dialog.setText(msg) dialog.exec_() + + def update_all(self): + """Update all items that are currently 'outdated' in the view""" + # Get the source model through the proxy model + model = self.model().sourceModel() + + # Get all items from outdated groups + outdated_items = [] + for index in iter_model_rows(model, + column=0, + include_root=False): + item = index.data(model.ItemRole) + + if not item.get("isGroupNode"): + continue + + # Only the group nodes contain the "highest_version" data and as + # such we find only the groups and take its children. + if not model.outdated(item): + continue + + # Collect all children which we want to update + children = item.children() + outdated_items.extend(children) + + if not outdated_items: + log.info("Nothing to update.") + return + + # Trigger update to latest + for item in outdated_items: + try: + api.update(item, -1) + except AssertionError: + self._show_version_error_dialog(None, [item]) + log.warning("Update failed", exc_info=True) + self.data_changed.emit() diff --git a/openpype/tools/sceneinventory/window.py b/openpype/tools/sceneinventory/window.py index d9d34dbb08..b23c45c0f4 100644 --- a/openpype/tools/sceneinventory/window.py +++ b/openpype/tools/sceneinventory/window.py @@ -21,8 +21,6 @@ from .model import ( ) from .view import SceneInventoryView -from ..utils.lib import iter_model_rows - log = logging.getLogger(__name__) module = sys.modules[__name__] @@ -172,40 +170,7 @@ class SceneInventoryWindow(QtWidgets.QDialog): ) def _on_update_all(self): - """Update all items that are currently 'outdated' in the view""" - - # Get all items from outdated groups - outdated_items = [] - for index in iter_model_rows(self._model, - column=0, - include_root=False): - item = index.data(self._model.ItemRole) - - if not item.get("isGroupNode"): - continue - - # Only the group nodes contain the "highest_version" data and as - # such we find only the groups and take its children. - if not self._model.outdated(item): - continue - - # Collect all children which we want to update - children = item.children() - outdated_items.extend(children) - - if not outdated_items: - log.info("Nothing to update.") - return - - # Trigger update to latest - # Logic copied from SceneInventoryView._build_item_menu_for_selection - for item in outdated_items: - try: - api.update(item, -1) - except AssertionError: - self._show_version_error_dialog(None, [item]) - log.warning("Update failed", exc_info=True) - self._view.data_changed.emit() + self._view.update_all() def show(root=None, debug=False, parent=None, items=None): From f65b202ae34d58981df916ac6ca7897ba303b69a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Mar 2022 13:23:09 +0100 Subject: [PATCH 23/28] Added more log messages --- .../plugins/publish/collect_texture.py | 65 +++++++++++++------ 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py b/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py index e441218ca7..ea0b6cdf41 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py @@ -104,6 +104,9 @@ class CollectTextures(pyblish.api.ContextPlugin): self.input_naming_groups["workfile"], self.color_space ) + self.log.info("Parsed groups from workfile " + "name '{}': {}".format(repre_file, + formatting_data)) formatting_data.update(explicit_data) fill_pairs = prepare_template_data(formatting_data) @@ -155,19 +158,24 @@ class CollectTextures(pyblish.api.ContextPlugin): } resource_files[workfile_subset].append(item) - formatting_data = self._get_parsed_groups( - repre_file, - self.input_naming_patterns["textures"], - self.input_naming_groups["textures"], - self.color_space - ) - if ext in self.texture_extensions: + formatting_data = self._get_parsed_groups( + repre_file, + self.input_naming_patterns["textures"], + self.input_naming_groups["textures"], + self.color_space + ) + + self.log.info("Parsed groups from texture " + "name '{}': {}".format(repre_file, + formatting_data)) + c_space = self._get_color_space( repre_file, self.color_space ) + # optional value channel = self._get_channel_name( repre_file, self.input_naming_patterns["textures"], @@ -175,6 +183,7 @@ class CollectTextures(pyblish.api.ContextPlugin): self.color_space ) + # optional value shader = self._get_shader_name( repre_file, self.input_naming_patterns["textures"], @@ -260,6 +269,13 @@ class CollectTextures(pyblish.api.ContextPlugin): for asset_build, version, subset, family in asset_builds: if not main_version: main_version = version + + try: + version_int = int(version or main_version or 1) + except ValueError: + self.log.error("Parsed version {} is not " + "an number".format(version)) + new_instance = context.create_instance(subset) new_instance.data.update( { @@ -268,7 +284,7 @@ class CollectTextures(pyblish.api.ContextPlugin): "label": subset, "name": subset, "family": family, - "version": int(version or main_version or 1), + "version": version_int, "asset_build": asset_build # remove in validator } ) @@ -393,12 +409,15 @@ class CollectTextures(pyblish.api.ContextPlugin): Unknown format of channel name and color spaces >> cs are known list - 'color_space' used as a placeholder """ - found = self._parse_key(name, input_naming_patterns, - input_naming_groups, color_spaces, 'shader') - if found: - return found + found = None + try: + found = self._parse_key(name, input_naming_patterns, + input_naming_groups, color_spaces, + 'shader') + except ValueError: + self.log.warning("Didn't find shader in {}".format(name)) - self.log.warning("Didn't find shader in {}".format(name)) + return found def _get_channel_name(self, name, input_naming_patterns, input_naming_groups, color_spaces): @@ -407,12 +426,15 @@ class CollectTextures(pyblish.api.ContextPlugin): Unknown format of channel name and color spaces >> cs are known list - 'color_space' used as a placeholder """ - found = self._parse_key(name, input_naming_patterns, - input_naming_groups, color_spaces, 'channel') - if found: - return found + found = None + try: + found = self._parse_key(name, input_naming_patterns, + input_naming_groups, color_spaces, + 'channel') + except ValueError: + self.log.warning("Didn't find channel in {}".format(name)) - self.log.warning("Didn't find channel in {}".format(name)) + return found def _parse_key(self, name, input_naming_patterns, input_naming_groups, color_spaces, key): @@ -437,8 +459,8 @@ class CollectTextures(pyblish.api.ContextPlugin): try: parsed_value = parsed_groups[key] return parsed_value - except IndexError: - msg = ("input_naming_groups must " + + except (IndexError, KeyError): + msg = ("'Textures group positions' must " + "have '{}' key".format(key)) raise ValueError(msg) @@ -468,7 +490,8 @@ class CollectTextures(pyblish.api.ContextPlugin): self.log.warning("No of parsed groups doesn't match " "no of group labels") - return {} + raise ValueError("Name '{}' cannot be parsed by any " + "'{}' patterns".format(name, input_naming_patterns)) def _update_representations(self, upd_representations): """Frames dont have sense for textures, add collected udims instead.""" From 69b0012fd9427c554df753f7c2fdc43fc1c60bea Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 4 Mar 2022 13:46:32 +0100 Subject: [PATCH 24/28] nuke: subset filtering on baking presets --- .../publish/extract_review_data_mov.py | 26 ++++++++++++++++++- .../defaults/project_settings/nuke.json | 3 ++- .../schemas/schema_nuke_publish.json | 6 +++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py index 5bbc88266a..1071834497 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py +++ b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py @@ -1,4 +1,5 @@ import os +import re import pyblish.api import openpype from openpype.hosts.nuke.api import plugin @@ -25,6 +26,7 @@ class ExtractReviewDataMov(openpype.api.Extractor): def process(self, instance): families = instance.data["families"] task_type = instance.context.data["taskType"] + subset = instance.data["subset"] self.log.info("Creating staging dir...") if "representations" not in instance.data: @@ -46,6 +48,7 @@ class ExtractReviewDataMov(openpype.api.Extractor): for o_name, o_data in self.outputs.items(): f_families = o_data["filter"]["families"] f_task_types = o_data["filter"]["task_types"] + f_subsets = o_data["filter"]["sebsets"] # test if family found in context test_families = any([ @@ -69,11 +72,25 @@ class ExtractReviewDataMov(openpype.api.Extractor): bool(not f_task_types) ]) + # test subsets from filter + test_subsets = any([ + # check if any of subset filter inputs + # converted to regex patern is not found in subset + # we keep strict case sensitivity + bool(next(( + s for s in f_subsets + if re.search(re.compile(s), subset) + ), None)), + # but if no subsets were set then make this acuntable too + bool(not f_subsets) + ]) + # we need all filters to be positive for this # preset to be activated test_all = all([ test_families, - test_task_types + test_task_types, + test_subsets ]) # if it is not positive then skip this preset @@ -120,6 +137,13 @@ class ExtractReviewDataMov(openpype.api.Extractor): if generated_repres: # assign to representations instance.data["representations"] += generated_repres + else: + instance.data["families"].remove("review") + self.log.info(( + "Removing `review` from families. " + "Not available baking profile." + )) + self.log.debug(instance.data["families"]) self.log.debug( "_ representations: {}".format( diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index e30296d0ad..6992fb6e3e 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -116,7 +116,8 @@ "baking": { "filter": { "task_types": [], - "families": [] + "families": [], + "sebsets": [] }, "extension": "mov", "viewer_process_override": "", diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json index 4c94801796..1636a8d700 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json @@ -195,6 +195,12 @@ "label": "Families", "type": "list", "object_type": "text" + }, + { + "key": "sebsets", + "label": "Subsets", + "type": "list", + "object_type": "text" } ] }, From 10fb4f68b9fae26820b4033cdeed73639eee70bc Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 4 Mar 2022 14:59:29 +0100 Subject: [PATCH 25/28] Remove log Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/tools/sceneinventory/window.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/tools/sceneinventory/window.py b/openpype/tools/sceneinventory/window.py index b23c45c0f4..7dee32e90b 100644 --- a/openpype/tools/sceneinventory/window.py +++ b/openpype/tools/sceneinventory/window.py @@ -21,7 +21,6 @@ from .model import ( ) from .view import SceneInventoryView -log = logging.getLogger(__name__) module = sys.modules[__name__] module.window = None From e27896f4bbae2d9bcf02abdea39b534e1ebe6ef2 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 4 Mar 2022 15:00:17 +0100 Subject: [PATCH 26/28] Remove unused import --- openpype/tools/sceneinventory/window.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/tools/sceneinventory/window.py b/openpype/tools/sceneinventory/window.py index 7dee32e90b..095d30cac0 100644 --- a/openpype/tools/sceneinventory/window.py +++ b/openpype/tools/sceneinventory/window.py @@ -1,6 +1,5 @@ import os import sys -import logging from Qt import QtWidgets, QtCore from avalon.vendor import qtawesome From ee53add80fb8af7147508c1864f7fab7aae232db Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 17:13:03 +0100 Subject: [PATCH 27/28] don't set version if is not available --- openpype/pipeline/create/context.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index e11d32091f..706279fd72 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -399,15 +399,6 @@ class CreatedInstance: self._data["active"] = data.get("active", True) self._data["creator_identifier"] = creator.identifier - # QUESTION handle version of instance here or in creator? - version = None - if not new: - version = data.get("version") - - if version is None: - version = 1 - self._data["version"] = version - # Pop from source data all keys that are defined in `_data` before # this moment and through their values away # - they should be the same and if are not then should not change From bb33d63526b342377b6c5228e36c72d140d6421e Mon Sep 17 00:00:00 2001 From: OpenPype Date: Sat, 5 Mar 2022 03:36:36 +0000 Subject: [PATCH 28/28] [Automated] Bump version --- CHANGELOG.md | 44 ++++++++++++++++++++++---------------------- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 348f7dc1b8..711517e6c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [3.9.0-nightly.5](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.9.0-nightly.6](https://github.com/pypeclub/OpenPype/tree/HEAD) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.8.2...HEAD) @@ -12,56 +12,56 @@ - Documentation: fixed broken links [\#2799](https://github.com/pypeclub/OpenPype/pull/2799) - Documentation: broken link fix [\#2785](https://github.com/pypeclub/OpenPype/pull/2785) -- Documentation: link fixes [\#2772](https://github.com/pypeclub/OpenPype/pull/2772) -- Update docusaurus to latest version [\#2760](https://github.com/pypeclub/OpenPype/pull/2760) +- Various testing updates [\#2726](https://github.com/pypeclub/OpenPype/pull/2726) **🚀 Enhancements** +- Ftrack: Can sync fps as string [\#2836](https://github.com/pypeclub/OpenPype/pull/2836) - General: Color dialog UI fixes [\#2817](https://github.com/pypeclub/OpenPype/pull/2817) +- Nuke: adding Reformat to baking mov plugin [\#2811](https://github.com/pypeclub/OpenPype/pull/2811) +- Manager: Update all to latest button [\#2805](https://github.com/pypeclub/OpenPype/pull/2805) - General: Set context environments for non host applications [\#2803](https://github.com/pypeclub/OpenPype/pull/2803) +- Houdini: Remove duplicate ValidateOutputNode plug-in [\#2780](https://github.com/pypeclub/OpenPype/pull/2780) - Tray publisher: New Tray Publisher host \(beta\) [\#2778](https://github.com/pypeclub/OpenPype/pull/2778) +- Slack: Added regex for filtering on subset names [\#2775](https://github.com/pypeclub/OpenPype/pull/2775) - Houdini: Implement Reset Frame Range [\#2770](https://github.com/pypeclub/OpenPype/pull/2770) -- Pyblish Pype: Remove redundant new line in installed fonts printing [\#2758](https://github.com/pypeclub/OpenPype/pull/2758) - Flame: use Shot Name on segment for asset name [\#2751](https://github.com/pypeclub/OpenPype/pull/2751) -- Flame: adding validator source clip [\#2746](https://github.com/pypeclub/OpenPype/pull/2746) -- Ftrack: Disable ftrack module by default [\#2732](https://github.com/pypeclub/OpenPype/pull/2732) +- Houdini: Move Houdini Save Current File to beginning of ExtractorOrder [\#2747](https://github.com/pypeclub/OpenPype/pull/2747) - RoyalRender: Minor enhancements [\#2700](https://github.com/pypeclub/OpenPype/pull/2700) **🐛 Bug fixes** +- Maya: Stop creation of reviews for Cryptomattes [\#2832](https://github.com/pypeclub/OpenPype/pull/2832) +- Deadline: Remove recreated event [\#2828](https://github.com/pypeclub/OpenPype/pull/2828) +- Deadline: Added missing events folder [\#2827](https://github.com/pypeclub/OpenPype/pull/2827) - Settings: Missing document with OP versions may break start of OpenPype [\#2825](https://github.com/pypeclub/OpenPype/pull/2825) +- Deadline: more detailed temp file name for environment json [\#2824](https://github.com/pypeclub/OpenPype/pull/2824) +- General: Host name was formed from obsolete code [\#2821](https://github.com/pypeclub/OpenPype/pull/2821) - Settings UI: Fix "Apply from" action [\#2820](https://github.com/pypeclub/OpenPype/pull/2820) +- Ftrack: Job killer with missing user [\#2819](https://github.com/pypeclub/OpenPype/pull/2819) +- StandalonePublisher: use dynamic groups in subset names [\#2816](https://github.com/pypeclub/OpenPype/pull/2816) - Settings UI: Search case sensitivity [\#2810](https://github.com/pypeclub/OpenPype/pull/2810) - Flame Babypublisher optimalization [\#2806](https://github.com/pypeclub/OpenPype/pull/2806) - resolve: fixing fusion module loading [\#2802](https://github.com/pypeclub/OpenPype/pull/2802) +- Ftrack: Unset task ids from asset versions before tasks are removed [\#2800](https://github.com/pypeclub/OpenPype/pull/2800) +- Slack: fail gracefully if slack exception [\#2798](https://github.com/pypeclub/OpenPype/pull/2798) - Flame: Fix version string in default settings [\#2783](https://github.com/pypeclub/OpenPype/pull/2783) -- After Effects: Fix typo in name `afftereffects` -\> `aftereffects` [\#2768](https://github.com/pypeclub/OpenPype/pull/2768) -- Avoid renaming udim indexes [\#2765](https://github.com/pypeclub/OpenPype/pull/2765) +- Houdini: Fix open last workfile [\#2767](https://github.com/pypeclub/OpenPype/pull/2767) - Maya: Fix `unique\_namespace` when in an namespace that is empty [\#2759](https://github.com/pypeclub/OpenPype/pull/2759) -- Loader UI: Fix right click in representation widget [\#2757](https://github.com/pypeclub/OpenPype/pull/2757) -- Aftereffects 2022 and Deadline [\#2748](https://github.com/pypeclub/OpenPype/pull/2748) -- Flame: bunch of bugs [\#2745](https://github.com/pypeclub/OpenPype/pull/2745) -- Maya: Save current scene on workfile publish [\#2744](https://github.com/pypeclub/OpenPype/pull/2744) -- Version Up: Preserve parts of filename after version number \(like subversion\) on version\_up [\#2741](https://github.com/pypeclub/OpenPype/pull/2741) - Maya: Remove some unused code [\#2709](https://github.com/pypeclub/OpenPype/pull/2709) +- Multiple hosts: unify menu style across hosts [\#2693](https://github.com/pypeclub/OpenPype/pull/2693) **Merged pull requests:** +- General: Move change context functions [\#2839](https://github.com/pypeclub/OpenPype/pull/2839) +- Tools: Don't use avalon tools code [\#2829](https://github.com/pypeclub/OpenPype/pull/2829) - Move Unreal Implementation to OpenPype [\#2823](https://github.com/pypeclub/OpenPype/pull/2823) -- Ftrack: Job killer with missing user [\#2819](https://github.com/pypeclub/OpenPype/pull/2819) -- Ftrack: Unset task ids from asset versions before tasks are removed [\#2800](https://github.com/pypeclub/OpenPype/pull/2800) -- Slack: fail gracefully if slack exception [\#2798](https://github.com/pypeclub/OpenPype/pull/2798) +- Nuke: Use AVALON\_APP to get value for "app" key [\#2818](https://github.com/pypeclub/OpenPype/pull/2818) - Ftrack: Moved module one hierarchy level higher [\#2792](https://github.com/pypeclub/OpenPype/pull/2792) - SyncServer: Moved module one hierarchy level higher [\#2791](https://github.com/pypeclub/OpenPype/pull/2791) - Royal render: Move module one hierarchy level higher [\#2790](https://github.com/pypeclub/OpenPype/pull/2790) - Deadline: Move module one hierarchy level higher [\#2789](https://github.com/pypeclub/OpenPype/pull/2789) -- Houdini: Remove duplicate ValidateOutputNode plug-in [\#2780](https://github.com/pypeclub/OpenPype/pull/2780) -- Slack: Added regex for filtering on subset names [\#2775](https://github.com/pypeclub/OpenPype/pull/2775) -- Houdini: Fix open last workfile [\#2767](https://github.com/pypeclub/OpenPype/pull/2767) - General: Extract template formatting from anatomy [\#2766](https://github.com/pypeclub/OpenPype/pull/2766) -- Harmony: Rendering in Deadline didn't work in other machines than submitter [\#2754](https://github.com/pypeclub/OpenPype/pull/2754) -- Houdini: Move Houdini Save Current File to beginning of ExtractorOrder [\#2747](https://github.com/pypeclub/OpenPype/pull/2747) -- Maya: set Deadline job/batch name to original source workfile name instead of published workfile [\#2733](https://github.com/pypeclub/OpenPype/pull/2733) ## [3.8.2](https://github.com/pypeclub/OpenPype/tree/3.8.2) (2022-02-07) diff --git a/openpype/version.py b/openpype/version.py index b41951a34c..d977e87243 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.9.0-nightly.5" +__version__ = "3.9.0-nightly.6" diff --git a/pyproject.toml b/pyproject.toml index 851bf3f735..2469cb76a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.9.0-nightly.5" # OpenPype +version = "3.9.0-nightly.6" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License"