From a2b73014da7266280f65bc9c800ae264883da78e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 10 Apr 2024 11:14:08 +0200 Subject: [PATCH 01/44] mark known bare except handling with noqa --- client/ayon_core/hosts/blender/api/lib.py | 6 +++--- .../hosts/photoshop/plugins/create/create_image.py | 2 +- client/ayon_core/pipeline/create/context.py | 12 ++++++------ client/ayon_core/tools/adobe_webserver/app.py | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/client/ayon_core/hosts/blender/api/lib.py b/client/ayon_core/hosts/blender/api/lib.py index 458a275b51..32137f0fcd 100644 --- a/client/ayon_core/hosts/blender/api/lib.py +++ b/client/ayon_core/hosts/blender/api/lib.py @@ -33,7 +33,7 @@ def load_scripts(paths): if register: try: register() - except: + except: # noqa E722 traceback.print_exc() else: print("\nWarning! '%s' has no register function, " @@ -45,7 +45,7 @@ def load_scripts(paths): if unregister: try: unregister() - except: + except: # noqa E722 traceback.print_exc() def test_reload(mod): @@ -57,7 +57,7 @@ def load_scripts(paths): try: return importlib.reload(mod) - except: + except: # noqa E722 traceback.print_exc() def test_register(mod): diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py index 26f2469844..97543e96de 100644 --- a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py @@ -53,7 +53,7 @@ class ImageCreator(Creator): stub.select_layers(stub.get_layers()) try: group = stub.group_selected_layers(product_name_from_ui) - except: + except: # noqa E722 raise CreatorError("Cannot group locked Background layer!") groups_to_create.append(group) diff --git a/client/ayon_core/pipeline/create/context.py b/client/ayon_core/pipeline/create/context.py index ca9896fb3f..c223b52d03 100644 --- a/client/ayon_core/pipeline/create/context.py +++ b/client/ayon_core/pipeline/create/context.py @@ -2053,7 +2053,7 @@ class CreateContext: exc_info = sys.exc_info() self.log.warning(error_message.format(identifier, exc_info[1])) - except: + except: # noqa: E722 add_traceback = True exc_info = sys.exc_info() self.log.warning( @@ -2163,7 +2163,7 @@ class CreateContext: exc_info = sys.exc_info() self.log.warning(error_message.format(identifier, exc_info[1])) - except: + except: # noqa: E722 failed = True add_traceback = True exc_info = sys.exc_info() @@ -2197,7 +2197,7 @@ class CreateContext: try: convertor.find_instances() - except: + except: # noqa: E722 failed_info.append( prepare_failed_convertor_operation_info( convertor.identifier, sys.exc_info() @@ -2373,7 +2373,7 @@ class CreateContext: exc_info = sys.exc_info() self.log.warning(error_message.format(identifier, exc_info[1])) - except: + except: # noqa: E722 failed = True add_traceback = True exc_info = sys.exc_info() @@ -2440,7 +2440,7 @@ class CreateContext: error_message.format(identifier, exc_info[1]) ) - except: + except: # noqa: E722 failed = True add_traceback = True exc_info = sys.exc_info() @@ -2546,7 +2546,7 @@ class CreateContext: try: self.run_convertor(convertor_identifier) - except: + except: # noqa: E722 failed_info.append( prepare_failed_convertor_operation_info( convertor_identifier, sys.exc_info() diff --git a/client/ayon_core/tools/adobe_webserver/app.py b/client/ayon_core/tools/adobe_webserver/app.py index 7d97d7d66d..819cfa8084 100644 --- a/client/ayon_core/tools/adobe_webserver/app.py +++ b/client/ayon_core/tools/adobe_webserver/app.py @@ -109,7 +109,7 @@ class WebServerTool: try: sock.bind((host_name, port)) result = False - except: + except: # noqa E722 print("Port is in use") return result From 78a895b720fda253a37725ce30814ca85abafdb9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 10 Apr 2024 13:37:11 +0200 Subject: [PATCH 02/44] change port check logic --- client/ayon_core/tools/adobe_webserver/app.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/tools/adobe_webserver/app.py b/client/ayon_core/tools/adobe_webserver/app.py index 819cfa8084..6ea9745c59 100644 --- a/client/ayon_core/tools/adobe_webserver/app.py +++ b/client/ayon_core/tools/adobe_webserver/app.py @@ -104,14 +104,11 @@ class WebServerTool: again. In that case, use existing running webserver. Check here is easier than capturing exception from thread. """ - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - result = True - try: - sock.bind((host_name, port)) - result = False - except: # noqa E722 - print("Port is in use") + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as con: + result = con.connect_ex((host_name, port)) == 0 + if result: + print("Port is in use") return result def call(self, func): From e587ef53440e18649cce4642517705d538011cf2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 12 Apr 2024 11:03:34 +0200 Subject: [PATCH 03/44] log which port is in use Co-authored-by: Roy Nieterau --- client/ayon_core/tools/adobe_webserver/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/adobe_webserver/app.py b/client/ayon_core/tools/adobe_webserver/app.py index 6ea9745c59..26bf638c91 100644 --- a/client/ayon_core/tools/adobe_webserver/app.py +++ b/client/ayon_core/tools/adobe_webserver/app.py @@ -108,7 +108,7 @@ class WebServerTool: result = con.connect_ex((host_name, port)) == 0 if result: - print("Port is in use") + print(f"Port {port} is already in use") return result def call(self, func): From 8e87ef674daba9f3bb7ae6edc1fd89e617ee304e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 19 Apr 2024 20:32:36 +0200 Subject: [PATCH 04/44] Maya: Implement workfile template run script placeholder --- .../plugins/template/script_placeholder.py | 201 ++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 client/ayon_core/hosts/maya/plugins/template/script_placeholder.py diff --git a/client/ayon_core/hosts/maya/plugins/template/script_placeholder.py b/client/ayon_core/hosts/maya/plugins/template/script_placeholder.py new file mode 100644 index 0000000000..893e2ec0cb --- /dev/null +++ b/client/ayon_core/hosts/maya/plugins/template/script_placeholder.py @@ -0,0 +1,201 @@ +from maya import cmds + +from ayon_core.hosts.maya.api.workfile_template_builder import ( + MayaPlaceholderPlugin +) +from ayon_core.lib import NumberDef, TextDef, EnumDef +from ayon_core.lib.events import weakref_partial + + +EXAMPLE_SCRIPT = """ +# Access maya commands +from maya import cmds + +# Access the placeholder node +placeholder_node = placeholder.scene_identifier + +# Access the event callback +if event is None: + print(f"Populating {placeholder}") +else: + if event.topic == "template.depth_processed": + print(f"Processed depth: {event.get('depth')}") + elif event.topic == "template.finished": + print("Build finished.") +""".strip() + + +class MayaPlaceholderScriptPlugin(MayaPlaceholderPlugin): + """Execute a script at the given `order` during workfile build. + + This is a very low-level placeholder to run Python scripts at a given + point in time during the workfile template build. + + It can create either a locator or an objectSet as placeholder node. + It defaults to an objectSet, since allowing to run on e.g. other + placeholder node members can be useful, e.g. using: + + >>> members = cmds.sets(placeholder.scene_identifier, query=True) + + """ + + identifier = "maya.runscript" + label = "Run Python Script" + + use_selection_as_parent = False + + def get_placeholder_options(self, options=None): + options = options or {} + return [ + NumberDef( + "order", + label="Order", + default=options.get("order") or 0, + decimals=0, + minimum=0, + maximum=999, + tooltip=( + "Order" + "\nOrder defines asset loading priority (0 to 999)" + "\nPriority rule is : \"lowest is first to load\"." + ) + ), + TextDef( + "prepare_script", + label="Run at\nprepare", + tooltip="Run before populate at prepare order", + multiline=True, + default=options.get("prepare_script", "") + ), + TextDef( + "populate_script", + label="Run at\npopulate", + tooltip="Run script at populate node order
" + "This is the default behavior", + multiline=True, + default=options.get("populate_script", EXAMPLE_SCRIPT) + ), + TextDef( + "depth_processed_script", + label="Run after\ndepth\niteration", + tooltip="Run script after every build depth iteration", + multiline=True, + default=options.get("depth_processed_script", "") + ), + TextDef( + "finished_script", + label="Run after\nbuild", + tooltip=( + "Run script at build finished.
" + "Note: this even runs if other placeholders had " + "errors during the build" + ), + multiline=True, + default=options.get("finished_script", "") + ), + EnumDef( + "create_nodetype", + label="Nodetype", + items={ + "spaceLocator": "Locator", + "objectSet": "ObjectSet" + }, + tooltip=( + "The placeholder's node type to be created.
" + "Note this only works on create, not on update" + ), + default=options.get("create_nodetype", "objectSet") + ), + ] + + def create_placeholder(self, placeholder_data): + nodetype = placeholder_data.get("create_nodetype", "objectSet") + + if nodetype == "spaceLocator": + super(MayaPlaceholderScriptPlugin, self).create_placeholder( + placeholder_data + ) + elif nodetype == "objectSet": + placeholder_data["plugin_identifier"] = self.identifier + + # Create maya objectSet on selection + selection = cmds.ls(selection=True, long=True) + name = self._create_placeholder_name(placeholder_data) + node = cmds.sets(selection, name=name) + + self.imprint(node, placeholder_data) + + def prepare_placeholders(self, placeholders): + super(MayaPlaceholderScriptPlugin, self).prepare_placeholders( + placeholders + ) + for placeholder in placeholders: + prepare_script = placeholder.data.get("prepare_script") + if not prepare_script: + continue + + self.run_script(placeholder, prepare_script) + + def populate_placeholder(self, placeholder): + + populate_script = placeholder.data.get("populate_script") + depth_script = placeholder.data.get("depth_processed_script") + finished_script = placeholder.data.get("finished_script") + + # Run now + if populate_script: + self.run_script(placeholder, populate_script) + + if not any([depth_script, finished_script]): + # No callback scripts to run + if not placeholder.data.get("keep_placeholder", True): + self.delete_placeholder(placeholder) + return + + # Run at each depth processed + if depth_script: + callback = weakref_partial( + self.run_script, placeholder, depth_script) + self.builder.register_on_depth_processed_callback( + callback, order=placeholder.order) + + # Run at build finish + if finished_script: + callback = weakref_partial( + self.run_script, placeholder, finished_script) + self.builder.register_on_finished_callback( + callback, order=placeholder.order) + + # If placeholder should be deleted, delete it after finish so + # the scripts have access to it up to the last run + if not placeholder.data.get("keep_placeholder", True): + delete_callback = weakref_partial( + self.delete_placeholder, placeholder) + self.builder.register_on_finished_callback( + delete_callback, order=placeholder.order + 1) + + def run_script(self, placeholder, script, event=None): + """Run script + + Even though `placeholder` is an unused arguments by exposing it as + an input argument it means it makes it available through + globals()/locals() in the `exec` call, giving the script access + to the placeholder. + + For example: + >>> node = placeholder.scene_identifier + + In the case the script is running at a callback level (not during + populate) then it has access to the `event` as well, otherwise the + value is None if it runs during `populate_placeholder` directly. + + For example adding this as the callback script: + >>> if event is not None: + >>> if event.topic == "on_depth_processed": + >>> print(f"Processed depth: {event.get('depth')}") + >>> elif event.topic == "on_finished": + >>> print("Build finished.") + + """ + self.log.debug(f"Running script at event: {event}") + exec(script, locals()) From 7cf7e33452b22499db077876343af210913b8dd2 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 19 Apr 2024 22:35:57 +0200 Subject: [PATCH 05/44] Refactor adding callbacks --- .../hosts/maya/plugins/workfile_build/script_placeholder.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/workfile_build/script_placeholder.py b/client/ayon_core/hosts/maya/plugins/workfile_build/script_placeholder.py index 893e2ec0cb..62e10ba023 100644 --- a/client/ayon_core/hosts/maya/plugins/workfile_build/script_placeholder.py +++ b/client/ayon_core/hosts/maya/plugins/workfile_build/script_placeholder.py @@ -156,14 +156,14 @@ class MayaPlaceholderScriptPlugin(MayaPlaceholderPlugin): if depth_script: callback = weakref_partial( self.run_script, placeholder, depth_script) - self.builder.register_on_depth_processed_callback( + self.builder.add_on_depth_processed_callback( callback, order=placeholder.order) # Run at build finish if finished_script: callback = weakref_partial( self.run_script, placeholder, finished_script) - self.builder.register_on_finished_callback( + self.builder.add_on_finished_callback( callback, order=placeholder.order) # If placeholder should be deleted, delete it after finish so @@ -171,7 +171,7 @@ class MayaPlaceholderScriptPlugin(MayaPlaceholderPlugin): if not placeholder.data.get("keep_placeholder", True): delete_callback = weakref_partial( self.delete_placeholder, placeholder) - self.builder.register_on_finished_callback( + self.builder.add_on_finished_callback( delete_callback, order=placeholder.order + 1) def run_script(self, placeholder, script, event=None): From 13fbc5b74f3bc83157bd927ab6d5ff0cd69b9c5f Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:13:37 +0200 Subject: [PATCH 06/44] added folder type to template data --- client/ayon_core/pipeline/template_data.py | 5 ++-- .../publish/collect_anatomy_instance_data.py | 28 +++++++------------ 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/client/ayon_core/pipeline/template_data.py b/client/ayon_core/pipeline/template_data.py index 526c7d35c5..02bccb5f49 100644 --- a/client/ayon_core/pipeline/template_data.py +++ b/client/ayon_core/pipeline/template_data.py @@ -73,8 +73,8 @@ def get_folder_template_data(folder_entity, project_name): - 'parent' - direct parent name, project name used if is under project - Required document fields: - Folder: 'path' -> Plan to require: 'folderType' + Required entity fields: + Folder: 'path', 'folderType' Args: folder_entity (Dict[str, Any]): Folder entity. @@ -101,6 +101,7 @@ def get_folder_template_data(folder_entity, project_name): return { "folder": { "name": folder_name, + "type": folder_entity["folderType"], }, "asset": folder_name, "hierarchy": hierarchy, diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index f8cc81e718..f0119ef42e 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -33,6 +33,7 @@ import collections import pyblish.api import ayon_api +from ayon_core.pipeline.template_data import get_folder_template_data from ayon_core.pipeline.version_start import get_versioning_start @@ -383,24 +384,11 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): # - 'folder', 'hierarchy', 'parent', 'folder' folder_entity = instance.data.get("folderEntity") if folder_entity: - folder_name = folder_entity["name"] - folder_path = folder_entity["path"] - hierarchy_parts = folder_path.split("/") - hierarchy_parts.pop(0) - hierarchy_parts.pop(-1) - parent_name = project_entity["name"] - if hierarchy_parts: - parent_name = hierarchy_parts[-1] - - hierarchy = "/".join(hierarchy_parts) - anatomy_data.update({ - "asset": folder_name, - "hierarchy": hierarchy, - "parent": parent_name, - "folder": { - "name": folder_name, - }, - }) + folder_data = get_folder_template_data( + folder_entity, + project_entity["name"] + ) + anatomy_data.update(folder_data) return if instance.data.get("newAssetPublishing"): @@ -418,6 +406,10 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): "parent": parent_name, "folder": { "name": folder_name, + # TODO get folder type from hierarchy + # Using 'Shot' is current default behavior of editorial + # (or 'newAssetPublishing') publishing. + "type": "Shot", }, }) From e194652f65d122f769c028e3df5aca539f186018 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:59:16 +0200 Subject: [PATCH 07/44] add path to folder keys --- client/ayon_core/pipeline/template_data.py | 1 + .../ayon_core/plugins/publish/collect_anatomy_instance_data.py | 1 + 2 files changed, 2 insertions(+) diff --git a/client/ayon_core/pipeline/template_data.py b/client/ayon_core/pipeline/template_data.py index 02bccb5f49..d5f06d6a59 100644 --- a/client/ayon_core/pipeline/template_data.py +++ b/client/ayon_core/pipeline/template_data.py @@ -102,6 +102,7 @@ def get_folder_template_data(folder_entity, project_name): "folder": { "name": folder_name, "type": folder_entity["folderType"], + "path": path, }, "asset": folder_name, "hierarchy": hierarchy, diff --git a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py index f0119ef42e..ad5a5d43fc 100644 --- a/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py +++ b/client/ayon_core/plugins/publish/collect_anatomy_instance_data.py @@ -406,6 +406,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): "parent": parent_name, "folder": { "name": folder_name, + "path": instance.data["folderPath"], # TODO get folder type from hierarchy # Using 'Shot' is current default behavior of editorial # (or 'newAssetPublishing') publishing. From 9be28b8051124fd8d699b665299c6eeb98cbc63b Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:00:16 +0200 Subject: [PATCH 08/44] Fix `publish_attributes` access --- .../plugins/publish/validate_alembic_options_defaults.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py index 5197100406..a9de510e67 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py @@ -29,13 +29,7 @@ class ValidateAlembicDefaultsPointcache( @classmethod def _get_publish_attributes(cls, instance): - attributes = instance.data["publish_attributes"][ - cls.plugin_name( - instance.data["publish_attributes"] - ) - ] - - return attributes + return instance.data["publish_attributes"][cls.plugin_name] def process(self, instance): if not self.is_active(instance.data): From b05cac07b8b9cb596aa090c77b0425a216cadd65 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:01:52 +0200 Subject: [PATCH 09/44] Fix repair logic --- .../maya/plugins/publish/validate_alembic_options_defaults.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py index a9de510e67..940e4f3869 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py @@ -69,13 +69,11 @@ class ValidateAlembicDefaultsPointcache( ) # Set the settings values on the create context then save to workfile. - publish_attributes = instance.data["publish_attributes"] - plugin_name = cls.plugin_name(publish_attributes) attributes = cls._get_publish_attributes(instance) settings = cls._get_settings(instance.context) create_publish_attributes = create_instance.data["publish_attributes"] for key in attributes: - create_publish_attributes[plugin_name][key] = settings[key] + create_publish_attributes[cls.plugin_name][key] = settings[key] create_context.save_changes() From 8567e54bb45547bd6b6a31d05123d5c80af626fe Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:04:33 +0200 Subject: [PATCH 10/44] Fix label --- .../maya/plugins/publish/validate_alembic_options_defaults.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py index 940e4f3869..1045ef3c70 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py @@ -85,6 +85,6 @@ class ValidateAlembicDefaultsAnimation( The defaults are defined in the project settings. """ - label = "Validate Alembic Options Defaults" + label = "Validate Alembic Options Defaults" families = ["animation"] plugin_name = "ExtractAnimation" From 665a4e226a6cc42039afe938b57ea33d269592d4 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:12:17 +0200 Subject: [PATCH 11/44] Fix `writeCreases` support --- .../hosts/maya/plugins/publish/extract_pointcache.py | 8 ++++++++ server_addon/maya/server/settings/publishers.py | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py b/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py index d7f9594374..d34634bff8 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py @@ -63,6 +63,7 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin): wholeFrameGeo = False worldSpace = True writeColorSets = False + writeCreases = False writeFaceSets = False writeNormals = True writeUVSets = False @@ -354,6 +355,13 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin): default=cls.writeColorSets, tooltip="Write vertex colors with the geometry." ), + "writeCreases": BoolDef( + "writeCreases", + label="Write Creases", + default=cls.writeCreases, + tooltip="Write the geometry's edge and vertex crease " + "information." + ), "writeFaceSets": BoolDef( "writeFaceSets", label="Write Face Sets", diff --git a/server_addon/maya/server/settings/publishers.py b/server_addon/maya/server/settings/publishers.py index 8dcffbb59a..ee74eaa553 100644 --- a/server_addon/maya/server/settings/publishers.py +++ b/server_addon/maya/server/settings/publishers.py @@ -406,6 +406,10 @@ class ExtractAlembicModel(BaseSettingsModel): title="Write Color Sets", description="Write vertex colors with the geometry." ) + writeCreases: bool = SettingsField( + title="Write Creases", + description="Write the geometry's edge and vertex crease information." + ) writeFaceSets: bool = SettingsField( title="Write Face Sets", description="Write face sets with the geometry." @@ -1643,6 +1647,7 @@ DEFAULT_PUBLISH_SETTINGS = { "wholeFrameGeo": False, "worldSpace": True, "writeColorSets": False, + "writeCreases": False, "writeFaceSets": False, "writeNormals": True, "writeUVSets": False, From 141767ef717cf8b15baec850de71f0dc2f013871 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:13:49 +0200 Subject: [PATCH 12/44] Cosmetics --- .../maya/plugins/publish/validate_alembic_options_defaults.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py index 1045ef3c70..476f837135 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py @@ -36,7 +36,6 @@ class ValidateAlembicDefaultsPointcache( return settings = self._get_settings(instance.context) - attributes = self._get_publish_attributes(instance) msg = ( From 0352c17a09108cfcf768aac58da2fef5c67cf12a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:18:20 +0200 Subject: [PATCH 13/44] Add support for extracting the user attributes --- client/ayon_core/hosts/maya/api/alembic.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/ayon_core/hosts/maya/api/alembic.py b/client/ayon_core/hosts/maya/api/alembic.py index bf887df4c7..954c0a0888 100644 --- a/client/ayon_core/hosts/maya/api/alembic.py +++ b/client/ayon_core/hosts/maya/api/alembic.py @@ -63,6 +63,8 @@ def extract_alembic( startFrame=None, step=1.0, stripNamespaces=True, + userAttr=None, + userAttrPrefix=None, uvWrite=True, verbose=False, wholeFrameGeo=False, @@ -137,6 +139,12 @@ def extract_alembic( object with the namespace taco:foo:bar appears as bar in the Alembic file. + userAttr (list of str, optional): A specific user defined attribute to + write out. Defaults to []. + + userAttrPrefix (list of str, optional): Prefix filter for determining + which user defined attributes to write out. Defaults to []. + uvWrite (bool): When on, UV data from polygon meshes and subdivision objects are written to the Alembic file. Only the current UV map is included. @@ -183,6 +191,8 @@ def extract_alembic( # Ensure list arguments are valid. attr = attr or [] attrPrefix = attrPrefix or [] + userAttr = userAttr or [] + userAttrPrefix = userAttrPrefix or [] root = root or [] # Pass the start and end frame on as `frameRange` so that it @@ -226,6 +236,8 @@ def extract_alembic( "step": step, "attr": attr, "attrPrefix": attrPrefix, + "userAttr": userAttr, + "userAttrPrefix": userAttrPrefix, "stripNamespaces": stripNamespaces, "verbose": verbose, "preRollStartFrame": preRollStartFrame From 367eba0f493d196525a308bac3085d87772553d9 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:25:00 +0200 Subject: [PATCH 14/44] Remove `autoSubd` in favor of legacy `writeCreases` usage across the codebase and existing instances --- .../plugins/publish/extract_pointcache.py | 20 +------------------ .../maya/server/settings/publishers.py | 13 ------------ 2 files changed, 1 insertion(+), 32 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py b/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py index d34634bff8..cff32ebb67 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py @@ -40,7 +40,6 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin): # From settings attr = [] attrPrefix = [] - autoSubd = False bake_attributes = [] bake_attribute_prefixes = [] dataFormat = "ogawa" @@ -174,9 +173,6 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin): "writeVisibility": attribute_values.get( "writeVisibility", self.writeVisibility ), - "autoSubd": attribute_values.get( - "autoSubd", self.autoSubd - ), "uvsOnly": attribute_values.get( "uvsOnly", self.uvsOnly ), @@ -250,7 +246,7 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin): with maintained_selection(): cmds.select(instance.data["proxy"]) extract_alembic(**kwargs) - + raise RuntimeError("FAIL") representation = { "name": "proxy", "ext": "abc", @@ -269,20 +265,6 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin): return [] override_defs = OrderedDict({ - "autoSubd": BoolDef( - "autoSubd", - label="Auto Subd", - default=cls.autoSubd, - tooltip=( - "If this flag is present and the mesh has crease edges, " - "crease vertices or holes, the mesh (OPolyMesh) would now " - "be written out as an OSubD and crease info will be stored" - " in the Alembic file. Otherwise, creases info won't be " - "preserved in Alembic file unless a custom Boolean " - "attribute SubDivisionMesh has been added to mesh node and" - " its value is true." - ) - ), "eulerFilter": BoolDef( "eulerFilter", label="Euler Filter", diff --git a/server_addon/maya/server/settings/publishers.py b/server_addon/maya/server/settings/publishers.py index ee74eaa553..3a82d649e1 100644 --- a/server_addon/maya/server/settings/publishers.py +++ b/server_addon/maya/server/settings/publishers.py @@ -46,7 +46,6 @@ def extract_alembic_overrides_enum(): return [ {"label": "Custom Attributes", "value": "attr"}, {"label": "Custom Attributes Prefix", "value": "attrPrefix"}, - {"label": "Auto Subd", "value": "autoSubd"}, {"label": "Data Format", "value": "dataFormat"}, {"label": "Euler Filter", "value": "eulerFilter"}, {"label": "Mel Per Frame Callback", "value": "melPerFrameCallback"}, @@ -344,17 +343,6 @@ class ExtractAlembicModel(BaseSettingsModel): families: list[str] = SettingsField( default_factory=list, title="Families") - autoSubd: bool = SettingsField( - title="Auto Subd", - description=( - "If this flag is present and the mesh has crease edges, crease " - "vertices or holes, the mesh (OPolyMesh) would now be written out " - "as an OSubD and crease info will be stored in the Alembic file. " - "Otherwise, creases info won't be preserved in Alembic file unless" - " a custom Boolean attribute SubDivisionMesh has been added to " - "mesh node and its value is true." - ) - ) eulerFilter: bool = SettingsField( title="Euler Filter", description="Apply Euler filter while sampling rotations." @@ -1615,7 +1603,6 @@ DEFAULT_PUBLISH_SETTINGS = { ], "attr": "", "attrPrefix": "", - "autoSubd": False, "bake_attributes": [], "bake_attribute_prefixes": [], "dataFormat": "ogawa", From 26eb91781365c44036b02e8644f3fb2774e78c5a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:26:53 +0200 Subject: [PATCH 15/44] Add `uvsOnly` argument --- client/ayon_core/hosts/maya/api/alembic.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/ayon_core/hosts/maya/api/alembic.py b/client/ayon_core/hosts/maya/api/alembic.py index 954c0a0888..a815dc7465 100644 --- a/client/ayon_core/hosts/maya/api/alembic.py +++ b/client/ayon_core/hosts/maya/api/alembic.py @@ -65,6 +65,7 @@ def extract_alembic( stripNamespaces=True, userAttr=None, userAttrPrefix=None, + uvsOnly=False, uvWrite=True, verbose=False, wholeFrameGeo=False, @@ -145,6 +146,9 @@ def extract_alembic( userAttrPrefix (list of str, optional): Prefix filter for determining which user defined attributes to write out. Defaults to []. + uvsOnly (bool): When on, only uv data for PolyMesh and SubD shapes + will be written to the Alembic file. + uvWrite (bool): When on, UV data from polygon meshes and subdivision objects are written to the Alembic file. Only the current UV map is included. @@ -225,6 +229,7 @@ def extract_alembic( "preRoll": preRoll, "renderableOnly": renderableOnly, "uvWrite": uvWrite, + "uvsOnly": uvsOnly, "writeColorSets": writeColorSets, "writeFaceSets": writeFaceSets, "wholeFrameGeo": wholeFrameGeo, From e43a4ba481e7f307ec114cae2b735dfbdbfae704 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:28:27 +0200 Subject: [PATCH 16/44] Fix usage of `root` argument --- client/ayon_core/hosts/maya/api/alembic.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/hosts/maya/api/alembic.py b/client/ayon_core/hosts/maya/api/alembic.py index a815dc7465..3722774bba 100644 --- a/client/ayon_core/hosts/maya/api/alembic.py +++ b/client/ayon_core/hosts/maya/api/alembic.py @@ -227,6 +227,7 @@ def extract_alembic( "eulerFilter": eulerFilter, "noNormals": noNormals, "preRoll": preRoll, + "root": root, "renderableOnly": renderableOnly, "uvWrite": uvWrite, "uvsOnly": uvsOnly, From 828f7bbce2038a1a38a6fbde66d71fc8143083ae Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:37:42 +0200 Subject: [PATCH 17/44] Fix `writeNormals` usage --- .../hosts/maya/plugins/publish/extract_pointcache.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py b/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py index cff32ebb67..3c0350ec65 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py @@ -176,9 +176,6 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin): "uvsOnly": attribute_values.get( "uvsOnly", self.uvsOnly ), - "writeNormals": attribute_values.get( - "writeNormals", self.writeNormals - ), "melPerFrameCallback": attribute_values.get( "melPerFrameCallback", self.melPerFrameCallback ), @@ -190,7 +187,12 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin): ), "pythonPostJobCallback": attribute_values.get( "pythonPostJobCallback", self.pythonPostJobCallback - ) + ), + # Note that this converts `writeNormals` to `noNormals` for the + # `AbcExport` equivalent in `extract_alembic` + "noNormals": not attribute_values.get( + "writeNormals", self.writeNormals + ), } if instance.data.get("visibleOnly", False): From 5193d2664fed856399690869572f6a33dbb9f94a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:38:01 +0200 Subject: [PATCH 18/44] Fix passing of callbacks --- client/ayon_core/hosts/maya/api/alembic.py | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/client/ayon_core/hosts/maya/api/alembic.py b/client/ayon_core/hosts/maya/api/alembic.py index 3722774bba..e9bf6d71ca 100644 --- a/client/ayon_core/hosts/maya/api/alembic.py +++ b/client/ayon_core/hosts/maya/api/alembic.py @@ -54,9 +54,13 @@ def extract_alembic( endFrame=None, eulerFilter=True, frameRange="", + melPerFrameCallback=None, + melPostJobCallback=None, noNormals=False, preRoll=False, preRollStartFrame=0, + pythonPerFrameCallback=None, + pythonPostJobCallback=None, renderableOnly=False, root=None, selection=True, @@ -105,6 +109,11 @@ def extract_alembic( string formatted as: "startFrame endFrame". This argument overrides `startFrame` and `endFrame` arguments. + melPerFrameCallback (Optional[str]): MEL callback run per frame. + + melPostJobCallback (Optional[str]): MEL callback after last frame is + written. + noNormals (bool): When on, normal data from the original polygon objects is not included in the exported Alembic cache file. @@ -116,6 +125,11 @@ def extract_alembic( dependent translations and can be used to evaluate run-up that isn't actually translated. Defaults to 0. + pythonPerFrameCallback (Optional[str]): Python callback run per frame. + + pythonPostJobCallback (Optional[str]): Python callback after last frame + is written. + renderableOnly (bool): When on, any non-renderable nodes or hierarchy, such as hidden objects, are not included in the Alembic file. Defaults to False. @@ -282,6 +296,17 @@ def extract_alembic( if maya_version >= 2018: options['autoSubd'] = options.pop('writeCreases', False) + # Only add callbacks if they are set so that we're not passing `None` + callbacks = { + "melPerFrameCallback": melPerFrameCallback, + "melPostJobCallback": melPostJobCallback, + "pythonPerFrameCallback": pythonPerFrameCallback, + "pythonPostJobCallback": pythonPostJobCallback, + } + for key, callback in callbacks.items(): + if callback: + options[key] = str(callback) + # Format the job string from options job_args = list() for key, value in options.items(): From ba50a64a3842930ef77cf985093f139aa4cc1961 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:39:45 +0200 Subject: [PATCH 19/44] Remove debugging error --- .../ayon_core/hosts/maya/plugins/publish/extract_pointcache.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py b/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py index 3c0350ec65..05deaa0834 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py @@ -248,7 +248,6 @@ class ExtractAlembic(publish.Extractor, AYONPyblishPluginMixin): with maintained_selection(): cmds.select(instance.data["proxy"]) extract_alembic(**kwargs) - raise RuntimeError("FAIL") representation = { "name": "proxy", "ext": "abc", From 02ccceaf24caaa8ca52c447a21065001029fc57f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 16:48:36 +0200 Subject: [PATCH 20/44] Bump Maya addon version --- server_addon/maya/package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/maya/package.py b/server_addon/maya/package.py index 5c6ce923aa..fe3e3039f5 100644 --- a/server_addon/maya/package.py +++ b/server_addon/maya/package.py @@ -1,3 +1,3 @@ name = "maya" title = "Maya" -version = "0.1.17" +version = "0.1.18" From 2bf6ed71156d912aa2ce4329e6091761fc47fd5b Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 17:18:23 +0200 Subject: [PATCH 21/44] Do not modify `roots` in-place so that `roots` remains the roots and doesn't contain the descendendants + ignore intermediate objects since they would not be exported anyway --- .../hosts/maya/plugins/publish/extract_pointcache.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py b/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py index 05deaa0834..cc930e49cc 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_pointcache.py @@ -6,6 +6,7 @@ from maya import cmds from ayon_core.pipeline import publish from ayon_core.hosts.maya.api.alembic import extract_alembic from ayon_core.hosts.maya.api.lib import ( + get_all_children, suspended_refresh, maintained_selection, iter_visible_nodes_in_range @@ -518,9 +519,7 @@ class ExtractAnimation(ExtractAlembic): roots = cmds.sets(out_set, query=True) or [] # Include all descendants - nodes = roots - nodes += cmds.listRelatives( - roots, allDescendents=True, fullPath=True - ) or [] + nodes = roots.copy() + nodes.extend(get_all_children(roots, ignore_intermediate_objects=True)) return nodes, roots From 1513660b7e842f31bc33bacf464e44ea0edc62b4 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 22:51:36 +0200 Subject: [PATCH 22/44] Pass `preRollStartFrame` as separate argument --- client/ayon_core/hosts/maya/api/alembic.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/hosts/maya/api/alembic.py b/client/ayon_core/hosts/maya/api/alembic.py index e9bf6d71ca..6bd00e1cb1 100644 --- a/client/ayon_core/hosts/maya/api/alembic.py +++ b/client/ayon_core/hosts/maya/api/alembic.py @@ -22,7 +22,6 @@ ALEMBIC_ARGS = { "melPostJobCallback": str, "noNormals": bool, "preRoll": bool, - "preRollStartFrame": int, "pythonPerFrameCallback": str, "pythonPostJobCallback": str, "renderableOnly": bool, @@ -259,8 +258,7 @@ def extract_alembic( "userAttr": userAttr, "userAttrPrefix": userAttrPrefix, "stripNamespaces": stripNamespaces, - "verbose": verbose, - "preRollStartFrame": preRollStartFrame + "verbose": verbose } # Validate options @@ -340,7 +338,11 @@ def extract_alembic( # exports are made. (PLN-31) # TODO: Make sure this actually fixes the issues with evaluation("off"): - cmds.AbcExport(j=job_str, verbose=verbose) + cmds.AbcExport( + j=job_str, + verbose=verbose, + preRollStartFrame=preRollStartFrame + ) if verbose: log.debug("Extracted Alembic to: %s", file) From 0e23615cbd037af3638972bf9694ebaa674989bd Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 1 May 2024 22:54:45 +0200 Subject: [PATCH 23/44] Enable uvWrite in settings by default --- server_addon/maya/server/settings/publishers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/maya/server/settings/publishers.py b/server_addon/maya/server/settings/publishers.py index 3a82d649e1..c3983e0067 100644 --- a/server_addon/maya/server/settings/publishers.py +++ b/server_addon/maya/server/settings/publishers.py @@ -1626,7 +1626,7 @@ DEFAULT_PUBLISH_SETTINGS = { "renderableOnly": False, "stripNamespaces": True, "uvsOnly": False, - "uvWrite": False, + "uvWrite": True, "userAttr": "", "userAttrPrefix": "", "verbose": False, From 99e5cf99602c3b2321445776bf63e6cefe85a19c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 3 May 2024 16:49:23 +0200 Subject: [PATCH 24/44] Tweak the validation report --- .../validate_alembic_options_defaults.py | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py index 476f837135..c19bc6a0f4 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py @@ -1,3 +1,4 @@ +import inspect import pyblish.api from ayon_core.pipeline import OptionalPyblishPluginMixin @@ -39,11 +40,11 @@ class ValidateAlembicDefaultsPointcache( attributes = self._get_publish_attributes(instance) msg = ( - "Alembic Extract setting \"{}\" is not the default value:" - "\nCurrent: {}" - "\nDefault Value: {}\n" + "Alembic extract setting \"{}\" is not the default value:" + "\n- Current: {}" + "\n- Default: {}\n" ) - errors = [] + invalid = False for key, value in attributes.items(): default_value = settings[key] @@ -54,10 +55,32 @@ class ValidateAlembicDefaultsPointcache( default_value = sorted(default_value) if value != default_value: - errors.append(msg.format(key, value, default_value)) + self.log.error( + msg.format(key, value, default_value) + ) + invalid = True - if errors: - raise PublishValidationError("\n".join(errors)) + if invalid: + raise PublishValidationError( + "Detected alembic options that differ from the default value.", + description=self.get_description() + ) + + @staticmethod + def get_description(): + return inspect.cleandoc( + """### Alembic Extract settings differ from defaults + + The alembic export options differ from the project default values. + + If this is intentional you can disable this validation by + disabling **Validate Alembic Options Default**. + + If not you may use the "Repair" action to revert all the options to + their default values. + + """ + ) @classmethod def repair(cls, instance): From 4ac8123cb92c0220781b5023b0616d01ce68e042 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 3 May 2024 16:57:17 +0200 Subject: [PATCH 25/44] Simplify messaging --- .../validate_alembic_options_defaults.py | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py index c19bc6a0f4..800c05c8a6 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py @@ -39,12 +39,7 @@ class ValidateAlembicDefaultsPointcache( settings = self._get_settings(instance.context) attributes = self._get_publish_attributes(instance) - msg = ( - "Alembic extract setting \"{}\" is not the default value:" - "\n- Current: {}" - "\n- Default: {}\n" - ) - invalid = False + invalid = {} for key, value in attributes.items(): default_value = settings[key] @@ -55,14 +50,17 @@ class ValidateAlembicDefaultsPointcache( default_value = sorted(default_value) if value != default_value: - self.log.error( - msg.format(key, value, default_value) - ) - invalid = True + invalid[key] = value, default_value if invalid: + non_defaults = "\n".join( + f"- {key}: {value} \t(default: {default_value})" + for key, (value, default_value) in invalid.items() + ) + raise PublishValidationError( - "Detected alembic options that differ from the default value.", + "Alembic extract options differ from default values:\n" + f"{non_defaults}", description=self.get_description() ) From b4b1e2af4cab5b442ce84592f97b9c27b51b16b3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 3 May 2024 17:02:26 +0200 Subject: [PATCH 26/44] Ignore attributes not found in settings with a warning --- .../publish/validate_alembic_options_defaults.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py index 800c05c8a6..0abb734c9b 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py @@ -41,6 +41,17 @@ class ValidateAlembicDefaultsPointcache( invalid = {} for key, value in attributes.items(): + if key not in settings: + # This may occur if attributes have changed over time and an + # existing instance has older legacy attributes that do not + # match the current settings definition. + self.log.warning( + "Publish attribute %s not found in Alembic Export " + "default settings. Ignoring validation for attribute.", + key + ) + continue + default_value = settings[key] # Lists are best to compared sorted since we cant rely on the order From bd1f7dce4a55407c3e200b84a64083957b9b3234 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 6 May 2024 23:52:18 +0200 Subject: [PATCH 27/44] Fix repair for instances with older publish attributes that mismatch settings --- .../publish/validate_alembic_options_defaults.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py index 0abb734c9b..11f4c313fa 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_alembic_options_defaults.py @@ -100,11 +100,20 @@ class ValidateAlembicDefaultsPointcache( ) # Set the settings values on the create context then save to workfile. - attributes = cls._get_publish_attributes(instance) settings = cls._get_settings(instance.context) - create_publish_attributes = create_instance.data["publish_attributes"] + attributes = cls._get_publish_attributes(create_instance) for key in attributes: - create_publish_attributes[cls.plugin_name][key] = settings[key] + if key not in settings: + # This may occur if attributes have changed over time and an + # existing instance has older legacy attributes that do not + # match the current settings definition. + cls.log.warning( + "Publish attribute %s not found in Alembic Export " + "default settings. Ignoring repair for attribute.", + key + ) + continue + attributes[key] = settings[key] create_context.save_changes() From a3c481732fc705ff65b9504f55472ff411d4207c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 May 2024 12:49:21 +0200 Subject: [PATCH 28/44] Update proper exception to tame linter Generic exception will be most likely JSON broken response, which is caught in ValueError in ws_stub --- .../hosts/photoshop/plugins/create/create_image.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py index 97543e96de..e18644d038 100644 --- a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py @@ -35,7 +35,10 @@ class ImageCreator(Creator): create_empty_group = False stub = api.stub() # only after PS is up - top_level_selected_items = stub.get_selected_layers() + try: + top_level_selected_items = stub.get_selected_layers() + except ValueError: + raise CreatorError("Cannot group locked Background layer!") if pre_create_data.get("use_selection"): only_single_item_selected = len(top_level_selected_items) == 1 if ( @@ -51,10 +54,8 @@ class ImageCreator(Creator): groups_to_create.append(group) else: stub.select_layers(stub.get_layers()) - try: - group = stub.group_selected_layers(product_name_from_ui) - except: # noqa E722 - raise CreatorError("Cannot group locked Background layer!") + group = stub.group_selected_layers(product_name_from_ui) + groups_to_create.append(group) # create empty group if nothing selected From f1afd1653e9f4afd8f9f2ca1564684c7f8d9a8cb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 May 2024 12:55:18 +0200 Subject: [PATCH 29/44] Fix exception even for different use case --- .../photoshop/plugins/create/create_image.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py index e18644d038..a44c3490c6 100644 --- a/client/ayon_core/hosts/photoshop/plugins/create/create_image.py +++ b/client/ayon_core/hosts/photoshop/plugins/create/create_image.py @@ -35,11 +35,12 @@ class ImageCreator(Creator): create_empty_group = False stub = api.stub() # only after PS is up - try: - top_level_selected_items = stub.get_selected_layers() - except ValueError: - raise CreatorError("Cannot group locked Background layer!") if pre_create_data.get("use_selection"): + try: + top_level_selected_items = stub.get_selected_layers() + except ValueError: + raise CreatorError("Cannot group locked Background layer!") + only_single_item_selected = len(top_level_selected_items) == 1 if ( only_single_item_selected or @@ -53,8 +54,11 @@ class ImageCreator(Creator): group = stub.group_selected_layers(product_name_from_ui) groups_to_create.append(group) else: - stub.select_layers(stub.get_layers()) - group = stub.group_selected_layers(product_name_from_ui) + try: + stub.select_layers(stub.get_layers()) + group = stub.group_selected_layers(product_name_from_ui) + except ValueError: + raise CreatorError("Cannot group locked Background layer!") groups_to_create.append(group) From ab1b49b988183d0de95db27a249e89cfb59ed364 Mon Sep 17 00:00:00 2001 From: Braden Jennings Date: Thu, 29 Feb 2024 12:23:57 +1300 Subject: [PATCH 30/44] enhancement/AY-1456_double_click_at_instance_switch_to_publish_tab --- .../tools/publisher/widgets/card_view_widgets.py | 11 +++++++++++ .../tools/publisher/widgets/list_view_widgets.py | 6 ++++++ .../tools/publisher/widgets/overview_widget.py | 10 ++++++++++ 3 files changed, 27 insertions(+) diff --git a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py index 47c5399cf7..7d178e98e5 100644 --- a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py @@ -534,6 +534,8 @@ class InstanceCardView(AbstractInstanceView): Wrapper of all widgets in card view. """ + double_clicked = QtCore.Signal() + def __init__(self, controller, parent): super(InstanceCardView, self).__init__(parent) @@ -578,6 +580,9 @@ class InstanceCardView(AbstractInstanceView): self.sizePolicy().verticalPolicy() ) + def mouseDoubleClickEvent(self, event): + self.double_clicked.emit() + def sizeHint(self): """Modify sizeHint based on visibility of scroll bars.""" # Calculate width hint by content widget and vertical scroll bar @@ -715,6 +720,7 @@ class InstanceCardView(AbstractInstanceView): ) group_widget.active_changed.connect(self._on_active_changed) group_widget.selected.connect(self._on_widget_selection) + # group_widget.double_clicked.connect(self._on_widget_double_clicked) self._content_layout.insertWidget(widget_idx, group_widget) self._widgets_by_group[group_name] = group_widget @@ -825,6 +831,11 @@ class InstanceCardView(AbstractInstanceView): self.selection_changed.emit() + # def _on_widget_double_clicked(self): + # print("_on_widget_double_clicked") + # widgets = self._get_selected_widgets() + # print(widgets) + def _select_item_clear(self, instance_id, group_name, new_widget): """Select specific item by instance id and clear previous selection. diff --git a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py index 3322a73be6..8dfabed8e9 100644 --- a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py @@ -425,6 +425,9 @@ class InstanceListView(AbstractInstanceView): This is public access to and from list view. """ + + double_clicked = QtCore.Signal() + def __init__(self, controller, parent): super(InstanceListView, self).__init__(parent) @@ -474,6 +477,9 @@ class InstanceListView(AbstractInstanceView): self._active_toggle_enabled = True + def mouseDoubleClickEvent(self, event): + self.double_clicked.emit() + def _on_expand(self, index): self._update_widget_expand_state(index, True) diff --git a/client/ayon_core/tools/publisher/widgets/overview_widget.py b/client/ayon_core/tools/publisher/widgets/overview_widget.py index dd82185830..82e4153681 100644 --- a/client/ayon_core/tools/publisher/widgets/overview_widget.py +++ b/client/ayon_core/tools/publisher/widgets/overview_widget.py @@ -113,9 +113,15 @@ class OverviewWidget(QtWidgets.QFrame): product_list_view.selection_changed.connect( self._on_product_change ) + product_list_view.double_clicked.connect( + self._on_double_clicked + ) product_view_cards.selection_changed.connect( self._on_product_change ) + product_view_cards.double_clicked.connect( + self._on_double_clicked + ) # Active instances changed product_list_view.active_changed.connect( self._on_active_changed @@ -293,6 +299,10 @@ class OverviewWidget(QtWidgets.QFrame): instances, context_selected, convertor_identifiers ) + def _on_double_clicked(self): + from ayon_core.tools.utils import host_tools + host_tools.show_publisher(tab="publish") + def _on_active_changed(self): if self._refreshing_instances: return From 427d36c5ac796fdb6993ae34483e8eebf06ff874 Mon Sep 17 00:00:00 2001 From: Braden Jennings Date: Thu, 29 Feb 2024 12:25:02 +1300 Subject: [PATCH 31/44] enhancement/AY-1456_double_click_at_instance_switch_to_publish_tab --- .../ayon_core/tools/publisher/widgets/card_view_widgets.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py index 7d178e98e5..d1d062c18f 100644 --- a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py @@ -720,7 +720,6 @@ class InstanceCardView(AbstractInstanceView): ) group_widget.active_changed.connect(self._on_active_changed) group_widget.selected.connect(self._on_widget_selection) - # group_widget.double_clicked.connect(self._on_widget_double_clicked) self._content_layout.insertWidget(widget_idx, group_widget) self._widgets_by_group[group_name] = group_widget @@ -831,11 +830,6 @@ class InstanceCardView(AbstractInstanceView): self.selection_changed.emit() - # def _on_widget_double_clicked(self): - # print("_on_widget_double_clicked") - # widgets = self._get_selected_widgets() - # print(widgets) - def _select_item_clear(self, instance_id, group_name, new_widget): """Select specific item by instance id and clear previous selection. From 58998d970c41d1a17275722c3eb97df7dd645325 Mon Sep 17 00:00:00 2001 From: Braden Jennings Date: Thu, 29 Feb 2024 15:25:43 +1300 Subject: [PATCH 32/44] enhancement/AY-1456_double_click_at_instance_switch_to_publish_tab --- client/ayon_core/tools/publisher/widgets/list_view_widgets.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py index 8dfabed8e9..90f5f3fab9 100644 --- a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py @@ -457,6 +457,7 @@ class InstanceListView(AbstractInstanceView): instance_view.collapsed.connect(self._on_collapse) instance_view.expanded.connect(self._on_expand) instance_view.toggle_requested.connect(self._on_toggle_request) + instance_view.doubleClicked.connect(self._on_double_clicked) self._group_items = {} self._group_widgets = {} @@ -477,7 +478,7 @@ class InstanceListView(AbstractInstanceView): self._active_toggle_enabled = True - def mouseDoubleClickEvent(self, event): + def _on_double_clicked(self, event): self.double_clicked.emit() def _on_expand(self, index): From 2f326c1467ca26dbedf76a56784d2a37b9dbf2b8 Mon Sep 17 00:00:00 2001 From: Braden Jennings Date: Tue, 5 Mar 2024 09:57:08 +1300 Subject: [PATCH 33/44] enhancement/AY-1456_double_click_at_instance_switch_to_publish_tab --- .../ayon_core/tools/publisher/widgets/list_view_widgets.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py index 90f5f3fab9..7c4c07baea 100644 --- a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py @@ -457,7 +457,7 @@ class InstanceListView(AbstractInstanceView): instance_view.collapsed.connect(self._on_collapse) instance_view.expanded.connect(self._on_expand) instance_view.toggle_requested.connect(self._on_toggle_request) - instance_view.doubleClicked.connect(self._on_double_clicked) + instance_view.doubleClicked.connect(self.double_clicked) self._group_items = {} self._group_widgets = {} @@ -478,9 +478,6 @@ class InstanceListView(AbstractInstanceView): self._active_toggle_enabled = True - def _on_double_clicked(self, event): - self.double_clicked.emit() - def _on_expand(self, index): self._update_widget_expand_state(index, True) From dd776b728ba510283ea17b96dc6b65fa65a8809e Mon Sep 17 00:00:00 2001 From: Braden Jennings Date: Tue, 5 Mar 2024 10:02:23 +1300 Subject: [PATCH 34/44] enhancement/AY-1456_double_click_at_instance_switch_to_publish_tab --- .../ayon_core/tools/publisher/widgets/overview_widget.py | 9 +++------ client/ayon_core/tools/publisher/window.py | 3 +++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/overview_widget.py b/client/ayon_core/tools/publisher/widgets/overview_widget.py index 82e4153681..cedf52ae01 100644 --- a/client/ayon_core/tools/publisher/widgets/overview_widget.py +++ b/client/ayon_core/tools/publisher/widgets/overview_widget.py @@ -18,6 +18,7 @@ class OverviewWidget(QtWidgets.QFrame): instance_context_changed = QtCore.Signal() create_requested = QtCore.Signal() convert_requested = QtCore.Signal() + publish_tab_requested = QtCore.Signal() anim_end_value = 200 anim_duration = 200 @@ -114,13 +115,13 @@ class OverviewWidget(QtWidgets.QFrame): self._on_product_change ) product_list_view.double_clicked.connect( - self._on_double_clicked + self.publish_tab_requested ) product_view_cards.selection_changed.connect( self._on_product_change ) product_view_cards.double_clicked.connect( - self._on_double_clicked + self.publish_tab_requested ) # Active instances changed product_list_view.active_changed.connect( @@ -299,10 +300,6 @@ class OverviewWidget(QtWidgets.QFrame): instances, context_selected, convertor_identifiers ) - def _on_double_clicked(self): - from ayon_core.tools.utils import host_tools - host_tools.show_publisher(tab="publish") - def _on_active_changed(self): if self._refreshing_instances: return diff --git a/client/ayon_core/tools/publisher/window.py b/client/ayon_core/tools/publisher/window.py index 123864ff6c..1b13ced317 100644 --- a/client/ayon_core/tools/publisher/window.py +++ b/client/ayon_core/tools/publisher/window.py @@ -258,6 +258,9 @@ class PublisherWindow(QtWidgets.QDialog): overview_widget.convert_requested.connect( self._on_convert_requested ) + overview_widget.publish_tab_requested.connect( + self._go_to_publish_tab + ) save_btn.clicked.connect(self._on_save_clicked) reset_btn.clicked.connect(self._on_reset_clicked) From b8f0d590f16cf0d7a6e4add5daef27cfb5140a1e Mon Sep 17 00:00:00 2001 From: Braden Jennings Date: Mon, 11 Mar 2024 10:30:42 +1300 Subject: [PATCH 35/44] right clicking tree view doen't switch tab --- .../tools/publisher/widgets/card_view_widgets.py | 3 ++- .../tools/publisher/widgets/list_view_widgets.py | 14 +++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py index d1d062c18f..28ed237fe6 100644 --- a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py @@ -581,7 +581,8 @@ class InstanceCardView(AbstractInstanceView): ) def mouseDoubleClickEvent(self, event): - self.double_clicked.emit() + if event.button() == QtCore.Qt.LeftButton: + self.double_clicked.emit() def sizeHint(self): """Modify sizeHint based on visibility of scroll bars.""" diff --git a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py index 7c4c07baea..eb5f41be4a 100644 --- a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py @@ -379,7 +379,7 @@ class InstanceTreeView(QtWidgets.QTreeView): "double click" as 2x "single click". """ if event.button() != QtCore.Qt.LeftButton: - return + return False pressed_group_index = None pos_index = self.indexAt(event.pos()) @@ -388,13 +388,17 @@ class InstanceTreeView(QtWidgets.QTreeView): self._pressed_group_index = pressed_group_index + return True + def mousePressEvent(self, event): - self._mouse_press(event) - super(InstanceTreeView, self).mousePressEvent(event) + handled = self._mouse_press(event) + if handled: + super(InstanceTreeView, self).mousePressEvent(event) def mouseDoubleClickEvent(self, event): - self._mouse_press(event) - super(InstanceTreeView, self).mouseDoubleClickEvent(event) + handled = self._mouse_press(event) + if handled: + super(InstanceTreeView, self).mouseDoubleClickEvent(event) def _mouse_release(self, event, pressed_index): if event.button() != QtCore.Qt.LeftButton: From 7c028a752cd012e9914c1d3a29829869708c032c Mon Sep 17 00:00:00 2001 From: Braden Jennings Date: Mon, 11 Mar 2024 10:52:27 +1300 Subject: [PATCH 36/44] clicking in empty area in tree view should switch tab --- client/ayon_core/tools/publisher/widgets/list_view_widgets.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py index eb5f41be4a..f142faff83 100644 --- a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py @@ -317,6 +317,7 @@ class InstanceListGroupWidget(QtWidgets.QFrame): class InstanceTreeView(QtWidgets.QTreeView): """View showing instances and their groups.""" toggle_requested = QtCore.Signal(int) + double_clicked = QtCore.Signal() def __init__(self, *args, **kwargs): super(InstanceTreeView, self).__init__(*args, **kwargs) @@ -396,6 +397,7 @@ class InstanceTreeView(QtWidgets.QTreeView): super(InstanceTreeView, self).mousePressEvent(event) def mouseDoubleClickEvent(self, event): + self.double_clicked.emit() handled = self._mouse_press(event) if handled: super(InstanceTreeView, self).mouseDoubleClickEvent(event) @@ -461,7 +463,7 @@ class InstanceListView(AbstractInstanceView): instance_view.collapsed.connect(self._on_collapse) instance_view.expanded.connect(self._on_expand) instance_view.toggle_requested.connect(self._on_toggle_request) - instance_view.doubleClicked.connect(self.double_clicked) + instance_view.double_clicked.connect(self.double_clicked) self._group_items = {} self._group_widgets = {} From 55256d59a3ad315974ce3a6aa9f70d00e26561af Mon Sep 17 00:00:00 2001 From: Braden Jennings Date: Mon, 11 Mar 2024 10:56:15 +1300 Subject: [PATCH 37/44] clicking in empty area in tree view should switch tab --- client/ayon_core/tools/publisher/widgets/list_view_widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py index f142faff83..6a7335bd74 100644 --- a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py @@ -397,9 +397,9 @@ class InstanceTreeView(QtWidgets.QTreeView): super(InstanceTreeView, self).mousePressEvent(event) def mouseDoubleClickEvent(self, event): - self.double_clicked.emit() handled = self._mouse_press(event) if handled: + self.double_clicked.emit() super(InstanceTreeView, self).mouseDoubleClickEvent(event) def _mouse_release(self, event, pressed_index): From e2849b83e0fbb3d90edf637b3a3161172facecf8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 10:58:24 +0200 Subject: [PATCH 38/44] card widgets can tell if they should trigger double click --- .../publisher/widgets/card_view_widgets.py | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py index 28ed237fe6..4e34f9b58c 100644 --- a/client/ayon_core/tools/publisher/widgets/card_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/card_view_widgets.py @@ -52,6 +52,7 @@ class SelectionTypes: class BaseGroupWidget(QtWidgets.QWidget): selected = QtCore.Signal(str, str, str) removed_selected = QtCore.Signal() + double_clicked = QtCore.Signal() def __init__(self, group_name, parent): super(BaseGroupWidget, self).__init__(parent) @@ -192,6 +193,7 @@ class ConvertorItemsGroupWidget(BaseGroupWidget): else: widget = ConvertorItemCardWidget(item, self) widget.selected.connect(self._on_widget_selection) + widget.double_clicked(self.double_clicked) self._widgets_by_id[item.id] = widget self._content_layout.insertWidget(widget_idx, widget) widget_idx += 1 @@ -254,6 +256,7 @@ class InstanceGroupWidget(BaseGroupWidget): ) widget.selected.connect(self._on_widget_selection) widget.active_changed.connect(self._on_active_changed) + widget.double_clicked.connect(self.double_clicked) self._widgets_by_id[instance.id] = widget self._content_layout.insertWidget(widget_idx, widget) widget_idx += 1 @@ -271,6 +274,7 @@ class CardWidget(BaseClickableFrame): # Group identifier of card # - this must be set because if send when mouse is released with card id _group_identifier = None + double_clicked = QtCore.Signal() def __init__(self, parent): super(CardWidget, self).__init__(parent) @@ -279,6 +283,11 @@ class CardWidget(BaseClickableFrame): self._selected = False self._id = None + def mouseDoubleClickEvent(self, event): + super(CardWidget, self).mouseDoubleClickEvent(event) + if self._is_valid_double_click(event): + self.double_clicked.emit() + @property def id(self): """Id of card.""" @@ -312,6 +321,9 @@ class CardWidget(BaseClickableFrame): self.selected.emit(self._id, self._group_identifier, selection_type) + def _is_valid_double_click(self, event): + return True + class ContextCardWidget(CardWidget): """Card for global context. @@ -527,6 +539,15 @@ class InstanceCardWidget(CardWidget): def _on_expend_clicked(self): self._set_expanded() + def _is_valid_double_click(self, event): + widget = self.childAt(event.pos()) + if ( + widget is self._active_checkbox + or widget is self._expand_btn + ): + return False + return True + class InstanceCardView(AbstractInstanceView): """Publish access to card view. @@ -580,10 +601,6 @@ class InstanceCardView(AbstractInstanceView): self.sizePolicy().verticalPolicy() ) - def mouseDoubleClickEvent(self, event): - if event.button() == QtCore.Qt.LeftButton: - self.double_clicked.emit() - def sizeHint(self): """Modify sizeHint based on visibility of scroll bars.""" # Calculate width hint by content widget and vertical scroll bar @@ -721,6 +738,7 @@ class InstanceCardView(AbstractInstanceView): ) group_widget.active_changed.connect(self._on_active_changed) group_widget.selected.connect(self._on_widget_selection) + group_widget.double_clicked.connect(self.double_clicked) self._content_layout.insertWidget(widget_idx, group_widget) self._widgets_by_group[group_name] = group_widget @@ -761,6 +779,7 @@ class InstanceCardView(AbstractInstanceView): widget = ContextCardWidget(self._content_widget) widget.selected.connect(self._on_widget_selection) + widget.double_clicked.connect(self.double_clicked) self._context_widget = widget @@ -784,6 +803,7 @@ class InstanceCardView(AbstractInstanceView): CONVERTOR_ITEM_GROUP, self._content_widget ) group_widget.selected.connect(self._on_widget_selection) + group_widget.double_clicked.connect(self.double_clicked) self._content_layout.insertWidget(1, group_widget) self._convertor_items_group = group_widget From 8b17cf5485c52bf72c5b624ee4a1c9788f83fe92 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 10:58:48 +0200 Subject: [PATCH 39/44] list view widgets can tell if should trigger double click --- .../publisher/widgets/list_view_widgets.py | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py index 6a7335bd74..aa8f26998a 100644 --- a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py @@ -110,6 +110,7 @@ class InstanceListItemWidget(QtWidgets.QWidget): This is required to be able use custom checkbox on custom place. """ active_changed = QtCore.Signal(str, bool) + double_clicked = QtCore.Signal() def __init__(self, instance, parent): super(InstanceListItemWidget, self).__init__(parent) @@ -149,6 +150,12 @@ class InstanceListItemWidget(QtWidgets.QWidget): self._set_valid_property(instance.has_valid_context) + def mouseDoubleClickEvent(self, event): + widget = self.childAt(event.pos()) + super(InstanceListItemWidget, self).mouseDoubleClickEvent(event) + if widget is not self._active_checkbox: + self.double_clicked.emit() + def _set_valid_property(self, valid): if self._has_valid_context == valid: return @@ -209,6 +216,8 @@ class InstanceListItemWidget(QtWidgets.QWidget): class ListContextWidget(QtWidgets.QFrame): """Context (or global attributes) widget.""" + double_clicked = QtCore.Signal() + def __init__(self, parent): super(ListContextWidget, self).__init__(parent) @@ -225,6 +234,10 @@ class ListContextWidget(QtWidgets.QFrame): self.label_widget = label_widget + def mouseDoubleClickEvent(self, event): + super(ListContextWidget, self).mouseDoubleClickEvent(event) + self.double_clicked.emit() + class InstanceListGroupWidget(QtWidgets.QFrame): """Widget representing group of instances. @@ -392,15 +405,12 @@ class InstanceTreeView(QtWidgets.QTreeView): return True def mousePressEvent(self, event): - handled = self._mouse_press(event) - if handled: - super(InstanceTreeView, self).mousePressEvent(event) + self._mouse_press(event) + super(InstanceTreeView, self).mousePressEvent(event) def mouseDoubleClickEvent(self, event): - handled = self._mouse_press(event) - if handled: - self.double_clicked.emit() - super(InstanceTreeView, self).mouseDoubleClickEvent(event) + self._mouse_press(event) + super(InstanceTreeView, self).mouseDoubleClickEvent(event) def _mouse_release(self, event, pressed_index): if event.button() != QtCore.Qt.LeftButton: @@ -697,6 +707,7 @@ class InstanceListView(AbstractInstanceView): self._active_toggle_enabled ) widget.active_changed.connect(self._on_active_changed) + widget.double_clicked.connect(self.double_clicked) self._instance_view.setIndexWidget(proxy_index, widget) self._widgets_by_id[instance.id] = widget @@ -727,6 +738,7 @@ class InstanceListView(AbstractInstanceView): ) proxy_index = self._proxy_model.mapFromSource(index) widget = ListContextWidget(self._instance_view) + widget.double_clicked.connect(self.double_clicked) self._instance_view.setIndexWidget(proxy_index, widget) self._context_widget = widget From 168f8cdcc20435105bcf2f73bbca0ea22b08b298 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 10:59:05 +0200 Subject: [PATCH 40/44] revert output of '_mouse_press' --- client/ayon_core/tools/publisher/widgets/list_view_widgets.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py index aa8f26998a..71be0ab1a4 100644 --- a/client/ayon_core/tools/publisher/widgets/list_view_widgets.py +++ b/client/ayon_core/tools/publisher/widgets/list_view_widgets.py @@ -393,7 +393,7 @@ class InstanceTreeView(QtWidgets.QTreeView): "double click" as 2x "single click". """ if event.button() != QtCore.Qt.LeftButton: - return False + return pressed_group_index = None pos_index = self.indexAt(event.pos()) @@ -402,8 +402,6 @@ class InstanceTreeView(QtWidgets.QTreeView): self._pressed_group_index = pressed_group_index - return True - def mousePressEvent(self, event): self._mouse_press(event) super(InstanceTreeView, self).mousePressEvent(event) From a19350eea9925cb5c8ba6b3af08bbef8bf953fe5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 12:10:50 +0200 Subject: [PATCH 41/44] implemented helper function to determine if current process is ayon launcher --- client/ayon_core/lib/__init__.py | 2 ++ client/ayon_core/lib/ayon_info.py | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/client/ayon_core/lib/__init__.py b/client/ayon_core/lib/__init__.py index e436396c6c..e25d3479ee 100644 --- a/client/ayon_core/lib/__init__.py +++ b/client/ayon_core/lib/__init__.py @@ -139,6 +139,7 @@ from .path_tools import ( ) from .ayon_info import ( + is_in_ayon_launcher_process, is_running_from_build, is_using_ayon_console, is_staging_enabled, @@ -248,6 +249,7 @@ __all__ = [ "Logger", + "is_in_ayon_launcher_process", "is_running_from_build", "is_using_ayon_console", "is_staging_enabled", diff --git a/client/ayon_core/lib/ayon_info.py b/client/ayon_core/lib/ayon_info.py index fc09a7c90c..c4333fab95 100644 --- a/client/ayon_core/lib/ayon_info.py +++ b/client/ayon_core/lib/ayon_info.py @@ -1,4 +1,5 @@ import os +import sys import json import datetime import platform @@ -25,6 +26,18 @@ def get_ayon_launcher_version(): return content["__version__"] +def is_in_ayon_launcher_process(): + """Determine if current process is running from AYON launcher. + + Returns: + bool: True if running from AYON launcher. + + """ + ayon_executable_path = os.path.normpath(os.environ["AYON_EXECUTABLE"]) + executable_path = os.path.normpath(sys.executable) + return ayon_executable_path == executable_path + + def is_running_from_build(): """Determine if current process is running from build or code. From 0bba27e4dd97b28be9d47c5aaa08f526b97c928c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 15:16:52 +0200 Subject: [PATCH 42/44] removed unused imports --- client/ayon_core/hosts/max/api/lib.py | 3 --- .../hosts/maya/plugins/create/create_animation_pointcache.py | 1 - client/ayon_core/hosts/traypublisher/csv_publish.py | 2 -- 3 files changed, 6 deletions(-) diff --git a/client/ayon_core/hosts/max/api/lib.py b/client/ayon_core/hosts/max/api/lib.py index d9a3af3336..0e3abe25ec 100644 --- a/client/ayon_core/hosts/max/api/lib.py +++ b/client/ayon_core/hosts/max/api/lib.py @@ -6,12 +6,9 @@ import json from typing import Any, Dict, Union import six -import ayon_api from ayon_core.pipeline import ( get_current_project_name, - get_current_folder_path, - get_current_task_name, colorspace ) from ayon_core.settings import get_project_settings diff --git a/client/ayon_core/hosts/maya/plugins/create/create_animation_pointcache.py b/client/ayon_core/hosts/maya/plugins/create/create_animation_pointcache.py index 08d50a1ab8..069762e4ae 100644 --- a/client/ayon_core/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/client/ayon_core/hosts/maya/plugins/create/create_animation_pointcache.py @@ -6,7 +6,6 @@ from ayon_core.lib import ( BoolDef, NumberDef, ) -from ayon_core.pipeline import CreatedInstance def _get_animation_attr_defs(cls): diff --git a/client/ayon_core/hosts/traypublisher/csv_publish.py b/client/ayon_core/hosts/traypublisher/csv_publish.py index b43792a357..2762172936 100644 --- a/client/ayon_core/hosts/traypublisher/csv_publish.py +++ b/client/ayon_core/hosts/traypublisher/csv_publish.py @@ -1,5 +1,3 @@ -import os - import pyblish.api import pyblish.util From 270921f63eb9a4032cf0f1d04f1056b71eab0071 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 15:18:10 +0200 Subject: [PATCH 43/44] use preferred order in condition --- .../modules/deadline/plugins/publish/validate_deadline_pools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/modules/deadline/plugins/publish/validate_deadline_pools.py b/client/ayon_core/modules/deadline/plugins/publish/validate_deadline_pools.py index 5094b3deaf..2fb511bf51 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/validate_deadline_pools.py +++ b/client/ayon_core/modules/deadline/plugins/publish/validate_deadline_pools.py @@ -72,7 +72,7 @@ class ValidateDeadlinePools(OptionalPyblishPluginMixin, auth=auth, log=self.log) # some DL return "none" as a pool name - if not "none" in pools: + if "none" not in pools: pools.append("none") self.log.info("Available pools: {}".format(pools)) self.pools_per_url[deadline_url] = pools From 2e6ee298a7aa87c869af9d5f18a57173b2933aac Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 9 May 2024 15:18:25 +0200 Subject: [PATCH 44/44] removed unnecessary check for 'deadline' key --- .../modules/deadline/plugins/publish/submit_publish_job.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py index 06dd62e18b..0f505dce78 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_publish_job.py @@ -467,8 +467,6 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin, # Inject deadline url to instances to query DL for job id for overrides for inst in instances: - if not "deadline" in inst: - inst["deadline"] = {} inst["deadline"] = instance.data["deadline"] # publish job file