From 91331acc4a58188fb4458736e10c1704c5308465 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Wed, 1 Feb 2023 12:08:55 +0100 Subject: [PATCH 01/33] create namespace if it doesn't already exists in maya + replace _ by : in namespace --- openpype/hosts/maya/api/plugin.py | 2 +- openpype/hosts/maya/plugins/load/load_reference.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index 916fddd923..98d6492003 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -148,7 +148,7 @@ class ReferenceLoader(Loader): count = options.get("count") or 1 for c in range(0, count): namespace = namespace or lib.unique_namespace( - "{}_{}_".format(asset["name"], context["subset"]["name"]), + "{}:{}_".format(asset["name"], context["subset"]["name"]), prefix="_" if asset["name"][0].isdigit() else "", suffix="_", ) diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index 461f4258aa..44d5147fe7 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -125,7 +125,7 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): except ValueError: family = "model" - group_name = "{}:_GRP".format(namespace) + group_name = "{}GRP".format(namespace) # True by default to keep legacy behaviours attach_to_root = options.get("attach_to_root", True) @@ -133,6 +133,10 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): cmds.loadPlugin("AbcImport.mll", quiet=True) file_url = self.prepare_root_value(self.fname, context["project"]["name"]) + + if not cmds.namespace(exists=namespace): + cmds.namespace(add=namespace) + nodes = cmds.file(file_url, namespace=namespace, sharedReferenceFile=False, From f0a6c933ea3ca4bb55cf6e2605c1ba9440feeedb Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Wed, 1 Feb 2023 17:58:35 +0100 Subject: [PATCH 02/33] create settings and doc for the custom naming of referenced assets in maya --- .../defaults/project_settings/maya.json | 3 +++ .../schemas/schema_maya_load.json | 17 +++++++++++++++++ website/docs/admin_hosts_maya.md | 13 +++++++++++++ 3 files changed, 33 insertions(+) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 19d8667002..163210949e 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -1047,6 +1047,9 @@ 125, 255 ] + }, + "reference_loader": { + "naming": "" } }, "workfile_build": { diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_load.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_load.json index 6b2315abc0..63c65648b6 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_load.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_load.json @@ -91,6 +91,23 @@ "key": "yetiRig" } ] + }, + { + "type": "dict", + "collapsible": true, + "key": "reference_loader", + "label": "Reference Loader", + "children": [ + { + "type": "text", + "label": "Naming", + "key": "naming" + }, + { + "type": "label", + "label": "Here's a link to the doc where you can find explanations about customing the naming of referenced assets: https://openpype.io/docs/admin_hosts_maya#load-plugins" + } + ] } ] } diff --git a/website/docs/admin_hosts_maya.md b/website/docs/admin_hosts_maya.md index 5c8c48b2e3..c324ff9ab2 100644 --- a/website/docs/admin_hosts_maya.md +++ b/website/docs/admin_hosts_maya.md @@ -170,6 +170,19 @@ These options are set on the camera shape when publishing the review. They corre ![Extract Playblast Settings](assets/maya-admin_extract_playblast_settings_camera_options.png) + +## Load Plugins + +### Reference Loader > Namespace +Here you can create your own custom naming for the reference loader.
+The custom naming is split into two parts separated by a ":" symbol. The first half is the namespace and the second half is the group name of the reference. If you don't set the namespace or the group name (or both), the default namespace and group name will be applied. +Here's the different variables you can use:
+Asset name: {asset[name]}
+Asset type: {asset[type]}
+Subset name: {subset[name]}
+Subset family: {subset[data][family]}
+Example: {asset[name]}_{subset[name]}:GRP + ## Custom Menu You can add your custom tools menu into Maya by extending definitions in **Maya -> Scripts Menu Definition**. ![Custom menu definition](assets/maya-admin_scriptsmenu.png) From 6ceb7ef8b3e0385e47089a8c34ebb2b26b2bacf3 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Wed, 1 Feb 2023 18:00:57 +0100 Subject: [PATCH 03/33] reset group name variable as before --- openpype/hosts/maya/plugins/load/load_reference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index 44d5147fe7..a206b63b46 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -125,7 +125,7 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): except ValueError: family = "model" - group_name = "{}GRP".format(namespace) + group_name = "{}_GRP".format(namespace) # True by default to keep legacy behaviours attach_to_root = options.get("attach_to_root", True) From 1c207b91f40031e030df0842c1ca009b2588043e Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Thu, 2 Feb 2023 12:28:09 +0100 Subject: [PATCH 04/33] process custom reference naming for maya --- openpype/hosts/maya/api/plugin.py | 26 +++++++++++++++++-- .../hosts/maya/plugins/load/load_reference.py | 5 ++-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index 98d6492003..61966a049c 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -143,16 +143,37 @@ class ReferenceLoader(Loader): assert os.path.exists(self.fname), "%s does not exist." % self.fname asset = context['asset'] + subset = context['subset'] + settings = get_project_settings(os.environ['AVALON_PROJECT']) loaded_containers = [] count = options.get("count") or 1 for c in range(0, count): namespace = namespace or lib.unique_namespace( - "{}:{}_".format(asset["name"], context["subset"]["name"]), + "{}_{}_".format(asset["name"], context["subset"]["name"]), prefix="_" if asset["name"][0].isdigit() else "", suffix="_", ) + custom_naming = settings['maya']['load']['reference_loader']['naming'] # noqa + group_name = None + + if custom_naming: + custom_naming = custom_naming.format( + asset=asset, + subset=subset + ) + if ':' in custom_naming: + if custom_naming[0] == ':': + group_name = "{}{}".format(namespace, custom_naming) + elif custom_naming[-1] == ':': + namespace = custom_naming.split(':')[0] + else: + namespace = custom_naming.split(':')[0] + group_name = custom_naming + else: + namespace = custom_naming + # Offset loaded subset if "offset" in options: offset = [i * c for i in options["offset"]] @@ -164,7 +185,8 @@ class ReferenceLoader(Loader): context=context, name=name, namespace=namespace, - options=options + options=options, + group_name=group_name ) # Only containerize if any nodes were loaded by the Loader diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index a206b63b46..cca45edbad 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -117,7 +117,7 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): # Name of creator class that will be used to create animation instance animation_creator_name = "CreateAnimation" - def process_reference(self, context, name, namespace, options): + def process_reference(self, context, name, namespace, options, group_name=None): # noqa import maya.cmds as cmds try: @@ -125,7 +125,8 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): except ValueError: family = "model" - group_name = "{}_GRP".format(namespace) + if not group_name: + group_name = "{}:_GRP".format(namespace) # True by default to keep legacy behaviours attach_to_root = options.get("attach_to_root", True) From 3597b73ce6c1c6393082b4bfdbfa853d3dad5e4e Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Thu, 23 Feb 2023 11:12:04 +0100 Subject: [PATCH 05/33] change the token/description display for the reference loader in maya docs --- website/docs/admin_hosts_maya.md | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/website/docs/admin_hosts_maya.md b/website/docs/admin_hosts_maya.md index c324ff9ab2..e80442837c 100644 --- a/website/docs/admin_hosts_maya.md +++ b/website/docs/admin_hosts_maya.md @@ -174,14 +174,25 @@ These options are set on the camera shape when publishing the review. They corre ## Load Plugins ### Reference Loader > Namespace -Here you can create your own custom naming for the reference loader.
+Here you can create your own custom naming for the reference loader. + The custom naming is split into two parts separated by a ":" symbol. The first half is the namespace and the second half is the group name of the reference. If you don't set the namespace or the group name (or both), the default namespace and group name will be applied. -Here's the different variables you can use:
-Asset name: {asset[name]}
-Asset type: {asset[type]}
-Subset name: {subset[name]}
-Subset family: {subset[data][family]}
-Example: {asset[name]}_{subset[name]}:GRP +Here's the different variables you can use: + +
+
+ +| Token | Description | +|---|---| +|`{asset[name]}` | Asset name | +|`{asset[type]}` | Asset type | +|`{subset[name]}` | Subset name | +|`{subset[data][family]}` | Subset family | + +
+
+ +Example: `{asset[name]}_{subset[name]}:GRP` ## Custom Menu You can add your custom tools menu into Maya by extending definitions in **Maya -> Scripts Menu Definition**. From b3d62c51749534493a7cef0b8bc547ff27f7287c Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Fri, 24 Feb 2023 10:08:29 +0100 Subject: [PATCH 06/33] remove create namespace if it doesn't exsits --- openpype/hosts/maya/plugins/load/load_reference.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index cca45edbad..97edf1110d 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -135,9 +135,6 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): file_url = self.prepare_root_value(self.fname, context["project"]["name"]) - if not cmds.namespace(exists=namespace): - cmds.namespace(add=namespace) - nodes = cmds.file(file_url, namespace=namespace, sharedReferenceFile=False, From 45f59b4e5873e5749354676cca22b30416b20a38 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Fri, 24 Feb 2023 12:20:01 +0100 Subject: [PATCH 07/33] raise value error for wrong namespace format + make namespace unique --- openpype/hosts/maya/api/plugin.py | 53 +++++++++++-------- .../hosts/maya/plugins/load/load_reference.py | 2 - 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index 61966a049c..cd1363e46e 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -1,3 +1,4 @@ +from curses.ascii import isdigit import os from maya import cmds @@ -148,31 +149,41 @@ class ReferenceLoader(Loader): loaded_containers = [] count = options.get("count") or 1 - for c in range(0, count): - namespace = namespace or lib.unique_namespace( - "{}_{}_".format(asset["name"], context["subset"]["name"]), - prefix="_" if asset["name"][0].isdigit() else "", - suffix="_", - ) + for c in range(0, count): custom_naming = settings['maya']['load']['reference_loader']['naming'] # noqa group_name = None - if custom_naming: - custom_naming = custom_naming.format( - asset=asset, - subset=subset + if ':' not in custom_naming: + raise ValueError( + "Wrong format for namespace, missing ':' separator" ) - if ':' in custom_naming: - if custom_naming[0] == ':': - group_name = "{}{}".format(namespace, custom_naming) - elif custom_naming[-1] == ':': - namespace = custom_naming.split(':')[0] - else: - namespace = custom_naming.split(':')[0] - group_name = custom_naming - else: - namespace = custom_naming + elif custom_naming.strip()[0] == ':': + raise ValueError( + "Wrong format for namespace, \ +missing content before ':' separator" + ) + elif custom_naming.strip()[-1] == ':': + raise ValueError( + "Wrong format for namespace, \ +missing content after ':' separator" + ) + + custom_naming = custom_naming.format( + asset=asset, + subset=subset + ) + + namespace = custom_naming.split(':')[0] + namespace = lib.unique_namespace( + namespace, + prefix="_" if namespace[0].isdigit() else "", + suffix="_" + ) + group_name = "{}:{}".format( + namespace, + custom_naming.split(":")[-1] + ) # Offset loaded subset if "offset" in options: @@ -209,7 +220,7 @@ class ReferenceLoader(Loader): return loaded_containers - def process_reference(self, context, name, namespace, data): + def process_reference(self, context, name, namespace, data, group_name=None): # noqa """To be implemented by subclass""" raise NotImplementedError("Must be implemented by subclass") diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index 97edf1110d..9565d51580 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -125,8 +125,6 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): except ValueError: family = "model" - if not group_name: - group_name = "{}:_GRP".format(namespace) # True by default to keep legacy behaviours attach_to_root = options.get("attach_to_root", True) From 14f6902fc09e3a8cb7aca8d7518de4de9fe45cf6 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Fri, 24 Feb 2023 12:24:54 +0100 Subject: [PATCH 08/33] modify doc --- website/docs/admin_hosts_maya.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/admin_hosts_maya.md b/website/docs/admin_hosts_maya.md index e80442837c..25e69a0b53 100644 --- a/website/docs/admin_hosts_maya.md +++ b/website/docs/admin_hosts_maya.md @@ -176,7 +176,7 @@ These options are set on the camera shape when publishing the review. They corre ### Reference Loader > Namespace Here you can create your own custom naming for the reference loader. -The custom naming is split into two parts separated by a ":" symbol. The first half is the namespace and the second half is the group name of the reference. If you don't set the namespace or the group name (or both), the default namespace and group name will be applied. +The custom naming is split into two parts separated by a ":" symbol. The first half is the namespace and the second half is the group name of the reference. If you don't set the namespace or the group name, an error will occur. Here's the different variables you can use:
From bad4f51653be9a64210b84444f86a23465db072a Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Fri, 24 Feb 2023 12:27:46 +0100 Subject: [PATCH 09/33] set default value for reference loader naming --- openpype/settings/defaults/project_settings/maya.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 163210949e..7e26020452 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -1049,7 +1049,7 @@ ] }, "reference_loader": { - "naming": "" + "naming": "{asset[name]}_{subset[name]}_:_GRP" } }, "workfile_build": { From 599af2de232f9966daec9cd15b54524c2a494948 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Fri, 24 Feb 2023 12:30:51 +0100 Subject: [PATCH 10/33] remove unused import --- openpype/hosts/maya/api/plugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index cd1363e46e..037d535073 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -1,4 +1,3 @@ -from curses.ascii import isdigit import os from maya import cmds From 8879c8e9e9c42ffbfd66e9073d21fc96f09784c7 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Fri, 24 Feb 2023 12:43:54 +0100 Subject: [PATCH 11/33] remove _ suffix on unique namespace --- openpype/hosts/maya/api/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index 037d535073..15d6a4e5a4 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -177,7 +177,7 @@ missing content after ':' separator" namespace = lib.unique_namespace( namespace, prefix="_" if namespace[0].isdigit() else "", - suffix="_" + suffix="" ) group_name = "{}:{}".format( namespace, From e88288c53a99b48d11af3b2d8165f36f8c2cf2f4 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Fri, 24 Feb 2023 15:06:49 +0100 Subject: [PATCH 12/33] add group_name argument to process_reference method for ReferenceLoader subclasses --- openpype/hosts/maya/plugins/load/_load_animation.py | 2 +- openpype/hosts/maya/plugins/load/load_look.py | 2 +- openpype/hosts/maya/plugins/load/load_yeti_rig.py | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/_load_animation.py b/openpype/hosts/maya/plugins/load/_load_animation.py index b419a730b5..24bff4ca6b 100644 --- a/openpype/hosts/maya/plugins/load/_load_animation.py +++ b/openpype/hosts/maya/plugins/load/_load_animation.py @@ -14,7 +14,7 @@ class AbcLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): icon = "code-fork" color = "orange" - def process_reference(self, context, name, namespace, data): + def process_reference(self, context, name, namespace, data, group_name=None): # noqa import maya.cmds as cmds from openpype.hosts.maya.api.lib import unique_namespace diff --git a/openpype/hosts/maya/plugins/load/load_look.py b/openpype/hosts/maya/plugins/load/load_look.py index 8f3e017658..a9d493382c 100644 --- a/openpype/hosts/maya/plugins/load/load_look.py +++ b/openpype/hosts/maya/plugins/load/load_look.py @@ -28,7 +28,7 @@ class LookLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): icon = "code-fork" color = "orange" - def process_reference(self, context, name, namespace, options): + def process_reference(self, context, name, namespace, options, group_name=None): # noqa import maya.cmds as cmds with lib.maintained_selection(): diff --git a/openpype/hosts/maya/plugins/load/load_yeti_rig.py b/openpype/hosts/maya/plugins/load/load_yeti_rig.py index 6a13d2e145..b8066871b0 100644 --- a/openpype/hosts/maya/plugins/load/load_yeti_rig.py +++ b/openpype/hosts/maya/plugins/load/load_yeti_rig.py @@ -19,8 +19,7 @@ class YetiRigLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): def process_reference( self, context, name=None, namespace=None, options=None ): - - group_name = "{}:{}".format(namespace, name) + group_name = options['group_name'] with lib.maintained_selection(): file_url = self.prepare_root_value( self.fname, context["project"]["name"] From 307e57bbfdb626859b2743520ca5712111ae17a4 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Wed, 29 Mar 2023 16:33:04 +0200 Subject: [PATCH 13/33] change variables name for template --- openpype/hosts/maya/api/plugin.py | 10 ++++++---- openpype/settings/defaults/project_settings/maya.json | 2 +- website/docs/admin_hosts_maya.md | 8 ++++---- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index 15d6a4e5a4..2c5d1f1def 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -144,13 +144,13 @@ class ReferenceLoader(Loader): asset = context['asset'] subset = context['subset'] - settings = get_project_settings(os.environ['AVALON_PROJECT']) + settings = get_project_settings(context['project']['name']) + custom_naming = settings['maya']['load']['reference_loader']['naming'] loaded_containers = [] count = options.get("count") or 1 for c in range(0, count): - custom_naming = settings['maya']['load']['reference_loader']['naming'] # noqa group_name = None if ':' not in custom_naming: @@ -169,8 +169,10 @@ missing content after ':' separator" ) custom_naming = custom_naming.format( - asset=asset, - subset=subset + asset_name=asset['name'], + asset_type=asset['type'], + subset=subset['name'], + family=subset['data'].get('family') or subset['data']['families'][0] # noqa ) namespace = custom_naming.split(':')[0] diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 7e26020452..8e57b09aff 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -1049,7 +1049,7 @@ ] }, "reference_loader": { - "naming": "{asset[name]}_{subset[name]}_:_GRP" + "naming": "{asset_name}_{subset}_:_GRP" } }, "workfile_build": { diff --git a/website/docs/admin_hosts_maya.md b/website/docs/admin_hosts_maya.md index 25e69a0b53..6f64dfa4e6 100644 --- a/website/docs/admin_hosts_maya.md +++ b/website/docs/admin_hosts_maya.md @@ -184,10 +184,10 @@ Here's the different variables you can use: | Token | Description | |---|---| -|`{asset[name]}` | Asset name | -|`{asset[type]}` | Asset type | -|`{subset[name]}` | Subset name | -|`{subset[data][family]}` | Subset family | +|`{asset_name}` | Asset name | +|`{asset_type}` | Asset type | +|`{subset}` | Subset name | +|`{family}` | Subset family |
From 77ce35251ab5cca69588ac59c9554e3df5d9eb09 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Mon, 3 Apr 2023 11:57:07 +0200 Subject: [PATCH 14/33] fix example for custom namespace in doc --- website/docs/admin_hosts_maya.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/admin_hosts_maya.md b/website/docs/admin_hosts_maya.md index 6f64dfa4e6..b7688989bc 100644 --- a/website/docs/admin_hosts_maya.md +++ b/website/docs/admin_hosts_maya.md @@ -192,7 +192,7 @@ Here's the different variables you can use: -Example: `{asset[name]}_{subset[name]}:GRP` +Example: `{asset_name}_{subset}_:_GRP` ## Custom Menu You can add your custom tools menu into Maya by extending definitions in **Maya -> Scripts Menu Definition**. From 92a35bc7d2c4630ec3aed843f543ce15d00c89a6 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Mon, 3 Apr 2023 11:59:49 +0200 Subject: [PATCH 15/33] fix layout --- openpype/hosts/maya/api/plugin.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index 2c5d1f1def..f434d1416c 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -159,20 +159,23 @@ class ReferenceLoader(Loader): ) elif custom_naming.strip()[0] == ':': raise ValueError( - "Wrong format for namespace, \ -missing content before ':' separator" + "Wrong format for namespace, missing content before ':' " + "separator" ) elif custom_naming.strip()[-1] == ':': raise ValueError( - "Wrong format for namespace, \ -missing content after ':' separator" + "Wrong format for namespace, missing content after ':' " + "separator" ) custom_naming = custom_naming.format( asset_name=asset['name'], asset_type=asset['type'], subset=subset['name'], - family=subset['data'].get('family') or subset['data']['families'][0] # noqa + family=( + subset['data'].get('family') or + subset['data']['families'][0] + ) ) namespace = custom_naming.split(':')[0] From 6ecaa7dd0530656a06e52b7779d9ab9d54e2c110 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Mon, 3 Apr 2023 12:43:16 +0200 Subject: [PATCH 16/33] move custom naming logic outside the for loop since it is not dynamic --- openpype/hosts/maya/api/plugin.py | 50 +++++++++++++++---------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index f434d1416c..82ed9c0282 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -148,36 +148,36 @@ class ReferenceLoader(Loader): custom_naming = settings['maya']['load']['reference_loader']['naming'] loaded_containers = [] + if ':' not in custom_naming: + raise ValueError( + "Wrong format for namespace, missing ':' separator" + ) + elif custom_naming.strip()[0] == ':': + raise ValueError( + "Wrong format for namespace, missing content before ':' " + "separator" + ) + elif custom_naming.strip()[-1] == ':': + raise ValueError( + "Wrong format for namespace, missing content after ':' " + "separator" + ) + + custom_naming = custom_naming.format( + asset_name=asset['name'], + asset_type=asset['type'], + subset=subset['name'], + family=( + subset['data'].get('family') or + subset['data']['families'][0] + ) + ) + count = options.get("count") or 1 for c in range(0, count): group_name = None - if ':' not in custom_naming: - raise ValueError( - "Wrong format for namespace, missing ':' separator" - ) - elif custom_naming.strip()[0] == ':': - raise ValueError( - "Wrong format for namespace, missing content before ':' " - "separator" - ) - elif custom_naming.strip()[-1] == ':': - raise ValueError( - "Wrong format for namespace, missing content after ':' " - "separator" - ) - - custom_naming = custom_naming.format( - asset_name=asset['name'], - asset_type=asset['type'], - subset=subset['name'], - family=( - subset['data'].get('family') or - subset['data']['families'][0] - ) - ) - namespace = custom_naming.split(':')[0] namespace = lib.unique_namespace( namespace, From f99cb4e2b3399bf6cf36d15f17552eeab2dc36cf Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Mon, 3 Apr 2023 17:38:18 +0200 Subject: [PATCH 17/33] separate custom namespace settings into two different fields --- openpype/settings/defaults/project_settings/maya.json | 3 ++- .../projects_schema/schemas/schema_maya_load.json | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 8e57b09aff..6c9ac1c075 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -1049,7 +1049,8 @@ ] }, "reference_loader": { - "naming": "{asset_name}_{subset}_:_GRP" + "namespace": "{asset_name}_{subset}", + "group_name": "GRP" } }, "workfile_build": { diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_load.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_load.json index 63c65648b6..c1895c4824 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_load.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_load.json @@ -100,8 +100,13 @@ "children": [ { "type": "text", - "label": "Naming", - "key": "naming" + "label": "Namespace", + "key": "namespace" + }, + { + "type": "text", + "label": "Group name", + "key": "group_name" }, { "type": "label", From cc369928c1c1dbd78ed7b7d146e31970659c136d Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Mon, 3 Apr 2023 17:38:59 +0200 Subject: [PATCH 18/33] modify doc to match new settings --- .../docs/assets/maya-admin_custom_namespace.png | Bin 0 -> 14002 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 website/docs/assets/maya-admin_custom_namespace.png diff --git a/website/docs/assets/maya-admin_custom_namespace.png b/website/docs/assets/maya-admin_custom_namespace.png new file mode 100644 index 0000000000000000000000000000000000000000..a65eef2e0b13c1bf8e6c56d7811b9665265dbbb0 GIT binary patch literal 14002 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­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 literal 0 HcmV?d00001 From 73c2ef6d560bdaf3ca651d1464f398964829feb1 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Mon, 3 Apr 2023 17:40:50 +0200 Subject: [PATCH 19/33] add group_name argument to process_reference method --- openpype/hosts/maya/plugins/load/load_xgen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/load/load_xgen.py b/openpype/hosts/maya/plugins/load/load_xgen.py index 7e6cabc77c..ac646057f8 100644 --- a/openpype/hosts/maya/plugins/load/load_xgen.py +++ b/openpype/hosts/maya/plugins/load/load_xgen.py @@ -35,7 +35,7 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): xgd_file = xgen_file.replace(".xgen", ".xgd") return xgen_file, xgd_file - def process_reference(self, context, name, namespace, options): + def process_reference(self, context, name, namespace, options, group_name=None): # noqa # Validate workfile has a path. if current_file() is None: QtWidgets.QMessageBox.warning( From d13fc645e748cab03de7c5c87fb112baf15da5bd Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Mon, 3 Apr 2023 17:42:03 +0200 Subject: [PATCH 20/33] adapt custom namespace and group name to new settings + refactoring --- openpype/hosts/maya/api/plugin.py | 55 +++++++++++++++---------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index 82ed9c0282..b305984449 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -145,25 +145,15 @@ class ReferenceLoader(Loader): asset = context['asset'] subset = context['subset'] settings = get_project_settings(context['project']['name']) - custom_naming = settings['maya']['load']['reference_loader']['naming'] + custom_naming = settings['maya']['load']['reference_loader'] loaded_containers = [] - if ':' not in custom_naming: - raise ValueError( - "Wrong format for namespace, missing ':' separator" - ) - elif custom_naming.strip()[0] == ':': - raise ValueError( - "Wrong format for namespace, missing content before ':' " - "separator" - ) - elif custom_naming.strip()[-1] == ':': - raise ValueError( - "Wrong format for namespace, missing content after ':' " - "separator" - ) + if not custom_naming['namespace']: + raise ValueError("No namespace specified") + elif not custom_naming['group_name']: + raise ValueError("No group name specified") - custom_naming = custom_naming.format( + custom_namespace = custom_naming['namespace'].format( asset_name=asset['name'], asset_type=asset['type'], subset=subset['name'], @@ -173,22 +163,29 @@ class ReferenceLoader(Loader): ) ) + custom_group_name = custom_naming['group_name'].format( + asset_name=asset['name'], + asset_type=asset['type'], + subset=subset['name'], + family=( + subset['data'].get('family') or + subset['data']['families'][0] + ) + ) + + namespace = lib.unique_namespace( + custom_namespace, + prefix="_" if custom_namespace[0].isdigit() else "", + suffix="" + ) + group_name = "{}:{}".format( + namespace, + custom_group_name + ) + count = options.get("count") or 1 for c in range(0, count): - group_name = None - - namespace = custom_naming.split(':')[0] - namespace = lib.unique_namespace( - namespace, - prefix="_" if namespace[0].isdigit() else "", - suffix="" - ) - group_name = "{}:{}".format( - namespace, - custom_naming.split(":")[-1] - ) - # Offset loaded subset if "offset" in options: offset = [i * c for i in options["offset"]] From a62ab4fcc0a16a3fbfb3d8c3076e582d9a8ce7eb Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Tue, 4 Apr 2023 11:58:00 +0200 Subject: [PATCH 21/33] don't need to instanciate gorup_name since it is passed in arguments --- openpype/settings/defaults/project_settings/maya.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 6c9ac1c075..1f4858826f 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -1050,7 +1050,7 @@ }, "reference_loader": { "namespace": "{asset_name}_{subset}", - "group_name": "GRP" + "group_name": "_GRP" } }, "workfile_build": { From cf0debf04bfc2998617e0a511259971c4e7781ed Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Wed, 5 Apr 2023 11:09:57 +0200 Subject: [PATCH 22/33] move namespace and group_name logic into for loop --- openpype/hosts/maya/api/plugin.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index b305984449..62abf708ba 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -173,19 +173,19 @@ class ReferenceLoader(Loader): ) ) - namespace = lib.unique_namespace( - custom_namespace, - prefix="_" if custom_namespace[0].isdigit() else "", - suffix="" - ) - group_name = "{}:{}".format( - namespace, - custom_group_name - ) - count = options.get("count") or 1 for c in range(0, count): + namespace = lib.unique_namespace( + custom_namespace, + prefix="_" if custom_namespace[0].isdigit() else "", + suffix="" + ) + group_name = "{}:{}".format( + namespace, + custom_group_name + ) + # Offset loaded subset if "offset" in options: offset = [i * c for i in options["offset"]] @@ -221,7 +221,7 @@ class ReferenceLoader(Loader): return loaded_containers - def process_reference(self, context, name, namespace, data, group_name=None): # noqa + def process_reference(self, context, name, namespace, options, group_name=None): # noqa """To be implemented by subclass""" raise NotImplementedError("Must be implemented by subclass") From 9cfa795f47a92336b3f4699bddfd426a5650e957 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Wed, 5 Apr 2023 11:32:17 +0200 Subject: [PATCH 23/33] remove 'group_name' from arguments and add it to 'options' dict --- openpype/hosts/maya/api/plugin.py | 7 ++++--- openpype/hosts/maya/plugins/load/_load_animation.py | 4 ++-- openpype/hosts/maya/plugins/load/load_look.py | 2 +- openpype/hosts/maya/plugins/load/load_reference.py | 3 ++- openpype/hosts/maya/plugins/load/load_xgen.py | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index 62abf708ba..f09c68059a 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -186,6 +186,8 @@ class ReferenceLoader(Loader): custom_group_name ) + options['group_name'] = group_name + # Offset loaded subset if "offset" in options: offset = [i * c for i in options["offset"]] @@ -197,8 +199,7 @@ class ReferenceLoader(Loader): context=context, name=name, namespace=namespace, - options=options, - group_name=group_name + options=options ) # Only containerize if any nodes were loaded by the Loader @@ -221,7 +222,7 @@ class ReferenceLoader(Loader): return loaded_containers - def process_reference(self, context, name, namespace, options, group_name=None): # noqa + def process_reference(self, context, name, namespace, options): """To be implemented by subclass""" raise NotImplementedError("Must be implemented by subclass") diff --git a/openpype/hosts/maya/plugins/load/_load_animation.py b/openpype/hosts/maya/plugins/load/_load_animation.py index 24bff4ca6b..2ba5fe6b64 100644 --- a/openpype/hosts/maya/plugins/load/_load_animation.py +++ b/openpype/hosts/maya/plugins/load/_load_animation.py @@ -14,7 +14,7 @@ class AbcLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): icon = "code-fork" color = "orange" - def process_reference(self, context, name, namespace, data, group_name=None): # noqa + def process_reference(self, context, name, namespace, options): import maya.cmds as cmds from openpype.hosts.maya.api.lib import unique_namespace @@ -41,7 +41,7 @@ class AbcLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): namespace=namespace, sharedReferenceFile=False, groupReference=True, - groupName="{}:{}".format(namespace, name), + groupName=options['group_name'], reference=True, returnNewNodes=True) diff --git a/openpype/hosts/maya/plugins/load/load_look.py b/openpype/hosts/maya/plugins/load/load_look.py index a9d493382c..8f3e017658 100644 --- a/openpype/hosts/maya/plugins/load/load_look.py +++ b/openpype/hosts/maya/plugins/load/load_look.py @@ -28,7 +28,7 @@ class LookLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): icon = "code-fork" color = "orange" - def process_reference(self, context, name, namespace, options, group_name=None): # noqa + def process_reference(self, context, name, namespace, options): import maya.cmds as cmds with lib.maintained_selection(): diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index 9565d51580..c2b321b789 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -117,7 +117,7 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): # Name of creator class that will be used to create animation instance animation_creator_name = "CreateAnimation" - def process_reference(self, context, name, namespace, options, group_name=None): # noqa + def process_reference(self, context, name, namespace, options): import maya.cmds as cmds try: @@ -127,6 +127,7 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): # True by default to keep legacy behaviours attach_to_root = options.get("attach_to_root", True) + group_name = options["group_name"] with maintained_selection(): cmds.loadPlugin("AbcImport.mll", quiet=True) diff --git a/openpype/hosts/maya/plugins/load/load_xgen.py b/openpype/hosts/maya/plugins/load/load_xgen.py index ac646057f8..7e6cabc77c 100644 --- a/openpype/hosts/maya/plugins/load/load_xgen.py +++ b/openpype/hosts/maya/plugins/load/load_xgen.py @@ -35,7 +35,7 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): xgd_file = xgen_file.replace(".xgen", ".xgd") return xgen_file, xgd_file - def process_reference(self, context, name, namespace, options, group_name=None): # noqa + def process_reference(self, context, name, namespace, options): # Validate workfile has a path. if current_file() is None: QtWidgets.QMessageBox.warning( From fc928a198770148efe21630901eb3241ea3ab54d Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Wed, 5 Apr 2023 11:38:08 +0200 Subject: [PATCH 24/33] change RaiseError to OP LoadError --- openpype/hosts/maya/api/plugin.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index f09c68059a..494233bd97 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -12,6 +12,7 @@ from openpype.pipeline import ( AVALON_CONTAINER_ID, Anatomy, ) +from openpype.pipeline.load import LoadError from openpype.settings import get_project_settings from .pipeline import containerise from . import lib @@ -149,9 +150,11 @@ class ReferenceLoader(Loader): loaded_containers = [] if not custom_naming['namespace']: - raise ValueError("No namespace specified") + raise LoadError("No namespace specified in " + "Maya ReferenceLoader settings") elif not custom_naming['group_name']: - raise ValueError("No group name specified") + raise LoadError("No group name specified in " + "Maya ReferenceLoader settings") custom_namespace = custom_naming['namespace'].format( asset_name=asset['name'], From c38ee972460195f2b086739e276a6c59399be42c Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Thu, 6 Apr 2023 12:07:22 +0200 Subject: [PATCH 25/33] refactor custom_namespace and custom_group_name --- openpype/hosts/maya/api/plugin.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index 494233bd97..bb21da31b9 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -156,24 +156,22 @@ class ReferenceLoader(Loader): raise LoadError("No group name specified in " "Maya ReferenceLoader settings") - custom_namespace = custom_naming['namespace'].format( - asset_name=asset['name'], - asset_type=asset['type'], - subset=subset['name'], - family=( + formatting_data = { + "asset_name": asset['name'], + "asset_type": asset['type'], + "subset": subset['name'], + "family": ( subset['data'].get('family') or subset['data']['families'][0] ) + } + + custom_namespace = custom_naming['namespace'].format( + **formatting_data ) custom_group_name = custom_naming['group_name'].format( - asset_name=asset['name'], - asset_type=asset['type'], - subset=subset['name'], - family=( - subset['data'].get('family') or - subset['data']['families'][0] - ) + **formatting_data ) count = options.get("count") or 1 From 163e01542ca061162dd8be3850287032d4b0ff63 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Fri, 7 Apr 2023 14:34:22 +0200 Subject: [PATCH 26/33] create get_custom_namespace function to control the position of the unique index --- openpype/hosts/maya/api/plugin.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index bb21da31b9..7f66415016 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -1,4 +1,5 @@ import os +import re from maya import cmds @@ -83,6 +84,25 @@ def get_reference_node_parents(ref): return parents +def get_custom_namespace(custom_namespace): + split = re.split("([#]+)", custom_namespace, 1) + + if len(split) == 3: + base, padding, suffix = split + padding = "%0{}d".format(len(padding)) + else: + base = split[0] + padding = "%02d" + suffix = "" + + return lib.unique_namespace( + base, + format=padding, + prefix="_" if not base or base[0].isdigit() else "", + suffix=suffix + ) + + class Creator(LegacyCreator): defaults = ['Main'] @@ -177,11 +197,7 @@ class ReferenceLoader(Loader): count = options.get("count") or 1 for c in range(0, count): - namespace = lib.unique_namespace( - custom_namespace, - prefix="_" if custom_namespace[0].isdigit() else "", - suffix="" - ) + namespace = get_custom_namespace(custom_namespace) group_name = "{}:{}".format( namespace, custom_group_name From 265fec770bc0d9462133f71c83c1adfe848b8d12 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Fri, 7 Apr 2023 14:52:58 +0200 Subject: [PATCH 27/33] add docstrings for get_custom_namespace function --- openpype/hosts/maya/api/plugin.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index 7f66415016..714278ba6c 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -85,6 +85,25 @@ def get_reference_node_parents(ref): def get_custom_namespace(custom_namespace): + """Return unique namespace. + + The input namespace can contain a single group + of '#' number tokens to indicate where the namespace's + unique index should go. The amount of tokens defines + the zero padding of the number, e.g ### turns into 001. + + Warning: Note that a namespace will always be + prefixed with a _ if it starts with a digit + + Example: + >>> get_custom_namespace("myspace_##_") + # myspace_01_ + >>> get_custom_namespace("##_myspace") + # _01_myspace + >>> get_custom_namespace("myspace##") + # myspace01 + + """ split = re.split("([#]+)", custom_namespace, 1) if len(split) == 3: @@ -92,7 +111,7 @@ def get_custom_namespace(custom_namespace): padding = "%0{}d".format(len(padding)) else: base = split[0] - padding = "%02d" + padding = "%02d" # default padding suffix = "" return lib.unique_namespace( From 2304c32deed2b99dd039bef454940fc363558f52 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Fri, 7 Apr 2023 14:58:29 +0200 Subject: [PATCH 28/33] docs: add explanations for the index position in the namespace --- website/docs/admin_hosts_maya.md | 31 ++++++++++++++++++ .../assets/maya-admin_custom_namespace.png | Bin 14002 -> 12898 bytes 2 files changed, 31 insertions(+) diff --git a/website/docs/admin_hosts_maya.md b/website/docs/admin_hosts_maya.md index b7688989bc..3f61fde7ca 100644 --- a/website/docs/admin_hosts_maya.md +++ b/website/docs/admin_hosts_maya.md @@ -106,6 +106,37 @@ or Deadlines **Draft Tile Assembler**. This is useful to fix some specific renderer glitches and advanced hacking of Maya Scene files. `Patch name` is label for patch for easier orientation. `Patch regex` is regex used to find line in file, after `Patch line` string is inserted. Note that you need to add line ending. +## Load Plugins + +### Reference Loader + +#### Namespace and Group Name +Here you can create your own custom naming for the reference loader. + +The custom naming is split into two parts: namespace and group name. If you don't set the namespace or the group name, an error will occur. +Here's the different variables you can use: + +
+
+ +| Token | Description | +|---|---| +|`{asset_name}` | Asset name | +|`{asset_type}` | Asset type | +|`{subset}` | Subset name | +|`{family}` | Subset family | + +
+
+ +The namespace field can contain a single group of '#' number tokens to indicate where the namespace's unique index should go. The amount of tokens defines the zero padding of the number, e.g ### turns into 001. + +Warning: Note that a namespace will always be prefixed with a _ if it starts with a digit. + +Example: + +![Namespace and Group Name](assets/maya-admin_custom_namespace.png) + ### Extract GPU Cache ![Maya GPU Cache](assets/maya-admin_gpu_cache.png) diff --git a/website/docs/assets/maya-admin_custom_namespace.png b/website/docs/assets/maya-admin_custom_namespace.png index a65eef2e0b13c1bf8e6c56d7811b9665265dbbb0..80707ea72788d2ce8b72004cbd60b27e06ceec1f 100644 GIT binary patch literal 12898 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
~MXKjVIrtZo9;A+iR3(ZlWL-5zf>f*+ zh=P{o#yd2O691AC!_41n3STZOU}vCO3k}|uMCLNp{qVLe2*}+gO^3MBG6qWO{KR-FVm^FO&Azx95DHbj(yN(t=|sUF*Htw%JZsFJ_jn8mhtr?tUe5zm?U)I0WU-H z8Gwkz$Vv*L%6kQpZML={Ay1N_6-ZWDr8{>VIZ+o@{2M>Zv#RdwU{qC0>PK9%=W#u? z4rWy!10P?7khl{Z%YK6B4}ELhJ8l!vU-c{045+5P&}T|7-Z?bd|*AfmfJ6cy1TyC%?e z`Lb_`{q)a`QCl76*RSWiz^i1Ry#6imCveVpIXRKYhspVIaSBlaKmd!%iz^&mX3}DT zp#d|m7!dVd#C+irnE0&lRUcmxb4@=8k-v`}ibh^Zbx6^^x~gq{uITWaW-Wyk z-|to9V=Qh0xV+;tXw}87|8ke)V+hWOP%i^PEuUwC%tRnVH5qyjOIh~kLO+KTS$m3# zcC*o!!(0gyzh!OYmenOsv;PuD46Bj0vgHWj{>U_YL%DwDB{dh)6R zU*70rn{f++(`UtxpzNT+!iv#Wo2~PQwZfxON?D`y^fLqw@}O@h+|PJ zd8)8B2-xB5dF})>Rgza@g{-i;Z;Und^Hn#(4p!fKqxKk>Faq9 z1M&}S=ceF@aaMXm_P$IrfN#76c&m@kojGY85sJ0631U{eQ?XbpA0_pvpTJct^HS|b zQZV7pr!qC{%&n9gKU41Hn|_GBky}!7k=|Q)G`l9Z)LbzAU3&V*;U$2qxZf&)Nghr% z1T!+6ONk_!QMP>N^%$9p4#LbR2<%Mhka^LK?!LZy!mnB|#Scb1Qx5w7h&H=bSpgEkDftM5_8XHl`zbt&Dewk@EEEJ3G!{k*d`S05L5rw9Ibdu~MXa+h({_zDE_)(avZ7JNNjXK;y;Q@!ve$;3oH z%n=|Aq`7r-hcj|AMAB}b8}S+kB5G84_d~(v#zuZH!{%O+b^&+1T3$bpKbC9YY!9_i zECpa`dU^@)2{Y5up;2e?+ZZDl=O7~NH#`1WrE_JVGj-+lpYHVYsSabGU}}J$7pV*Z zJWux;YPw&rzdz35owfdSpiO~KI~+WHsWiJQ|3=b(JcDM|%e2|b%Fw)#kGw;6mBqAn zs|w*%Z3mTwUPkm~-62+btpc{HkF9XaTPBBR!u|b${p#r{Grl#lq!sl8XsZcD+`EUx zI};{~WpKH+&M1^Qd%^Oul`aMF5ZWJ3Ai4n(Oy<2oIk;P*bi^;~FxS@0T|oka#}{9? zP>|H`e)CPOX!ZCC%S-tQ^VE(_OTvV$S!b=gnPHfcDf_`hakyv2u&2ar`0w&hqK$B6 z->}7>>zh1@*C~&Ilw#myD=%RbehR>*>mfNIJ0*ZukN2yKbyZrH699pUsSZ7TeHi?g zmWVZK1#zakyF33MPB)-M6b{Z&3~2aNeH%31CTaKQ=g(eVs{Md!o9*wP8-K$RuO@0x zTx1#+kKzTs`SvLV&2(a5T7VWJSg-zmzh#gTvC-wr!(%aVMVy(u=7ocHJwVdlC^yl* z*q>(zed~<4dly$G!<|^2c|Uvha&r1OGY&sLS7?=SpMp554*K)9aB)3O;WX)+{ZeC& z7H@cw2ydz)$+nK#7$Uc(@%J*A_&On>xIvt-S{P1`=^)y9?hywTLBObYtAF#K{{_nt zqE|6sA4wZu8+Ecu3sFhVpZR5NaAwUL{8d3oLa<{T=Y8Z2NiHxi@w8 z5E9^=;NxhW@xMaEbyu4g&{KO4M7}fTj9FFkg}g}E*u^vNesuGW;0w6&cfBl z?O+xV5yJ@o4YVf)8eE582+UdiBEc-E*^>9-IpH)s!~iIbz*zIPmYBdgSF4L-p$-NE!R@hdszO!HtPQX Dgt{X@ literal 14002 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­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 From 264f111edaab8fd9a4c7812b62fef76cc4a022e0 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Fri, 7 Apr 2023 15:06:31 +0200 Subject: [PATCH 29/33] modify default value for custom namespace settings --- openpype/settings/defaults/project_settings/maya.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 1f4858826f..a8689524db 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -1049,7 +1049,7 @@ ] }, "reference_loader": { - "namespace": "{asset_name}_{subset}", + "namespace": "{asset_name}_{subset}_##", "group_name": "_GRP" } }, From caf25ddf13bf8f4567f85e819579e3b310c360c2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Apr 2023 10:52:09 +0200 Subject: [PATCH 30/33] Publisher: Plugin active attribute is respected (#4798) * added helper function to get publish plugin settings * use new function to get plugin settings * remove disabled settings when settings are applied * added some comments * added check for active state of publish plugin * docstring changes * limit the active state check only for 'OptionalPyblishPluginMixin' * apply suggestions * show plugin name if label is not available --- openpype/pipeline/publish/lib.py | 122 +++++++++++------- openpype/tools/publisher/control.py | 49 ++++++- .../publisher/publish_report_viewer/model.py | 3 +- 3 files changed, 118 insertions(+), 56 deletions(-) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index 81913bcdd5..265a9c7822 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -354,6 +354,61 @@ def publish_plugins_discover(paths=None): return result +def _get_plugin_settings(host_name, project_settings, plugin, log): + """Get plugin settings based on host name and plugin name. + + Args: + host_name (str): Name of host. + project_settings (dict[str, Any]): Project settings. + plugin (pyliblish.Plugin): Plugin where settings are applied. + log (logging.Logger): Logger to log messages. + + Returns: + dict[str, Any]: Plugin settings {'attribute': 'value'}. + """ + + # Use project settings from host name category when available + try: + return ( + project_settings + [host_name] + ["publish"] + [plugin.__name__] + ) + except KeyError: + pass + + # Settings category determined from path + # - usually path is './/plugins/publish/' + # - category can be host name of addon name ('maya', 'deadline', ...) + filepath = os.path.normpath(inspect.getsourcefile(plugin)) + + split_path = filepath.rsplit(os.path.sep, 5) + if len(split_path) < 4: + log.warning( + 'plugin path too short to extract host {}'.format(filepath) + ) + return {} + + category_from_file = split_path[-4] + plugin_kind = split_path[-2] + + # TODO: change after all plugins are moved one level up + if category_from_file == "openpype": + category_from_file = "global" + + try: + return ( + project_settings + [category_from_file] + [plugin_kind] + [plugin.__name__] + ) + except KeyError: + pass + return {} + + def filter_pyblish_plugins(plugins): """Pyblish plugin filter which applies OpenPype settings. @@ -372,21 +427,21 @@ def filter_pyblish_plugins(plugins): # TODO: Don't use host from 'pyblish.api' but from defined host by us. # - kept becau on farm is probably used host 'shell' which propably # affect how settings are applied there - host = pyblish.api.current_host() + host_name = pyblish.api.current_host() project_name = os.environ.get("AVALON_PROJECT") - project_setting = get_project_settings(project_name) + project_settings = get_project_settings(project_name) system_settings = get_system_settings() # iterate over plugins for plugin in plugins[:]: + # Apply settings to plugins if hasattr(plugin, "apply_settings"): + # Use classmethod 'apply_settings' + # - can be used to target settings from custom settings place + # - skip default behavior when successful try: - # Use classmethod 'apply_settings' - # - can be used to target settings from custom settings place - # - skip default behavior when successful - plugin.apply_settings(project_setting, system_settings) - continue + plugin.apply_settings(project_settings, system_settings) except Exception: log.warning( @@ -395,53 +450,20 @@ def filter_pyblish_plugins(plugins): ).format(plugin.__name__), exc_info=True ) - - try: - config_data = ( - project_setting - [host] - ["publish"] - [plugin.__name__] + else: + # Automated + plugin_settins = _get_plugin_settings( + host_name, project_settings, plugin, log ) - except KeyError: - # host determined from path - file = os.path.normpath(inspect.getsourcefile(plugin)) - file = os.path.normpath(file) - - split_path = file.split(os.path.sep) - if len(split_path) < 4: - log.warning( - 'plugin path too short to extract host {}'.format(file) - ) - continue - - host_from_file = split_path[-4] - plugin_kind = split_path[-2] - - # TODO: change after all plugins are moved one level up - if host_from_file == "openpype": - host_from_file = "global" - - try: - config_data = ( - project_setting - [host_from_file] - [plugin_kind] - [plugin.__name__] - ) - except KeyError: - continue - - for option, value in config_data.items(): - if option == "enabled" and value is False: - log.info('removing plugin {}'.format(plugin.__name__)) - plugins.remove(plugin) - else: - log.info('setting {}:{} on plugin {}'.format( + for option, value in plugin_settins.items(): + log.info("setting {}:{} on plugin {}".format( option, value, plugin.__name__)) - setattr(plugin, option, value) + # Remove disabled plugins + if getattr(plugin, "enabled", True) is False: + plugins.remove(plugin) + def find_close_plugin(close_plugin_name, log): if close_plugin_name: diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index b62ae7ecc1..7754e4aa02 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -6,6 +6,7 @@ import collections import uuid import tempfile import shutil +import inspect from abc import ABCMeta, abstractmethod import six @@ -26,8 +27,8 @@ from openpype.pipeline import ( PublishValidationError, KnownPublishError, registered_host, - legacy_io, get_process_id, + OptionalPyblishPluginMixin, ) from openpype.pipeline.create import ( CreateContext, @@ -2307,6 +2308,37 @@ class PublisherController(BasePublisherController): def _process_main_thread_item(self, item): item() + def _is_publish_plugin_active(self, plugin): + """Decide if publish plugin is active. + + This is hack because 'active' is mis-used in mixin + 'OptionalPyblishPluginMixin' where 'active' is used for default value + of optional plugins. Because of that is 'active' state of plugin + which inherit from 'OptionalPyblishPluginMixin' ignored. That affects + headless publishing inside host, potentially remote publishing. + + We have to change that to match pyblish base, but we can do that + only when all hosts use Publisher because the change requires + change of settings schemas. + + Args: + plugin (pyblish.Plugin): Plugin which should be checked if is + active. + + Returns: + bool: Is plugin active. + """ + + if plugin.active: + return True + + if not plugin.optional: + return False + + if OptionalPyblishPluginMixin in inspect.getmro(plugin): + return True + return False + def _publish_iterator(self): """Main logic center of publishing. @@ -2315,11 +2347,9 @@ class PublisherController(BasePublisherController): states of currently processed publish plugin and instance. Also change state of processed orders like validation order has passed etc. - Also stops publishing if should stop on validation. - - QUESTION: - Does validate button still make sense? + Also stops publishing, if should stop on validation. """ + for idx, plugin in enumerate(self._publish_plugins): self._publish_progress = idx @@ -2344,6 +2374,11 @@ class PublisherController(BasePublisherController): # Add plugin to publish report self._publish_report.add_plugin_iter(plugin, self._publish_context) + # WARNING This is hack fix for optional plugins + if not self._is_publish_plugin_active(plugin): + self._publish_report.set_plugin_skipped() + continue + # Trigger callback that new plugin is going to be processed plugin_label = plugin.__name__ if hasattr(plugin, "label") and plugin.label: @@ -2450,7 +2485,11 @@ def collect_families_from_instances(instances, only_active=False): instances(list): List of publish instances from which are families collected. only_active(bool): Return families only for active instances. + + Returns: + list[str]: Families available on instances. """ + all_families = set() for instance in instances: if only_active: diff --git a/openpype/tools/publisher/publish_report_viewer/model.py b/openpype/tools/publisher/publish_report_viewer/model.py index 37da4ab3f2..ff10e091b8 100644 --- a/openpype/tools/publisher/publish_report_viewer/model.py +++ b/openpype/tools/publisher/publish_report_viewer/model.py @@ -162,7 +162,8 @@ class PluginsModel(QtGui.QStandardItemModel): items = [] for plugin_item in plugin_items: - item = QtGui.QStandardItem(plugin_item.label) + label = plugin_item.label or plugin_item.name + item = QtGui.QStandardItem(label) item.setData(False, ITEM_IS_GROUP_ROLE) item.setData(plugin_item.label, ITEM_LABEL_ROLE) item.setData(plugin_item.id, ITEM_ID_ROLE) From 380930e06f67458baa9fac43500e78bc7c756647 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Apr 2023 11:41:08 +0200 Subject: [PATCH 31/33] Settings: Version settings popup fix (#4822) * added no mouse relay attribute to popup * completer is not shown if widget is not visible * remove unused import --- openpype/tools/settings/settings/widgets.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/openpype/tools/settings/settings/widgets.py b/openpype/tools/settings/settings/widgets.py index fd04cb0a23..1beb168709 100644 --- a/openpype/tools/settings/settings/widgets.py +++ b/openpype/tools/settings/settings/widgets.py @@ -4,7 +4,6 @@ from qtpy import QtWidgets, QtCore, QtGui import qtawesome from openpype.client import get_projects -from openpype.pipeline import AvalonMongoDB from openpype.style import get_objected_colors from openpype.tools.utils.widgets import ImageButton from openpype.tools.utils.lib import paint_image_with_color @@ -97,6 +96,7 @@ class CompleterView(QtWidgets.QListView): # Open the widget unactivated self.setAttribute(QtCore.Qt.WA_ShowWithoutActivating) + self.setAttribute(QtCore.Qt.WA_NoMouseReplay) delegate = QtWidgets.QStyledItemDelegate() self.setItemDelegate(delegate) @@ -241,6 +241,18 @@ class SettingsLineEdit(PlaceholderLineEdit): if self._completer is not None: self._completer.set_text_filter(text) + def _completer_should_be_visible(self): + return ( + self.isVisible() + and (self.hasFocus() or self._completer.hasFocus()) + ) + + def _show_completer(self): + if self._completer_should_be_visible(): + self._focus_timer.start() + self._completer.show() + self._update_completer() + def _update_completer(self): if self._completer is None or not self._completer.isVisible(): return @@ -249,7 +261,7 @@ class SettingsLineEdit(PlaceholderLineEdit): self._completer.move(new_point) def _on_focus_timer(self): - if not self.hasFocus() and not self._completer.hasFocus(): + if not self._completer_should_be_visible(): self._completer.hide() self._focus_timer.stop() @@ -258,9 +270,7 @@ class SettingsLineEdit(PlaceholderLineEdit): self.focused_in.emit() if self._completer is not None: - self._focus_timer.start() - self._completer.show() - self._update_completer() + self._show_completer() def paintEvent(self, event): super(SettingsLineEdit, self).paintEvent(event) From 2a8f6a12a4424505a45fe24ef5f4732b83f81857 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 11 Apr 2023 16:23:48 +0200 Subject: [PATCH 32/33] bug report workflow run from our fork --- .github/workflows/update_bug_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update_bug_report.yml b/.github/workflows/update_bug_report.yml index 322bee4ff2..c2e5e9af42 100644 --- a/.github/workflows/update_bug_report.yml +++ b/.github/workflows/update_bug_report.yml @@ -15,7 +15,7 @@ jobs: with: ref: ${{ github.event.release.target_commitish }} - name: Update version - uses: ShaMan123/gha-populate-form-version@v2.0.2 + uses: ynput/gha-populate-form-version@main with: github_token: ${{ secrets.YNPUT_BOT_TOKEN }} registry: github From 681ac5babda9393ed34479a844722e1dbd29d703 Mon Sep 17 00:00:00 2001 From: Thomas Fricard Date: Tue, 11 Apr 2023 16:47:50 +0200 Subject: [PATCH 33/33] remove doubled doc for namespace --- website/docs/admin_hosts_maya.md | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/website/docs/admin_hosts_maya.md b/website/docs/admin_hosts_maya.md index 3f61fde7ca..0dd86d6e7c 100644 --- a/website/docs/admin_hosts_maya.md +++ b/website/docs/admin_hosts_maya.md @@ -202,29 +202,6 @@ These options are set on the camera shape when publishing the review. They corre ![Extract Playblast Settings](assets/maya-admin_extract_playblast_settings_camera_options.png) -## Load Plugins - -### Reference Loader > Namespace -Here you can create your own custom naming for the reference loader. - -The custom naming is split into two parts separated by a ":" symbol. The first half is the namespace and the second half is the group name of the reference. If you don't set the namespace or the group name, an error will occur. -Here's the different variables you can use: - -
-
- -| Token | Description | -|---|---| -|`{asset_name}` | Asset name | -|`{asset_type}` | Asset type | -|`{subset}` | Subset name | -|`{family}` | Subset family | - -
-
- -Example: `{asset_name}_{subset}_:_GRP` - ## Custom Menu You can add your custom tools menu into Maya by extending definitions in **Maya -> Scripts Menu Definition**. ![Custom menu definition](assets/maya-admin_scriptsmenu.png)