From 392dd02ba94e7f1da1247c39e4daaba3999c362b Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Tue, 21 Dec 2021 12:51:11 +0000 Subject: [PATCH 01/44] xml batch 1 --- .../help/validate_abc_primitive_to_detail.xml | 15 ++++++ .../help/validate_alembic_face_sets.xml | 22 +++++++++ .../help/validate_alembic_input_node.xml | 21 +++++++++ .../help/validate_animation_settings.xml | 31 ++++++++++++ .../publish/help/validate_vdb_input_node.xml | 22 +++++++++ .../plugins/publish/valiate_vdb_input_node.py | 47 ------------------- .../publish/validate_context_with_error.py | 1 + 7 files changed, 112 insertions(+), 47 deletions(-) create mode 100644 openpype/hosts/houdini/plugins/publish/help/validate_abc_primitive_to_detail.xml create mode 100644 openpype/hosts/houdini/plugins/publish/help/validate_alembic_face_sets.xml create mode 100644 openpype/hosts/houdini/plugins/publish/help/validate_alembic_input_node.xml create mode 100644 openpype/hosts/houdini/plugins/publish/help/validate_animation_settings.xml create mode 100644 openpype/hosts/houdini/plugins/publish/help/validate_vdb_input_node.xml delete mode 100644 openpype/hosts/houdini/plugins/publish/valiate_vdb_input_node.py diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_abc_primitive_to_detail.xml b/openpype/hosts/houdini/plugins/publish/help/validate_abc_primitive_to_detail.xml new file mode 100644 index 0000000000..0e2aa6c1f4 --- /dev/null +++ b/openpype/hosts/houdini/plugins/publish/help/validate_abc_primitive_to_detail.xml @@ -0,0 +1,15 @@ + + + +Primitive to Detail +## Invalid Primitive to Detail Attributes + +Primitives with inconsistent primitive to detail attributes were found. + +{message} + + + + + + \ No newline at end of file diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_alembic_face_sets.xml b/openpype/hosts/houdini/plugins/publish/help/validate_alembic_face_sets.xml new file mode 100644 index 0000000000..7bc149d7c3 --- /dev/null +++ b/openpype/hosts/houdini/plugins/publish/help/validate_alembic_face_sets.xml @@ -0,0 +1,22 @@ + + + +Alembic ROP Face Sets +## Invalid Alembic ROP Face Sets + +When groups are saved as Face Sets with the Alembic these show up +as shadingEngine connections in Maya - however, with animated groups +these connections in Maya won't work as expected, it won't update per +frame. Additionally, it can break shader assignments in some cases +where it requires to first break this connection to allow a shader to +be assigned. + +It is allowed to include Face Sets, so only an issue is logged to +identify that it could introduce issues down the pipeline. + + + + + + + \ No newline at end of file diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_alembic_input_node.xml b/openpype/hosts/houdini/plugins/publish/help/validate_alembic_input_node.xml new file mode 100644 index 0000000000..5be722ccb2 --- /dev/null +++ b/openpype/hosts/houdini/plugins/publish/help/validate_alembic_input_node.xml @@ -0,0 +1,21 @@ + + + +Alembic input +## Invalid Alembic input + +The node connected to the output is incorrect. +It contains primitive types that are not supported for alembic output. + +Problematic primitive is of type {primitive_type} + + + + + +The connected node cannot be of the following types for Alembic: + - VDB + - Volume + + + \ No newline at end of file diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_animation_settings.xml b/openpype/hosts/houdini/plugins/publish/help/validate_animation_settings.xml new file mode 100644 index 0000000000..8a2a396783 --- /dev/null +++ b/openpype/hosts/houdini/plugins/publish/help/validate_animation_settings.xml @@ -0,0 +1,31 @@ + + + +Frame token in output +## Frame range is missing frame token + +This validator will check the output parameter of the node if +the Valid Frame Range is not set to 'Render Current Frame' + +No frame token found in {nodepath} + +### How to repair? +Your you need to add `$F4` or similar frame based token to your path. +**Example:** + Good: 'my_vbd_cache.$F4.vdb' + Bad: 'my_vbd_cache.vdb' + + + + + + +If you render out a frame range it is mandatory to have the +frame token - '$F4' or similar - to ensure that each frame gets +written. If this is not the case you will override the same file +every time a frame is written out. + + + + + \ No newline at end of file diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_vdb_input_node.xml b/openpype/hosts/houdini/plugins/publish/help/validate_vdb_input_node.xml new file mode 100644 index 0000000000..8cc186a183 --- /dev/null +++ b/openpype/hosts/houdini/plugins/publish/help/validate_vdb_input_node.xml @@ -0,0 +1,22 @@ + + + +VDB input node +## Invalid VDB input node + +Validate that the node connected to the output node is of type VDB. + +Regardless of the amount of VDBs created the output will need to have an +equal amount of VDBs, points, primitives and vertices + +A VDB is an inherited type of Prim, holds the following data: + - Primitives: 1 + - Points: 1 + - Vertices: 1 + - VDBs: 1 + + + + + + \ No newline at end of file diff --git a/openpype/hosts/houdini/plugins/publish/valiate_vdb_input_node.py b/openpype/hosts/houdini/plugins/publish/valiate_vdb_input_node.py deleted file mode 100644 index 0ae1bc94eb..0000000000 --- a/openpype/hosts/houdini/plugins/publish/valiate_vdb_input_node.py +++ /dev/null @@ -1,47 +0,0 @@ -import pyblish.api -import openpype.api - - -class ValidateVDBInputNode(pyblish.api.InstancePlugin): - """Validate that the node connected to the output node is of type VDB. - - Regardless of the amount of VDBs create the output will need to have an - equal amount of VDBs, points, primitives and vertices - - A VDB is an inherited type of Prim, holds the following data: - - Primitives: 1 - - Points: 1 - - Vertices: 1 - - VDBs: 1 - - """ - - order = openpype.api.ValidateContentsOrder + 0.1 - families = ["vdbcache"] - hosts = ["houdini"] - label = "Validate Input Node (VDB)" - - def process(self, instance): - invalid = self.get_invalid(instance) - if invalid: - raise RuntimeError( - "Node connected to the output node is not" "of type VDB!" - ) - - @classmethod - def get_invalid(cls, instance): - - node = instance.data["output_node"] - - prims = node.geometry().prims() - nr_of_prims = len(prims) - - nr_of_points = len(node.geometry().points()) - if nr_of_points != nr_of_prims: - cls.log.error("The number of primitives and points do not match") - return [instance] - - for prim in prims: - if prim.numVertices() != 1: - cls.log.error("Found primitive with more than 1 vertex!") - return [instance] diff --git a/openpype/hosts/testhost/plugins/publish/validate_context_with_error.py b/openpype/hosts/testhost/plugins/publish/validate_context_with_error.py index 46e996a569..20fb47513e 100644 --- a/openpype/hosts/testhost/plugins/publish/validate_context_with_error.py +++ b/openpype/hosts/testhost/plugins/publish/validate_context_with_error.py @@ -2,6 +2,7 @@ import pyblish.api from openpype.pipeline import PublishValidationError + class ValidateInstanceAssetRepair(pyblish.api.Action): """Repair the instance asset.""" From dda5ddaa98537b60d02e5ca80a48b5208af9a788 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Dec 2021 14:09:09 +0100 Subject: [PATCH 02/44] Implemented change of validators to new publisher style for AE --- .../publish/help/validate_instance_asset.xml | 21 +++++++++++ .../publish/help/validate_scene_settings.xml | 36 +++++++++++++++++++ .../publish/validate_instance_asset.py | 12 +++---- .../publish/validate_scene_settings.py | 27 +++++++++++--- 4 files changed, 86 insertions(+), 10 deletions(-) create mode 100644 openpype/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml create mode 100644 openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml diff --git a/openpype/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml b/openpype/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml new file mode 100644 index 0000000000..580b0e552d --- /dev/null +++ b/openpype/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml @@ -0,0 +1,21 @@ + + + +Subset context + +## Invalid subset context + +Context of the given subset doesn't match your current scene. + +### How to repair? + +You can fix this with "repair" button on the right. + + +### __Detailed Info__ (optional) + +This might happen if you are reuse old workfile and open it in different context. + (Eg. you created subset "renderCompositingDefault" from asset "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing subset for "Robot" asset stayed in the workfile.) + + + \ No newline at end of file diff --git a/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml b/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml new file mode 100644 index 0000000000..603ab4805d --- /dev/null +++ b/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml @@ -0,0 +1,36 @@ + + + +Scene setting + +## Invalid scene setting found + +One of the settings in a scene doesn't match to asset settings in database. + + Invalid setting: + {invalid_setting_str} + +### How to repair? + +Change {invalid_keys_str} in the scene OR change them in asset database if they are wrong there. + + +### __Detailed Info__ (optional) + +This error is shown when for example resolution in the scene doesn't match to resolution set on the asset in the database. + Either value in the database or in the scene is wrong. + + + +Scene file doesn't exist + +## Scene file doesn't exist + +Collected scene {scene_url} doesn't exist. + +### How to repair? + +Re-save file, start publish from the beginning again. + + + \ No newline at end of file diff --git a/openpype/hosts/aftereffects/plugins/publish/validate_instance_asset.py b/openpype/hosts/aftereffects/plugins/publish/validate_instance_asset.py index eff89adcb3..2c8c1b4312 100644 --- a/openpype/hosts/aftereffects/plugins/publish/validate_instance_asset.py +++ b/openpype/hosts/aftereffects/plugins/publish/validate_instance_asset.py @@ -2,6 +2,7 @@ from avalon import api import pyblish.api import openpype.api from avalon import aftereffects +from openpype.pipeline import PublishValidationError class ValidateInstanceAssetRepair(pyblish.api.Action): @@ -29,7 +30,6 @@ class ValidateInstanceAssetRepair(pyblish.api.Action): data["asset"] = api.Session["AVALON_ASSET"] stub.imprint(instance[0], data) - class ValidateInstanceAsset(pyblish.api.InstancePlugin): """Validate the instance asset is the current selected context asset. @@ -53,9 +53,9 @@ class ValidateInstanceAsset(pyblish.api.InstancePlugin): current_asset = api.Session["AVALON_ASSET"] msg = ( f"Instance asset {instance_asset} is not the same " - f"as current context {current_asset}. PLEASE DO:\n" - f"Repair with 'A' action to use '{current_asset}'.\n" - f"If that's not correct value, close workfile and " - f"reopen via Workfiles!" + f"as current context {current_asset}." ) - assert instance_asset == current_asset, msg + + # assert instance_asset == current_asset, msg + if instance_asset != current_asset: + raise PublishValidationError(msg, "Subset context", DESCRIPTION) \ No newline at end of file diff --git a/openpype/hosts/aftereffects/plugins/publish/validate_scene_settings.py b/openpype/hosts/aftereffects/plugins/publish/validate_scene_settings.py index 7fba11957c..50e55599e2 100644 --- a/openpype/hosts/aftereffects/plugins/publish/validate_scene_settings.py +++ b/openpype/hosts/aftereffects/plugins/publish/validate_scene_settings.py @@ -7,6 +7,7 @@ import pyblish.api from avalon import aftereffects +from openpype.pipeline import PublishXmlValidationError import openpype.hosts.aftereffects.api as api stub = aftereffects.stub() @@ -103,12 +104,14 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin): self.log.info("current_settings:: {}".format(current_settings)) invalid_settings = [] + invalid_keys = set() for key, value in expected_settings.items(): if value != current_settings[key]: invalid_settings.append( "{} expected: {} found: {}".format(key, value, current_settings[key]) ) + invalid_keys.add(key) if ((expected_settings.get("handleStart") or expected_settings.get("handleEnd")) @@ -120,7 +123,23 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin): msg = "Found invalid settings:\n{}".format( "\n".join(invalid_settings) ) - assert not invalid_settings, msg - assert os.path.exists(instance.data.get("source")), ( - "Scene file not found (saved under wrong name)" - ) + + if invalid_settings: + invalid_keys_str = ",".join(invalid_keys) + formatting_data = { + "invalid_setting_str": msg, + "invalid_keys_str": invalid_keys_str + } + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) + + if not os.path.exists(instance.data.get("source")): + scene_url = instance.data.get("source") + msg = "Scene file {} not found (saved under wrong name)".format( + scene_url + ) + formatting_data = { + "scene_url": scene_url + } + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) From 8a67dba15d67ecc8092b9e53efd65a0f4d79b143 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Dec 2021 14:18:26 +0100 Subject: [PATCH 03/44] Fix formatting --- .../plugins/publish/help/validate_instance_asset.xml | 2 +- .../plugins/publish/help/validate_scene_settings.xml | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml b/openpype/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml index 580b0e552d..13f03a9b9a 100644 --- a/openpype/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml +++ b/openpype/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml @@ -15,7 +15,7 @@ You can fix this with "repair" button on the right. ### __Detailed Info__ (optional) This might happen if you are reuse old workfile and open it in different context. - (Eg. you created subset "renderCompositingDefault" from asset "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing subset for "Robot" asset stayed in the workfile.) +(Eg. you created subset "renderCompositingDefault" from asset "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing subset for "Robot" asset stayed in the workfile.) \ No newline at end of file diff --git a/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml b/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml index 603ab4805d..983dde42ce 100644 --- a/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml +++ b/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml @@ -6,22 +6,21 @@ ## Invalid scene setting found One of the settings in a scene doesn't match to asset settings in database. - - Invalid setting: - {invalid_setting_str} +Invalid setting: +{invalid_setting_str} ### How to repair? -Change {invalid_keys_str} in the scene OR change them in asset database if they are wrong there. +Change {invalid_keys_str} setting in the scene OR change them in asset database if they are wrong there. ### __Detailed Info__ (optional) This error is shown when for example resolution in the scene doesn't match to resolution set on the asset in the database. - Either value in the database or in the scene is wrong. +Either value in the database or in the scene is wrong. - + Scene file doesn't exist ## Scene file doesn't exist From 3e195d58ccc16c33498159a364ca758ad604d9cf Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Dec 2021 14:37:41 +0100 Subject: [PATCH 04/44] Fix formatting --- .../plugins/publish/help/validate_scene_settings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml b/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml index 983dde42ce..6dc51d9953 100644 --- a/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml +++ b/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml @@ -6,12 +6,12 @@ ## Invalid scene setting found One of the settings in a scene doesn't match to asset settings in database. -Invalid setting: + {invalid_setting_str} ### How to repair? -Change {invalid_keys_str} setting in the scene OR change them in asset database if they are wrong there. +Change values for {invalid_keys_str} in the scene OR change them in the asset database if they are wrong there. ### __Detailed Info__ (optional) From a14aaaf9b8e4feb4245bae8a4e07aa7a91043d33 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Dec 2021 14:48:08 +0100 Subject: [PATCH 05/44] Fix formatting --- .../plugins/publish/help/validate_scene_settings.xml | 2 +- .../plugins/publish/validate_instance_asset.py | 5 ++--- .../plugins/publish/validate_scene_settings.py | 8 ++++++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml b/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml index 6dc51d9953..36fa90456e 100644 --- a/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml +++ b/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml @@ -20,7 +20,7 @@ This error is shown when for example resolution in the scene doesn't match to re Either value in the database or in the scene is wrong. - + Scene file doesn't exist ## Scene file doesn't exist diff --git a/openpype/hosts/aftereffects/plugins/publish/validate_instance_asset.py b/openpype/hosts/aftereffects/plugins/publish/validate_instance_asset.py index 2c8c1b4312..491e07b6c4 100644 --- a/openpype/hosts/aftereffects/plugins/publish/validate_instance_asset.py +++ b/openpype/hosts/aftereffects/plugins/publish/validate_instance_asset.py @@ -2,7 +2,7 @@ from avalon import api import pyblish.api import openpype.api from avalon import aftereffects -from openpype.pipeline import PublishValidationError +from openpype.pipeline import PublishXmlValidationError class ValidateInstanceAssetRepair(pyblish.api.Action): @@ -56,6 +56,5 @@ class ValidateInstanceAsset(pyblish.api.InstancePlugin): f"as current context {current_asset}." ) - # assert instance_asset == current_asset, msg if instance_asset != current_asset: - raise PublishValidationError(msg, "Subset context", DESCRIPTION) \ No newline at end of file + raise PublishXmlValidationError(self, msg) diff --git a/openpype/hosts/aftereffects/plugins/publish/validate_scene_settings.py b/openpype/hosts/aftereffects/plugins/publish/validate_scene_settings.py index 50e55599e2..0e7a54005a 100644 --- a/openpype/hosts/aftereffects/plugins/publish/validate_scene_settings.py +++ b/openpype/hosts/aftereffects/plugins/publish/validate_scene_settings.py @@ -126,8 +126,12 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin): if invalid_settings: invalid_keys_str = ",".join(invalid_keys) + break_str = "
" + invalid_setting_str = "Found invalid settings:
{}".\ + format(break_str.join(invalid_settings)) + formatting_data = { - "invalid_setting_str": msg, + "invalid_setting_str": invalid_setting_str, "invalid_keys_str": invalid_keys_str } raise PublishXmlValidationError(self, msg, @@ -141,5 +145,5 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin): formatting_data = { "scene_url": scene_url } - raise PublishXmlValidationError(self, msg, + raise PublishXmlValidationError(self, msg, key="file_not_found", formatting_data=formatting_data) From 28040a2f6a4ae863578edf49baebd7bd5a85a514 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 21 Dec 2021 14:55:14 +0100 Subject: [PATCH 06/44] exception description can be different per instance under title --- .../publisher/widgets/validations_widget.py | 83 ++++++++++++++----- 1 file changed, 64 insertions(+), 19 deletions(-) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index 09e56d64cc..9f550725a5 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -10,6 +10,9 @@ from .widgets import ( ClickableFrame, IconValuePixmapLabel ) +from ..constants import ( + INSTANCE_ID_ROLE +) class ValidationErrorInstanceList(QtWidgets.QListView): @@ -47,6 +50,7 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): if there is a list (Valdation error may happen on context). """ selected = QtCore.Signal(int) + instance_changed = QtCore.Signal(int) def __init__(self, index, error_info, parent): super(ValidationErrorTitleWidget, self).__init__(parent) @@ -72,24 +76,37 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): title_frame_layout.addWidget(label_widget) instances_model = QtGui.QStandardItemModel() - instances = error_info["instances"] + error_info = error_info["error_info"] + + help_text_by_instance_id = {} context_validation = False if ( - not instances - or (len(instances) == 1 and instances[0] is None) + not error_info + or (len(error_info) == 1 and error_info[0][0] is None) ): context_validation = True toggle_instance_btn.setArrowType(QtCore.Qt.NoArrow) + help_text_by_instance_id[None] = error_info[0][1] else: items = [] - for instance in instances: + for instance, exception in error_info: label = instance.data.get("label") or instance.data.get("name") item = QtGui.QStandardItem(label) item.setFlags( QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable ) - item.setData(instance.id) + item.setData(instance.id, INSTANCE_ID_ROLE) items.append(item) + dsc = exception.description + detail = exception.detail + if detail: + dsc += "

{}".format(detail) + + help_text = dsc + if commonmark: + help_text = commonmark.commonmark(dsc) + + help_text_by_instance_id[instance.id] = help_text instances_model.invisibleRootItem().appendRows(items) @@ -114,6 +131,10 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): if not context_validation: toggle_instance_btn.clicked.connect(self._on_toggle_btn_click) + instances_view.selectionModel().selectionChanged.connect( + self._on_seleciton_change + ) + self._title_frame = title_frame self._toggle_instance_btn = toggle_instance_btn @@ -121,6 +142,9 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): self._instances_model = instances_model self._instances_view = instances_view + self._context_validation = context_validation + self._help_text_by_instance_id = help_text_by_instance_id + def _mouse_release_callback(self): """Mark this widget as selected on click.""" self.set_selected(True) @@ -145,6 +169,17 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): self._title_frame.setProperty("selected", value) self._title_frame.style().polish(self._title_frame) + def current_desctiption_text(self): + if self._context_validation: + return self._help_text_by_instance_id[None] + index = self._instances_view.currentIndex() + # TODO make sure instance is selected + if not index.isValid(): + index = self._instances_model.index(0, 0) + + indence_id = index.data(INSTANCE_ID_ROLE) + return self._help_text_by_instance_id[indence_id] + def set_selected(self, selected=None): """Change selected state of widget.""" if selected is None: @@ -167,6 +202,9 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): else: self._toggle_instance_btn.setArrowType(QtCore.Qt.RightArrow) + def _on_seleciton_change(self): + self.instance_changed.emit(self._index) + class ActionButton(ClickableFrame): """Plugin's action callback button. @@ -440,28 +478,28 @@ class ValidationsWidget(QtWidgets.QWidget): errors_by_title = [] for plugin_info in errors: titles = [] - exception_by_title = {} - instances_by_title = {} + error_info_by_title = {} for error_info in plugin_info["errors"]: exception = error_info["exception"] title = exception.title if title not in titles: titles.append(title) - instances_by_title[title] = [] - exception_by_title[title] = exception - instances_by_title[title].append(error_info["instance"]) + error_info_by_title[title] = [] + error_info_by_title[title].append( + (error_info["instance"], exception) + ) for title in titles: errors_by_title.append({ "plugin": plugin_info["plugin"], - "exception": exception_by_title[title], - "instances": instances_by_title[title] + "error_info": error_info_by_title[title] }) for idx, item in enumerate(errors_by_title): widget = ValidationErrorTitleWidget(idx, item, self) widget.selected.connect(self._on_select) + widget.instance_changed.connect(self._on_instance_change) self._errors_layout.addWidget(widget) self._title_widgets[idx] = widget self._error_info[idx] = item @@ -480,11 +518,18 @@ class ValidationsWidget(QtWidgets.QWidget): self._previous_select = self._title_widgets[index] error_item = self._error_info[index] - - dsc = error_item["exception"].description - if commonmark: - html = commonmark.commonmark(dsc) - self._error_details_input.setHtml(html) - else: - self._error_details_input.setMarkdown(dsc) self._actions_widget.set_plugin(error_item["plugin"]) + + self._update_description() + + def _on_instance_change(self, index): + if self._previous_select and self._previous_select.index != index: + return + self._update_description() + + def _update_description(self): + description = self._previous_select.current_desctiption_text() + if commonmark: + self._error_details_input.setHtml(description) + else: + self._error_details_input.setMarkdown(description) From 72cdaecef46e0fecb72f880b5a0681fc7f4f6ca9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 21 Dec 2021 15:02:13 +0100 Subject: [PATCH 07/44] fix context exception handling --- .../publisher/widgets/validations_widget.py | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index 9f550725a5..ba4df2eb8e 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -68,8 +68,7 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): toggle_instance_btn.setArrowType(QtCore.Qt.RightArrow) toggle_instance_btn.setMaximumWidth(14) - exception = error_info["exception"] - label_widget = QtWidgets.QLabel(exception.title, title_frame) + label_widget = QtWidgets.QLabel(error_info["title"], title_frame) title_frame_layout = QtWidgets.QHBoxLayout(title_frame) title_frame_layout.addWidget(toggle_instance_btn) @@ -86,7 +85,8 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): ): context_validation = True toggle_instance_btn.setArrowType(QtCore.Qt.NoArrow) - help_text_by_instance_id[None] = error_info[0][1] + description = self._prepare_description(error_info[0][1]) + help_text_by_instance_id[None] = description else: items = [] for instance, exception in error_info: @@ -97,16 +97,8 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): ) item.setData(instance.id, INSTANCE_ID_ROLE) items.append(item) - dsc = exception.description - detail = exception.detail - if detail: - dsc += "

{}".format(detail) - - help_text = dsc - if commonmark: - help_text = commonmark.commonmark(dsc) - - help_text_by_instance_id[instance.id] = help_text + description = self._prepare_description(exception) + help_text_by_instance_id[instance.id] = description instances_model.invisibleRootItem().appendRows(items) @@ -145,6 +137,17 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): self._context_validation = context_validation self._help_text_by_instance_id = help_text_by_instance_id + def _prepare_description(self, exception): + dsc = exception.description + detail = exception.detail + if detail: + dsc += "

{}".format(detail) + + description = dsc + if commonmark: + description = commonmark.commonmark(dsc) + return description + def _mouse_release_callback(self): """Mark this widget as selected on click.""" self.set_selected(True) @@ -493,7 +496,8 @@ class ValidationsWidget(QtWidgets.QWidget): for title in titles: errors_by_title.append({ "plugin": plugin_info["plugin"], - "error_info": error_info_by_title[title] + "error_info": error_info_by_title[title], + "title": title }) for idx, item in enumerate(errors_by_title): From 4759b6db371c9724748be4099ce19f5e191bc3c1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 11:00:50 +0100 Subject: [PATCH 08/44] raise PublishXmlValidationError in validate asset name --- .../publish/help/validate_asset_name.xml | 22 ++++++++++++++++++ .../plugins/publish/validate_asset_name.py | 23 ++++++++++++++----- 2 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml new file mode 100644 index 0000000000..ed8e36b1d9 --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml @@ -0,0 +1,22 @@ + + + +Subset context +## Invalid subset context + +Context of the given subset doesn't match your current scene. + +### How to repair? + +Yout can fix with "Repair" button on the right. This will use '{expected_asset}' asset name and overwrite '{found_asset}' asset name in scene metadata. + +After that restart publishing with Reload button. + + +### How could this happen? + +The subset was created in different scene with different context +or the scene file was copy pasted from different context. + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_asset_name.py b/openpype/hosts/tvpaint/plugins/publish/validate_asset_name.py index 4ce8d5347d..199b9a3b19 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_asset_name.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_asset_name.py @@ -1,5 +1,6 @@ import pyblish.api from avalon.tvpaint import pipeline +from openpype.pipeline import PublishXmlValidationError class FixAssetNames(pyblish.api.Action): @@ -27,7 +28,7 @@ class FixAssetNames(pyblish.api.Action): pipeline._write_instances(new_instance_items) -class ValidateMissingLayers(pyblish.api.ContextPlugin): +class ValidateAssetNames(pyblish.api.ContextPlugin): """Validate assset name present on instance. Asset name on instance should be the same as context's. @@ -48,8 +49,18 @@ class ValidateMissingLayers(pyblish.api.ContextPlugin): instance_label = ( instance.data.get("label") or instance.data["name"] ) - raise AssertionError(( - "Different asset name on instance then context's." - " Instance \"{}\" has asset name: \"{}\"" - " Context asset name is: \"{}\"" - ).format(instance_label, asset_name, context_asset_name)) + + raise PublishXmlValidationError( + self, + ( + "Different asset name on instance then context's." + " Instance \"{}\" has asset name: \"{}\"" + " Context asset name is: \"{}\"" + ).format( + instance_label, asset_name, context_asset_name + ), + formatting_data={ + "expected_asset": context_asset_name, + "found_asset": asset_name + } + ) From f13923d31ccae0a0455fb36bb09dd225c6b4e0e3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 11:04:10 +0100 Subject: [PATCH 09/44] raise PublishXmlValidationError in validate duplicated layer names --- .../help/validate_duplicated_layer_names.xml | 22 +++++++++++++++++++ .../validate_duplicated_layer_names.py | 15 +++++++++---- 2 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_duplicated_layer_names.xml diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_duplicated_layer_names.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_duplicated_layer_names.xml new file mode 100644 index 0000000000..5d798544c0 --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_duplicated_layer_names.xml @@ -0,0 +1,22 @@ + + + +Layer names +## Duplicated layer names + +Can't determine which layers should be published because there are duplicated layer names in the scene. + +### Duplicated layer names + +{layer_names} + +*Check layer names for all subsets in list on left side.* + +### How to repair? + +Hide/rename/remove layers that should not be published. + +If all of them should be published then you have duplicated subset names in the scene. In that case you have to recrete them and use different variant name. + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_duplicated_layer_names.py b/openpype/hosts/tvpaint/plugins/publish/validate_duplicated_layer_names.py index efccf19ef9..9f61bdbcd0 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_duplicated_layer_names.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_duplicated_layer_names.py @@ -1,4 +1,5 @@ import pyblish.api +from openpype.pipeline import PublishXmlValidationError class ValidateLayersGroup(pyblish.api.InstancePlugin): @@ -30,14 +31,20 @@ class ValidateLayersGroup(pyblish.api.InstancePlugin): "\"{}\"".format(layer_name) for layer_name in duplicated_layer_names ]) - - # Raise an error - raise AssertionError( + detail_lines = [ + "- {}".format(layer_name) + for layer_name in set(duplicated_layer_names) + ] + raise PublishXmlValidationError( + self, ( "Layers have duplicated names for instance {}." # Description what's wrong " There are layers with same name and one of them is marked" " for publishing so it is not possible to know which should" " be published. Please look for layers with names: {}" - ).format(instance.data["label"], layers_msg) + ).format(instance.data["label"], layers_msg), + formatting_data={ + "layer_names": "
".join(detail_lines) + } ) From 3417dc716af9c1081a51ab9c132bcded0330623d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 11:12:42 +0100 Subject: [PATCH 10/44] raise PublishXmlValidationError in validate layers visibility --- .../help/validate_layers_visibility.xml | 20 +++++++++++++++++ .../publish/validate_layers_visibility.py | 22 ++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml new file mode 100644 index 0000000000..fc69d5fd7b --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml @@ -0,0 +1,20 @@ + + + +Layers visiblity +## All layers are not visible + +All layers for subset "{instance_name}" are hidden. + +### Layer names for **{instance_name}** + +{layer_names} + +*Check layer names for all subsets in list on left side.* + +### How to repair? + +Make sure that at least one layer in the scene is visible or disable the subset before hitting publish button after refresh. + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_layers_visibility.py b/openpype/hosts/tvpaint/plugins/publish/validate_layers_visibility.py index 74ef34169e..7ea0587b8f 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_layers_visibility.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_layers_visibility.py @@ -1,6 +1,8 @@ import pyblish.api +from openpype.pipeline import PublishXmlValidationError +# TODO @iLLiCiTiT add repair action to disable instances? class ValidateLayersVisiblity(pyblish.api.InstancePlugin): """Validate existence of renderPass layers.""" @@ -9,8 +11,26 @@ class ValidateLayersVisiblity(pyblish.api.InstancePlugin): families = ["review", "renderPass", "renderLayer"] def process(self, instance): + layer_names = set() for layer in instance.data["layers"]: + layer_names.add(layer["name"]) if layer["visible"]: return - raise AssertionError("All layers of instance are not visible.") + instance_label = ( + instance.data.get("label") or instance.data["name"] + ) + + raise PublishXmlValidationError( + self, + "All layers of instance \"{}\" are not visible.".format( + instance_label + ), + formatting_data={ + "instance_name": instance_label, + "layer_names": "
".join([ + "- {}".format(layer_name) + for layer_name in layer_names + ]) + } + ) From fba2191226388f73c3dbf5e7c20773e58cb1259c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 11:57:31 +0100 Subject: [PATCH 11/44] raise PublishXmlValidationError in validate marks --- .../publish/help/validate_asset_name.xml | 2 +- .../plugins/publish/help/validate_marks.xml | 21 ++++++++++ .../tvpaint/plugins/publish/validate_marks.py | 38 ++++++++++++++++--- 3 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_marks.xml diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml index ed8e36b1d9..33a9ca4247 100644 --- a/openpype/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml @@ -8,7 +8,7 @@ Context of the given subset doesn't match your current scene. ### How to repair? -Yout can fix with "Repair" button on the right. This will use '{expected_asset}' asset name and overwrite '{found_asset}' asset name in scene metadata. +Yout can fix this with "Repair" button on the right. This will use '{expected_asset}' asset name and overwrite '{found_asset}' asset name in scene metadata. After that restart publishing with Reload button.
diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_marks.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_marks.xml new file mode 100644 index 0000000000..f0e01ebaa7 --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_marks.xml @@ -0,0 +1,21 @@ + + + +Frame range +## Invalid render frame range + +Scene frame range which will be rendered is defined by MarkIn and MarkOut. Expected frame range is {expected_frame_range} and current frame range is {current_frame_range}. + +It is also required that MarkIn and MarkOut are enabled in the scene. Their color is highlighted on timeline when are enabled. + +- MarkIn is {mark_in_enable_state} +- MarkOut is {mark_out_enable_state} + +### How to repair? + +Yout can fix this with "Repair" button on the right. That will change MarkOut to {expected_mark_out}. + +Or you can manually modify MarkIn and MarkOut in the scene timeline. + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_marks.py b/openpype/hosts/tvpaint/plugins/publish/validate_marks.py index e2ef81e4a4..5f569d3ba7 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_marks.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_marks.py @@ -2,6 +2,7 @@ import json import pyblish.api from avalon.tvpaint import lib +from openpype.pipeline import PublishXmlValidationError class ValidateMarksRepair(pyblish.api.Action): @@ -73,9 +74,34 @@ class ValidateMarks(pyblish.api.ContextPlugin): "expected": expected_data[k] } - if invalid: - raise AssertionError( - "Marks does not match database:\n{}".format( - json.dumps(invalid, sort_keys=True, indent=4) - ) - ) + # Validation ends + if not invalid: + return + + current_frame_range = ( + (current_data["markOut"] - current_data["markIn"]) + 1 + ) + expected_frame_range = ( + (expected_data["markOut"] - expected_data["markIn"]) + 1 + ) + mark_in_enable_state = "disabled" + if current_data["markInState"]: + mark_in_enable_state = "enabled" + + mark_out_enable_state = "disabled" + if current_data["markOutState"]: + mark_out_enable_state = "enabled" + + raise PublishXmlValidationError( + self, + "Marks does not match database:\n{}".format( + json.dumps(invalid, sort_keys=True, indent=4) + ), + formatting_data={ + "current_frame_range": str(current_frame_range), + "expected_frame_range": str(expected_frame_range), + "mark_in_enable_state": mark_in_enable_state, + "mark_out_enable_state": mark_out_enable_state, + "expected_mark_out": expected_data["markOut"] + } + ) From d17de8492da6c9c4aba019dab129ecb8f4a88088 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 12:59:09 +0100 Subject: [PATCH 12/44] raise PublishXmlValidationError in validate missing layer names --- .../help/validate_missing_layer_names.xml | 18 ++++++++++++++++++ .../publish/validate_missing_layer_names.py | 17 +++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_missing_layer_names.xml diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_missing_layer_names.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_missing_layer_names.xml new file mode 100644 index 0000000000..e96e7c5044 --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_missing_layer_names.xml @@ -0,0 +1,18 @@ + + + +Missing layers +## Missing layers for render pass + +Render pass subset "{instance_name}" has stored layer names that belong to it's rendering scope but layers were not found in scene. + +### Missing layer names + +{layer_names} + +### How to repair? + +Find layers that belong to subset {instance_name} and rename them back to expected layer names or remove the subset and create new with right layers. + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_missing_layer_names.py b/openpype/hosts/tvpaint/plugins/publish/validate_missing_layer_names.py index db9d354fcd..294ce6cf4f 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_missing_layer_names.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_missing_layer_names.py @@ -1,4 +1,5 @@ import pyblish.api +from openpype.pipeline import PublishXmlValidationError class ValidateMissingLayers(pyblish.api.InstancePlugin): @@ -30,13 +31,25 @@ class ValidateMissingLayers(pyblish.api.InstancePlugin): "\"{}\"".format(layer_name) for layer_name in missing_layer_names ]) + instance_label = ( + instance.data.get("label") or instance.data["name"] + ) + description_layer_names = "
".join([ + "- {}".format(layer_name) + for layer_name in missing_layer_names + ]) # Raise an error - raise AssertionError( + raise PublishXmlValidationError( + self, ( "Layers were not found by name for instance \"{}\"." # Description what's wrong " Layer names marked for publishing are not available" " in layers list. Missing layer names: {}" - ).format(instance.data["label"], layers_msg) + ).format(instance.data["label"], layers_msg), + formatting_data={ + "instance_name": instance_label, + "layer_names": description_layer_names + } ) From db5d03c9023235352b3935853b555cda70e9885e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 13:02:02 +0100 Subject: [PATCH 13/44] renamed validate_project_settings to validate_scene_settings --- ...te_project_settings.py => validate_scene_settings.py} | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) rename openpype/hosts/tvpaint/plugins/publish/{validate_project_settings.py => validate_scene_settings.py} (78%) diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_project_settings.py b/openpype/hosts/tvpaint/plugins/publish/validate_scene_settings.py similarity index 78% rename from openpype/hosts/tvpaint/plugins/publish/validate_project_settings.py rename to openpype/hosts/tvpaint/plugins/publish/validate_scene_settings.py index 84c03a9857..7efa146c54 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_project_settings.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_scene_settings.py @@ -3,11 +3,10 @@ import json import pyblish.api -class ValidateProjectSettings(pyblish.api.ContextPlugin): - """Validate project settings against database. - """ +class ValidateSceneSettings(pyblish.api.ContextPlugin): + """Validate scene settings against database.""" - label = "Validate Project Settings" + label = "Validate Scene Settings" order = pyblish.api.ValidatorOrder optional = True @@ -28,7 +27,7 @@ class ValidateProjectSettings(pyblish.api.ContextPlugin): if invalid: raise AssertionError( - "Project settings does not match database:\n{}".format( + "Scene settings does not match database:\n{}".format( json.dumps(invalid, sort_keys=True, indent=4) ) ) From 6b6961a84a6e8b4a74eca4b4119d8ea4a7c41c8e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 13:02:32 +0100 Subject: [PATCH 14/44] raise PublishXmlValidationError in validate scene settings --- .../publish/help/validate_scene_settings.xml | 26 +++++++++++++++ .../publish/validate_scene_settings.py | 32 ++++++++++++++----- 2 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_scene_settings.xml diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_scene_settings.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_scene_settings.xml new file mode 100644 index 0000000000..f741c71456 --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_scene_settings.xml @@ -0,0 +1,26 @@ + + + +Scene settings +## Invalid scene settings + +Scene settings do not match to expected values. + +**FPS** +- Expected value: {expected_fps} +- Current value: {current_fps} + +**Resolution** +- Expected value: {expected_width}x{expected_height} +- Current value: {current_width}x{current_height} + +**Pixel ratio** +- Expected value: {expected_pixel_ratio} +- Current value: {current_pixel_ratio} + +### How to repair? + +FPS and Pixel ratio can be modified in scene setting. Wrong resolution can be fixed with changing resolution of scene but due to TVPaint limitations it is possible that you will need to create new scene. + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_scene_settings.py b/openpype/hosts/tvpaint/plugins/publish/validate_scene_settings.py index 7efa146c54..d235215ac9 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_scene_settings.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_scene_settings.py @@ -1,9 +1,11 @@ import json import pyblish.api +from openpype.pipeline import PublishXmlValidationError -class ValidateSceneSettings(pyblish.api.ContextPlugin): +# TODO @iLliCiTiT add fix action for fps +class ValidateProjectSettings(pyblish.api.ContextPlugin): """Validate scene settings against database.""" label = "Validate Scene Settings" @@ -11,6 +13,7 @@ class ValidateSceneSettings(pyblish.api.ContextPlugin): optional = True def process(self, context): + expected_data = context.data["assetEntity"]["data"] scene_data = { "fps": context.data.get("sceneFps"), "resolutionWidth": context.data.get("sceneWidth"), @@ -19,15 +22,28 @@ class ValidateSceneSettings(pyblish.api.ContextPlugin): } invalid = {} for k in scene_data.keys(): - expected_value = context.data["assetEntity"]["data"][k] + expected_value = expected_data[k] if scene_data[k] != expected_value: invalid[k] = { "current": scene_data[k], "expected": expected_value } - if invalid: - raise AssertionError( - "Scene settings does not match database:\n{}".format( - json.dumps(invalid, sort_keys=True, indent=4) - ) - ) + if not invalid: + return + + raise PublishXmlValidationError( + self, + "Scene settings does not match database:\n{}".format( + json.dumps(invalid, sort_keys=True, indent=4) + ), + formatting_data={ + "expected_fps": expected_data["fps"], + "current_fps": scene_data["fps"], + "expected_width": expected_data["resolutionWidth"], + "expected_height": expected_data["resolutionHeight"], + "current_width": scene_data["resolutionWidth"], + "current_height": scene_data["resolutionWidth"], + "expected_pixel_ratio": expected_data["pixelAspect"], + "current_pixel_ratio": scene_data["pixelAspect"] + } + ) From f99036eb1dbb74aa5a435d8f1f962303e170aecc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 13:27:56 +0100 Subject: [PATCH 15/44] raise PublishXmlValidationError in validate pass groups --- .../help/validate_render_pass_group.xml | 14 +++++++ .../publish/validate_render_pass_group.py | 40 +++++++++++++------ 2 files changed, 41 insertions(+), 13 deletions(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_render_pass_group.xml diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_render_pass_group.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_render_pass_group.xml new file mode 100644 index 0000000000..df7bdf36e5 --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_render_pass_group.xml @@ -0,0 +1,14 @@ + + + +Render pass group +## Invalid group of Render Pass layers + +Layers of Render Pass {instance_name} belong to Render Group which is defined by TVPaint color group {expected_group}. But the layers are not in the group. + +### How to repair? + +Change the color group to {expected_group} on layers {layer_names}. + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_render_pass_group.py b/openpype/hosts/tvpaint/plugins/publish/validate_render_pass_group.py index 5047b8d729..0fbfca6c56 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_render_pass_group.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_render_pass_group.py @@ -1,5 +1,6 @@ import collections import pyblish.api +from openpype.pipeline import PublishXmlValidationError class ValidateLayersGroup(pyblish.api.InstancePlugin): @@ -26,11 +27,13 @@ class ValidateLayersGroup(pyblish.api.InstancePlugin): layer_names = instance.data["layer_names"] # Check if all layers from render pass are in right group invalid_layers_by_group_id = collections.defaultdict(list) + invalid_layer_names = set() for layer_name in layer_names: layer = layers_by_name.get(layer_name) _group_id = layer["group_id"] if _group_id != group_id: invalid_layers_by_group_id[_group_id].append(layer) + invalid_layer_names.add(layer_name) # Everything is OK and skip exception if not invalid_layers_by_group_id: @@ -61,16 +64,27 @@ class ValidateLayersGroup(pyblish.api.InstancePlugin): ) # Raise an error - raise AssertionError(( - # Short message - "Layers in wrong group." - # Description what's wrong - " Layers from render pass \"{}\" must be in group {} (id: {})." - # Detailed message - " Layers in wrong group: {}" - ).format( - instance.data["label"], - correct_group["name"], - correct_group["group_id"], - " | ".join(per_group_msgs) - )) + raise PublishXmlValidationError( + self, + ( + # Short message + "Layers in wrong group." + # Description what's wrong + " Layers from render pass \"{}\" must be in group {} (id: {})." + # Detailed message + " Layers in wrong group: {}" + ).format( + instance.data["label"], + correct_group["name"], + correct_group["group_id"], + " | ".join(per_group_msgs) + ), + formatting_data={ + "instance_name": ( + instance.data.get("label") or instance.data["name"] + ), + "expected_group": correct_group["name"], + "layer_names": ", ".join(invalid_layer_names) + + } + ) From df9e30eb7f8074d77a1701cc6bf2470ad82df487 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 13:33:11 +0100 Subject: [PATCH 16/44] raise PublishXmlValidationError in validate start frame --- .../plugins/publish/help/validate_start_frame.xml | 14 ++++++++++++++ .../plugins/publish/validate_start_frame.py | 12 +++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_start_frame.xml diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_start_frame.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_start_frame.xml new file mode 100644 index 0000000000..9052abf66c --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_start_frame.xml @@ -0,0 +1,14 @@ + + + +First frame +## MarkIn is not set to 0 + +MarkIn in your scene must start from 0 fram index but MarkIn is set to {current_start_frame}. + +### How to repair? + +You can modify MarkIn manually or hit the "Repair" button on the right which will change MarkIn to 0 (does not change MarkOut). + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_start_frame.py b/openpype/hosts/tvpaint/plugins/publish/validate_start_frame.py index d769d47736..48efd91055 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_start_frame.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_start_frame.py @@ -1,5 +1,6 @@ import pyblish.api from avalon.tvpaint import lib +from openpype.pipeline import PublishXmlValidationError class RepairStartFrame(pyblish.api.Action): @@ -24,4 +25,13 @@ class ValidateStartFrame(pyblish.api.ContextPlugin): def process(self, context): start_frame = lib.execute_george("tv_startframe") - assert int(start_frame) == 0, "Start frame has to be frame 0." + if start_frame == 0: + return + + raise PublishXmlValidationError( + self, + "Start frame has to be frame 0.", + formatting_data={ + "current_start_frame": start_frame + } + ) From 4c11ae83ee2843d1dc4bd4ac341d54137f3c3161 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 13:43:13 +0100 Subject: [PATCH 17/44] raise PublishXmlValidationError in validate workfile metadata --- .../help/validate_workfile_metadata.xml | 19 +++++++++++++++++++ .../publish/validate_workfile_metadata.py | 9 +++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_workfile_metadata.xml diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_workfile_metadata.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_workfile_metadata.xml new file mode 100644 index 0000000000..7397f6ef0b --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_workfile_metadata.xml @@ -0,0 +1,19 @@ + + + +Missing metadata +## Your scene miss context metadata + +Your scene does not contain metadata about {missing_metadata}. + +### How to repair? + +Resave the scene using Workfiles tool or hit the "Repair" button on the right. + + +### How this could happend? + +You're using scene file that was not created using Workfiles tool. + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py b/openpype/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py index 757da3294a..553d9af4e8 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py @@ -1,5 +1,6 @@ import pyblish.api from avalon.tvpaint import save_file +from openpype.pipeline import PublishXmlValidationError class ValidateWorkfileMetadataRepair(pyblish.api.Action): @@ -42,8 +43,12 @@ class ValidateWorkfileMetadata(pyblish.api.ContextPlugin): missing_keys.append(key) if missing_keys: - raise AssertionError( + raise PublishXmlValidationError( + self, "Current workfile is missing metadata about {}.".format( ", ".join(missing_keys) - ) + ), + formatting_data={ + "missing_metadata": ", ".join(missing_keys) + } ) From d7f6db8d38d6df7010f886802367863b045dd130 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Dec 2021 13:56:18 +0100 Subject: [PATCH 18/44] Added new style validators for New Publisher for Harmony --- .../plugins/publish/help/validate_audio.xml | 15 ++++++++ .../publish/help/validate_instances.xml | 25 +++++++++++++ .../publish/help/validate_scene_settings.xml | 35 +++++++++++++++++++ .../harmony/plugins/publish/validate_audio.py | 9 ++++- .../plugins/publish/validate_instances.py | 14 ++++++-- .../publish/validate_scene_settings.py | 32 ++++++++++++++--- 6 files changed, 123 insertions(+), 7 deletions(-) create mode 100644 openpype/hosts/harmony/plugins/publish/help/validate_audio.xml create mode 100644 openpype/hosts/harmony/plugins/publish/help/validate_instances.xml create mode 100644 openpype/hosts/harmony/plugins/publish/help/validate_scene_settings.xml diff --git a/openpype/hosts/harmony/plugins/publish/help/validate_audio.xml b/openpype/hosts/harmony/plugins/publish/help/validate_audio.xml new file mode 100644 index 0000000000..e9a183c675 --- /dev/null +++ b/openpype/hosts/harmony/plugins/publish/help/validate_audio.xml @@ -0,0 +1,15 @@ + + + +Missing audio file + +## Cannot locate linked audio file + +Audio file at {audio_url} cannot be found. + +### How to repair? + +Copy audio file to the highlighted location or remove audio link in the workfile. + + + \ No newline at end of file diff --git a/openpype/hosts/harmony/plugins/publish/help/validate_instances.xml b/openpype/hosts/harmony/plugins/publish/help/validate_instances.xml new file mode 100644 index 0000000000..3b040e8ea8 --- /dev/null +++ b/openpype/hosts/harmony/plugins/publish/help/validate_instances.xml @@ -0,0 +1,25 @@ + + + +Subset context + +## Invalid subset context + +Asset name found '{found}' in subsets, expected '{expected}'. + +### How to repair? + +You can fix this with `Repair` button on the right. This will use '{expected}' asset name and overwrite '{found}' asset name in scene metadata. + +After that restart `Publish` with a `Reload button`. + +If this is unwanted, close workfile and open again, that way different asset value would be used for context information. + + +### __Detailed Info__ (optional) + +This might happen if you are reuse old workfile and open it in different context. +(Eg. you created subset "renderCompositingDefault" from asset "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing subset for "Robot" asset stayed in the workfile.) + + + \ No newline at end of file diff --git a/openpype/hosts/harmony/plugins/publish/help/validate_scene_settings.xml b/openpype/hosts/harmony/plugins/publish/help/validate_scene_settings.xml new file mode 100644 index 0000000000..36fa90456e --- /dev/null +++ b/openpype/hosts/harmony/plugins/publish/help/validate_scene_settings.xml @@ -0,0 +1,35 @@ + + + +Scene setting + +## Invalid scene setting found + +One of the settings in a scene doesn't match to asset settings in database. + +{invalid_setting_str} + +### How to repair? + +Change values for {invalid_keys_str} in the scene OR change them in the asset database if they are wrong there. + + +### __Detailed Info__ (optional) + +This error is shown when for example resolution in the scene doesn't match to resolution set on the asset in the database. +Either value in the database or in the scene is wrong. + + + +Scene file doesn't exist + +## Scene file doesn't exist + +Collected scene {scene_url} doesn't exist. + +### How to repair? + +Re-save file, start publish from the beginning again. + + + \ No newline at end of file diff --git a/openpype/hosts/harmony/plugins/publish/validate_audio.py b/openpype/hosts/harmony/plugins/publish/validate_audio.py index c043b31ca6..9322968a9d 100644 --- a/openpype/hosts/harmony/plugins/publish/validate_audio.py +++ b/openpype/hosts/harmony/plugins/publish/validate_audio.py @@ -4,6 +4,8 @@ import pyblish.api from avalon import harmony +from openpype.pipeline import PublishXmlValidationError + class ValidateAudio(pyblish.api.InstancePlugin): """Ensures that there is an audio file in the scene. @@ -42,4 +44,9 @@ class ValidateAudio(pyblish.api.InstancePlugin): msg = "You are missing audio file:\n{}".format(audio_path) - assert os.path.isfile(audio_path), msg + formatting_data = { + "audio_url": audio_path + } + if os.path.isfile(audio_path): + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) diff --git a/openpype/hosts/harmony/plugins/publish/validate_instances.py b/openpype/hosts/harmony/plugins/publish/validate_instances.py index 78073a1978..9fb46dec49 100644 --- a/openpype/hosts/harmony/plugins/publish/validate_instances.py +++ b/openpype/hosts/harmony/plugins/publish/validate_instances.py @@ -1,8 +1,10 @@ import os +from avalon import harmony import pyblish.api import openpype.api -from avalon import harmony + +from openpype.pipeline import PublishXmlValidationError class ValidateInstanceRepair(pyblish.api.Action): @@ -45,4 +47,12 @@ class ValidateInstance(pyblish.api.InstancePlugin): "Instance asset is not the same as current asset:" f"\nInstance: {instance_asset}\nCurrent: {current_asset}" ) - assert instance_asset == current_asset, msg + + formatting_data = { + "found": instance_asset, + "expected": current_asset + } + if instance_asset != current_asset: + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) + diff --git a/openpype/hosts/harmony/plugins/publish/validate_scene_settings.py b/openpype/hosts/harmony/plugins/publish/validate_scene_settings.py index 0371e80095..e10adb885c 100644 --- a/openpype/hosts/harmony/plugins/publish/validate_scene_settings.py +++ b/openpype/hosts/harmony/plugins/publish/validate_scene_settings.py @@ -7,7 +7,9 @@ import re import pyblish.api from avalon import harmony + import openpype.hosts.harmony +from openpype.pipeline import PublishXmlValidationError class ValidateSceneSettingsRepair(pyblish.api.Action): @@ -102,6 +104,7 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin): self.log.debug("current scene settings {}".format(current_settings)) invalid_settings = [] + invalid_keys = set() for key, value in expected_settings.items(): if value != current_settings[key]: invalid_settings.append({ @@ -109,6 +112,7 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin): "expected": value, "current": current_settings[key] }) + invalid_keys.add(key) if ((expected_settings["handleStart"] or expected_settings["handleEnd"]) @@ -120,10 +124,30 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin): msg = "Found invalid settings:\n{}".format( json.dumps(invalid_settings, sort_keys=True, indent=4) ) - assert not invalid_settings, msg - assert os.path.exists(instance.context.data.get("scenePath")), ( - "Scene file not found (saved under wrong name)" - ) + + if invalid_settings: + invalid_keys_str = ",".join(invalid_keys) + break_str = "
" + invalid_setting_str = "Found invalid settings:
{}".\ + format(break_str.join(invalid_settings)) + + formatting_data = { + "invalid_setting_str": invalid_setting_str, + "invalid_keys_str": invalid_keys_str + } + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) + + scene_url = instance.context.data.get("scenePath") + if not os.path.exists(scene_url): + msg = "Scene file {} not found (saved under wrong name)".format( + scene_url + ) + formatting_data = { + "scene_url": scene_url + } + raise PublishXmlValidationError(self, msg, key="file_not_found", + formatting_data=formatting_data) def _update_frames(expected_settings): From b9763e0e21342e11909563447047af201e1d70cc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 14:13:03 +0100 Subject: [PATCH 19/44] raise PublishXmlValidationError in validate workfile project name --- .../help/validate_workfile_project_name.xml | 24 ++++++++++++++ .../publish/validate_workfile_project_name.py | 33 ++++++++++++------- 2 files changed, 45 insertions(+), 12 deletions(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_workfile_project_name.xml diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_workfile_project_name.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_workfile_project_name.xml new file mode 100644 index 0000000000..c4ffafc8b5 --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_workfile_project_name.xml @@ -0,0 +1,24 @@ + + + +Project name +## Your scene is from different project + +It is not possible to publish into project "{workfile_project_name}" when TVPaint was opened with project "{env_project_name}" in context. + +### How to repair? + +If the workfile belongs to project "{env_project_name}" then use Workfiles tool to resave it. + +Otherwise close TVPaint and launch it again from project you want to publish in. + + +### How this could happend? + +You've opened workfile from different project. You've opened TVPaint on a task from "{env_project_name}" then you've opened TVPaint again on task from "{workfile_project_name}" without closing the TVPaint. Because TVPaint can run only once the project didn't change. + +### Why it is important? +Because project may affect how TVPaint works or change publishing behavior it is dangerous to allow change project context in many ways. For example publishing will not run as expected. + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py b/openpype/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py index cc664d8030..36230ae38b 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py @@ -1,5 +1,6 @@ import os import pyblish.api +from openpype.pipeline import PublishXmlValidationError class ValidateWorkfileProjectName(pyblish.api.ContextPlugin): @@ -31,15 +32,23 @@ class ValidateWorkfileProjectName(pyblish.api.ContextPlugin): return # Raise an error - raise AssertionError(( - # Short message - "Workfile from different Project ({})." - # Description what's wrong - " It is not possible to publish when TVPaint was launched in" - "context of different project. Current context project is \"{}\"." - " Launch TVPaint in context of project \"{}\" and then publish." - ).format( - workfile_project_name, - env_project_name, - workfile_project_name, - )) + raise AssertionError( + self, + ( + # Short message + "Workfile from different Project ({})." + # Description what's wrong + " It is not possible to publish when TVPaint was launched in" + "context of different project. Current context project is" + " \"{}\". Launch TVPaint in context of project \"{}\"" + " and then publish." + ).format( + workfile_project_name, + env_project_name, + workfile_project_name, + ), + formatting_data={ + "workfile_project_name": workfile_project_name, + "expected_project_name": env_project_name + } + ) From 9221d47b060695debe4bd39e226bcfdbe1dbe41f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 15:53:47 +0100 Subject: [PATCH 20/44] fix used exception --- .../tvpaint/plugins/publish/validate_workfile_project_name.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py b/openpype/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py index 36230ae38b..0f25f2f7be 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py @@ -32,7 +32,7 @@ class ValidateWorkfileProjectName(pyblish.api.ContextPlugin): return # Raise an error - raise AssertionError( + raise PublishXmlValidationError( self, ( # Short message From 99feae84f28926a175eea31e1006705704314835 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 16:00:03 +0100 Subject: [PATCH 21/44] make sure all previous widget stay hidden --- openpype/tools/publisher/widgets/validations_widget.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index ba4df2eb8e..90bb4b062b 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -272,6 +272,7 @@ class ValidateActionsWidget(QtWidgets.QFrame): item = self._content_layout.takeAt(0) widget = item.widget() if widget: + widget.setVisible(False) widget.deleteLater() self._actions_mapping = {} From f6a7d3f51ab8b8f275615c4ac277c6813891a898 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 16:00:15 +0100 Subject: [PATCH 22/44] action icon is optional --- openpype/tools/publisher/widgets/validations_widget.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index 90bb4b062b..003d78fa56 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -226,13 +226,15 @@ class ActionButton(ClickableFrame): action_label = action.label or action.__name__ action_icon = getattr(action, "icon", None) label_widget = QtWidgets.QLabel(action_label, self) + icon_label = None if action_icon: icon_label = IconValuePixmapLabel(action_icon, self) layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(5, 0, 5, 0) layout.addWidget(label_widget, 1) - layout.addWidget(icon_label, 0) + if icon_label is not None: + layout.addWidget(icon_label, 0) self.setSizePolicy( QtWidgets.QSizePolicy.Minimum, From 2813317a17587a0d290547ec68d2af98132de5ba Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 20 Dec 2021 19:25:20 +0100 Subject: [PATCH 23/44] remove check of attr_plugins --- openpype/pipeline/create/context.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 7b0f50b1dc..2d748dd74f 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -306,8 +306,6 @@ class PublishAttributes: self._plugin_names_order = [] self._missing_plugins = [] self.attr_plugins = attr_plugins or [] - if not attr_plugins: - return origin_data = self._origin_data data = self._data From 43b5cc802d73c0af908216b674a8c214972c944a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 16:12:21 +0100 Subject: [PATCH 24/44] Change widget to Frame to make sure it has background with PySide --- openpype/tools/publisher/window.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/publisher/window.py b/openpype/tools/publisher/window.py index bb58813e55..b83c491c95 100644 --- a/openpype/tools/publisher/window.py +++ b/openpype/tools/publisher/window.py @@ -79,7 +79,7 @@ class PublisherWindow(QtWidgets.QDialog): # Content # Subset widget - subset_frame = QtWidgets.QWidget(self) + subset_frame = QtWidgets.QFrame(self) subset_views_widget = BorderedLabelWidget( "Subsets to publish", subset_frame From 3e683eadd0f5f133d99499bb0ce4a92935ad51eb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 16:15:31 +0100 Subject: [PATCH 25/44] changed validation widget to frame to make sure it has background --- .../tools/publisher/widgets/validations_widget.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index 003d78fa56..c9e5283d5f 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -414,8 +414,8 @@ class ValidationsWidget(QtWidgets.QWidget): errors_scroll.setWidget(errors_widget) - error_details_widget = QtWidgets.QWidget(self) - error_details_input = QtWidgets.QTextEdit(error_details_widget) + error_details_frame = QtWidgets.QFrame(self) + error_details_input = QtWidgets.QTextEdit(error_details_frame) error_details_input.setObjectName("InfoText") error_details_input.setTextInteractionFlags( QtCore.Qt.TextBrowserInteraction @@ -424,7 +424,7 @@ class ValidationsWidget(QtWidgets.QWidget): actions_widget = ValidateActionsWidget(controller, self) actions_widget.setFixedWidth(140) - error_details_layout = QtWidgets.QHBoxLayout(error_details_widget) + error_details_layout = QtWidgets.QHBoxLayout(error_details_frame) error_details_layout.addWidget(error_details_input, 1) error_details_layout.addWidget(actions_widget, 0) @@ -433,7 +433,7 @@ class ValidationsWidget(QtWidgets.QWidget): content_layout.setContentsMargins(0, 0, 0, 0) content_layout.addWidget(errors_scroll, 0) - content_layout.addWidget(error_details_widget, 1) + content_layout.addWidget(error_details_frame, 1) top_label = QtWidgets.QLabel("Publish validation report", self) top_label.setObjectName("PublishInfoMainLabel") @@ -447,7 +447,7 @@ class ValidationsWidget(QtWidgets.QWidget): self._top_label = top_label self._errors_widget = errors_widget self._errors_layout = errors_layout - self._error_details_widget = error_details_widget + self._error_details_frame = error_details_frame self._error_details_input = error_details_input self._actions_widget = actions_widget @@ -467,7 +467,7 @@ class ValidationsWidget(QtWidgets.QWidget): widget.deleteLater() self._top_label.setVisible(False) - self._error_details_widget.setVisible(False) + self._error_details_frame.setVisible(False) self._errors_widget.setVisible(False) self._actions_widget.setVisible(False) @@ -478,7 +478,7 @@ class ValidationsWidget(QtWidgets.QWidget): return self._top_label.setVisible(True) - self._error_details_widget.setVisible(True) + self._error_details_frame.setVisible(True) self._errors_widget.setVisible(True) errors_by_title = [] From 8dff901f8d810b9f8363dbca7053e94788d741fd Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 22 Dec 2021 16:41:52 +0000 Subject: [PATCH 26/44] more xml validator messages --- ..._settings.xml => validate_frame_token.xml} | 12 +-- .../publish/help/validate_vdb_input_node.xml | 22 ------ .../publish/help/validate_vdb_output_node.xml | 48 ++++++++++++ .../publish/validate_animation_settings.py | 51 ------------ .../plugins/publish/validate_frame_token.py | 17 ++-- .../plugins/publish/validate_output_node.py | 77 ------------------- .../publish/validate_sop_output_node.py | 2 +- .../publish/validate_vdb_input_node.py | 47 ----------- .../publish/validate_vdb_output_node.py | 66 +++++++++++----- 9 files changed, 113 insertions(+), 229 deletions(-) rename openpype/hosts/houdini/plugins/publish/help/{validate_animation_settings.xml => validate_frame_token.xml} (74%) delete mode 100644 openpype/hosts/houdini/plugins/publish/help/validate_vdb_input_node.xml create mode 100644 openpype/hosts/houdini/plugins/publish/help/validate_vdb_output_node.xml delete mode 100644 openpype/hosts/houdini/plugins/publish/validate_animation_settings.py delete mode 100644 openpype/hosts/houdini/plugins/publish/validate_output_node.py delete mode 100644 openpype/hosts/houdini/plugins/publish/validate_vdb_input_node.py diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_animation_settings.xml b/openpype/hosts/houdini/plugins/publish/help/validate_frame_token.xml similarity index 74% rename from openpype/hosts/houdini/plugins/publish/help/validate_animation_settings.xml rename to openpype/hosts/houdini/plugins/publish/help/validate_frame_token.xml index 8a2a396783..925113362a 100644 --- a/openpype/hosts/houdini/plugins/publish/help/validate_animation_settings.xml +++ b/openpype/hosts/houdini/plugins/publish/help/validate_frame_token.xml @@ -1,22 +1,22 @@ -Frame token in output -## Frame range is missing frame token +Output frame token +## Output path is missing frame token This validator will check the output parameter of the node if the Valid Frame Range is not set to 'Render Current Frame' -No frame token found in {nodepath} +No frame token found in: **{nodepath}** ### How to repair? -Your you need to add `$F4` or similar frame based token to your path. + +You need to add `$F4` or similar frame based token to your path. + **Example:** Good: 'my_vbd_cache.$F4.vdb' Bad: 'my_vbd_cache.vdb' - - diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_vdb_input_node.xml b/openpype/hosts/houdini/plugins/publish/help/validate_vdb_input_node.xml deleted file mode 100644 index 8cc186a183..0000000000 --- a/openpype/hosts/houdini/plugins/publish/help/validate_vdb_input_node.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - -VDB input node -## Invalid VDB input node - -Validate that the node connected to the output node is of type VDB. - -Regardless of the amount of VDBs created the output will need to have an -equal amount of VDBs, points, primitives and vertices - -A VDB is an inherited type of Prim, holds the following data: - - Primitives: 1 - - Points: 1 - - Vertices: 1 - - VDBs: 1 - - - - - - \ No newline at end of file diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_vdb_output_node.xml b/openpype/hosts/houdini/plugins/publish/help/validate_vdb_output_node.xml new file mode 100644 index 0000000000..822d1836c1 --- /dev/null +++ b/openpype/hosts/houdini/plugins/publish/help/validate_vdb_output_node.xml @@ -0,0 +1,48 @@ + + + +VDB output node +## Invalid VDB output nodes + +Validate that the node connected to the output node is of type VDB. + +Regardless of the amount of VDBs created the output will need to have an +equal amount of VDBs, points, primitives and vertices + +A VDB is an inherited type of Prim, holds the following data: + +- Primitives: 1 +- Points: 1 +- Vertices: 1 +- VDBs: 1 + + + + + + + +No SOP path +## No SOP Path in output node + +SOP Output node in '{node}' does not exist. Ensure a valid SOP output path is set. + + + + + + + +Wrong SOP path +## Wrong SOP Path in output node + +Output node {nodepath} is not a SOP node. +SOP Path must point to a SOP node, +instead found category type: {categoryname} + + + + + + + \ No newline at end of file diff --git a/openpype/hosts/houdini/plugins/publish/validate_animation_settings.py b/openpype/hosts/houdini/plugins/publish/validate_animation_settings.py deleted file mode 100644 index 5eb8f93d03..0000000000 --- a/openpype/hosts/houdini/plugins/publish/validate_animation_settings.py +++ /dev/null @@ -1,51 +0,0 @@ -import pyblish.api - -from openpype.hosts.houdini.api import lib - - -class ValidateAnimationSettings(pyblish.api.InstancePlugin): - """Validate if the unexpanded string contains the frame ('$F') token - - This validator will only check the output parameter of the node if - the Valid Frame Range is not set to 'Render Current Frame' - - Rules: - If you render out a frame range it is mandatory to have the - frame token - '$F4' or similar - to ensure that each frame gets - written. If this is not the case you will override the same file - every time a frame is written out. - - Examples: - Good: 'my_vbd_cache.$F4.vdb' - Bad: 'my_vbd_cache.vdb' - - """ - - order = pyblish.api.ValidatorOrder - label = "Validate Frame Settings" - families = ["vdbcache"] - - def process(self, instance): - - invalid = self.get_invalid(instance) - if invalid: - raise RuntimeError( - "Output settings do no match for '%s'" % instance - ) - - @classmethod - def get_invalid(cls, instance): - - node = instance[0] - - # Check trange parm, 0 means Render Current Frame - frame_range = node.evalParm("trange") - if frame_range == 0: - return [] - - output_parm = lib.get_output_parameter(node) - unexpanded_str = output_parm.unexpandedString() - - if "$F" not in unexpanded_str: - cls.log.error("No frame token found in '%s'" % node.path()) - return [instance] diff --git a/openpype/hosts/houdini/plugins/publish/validate_frame_token.py b/openpype/hosts/houdini/plugins/publish/validate_frame_token.py index 76b5910576..f66238f159 100644 --- a/openpype/hosts/houdini/plugins/publish/validate_frame_token.py +++ b/openpype/hosts/houdini/plugins/publish/validate_frame_token.py @@ -1,12 +1,12 @@ import pyblish.api from openpype.hosts.houdini.api import lib - +from openpype.pipeline import PublishXmlValidationError class ValidateFrameToken(pyblish.api.InstancePlugin): - """Validate if the unexpanded string contains the frame ('$F') token. + """Validate if the unexpanded string contains the frame ('$F') token - This validator will *only* check the output parameter of the node if + This validator will only check the output parameter of the node if the Valid Frame Range is not set to 'Render Current Frame' Rules: @@ -28,9 +28,14 @@ class ValidateFrameToken(pyblish.api.InstancePlugin): def process(self, instance): invalid = self.get_invalid(instance) + data = { + "nodepath": instance + } if invalid: - raise RuntimeError( - "Output settings do no match for '%s'" % instance + raise PublishXmlValidationError( + self, + "Output path for '%s' is missing $F4 token" % instance, + formatting_data=data ) @classmethod @@ -47,5 +52,5 @@ class ValidateFrameToken(pyblish.api.InstancePlugin): unexpanded_str = output_parm.unexpandedString() if "$F" not in unexpanded_str: - cls.log.error("No frame token found in '%s'" % node.path()) + # cls.log.info("No frame token found in '%s'" % node.path()) return [instance] diff --git a/openpype/hosts/houdini/plugins/publish/validate_output_node.py b/openpype/hosts/houdini/plugins/publish/validate_output_node.py deleted file mode 100644 index 0b60ab5c48..0000000000 --- a/openpype/hosts/houdini/plugins/publish/validate_output_node.py +++ /dev/null @@ -1,77 +0,0 @@ -import pyblish.api - - -class ValidateOutputNode(pyblish.api.InstancePlugin): - """Validate the instance SOP Output Node. - - This will ensure: - - The SOP Path is set. - - The SOP Path refers to an existing object. - - The SOP Path node is a SOP node. - - The SOP Path node has at least one input connection (has an input) - - The SOP Path has geometry data. - - """ - - order = pyblish.api.ValidatorOrder - families = ["pointcache", "vdbcache"] - hosts = ["houdini"] - label = "Validate Output Node" - - def process(self, instance): - - invalid = self.get_invalid(instance) - if invalid: - raise RuntimeError( - "Output node(s) `%s` are incorrect. " - "See plug-in log for details." % invalid - ) - - @classmethod - def get_invalid(cls, instance): - - import hou - - output_node = instance.data["output_node"] - - if output_node is None: - node = instance[0] - cls.log.error( - "SOP Output node in '%s' does not exist. " - "Ensure a valid SOP output path is set." % node.path() - ) - - return [node.path()] - - # Output node must be a Sop node. - if not isinstance(output_node, hou.SopNode): - cls.log.error( - "Output node %s is not a SOP node. " - "SOP Path must point to a SOP node, " - "instead found category type: %s" - % (output_node.path(), output_node.type().category().name()) - ) - return [output_node.path()] - - # For the sake of completeness also assert the category type - # is Sop to avoid potential edge case scenarios even though - # the isinstance check above should be stricter than this category - assert output_node.type().category().name() == "Sop", ( - "Output node %s is not of category Sop. This is a bug.." - % output_node.path() - ) - - # Check if output node has incoming connections - if not output_node.inputConnections(): - cls.log.error( - "Output node `%s` has no incoming connections" - % output_node.path() - ) - return [output_node.path()] - - # Ensure the output node has at least Geometry data - if not output_node.geometry(): - cls.log.error( - "Output node `%s` has no geometry data." % output_node.path() - ) - return [output_node.path()] diff --git a/openpype/hosts/houdini/plugins/publish/validate_sop_output_node.py b/openpype/hosts/houdini/plugins/publish/validate_sop_output_node.py index a5a07b1b1a..a37d376919 100644 --- a/openpype/hosts/houdini/plugins/publish/validate_sop_output_node.py +++ b/openpype/hosts/houdini/plugins/publish/validate_sop_output_node.py @@ -14,7 +14,7 @@ class ValidateSopOutputNode(pyblish.api.InstancePlugin): """ order = pyblish.api.ValidatorOrder - families = ["pointcache", "vdbcache"] + families = ["pointcache"] hosts = ["houdini"] label = "Validate Output Node" diff --git a/openpype/hosts/houdini/plugins/publish/validate_vdb_input_node.py b/openpype/hosts/houdini/plugins/publish/validate_vdb_input_node.py deleted file mode 100644 index 0ae1bc94eb..0000000000 --- a/openpype/hosts/houdini/plugins/publish/validate_vdb_input_node.py +++ /dev/null @@ -1,47 +0,0 @@ -import pyblish.api -import openpype.api - - -class ValidateVDBInputNode(pyblish.api.InstancePlugin): - """Validate that the node connected to the output node is of type VDB. - - Regardless of the amount of VDBs create the output will need to have an - equal amount of VDBs, points, primitives and vertices - - A VDB is an inherited type of Prim, holds the following data: - - Primitives: 1 - - Points: 1 - - Vertices: 1 - - VDBs: 1 - - """ - - order = openpype.api.ValidateContentsOrder + 0.1 - families = ["vdbcache"] - hosts = ["houdini"] - label = "Validate Input Node (VDB)" - - def process(self, instance): - invalid = self.get_invalid(instance) - if invalid: - raise RuntimeError( - "Node connected to the output node is not" "of type VDB!" - ) - - @classmethod - def get_invalid(cls, instance): - - node = instance.data["output_node"] - - prims = node.geometry().prims() - nr_of_prims = len(prims) - - nr_of_points = len(node.geometry().points()) - if nr_of_points != nr_of_prims: - cls.log.error("The number of primitives and points do not match") - return [instance] - - for prim in prims: - if prim.numVertices() != 1: - cls.log.error("Found primitive with more than 1 vertex!") - return [instance] diff --git a/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py b/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py index 1ba840b71d..f6e54f3ae2 100644 --- a/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py +++ b/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py @@ -1,8 +1,7 @@ import pyblish.api import openpype.api +from openpype.pipeline import PublishXmlValidationError import hou - - class ValidateVDBOutputNode(pyblish.api.InstancePlugin): """Validate that the node connected to the output node is of type VDB. @@ -23,32 +22,61 @@ class ValidateVDBOutputNode(pyblish.api.InstancePlugin): label = "Validate Output Node (VDB)" def process(self, instance): + + data = { + "node": instance + } + + output_node = instance.data["output_node"] + if output_node is None: + raise PublishXmlValidationError( + self, + "SOP Output node in '{node}' does not exist. Ensure a valid " + "SOP output path is set.".format(**data), + key="noSOP", + formatting_data=data + ) + + # Output node must be a Sop node. + if not isinstance(output_node, hou.SopNode): + data = { + "nodepath": output_node.path(), + "categoryname": output_node.type().category().name() + } + raise PublishXmlValidationError( + self, + "Output node {nodepath} is not a SOP node. SOP Path must" + "point to a SOP node, instead found category" + "type: {categoryname}".format(**data), + key="wrongSOP", + formatting_data=data + ) + return [node.path()] + invalid = self.get_invalid(instance) + if invalid: - raise RuntimeError( - "Node connected to the output node is not" " of type VDB!" + raise PublishXmlValidationError( + self, + "Output node(s) `{}` are incorrect. See plug-in" + "log for details.".format(invalid), + formatting_data=data ) @classmethod def get_invalid(cls, instance): - node = instance.data["output_node"] - if node is None: - cls.log.error( - "SOP path is not correctly set on " - "ROP node '%s'." % instance[0].path() - ) - return [instance] + output_node = instance.data["output_node"] frame = instance.data.get("frameStart", 0) - geometry = node.geometryAtFrame(frame) + geometry = output_node.geometryAtFrame(frame) if geometry is None: - # No geometry data on this node, maybe the node hasn't cooked? - cls.log.error( + # No geometry data on this output_node, maybe the node hasn't cooked? + cls.log.debug( "SOP node has no geometry data. " - "Is it cooked? %s" % node.path() + "Is it cooked? %s" % output_node.path() ) - return [node] + return [output_node] prims = geometry.prims() nr_of_prims = len(prims) @@ -57,17 +85,17 @@ class ValidateVDBOutputNode(pyblish.api.InstancePlugin): invalid_prim = False for prim in prims: if not isinstance(prim, hou.VDB): - cls.log.error("Found non-VDB primitive: %s" % prim) + cls.log.debug("Found non-VDB primitive: %s" % prim) invalid_prim = True if invalid_prim: return [instance] nr_of_points = len(geometry.points()) if nr_of_points != nr_of_prims: - cls.log.error("The number of primitives and points do not match") + cls.log.debug("The number of primitives and points do not match") return [instance] for prim in prims: if prim.numVertices() != 1: - cls.log.error("Found primitive with more than 1 vertex!") + cls.log.debug("Found primitive with more than 1 vertex!") return [instance] From df717bfb8c038bec50f4a949c9f9c2b356ca7a41 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 22 Dec 2021 18:53:25 +0100 Subject: [PATCH 27/44] Update openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml Co-authored-by: Milan Kolar --- .../tvpaint/plugins/publish/help/validate_layers_visibility.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml index fc69d5fd7b..2eaed22a19 100644 --- a/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml @@ -10,7 +10,7 @@ All layers for subset "{instance_name}" are hidden. {layer_names} -*Check layer names for all subsets in list on left side.* +*Check layer names for all subsets in the list on the left side.* ### How to repair? From f06bfd7c861c32ac41fd63bb86e9b5bf3c68f19b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 19:34:02 +0100 Subject: [PATCH 28/44] changed fixed width to min width --- openpype/tools/publisher/widgets/validations_widget.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index c9e5283d5f..fc78f93856 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -407,7 +407,7 @@ class ValidationsWidget(QtWidgets.QWidget): errors_scroll.setWidgetResizable(True) errors_widget = QtWidgets.QWidget(errors_scroll) - errors_widget.setFixedWidth(200) + errors_widget.setMinimumWidth(200) errors_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) errors_layout = QtWidgets.QVBoxLayout(errors_widget) errors_layout.setContentsMargins(0, 0, 0, 0) @@ -422,7 +422,7 @@ class ValidationsWidget(QtWidgets.QWidget): ) actions_widget = ValidateActionsWidget(controller, self) - actions_widget.setFixedWidth(140) + actions_widget.setMinimumWidth(140) error_details_layout = QtWidgets.QHBoxLayout(error_details_frame) error_details_layout.addWidget(error_details_input, 1) From ca5f1dbcba0c7d93c0840cf4d9bf0ca941534f5d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 21:15:20 +0100 Subject: [PATCH 29/44] make sure items has label in tooltip --- openpype/tools/publisher/widgets/validations_widget.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index fc78f93856..028e6a2ea3 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -95,6 +95,7 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): item.setFlags( QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable ) + item.setData(label, QtCore.Qt.ToolTipRole) item.setData(instance.id, INSTANCE_ID_ROLE) items.append(item) description = self._prepare_description(exception) From 197a5054cd8463892ffa3bb3f2de7931d4f101ec Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 21:15:30 +0100 Subject: [PATCH 30/44] disable horizontal scroll --- openpype/tools/publisher/widgets/validations_widget.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index 028e6a2ea3..a3f2c2069a 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -25,6 +25,7 @@ class ValidationErrorInstanceList(QtWidgets.QListView): self.setObjectName("ValidationErrorInstanceList") + self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.setSelectionMode(QtWidgets.QListView.ExtendedSelection) def minimumSizeHint(self): From 64bb579c816d746a67a68322fd2c3a4f642e468d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 21:39:44 +0100 Subject: [PATCH 31/44] resize title by longest instance name --- .../publisher/widgets/validations_widget.py | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index a3f2c2069a..c013b0b2e0 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -29,16 +29,16 @@ class ValidationErrorInstanceList(QtWidgets.QListView): self.setSelectionMode(QtWidgets.QListView.ExtendedSelection) def minimumSizeHint(self): - result = super(ValidationErrorInstanceList, self).minimumSizeHint() - result.setHeight(self.sizeHint().height()) - return result + return self.sizeHint() def sizeHint(self): + result = super(ValidationErrorInstanceList, self).sizeHint() row_count = self.model().rowCount() height = 0 if row_count > 0: height = self.sizeHintForRow(0) * row_count - return QtCore.QSize(self.width(), height) + result.setHeight(height) + return result class ValidationErrorTitleWidget(QtWidgets.QWidget): @@ -133,12 +133,30 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): self._toggle_instance_btn = toggle_instance_btn + self._view_layout = view_layout + self._instances_model = instances_model self._instances_view = instances_view self._context_validation = context_validation self._help_text_by_instance_id = help_text_by_instance_id + def sizeHint(self): + result = super().sizeHint() + expected_width = 0 + for idx in range(self._view_layout.count()): + expected_width += self._view_layout.itemAt(idx).sizeHint().width() + + if expected_width < 200: + expected_width = 200 + + if result.width() < expected_width: + result.setWidth(expected_width) + return result + + def minimumSizeHint(self): + return self.sizeHint() + def _prepare_description(self, exception): dsc = exception.description detail = exception.detail @@ -409,7 +427,6 @@ class ValidationsWidget(QtWidgets.QWidget): errors_scroll.setWidgetResizable(True) errors_widget = QtWidgets.QWidget(errors_scroll) - errors_widget.setMinimumWidth(200) errors_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) errors_layout = QtWidgets.QVBoxLayout(errors_widget) errors_layout.setContentsMargins(0, 0, 0, 0) @@ -518,6 +535,8 @@ class ValidationsWidget(QtWidgets.QWidget): if self._title_widgets: self._title_widgets[0].set_selected(True) + self.updateGeometry() + def _on_select(self, index): if self._previous_select: if self._previous_select.index == index: From 7c6d63f8930a6338b194ae339012c532914ca5d3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 23 Dec 2021 12:38:00 +0100 Subject: [PATCH 32/44] Added new style validators for New Publisher for Standalone Publisher --- .../publish/help/validate_frame_ranges.xml | 15 ++++++++ .../publish/help/validate_shot_duplicates.xml | 15 ++++++++ .../plugins/publish/help/validate_sources.xml | 16 +++++++++ .../publish/help/validate_task_existence.xml | 16 +++++++++ .../publish/help/validate_texture_batch.xml | 15 ++++++++ .../help/validate_texture_has_workfile.xml | 15 ++++++++ .../publish/help/validate_texture_name.xml | 32 +++++++++++++++++ .../help/validate_texture_versions.xml | 35 +++++++++++++++++++ .../help/validate_texture_workfiles.xml | 23 ++++++++++++ .../plugins/publish/validate_frame_ranges.py | 18 +++++++--- .../publish/validate_shot_duplicates.py | 9 +++-- .../plugins/publish/validate_sources.py | 18 +++++++--- .../publish/validate_task_existence.py | 9 ++++- .../plugins/publish/validate_texture_batch.py | 8 +++-- .../publish/validate_texture_has_workfile.py | 6 +++- .../plugins/publish/validate_texture_name.py | 21 ++++++++--- .../publish/validate_texture_versions.py | 15 ++++++-- .../publish/validate_texture_workfiles.py | 17 ++++++--- 18 files changed, 275 insertions(+), 28 deletions(-) create mode 100644 openpype/hosts/standalonepublisher/plugins/publish/help/validate_frame_ranges.xml create mode 100644 openpype/hosts/standalonepublisher/plugins/publish/help/validate_shot_duplicates.xml create mode 100644 openpype/hosts/standalonepublisher/plugins/publish/help/validate_sources.xml create mode 100644 openpype/hosts/standalonepublisher/plugins/publish/help/validate_task_existence.xml create mode 100644 openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_batch.xml create mode 100644 openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_has_workfile.xml create mode 100644 openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_name.xml create mode 100644 openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_versions.xml create mode 100644 openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_workfiles.xml diff --git a/openpype/hosts/standalonepublisher/plugins/publish/help/validate_frame_ranges.xml b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_frame_ranges.xml new file mode 100644 index 0000000000..933df1c7c5 --- /dev/null +++ b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_frame_ranges.xml @@ -0,0 +1,15 @@ + + + +Invalid frame range + +## Invalid frame range + +Expected duration or '{duration}' frames set in database, workfile contains only '{found}' frames. + +### How to repair? + +Modify configuration in the database or tweak frame range in the workfile. + + + \ No newline at end of file diff --git a/openpype/hosts/standalonepublisher/plugins/publish/help/validate_shot_duplicates.xml b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_shot_duplicates.xml new file mode 100644 index 0000000000..77b8727162 --- /dev/null +++ b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_shot_duplicates.xml @@ -0,0 +1,15 @@ + + + +Duplicate shots + +## Duplicate shot names + +Process contains duplicated shot names '{duplicates_str}'. + +### How to repair? + +Remove shot duplicates. + + + \ No newline at end of file diff --git a/openpype/hosts/standalonepublisher/plugins/publish/help/validate_sources.xml b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_sources.xml new file mode 100644 index 0000000000..d527d2173e --- /dev/null +++ b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_sources.xml @@ -0,0 +1,16 @@ + + + +Files not found + +## Source files not found + +Process contains duplicated shot names: +'{files_not_found}' + +### How to repair? + +Add missing files or run Publish again to collect new publishable files. + + + \ No newline at end of file diff --git a/openpype/hosts/standalonepublisher/plugins/publish/help/validate_task_existence.xml b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_task_existence.xml new file mode 100644 index 0000000000..a943f560d0 --- /dev/null +++ b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_task_existence.xml @@ -0,0 +1,16 @@ + + + +Task not found + +## Task not found in database + +Process contains tasks that don't exist in database: +'{task_not_found}' + +### How to repair? + +Remove set task or add task into database into proper place. + + + \ No newline at end of file diff --git a/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_batch.xml b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_batch.xml new file mode 100644 index 0000000000..a645df8d02 --- /dev/null +++ b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_batch.xml @@ -0,0 +1,15 @@ + + + +No texture files found + +## Batch doesn't contain texture files + +Batch must contain at least one texture file. + +### How to repair? + +Add texture file to the batch or check name if it follows naming convention to match texture files to the batch. + + + \ No newline at end of file diff --git a/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_has_workfile.xml b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_has_workfile.xml new file mode 100644 index 0000000000..077987a96d --- /dev/null +++ b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_has_workfile.xml @@ -0,0 +1,15 @@ + + + +No workfile found + +## Batch should contain workfile + +It is expected that published contains workfile that served as a source for textures. + +### How to repair? + +Add workfile to the batch, or disable this validator if you do not want workfile published. + + + \ No newline at end of file diff --git a/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_name.xml b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_name.xml new file mode 100644 index 0000000000..2610917736 --- /dev/null +++ b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_name.xml @@ -0,0 +1,32 @@ + + + +Asset name not found + +## Couldn't parse asset name from a file + +Unable to parse asset name from '{file_name}'. File name doesn't match configured naming convention. + +### How to repair? + +Check Settings: project_settings/standalonepublisher/publish/CollectTextures for naming convention. + + +### __Detailed Info__ (optional) + +This error happens when parsing cannot figure out name of asset texture files belong under. + + + +Missing keys + +## Texture file name is missing some required keys + +Texture '{file_name}' is missing values for {missing_str} keys. + +### How to repair? + +Fix name of texture file and Publish again. + + + diff --git a/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_versions.xml b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_versions.xml new file mode 100644 index 0000000000..1e536e604f --- /dev/null +++ b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_versions.xml @@ -0,0 +1,35 @@ + + + +Texture version + +## Texture version mismatch with workfile + +Workfile '{file_name}' version doesn't match with '{version}' of a texture. + +### How to repair? + +Rename either workfile or texture to contain matching versions + + +### __Detailed Info__ (optional) + +This might happen if you are trying to publish textures for older version of workfile (or the other way). +(Eg. publishing 'workfile_v001' and 'texture_file_v002') + + + +Too many versions + +## Too many versions published at same time + +It is currently expected to publish only batch with single version. + +Found {found} versions. + +### How to repair? + +Please remove files with different version and split publishing into multiple steps. + + + diff --git a/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_workfiles.xml b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_workfiles.xml new file mode 100644 index 0000000000..8187eb0bc8 --- /dev/null +++ b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_workfiles.xml @@ -0,0 +1,23 @@ + + + +No secondary workfile + +## No secondary workfile found + +Current process expects that primary workfile (for example with a extension '{extension}') will contain also 'secondary' workfile. + +Secondary workfile for '{file_name}' wasn't found. + +### How to repair? + +Attach secondary workfile or disable this validator and Publish again. + + +### __Detailed Info__ (optional) + +This process was implemented for a possible use case of first workfile coming from Mari, secondary workfile for textures from Substance. +Publish should contain both if primary workfile is present. + + + diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_frame_ranges.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_frame_ranges.py index 943cb73b98..c7a2e755b6 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_frame_ranges.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_frame_ranges.py @@ -1,8 +1,10 @@ import re import pyblish.api + import openpype.api from openpype import lib +from openpype.pipeline import PublishXmlValidationError class ValidateFrameRange(pyblish.api.InstancePlugin): @@ -48,9 +50,15 @@ class ValidateFrameRange(pyblish.api.InstancePlugin): files = [files] frames = len(files) - err_msg = "Frame duration from DB:'{}' ". format(int(duration)) +\ - " doesn't match number of files:'{}'".format(frames) +\ - " Please change frame range for Asset or limit no. of files" - assert frames == duration, err_msg + msg = "Frame duration from DB:'{}' ". format(int(duration)) +\ + " doesn't match number of files:'{}'".format(frames) +\ + " Please change frame range for Asset or limit no. of files" - self.log.debug("Valid ranges {} - {}".format(int(duration), frames)) + formatting_data = {"duration": duration, + "found": frames} + if frames == duration: + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) + + self.log.debug("Valid ranges expected '{}' - found '{}'". + format(int(duration), frames)) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_shot_duplicates.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_shot_duplicates.py index 85ec9379ce..0f957acad6 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_shot_duplicates.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_shot_duplicates.py @@ -1,6 +1,7 @@ import pyblish.api -import openpype.api +import openpype.api +from openpype.pipeline import PublishXmlValidationError class ValidateShotDuplicates(pyblish.api.ContextPlugin): """Validating no duplicate names are in context.""" @@ -20,4 +21,8 @@ class ValidateShotDuplicates(pyblish.api.ContextPlugin): shot_names.append(name) msg = "There are duplicate shot names:\n{}".format(duplicate_names) - assert not duplicate_names, msg + + formatting_data = {"duplicate_str": ','.join(duplicate_names)} + if duplicate_names: + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_sources.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_sources.py index eec675e97f..316f58988f 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_sources.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_sources.py @@ -1,8 +1,10 @@ -import pyblish.api -import openpype.api - import os +import pyblish.api + +import openpype.api +from openpype.pipeline import PublishXmlValidationError + class ValidateSources(pyblish.api.InstancePlugin): """Validates source files. @@ -11,7 +13,6 @@ class ValidateSources(pyblish.api.InstancePlugin): got deleted between starting of SP and now. """ - order = openpype.api.ValidateContentsOrder label = "Check source files" @@ -22,6 +23,7 @@ class ValidateSources(pyblish.api.InstancePlugin): def process(self, instance): self.log.info("instance {}".format(instance.data)) + missing_files = set() for repre in instance.data.get("representations") or []: files = [] if isinstance(repre["files"], str): @@ -34,4 +36,10 @@ class ValidateSources(pyblish.api.InstancePlugin): file_name) if not os.path.exists(source_file): - raise ValueError("File {} not found".format(source_file)) + missing_files.add(source_file) + + msg = "Files '{}' not found".format(','.join(missing_files)) + formatting_data = {"files_not_found": ' - {}'.join(missing_files)} + if missing_files: + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_task_existence.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_task_existence.py index e3b2ae1646..825092c81b 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_task_existence.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_task_existence.py @@ -1,6 +1,8 @@ import pyblish.api from avalon import io +from openpype.pipeline import PublishXmlValidationError + class ValidateTaskExistence(pyblish.api.ContextPlugin): """Validating tasks on instances are filled and existing.""" @@ -53,4 +55,9 @@ class ValidateTaskExistence(pyblish.api.ContextPlugin): "Asset: \"{}\" Task: \"{}\"".format(*missing_pair) ) - raise AssertionError(msg.format("\n".join(pair_msgs))) + msg = msg.format("\n".join(pair_msgs)) + + formatting_data = {"task_not_found": ' - {}'.join(pair_msgs)} + if pair_msgs: + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_batch.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_batch.py index d592a4a059..d66fb257bb 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_batch.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_batch.py @@ -1,6 +1,8 @@ import pyblish.api import openpype.api +from openpype.pipeline import PublishXmlValidationError + class ValidateTextureBatch(pyblish.api.InstancePlugin): """Validates that some texture files are present.""" @@ -15,8 +17,10 @@ class ValidateTextureBatch(pyblish.api.InstancePlugin): present = False for instance in instance.context: if instance.data["family"] == "textures": - self.log.info("Some textures present.") + self.log.info("At least some textures present.") return - assert present, "No textures found in published batch!" + msg = "No textures found in published batch!" + if not present: + raise PublishXmlValidationError(self, msg) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_has_workfile.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_has_workfile.py index 7cd540668c..0e67464f59 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_has_workfile.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_has_workfile.py @@ -1,5 +1,7 @@ import pyblish.api + import openpype.api +from openpype.pipeline import PublishXmlValidationError class ValidateTextureHasWorkfile(pyblish.api.InstancePlugin): @@ -17,4 +19,6 @@ class ValidateTextureHasWorkfile(pyblish.api.InstancePlugin): def process(self, instance): wfile = instance.data["versionData"].get("workfile") - assert wfile, "Textures are missing attached workfile" + msg = "Textures are missing attached workfile" + if not wfile: + raise PublishXmlValidationError(self, msg) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_name.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_name.py index f210be3631..751ad917ca 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_name.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_name.py @@ -1,6 +1,7 @@ import pyblish.api -import openpype.api +import openpype.api +from openpype.pipeline import PublishXmlValidationError class ValidateTextureBatchNaming(pyblish.api.InstancePlugin): """Validates that all instances had properly formatted name.""" @@ -16,12 +17,16 @@ class ValidateTextureBatchNaming(pyblish.api.InstancePlugin): if isinstance(file_name, list): file_name = file_name[0] - msg = "Couldnt find asset name in '{}'\n".format(file_name) + \ + msg = "Couldn't find asset name in '{}'\n".format(file_name) + \ "File name doesn't follow configured pattern.\n" + \ "Please rename the file." - assert "NOT_AVAIL" not in instance.data["asset_build"], msg - instance.data.pop("asset_build") + formatting_data = {"file_name": file_name} + if "NOT_AVAIL" in instance.data["asset_build"]: + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) + + instance.data.pop("asset_build") # not needed anymore if instance.data["family"] == "textures": file_name = instance.data["representations"][0]["files"][0] @@ -47,4 +52,10 @@ class ValidateTextureBatchNaming(pyblish.api.InstancePlugin): "Name of the texture file doesn't match expected pattern.\n" + \ "Please rename file(s) {}".format(file_name) - assert not missing_key_values, msg + missing_str = ','.join(["'{}'".format(key) + for key in missing_key_values]) + formatting_data = {"file_name": file_name, + "missing_str": missing_str} + if missing_key_values: + raise PublishXmlValidationError(self, msg, key="missing_values", + formatting_data=formatting_data) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_versions.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_versions.py index 90d0e8e512..84d9def895 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_versions.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_versions.py @@ -1,5 +1,7 @@ import pyblish.api + import openpype.api +from openpype.pipeline import PublishXmlValidationError class ValidateTextureBatchVersions(pyblish.api.InstancePlugin): @@ -25,14 +27,21 @@ class ValidateTextureBatchVersions(pyblish.api.InstancePlugin): self.log.info("No workfile present for textures") return - msg = "Not matching version: texture v{:03d} - workfile {}" - assert version_str in wfile, \ + if version_str not in wfile: + msg = "Not matching version: texture v{:03d} - workfile {}" msg.format( instance.data["version"], wfile ) + raise PublishXmlValidationError(self, msg) present_versions = set() for instance in instance.context: present_versions.add(instance.data["version"]) - assert len(present_versions) == 1, "Too many versions in a batch!" + if len(present_versions) != 1: + msg = "Too many versions in a batch!" + found = ','.join(["'{}'".format(val) for val in present_versions]) + formatting_data = {"found": found} + + raise PublishXmlValidationError(self, msg, key="too_many", + formatting_data=formatting_data) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_workfiles.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_workfiles.py index 25bb5aea4a..fa492a80d8 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_workfiles.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_workfiles.py @@ -1,11 +1,13 @@ import pyblish.api + import openpype.api +from openpype.pipeline import PublishXmlValidationError class ValidateTextureBatchWorkfiles(pyblish.api.InstancePlugin): """Validates that textures workfile has collected resources (optional). - Collected recourses means secondary workfiles (in most cases). + Collected resources means secondary workfiles (in most cases). """ label = "Validate Texture Workfile Has Resources" @@ -24,6 +26,13 @@ class ValidateTextureBatchWorkfiles(pyblish.api.InstancePlugin): self.log.warning("Only secondary workfile present!") return - msg = "No secondary workfiles present for workfile {}".\ - format(instance.data["name"]) - assert instance.data.get("resources"), msg + if not instance.data.get("resources"): + msg = "No secondary workfile present for workfile '{}'". \ + format(instance.data["name"]) + ext = self.main_workfile_extensions[0] + formatting_data = {"file_name": instance.data["name"], + "extension": ext} + + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data + ) From 830086516a772fc967379ba553961f16eda6bc43 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 7 Jan 2022 11:04:01 +0100 Subject: [PATCH 33/44] modified text of layers visibility validator exception --- .../plugins/publish/help/validate_layers_visibility.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml index 2eaed22a19..e7be735888 100644 --- a/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml @@ -4,7 +4,7 @@ Layers visiblity ## All layers are not visible -All layers for subset "{instance_name}" are hidden. +Layers visibility was changed during publishing which caused that all layers for subset "{instance_name}" are hidden. ### Layer names for **{instance_name}** @@ -14,7 +14,7 @@ All layers for subset "{instance_name}" are hidden. ### How to repair? -Make sure that at least one layer in the scene is visible or disable the subset before hitting publish button after refresh. +Reset publishing and do not change visibility of layers after hitting publish button. From 6fd45d99f9bcee675a3590d36396ca9b5acaea02 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 12 Jan 2022 18:10:17 +0100 Subject: [PATCH 34/44] Fix - wrong expression --- .../plugins/publish/validate_frame_ranges.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_frame_ranges.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_frame_ranges.py index c7a2e755b6..005157af62 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_frame_ranges.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_frame_ranges.py @@ -56,7 +56,7 @@ class ValidateFrameRange(pyblish.api.InstancePlugin): formatting_data = {"duration": duration, "found": frames} - if frames == duration: + if frames != duration: raise PublishXmlValidationError(self, msg, formatting_data=formatting_data) From edd0fb1ce9e06f374994ca37c401fffa30a29f9e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 13 Jan 2022 11:04:47 +0100 Subject: [PATCH 35/44] Fix - typo in key --- .../plugins/publish/validate_shot_duplicates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_shot_duplicates.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_shot_duplicates.py index 0f957acad6..fe655f6b74 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_shot_duplicates.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_shot_duplicates.py @@ -22,7 +22,7 @@ class ValidateShotDuplicates(pyblish.api.ContextPlugin): msg = "There are duplicate shot names:\n{}".format(duplicate_names) - formatting_data = {"duplicate_str": ','.join(duplicate_names)} + formatting_data = {"duplicates_str": ','.join(duplicate_names)} if duplicate_names: raise PublishXmlValidationError(self, msg, formatting_data=formatting_data) From ef695cb153e6bfd7676f08f294b7b75ce999fadb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 13 Jan 2022 11:09:03 +0100 Subject: [PATCH 36/44] Added new style validation for check for editorial resources --- .../help/validate_editorial_resources.xml | 17 +++++++++++++++++ .../publish/validate_editorial_resources.py | 7 +++++-- 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 openpype/hosts/standalonepublisher/plugins/publish/help/validate_editorial_resources.xml diff --git a/openpype/hosts/standalonepublisher/plugins/publish/help/validate_editorial_resources.xml b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_editorial_resources.xml new file mode 100644 index 0000000000..803de6bf11 --- /dev/null +++ b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_editorial_resources.xml @@ -0,0 +1,17 @@ + + + +Missing source video file + +## No attached video file found + +Process expects presence of source video file with same name prefix as an editorial file in same folder. +(example `simple_editorial_setup_Layer1.edl` expects `simple_editorial_setup.mp4` in same folder) + + +### How to repair? + +Copy source video file to the folder next to `.edl` file. (On a disk, do not put it into Standalone Publisher.) + + + diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_editorial_resources.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_editorial_resources.py index 6759b87ceb..7987bbc2d9 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_editorial_resources.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_editorial_resources.py @@ -1,5 +1,6 @@ import pyblish.api import openpype.api +from openpype.pipeline import PublishXmlValidationError class ValidateEditorialResources(pyblish.api.InstancePlugin): @@ -19,5 +20,7 @@ class ValidateEditorialResources(pyblish.api.InstancePlugin): f"Instance: {instance}, Families: " f"{[instance.data['family']] + instance.data['families']}") check_file = instance.data["editorialSourcePath"] - msg = f"Missing \"{check_file}\"." - assert check_file, msg + msg = f"Missing source video file." + + if not check_file: + raise PublishXmlValidationError(self, msg) From 5a5a172c3051a789ce0adbe94ab1e1c428fecea7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 17 Jan 2022 15:31:21 +0100 Subject: [PATCH 37/44] Update openpype/hosts/standalonepublisher/plugins/publish/validate_editorial_resources.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../plugins/publish/validate_editorial_resources.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_editorial_resources.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_editorial_resources.py index 7987bbc2d9..afb828474d 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_editorial_resources.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_editorial_resources.py @@ -20,7 +20,7 @@ class ValidateEditorialResources(pyblish.api.InstancePlugin): f"Instance: {instance}, Families: " f"{[instance.data['family']] + instance.data['families']}") check_file = instance.data["editorialSourcePath"] - msg = f"Missing source video file." + msg = "Missing source video file." if not check_file: raise PublishXmlValidationError(self, msg) From 9ab32bd634382a0f877b45487d7e89676f2f86e6 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 28 Feb 2022 11:39:14 +0100 Subject: [PATCH 38/44] 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 39/44] 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 f04ea594e1d86a86234cd3e39ab4c533956380db Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 1 Mar 2022 11:48:25 +0100 Subject: [PATCH 40/44] removed duplicated code --- .../tools/publisher/widgets/validations_widget.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index 0db30acd6e..798c1f9d92 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -203,17 +203,6 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): self._title_frame.setProperty("selected", value) self._title_frame.style().polish(self._title_frame) - def current_desctiption_text(self): - if self._context_validation: - return self._help_text_by_instance_id[None] - index = self._instances_view.currentIndex() - # TODO make sure instance is selected - if not index.isValid(): - index = self._instances_model.index(0, 0) - - indence_id = index.data(INSTANCE_ID_ROLE) - return self._help_text_by_instance_id[indence_id] - def set_selected(self, selected=None): """Change selected state of widget.""" if selected is None: @@ -557,9 +546,6 @@ class ValidationsWidget(QtWidgets.QWidget): self._previous_select = self._title_widgets[index] error_item = self._error_info[index] - self._actions_widget.set_plugin(error_item["plugin"]) - - self._update_description() self._actions_widget.set_plugin(error_item["plugin"]) From f65b202ae34d58981df916ac6ca7897ba303b69a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Mar 2022 13:23:09 +0100 Subject: [PATCH 41/44] 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 a0cd2870d6f3bd0b969f1e62b94a0fe5076eec9b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 14:18:17 +0100 Subject: [PATCH 42/44] recreate removed classes and functions --- openpype/pipeline/__init__.py | 2 + openpype/pipeline/publish/__init__.py | 12 +++-- openpype/pipeline/publish/lib.py | 56 ++++++++++++++++++++ openpype/pipeline/publish/publish_plugins.py | 26 ++++++++- 4 files changed, 92 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/__init__.py b/openpype/pipeline/__init__.py index e968df4011..79d6ce4d54 100644 --- a/openpype/pipeline/__init__.py +++ b/openpype/pipeline/__init__.py @@ -9,6 +9,7 @@ from .create import ( from .publish import ( PublishValidationError, + PublishXmlValidationError, KnownPublishError, OpenPypePyblishPluginMixin ) @@ -23,6 +24,7 @@ __all__ = ( "CreatedInstance", "PublishValidationError", + "PublishXmlValidationError", "KnownPublishError", "OpenPypePyblishPluginMixin" ) diff --git a/openpype/pipeline/publish/__init__.py b/openpype/pipeline/publish/__init__.py index ca958816fe..c2729a46ce 100644 --- a/openpype/pipeline/publish/__init__.py +++ b/openpype/pipeline/publish/__init__.py @@ -1,20 +1,26 @@ from .publish_plugins import ( PublishValidationError, + PublishXmlValidationError, KnownPublishError, - OpenPypePyblishPluginMixin + OpenPypePyblishPluginMixin, ) from .lib import ( DiscoverResult, - publish_plugins_discover + publish_plugins_discover, + load_help_content_from_plugin, + load_help_content_from_filepath, ) __all__ = ( "PublishValidationError", + "PublishXmlValidationError", "KnownPublishError", "OpenPypePyblishPluginMixin", "DiscoverResult", - "publish_plugins_discover" + "publish_plugins_discover", + "load_help_content_from_plugin", + "load_help_content_from_filepath", ) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index d3e4ec8a02..739b2c8806 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -1,6 +1,8 @@ import os import sys import types +import inspect +import xml.etree.ElementTree import six import pyblish.plugin @@ -28,6 +30,60 @@ class DiscoverResult: self.plugins[item] = value +class HelpContent: + def __init__(self, title, description, detail=None): + self.title = title + self.description = description + self.detail = detail + + +def load_help_content_from_filepath(filepath): + """Load help content from xml file. + Xml file may containt errors and warnings. + """ + errors = {} + warnings = {} + output = { + "errors": errors, + "warnings": warnings + } + if not os.path.exists(filepath): + return output + tree = xml.etree.ElementTree.parse(filepath) + root = tree.getroot() + for child in root: + child_id = child.attrib.get("id") + if child_id is None: + continue + + # Make sure ID is string + child_id = str(child_id) + + title = child.find("title").text + description = child.find("description").text + detail_node = child.find("detail") + detail = None + if detail_node is not None: + detail = detail_node.text + if child.tag == "error": + errors[child_id] = HelpContent(title, description, detail) + elif child.tag == "warning": + warnings[child_id] = HelpContent(title, description, detail) + return output + + +def load_help_content_from_plugin(plugin): + cls = plugin + if not inspect.isclass(plugin): + cls = plugin.__class__ + plugin_filepath = inspect.getfile(cls) + plugin_dir = os.path.dirname(plugin_filepath) + basename = os.path.splitext(os.path.basename(plugin_filepath))[0] + filename = basename + ".xml" + filepath = os.path.join(plugin_dir, "help", filename) + return load_help_content_from_filepath(filepath) + + def publish_plugins_discover(paths=None): """Find and return available pyblish plug-ins diff --git a/openpype/pipeline/publish/publish_plugins.py b/openpype/pipeline/publish/publish_plugins.py index b60b9f43a7..bce64ec709 100644 --- a/openpype/pipeline/publish/publish_plugins.py +++ b/openpype/pipeline/publish/publish_plugins.py @@ -1,3 +1,6 @@ +from .lib import load_help_content_from_plugin + + class PublishValidationError(Exception): """Validation error happened during publishing. @@ -12,13 +15,34 @@ class PublishValidationError(Exception): description(str): Detailed description of an error. It is possible to use Markdown syntax. """ - def __init__(self, message, title=None, description=None): + def __init__(self, message, title=None, description=None, detail=None): self.message = message self.title = title or "< Missing title >" self.description = description or message + self.detail = detail super(PublishValidationError, self).__init__(message) +class PublishXmlValidationError(PublishValidationError): + def __init__( + self, plugin, message, key=None, formatting_data=None + ): + if key is None: + key = "main" + + if not formatting_data: + formatting_data = {} + result = load_help_content_from_plugin(plugin) + content_obj = result["errors"][key] + description = content_obj.description.format(**formatting_data) + detail = content_obj.detail + if detail: + detail = detail.format(**formatting_data) + super(PublishXmlValidationError, self).__init__( + message, content_obj.title, description, detail + ) + + class KnownPublishError(Exception): """Publishing crashed because of known error. From bb01f66ba708f810ec11fcd85fbc2a550f53ed57 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 14:28:38 +0100 Subject: [PATCH 43/44] hound fixes --- .../hosts/houdini/plugins/publish/validate_vdb_output_node.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py b/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py index f6e54f3ae2..f672e78b5f 100644 --- a/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py +++ b/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py @@ -51,7 +51,6 @@ class ValidateVDBOutputNode(pyblish.api.InstancePlugin): key="wrongSOP", formatting_data=data ) - return [node.path()] invalid = self.get_invalid(instance) @@ -71,7 +70,8 @@ class ValidateVDBOutputNode(pyblish.api.InstancePlugin): frame = instance.data.get("frameStart", 0) geometry = output_node.geometryAtFrame(frame) if geometry is None: - # No geometry data on this output_node, maybe the node hasn't cooked? + # No geometry data on this output_node + # - maybe the node hasn't cooked? cls.log.debug( "SOP node has no geometry data. " "Is it cooked? %s" % output_node.path() From 0260e4f269e4b58932df08622bb1d3ac2c90e0a8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 14:30:47 +0100 Subject: [PATCH 44/44] add 2 spaces --- .../hosts/houdini/plugins/publish/validate_vdb_output_node.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py b/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py index f672e78b5f..0345f27d72 100644 --- a/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py +++ b/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py @@ -2,6 +2,8 @@ import pyblish.api import openpype.api from openpype.pipeline import PublishXmlValidationError import hou + + class ValidateVDBOutputNode(pyblish.api.InstancePlugin): """Validate that the node connected to the output node is of type VDB.