From c31cdf94d18da2946d2c620460a7c5980527667b Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Tue, 27 Jul 2021 10:30:26 +0200 Subject: [PATCH 1/5] initial support for configurable dirmap --- openpype/hosts/maya/api/__init__.py | 18 ++++++++++ .../defaults/project_settings/maya.json | 13 ++++++++ .../defaults/project_settings/unreal.json | 3 +- .../projects_schema/schema_project_maya.json | 33 +++++++++++++++++++ 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/api/__init__.py b/openpype/hosts/maya/api/__init__.py index 4697d212dee..027fa871e8a 100644 --- a/openpype/hosts/maya/api/__init__.py +++ b/openpype/hosts/maya/api/__init__.py @@ -26,6 +26,24 @@ def install(): + from openpype.settings import get_project_settings + + project_settings = get_project_settings(os.getenv("AVALON_PROJECT")) + mapping = project_settings["maya"]["maya-dirmap"]["paths"] or {} + if mapping.get("source-path") and project_settings["maya"]["maya-dirmap"]["enabled"] is True: + log.info("Processing directory mapping ...") + cmds.dirmap(en=True) + for k, sp in enumerate(mapping["source-path"]): + try: + print("{} -> {}".format(sp, mapping["destination-path"][k])) + cmds.dirmap(m=[sp, mapping["destination-path"][k]]) + cmds.dirmap(m=[mapping["destination-path"][k], sp]) + except IndexError: + # missing corresponding destination path + log.error(("invalid dirmap mapping, missing corresponding" + " destination directory.")) + break + pyblish.register_plugin_path(PUBLISH_PATH) avalon.register_plugin_path(avalon.Loader, LOAD_PATH) avalon.register_plugin_path(avalon.Creator, CREATE_PATH) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 284a1a00409..b92dc52b923 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -7,6 +7,19 @@ "workfile": "ma", "yetiRig": "ma" }, + "maya-dirmap": { + "enabled": true, + "paths": { + "source-path": [ + "foo1", + "foo2" + ], + "destination-path": [ + "bar1", + "bar2" + ] + } + }, "create": { "CreateLook": { "enabled": true, diff --git a/openpype/settings/defaults/project_settings/unreal.json b/openpype/settings/defaults/project_settings/unreal.json index 46b9ca2a181..dad61cd1f02 100644 --- a/openpype/settings/defaults/project_settings/unreal.json +++ b/openpype/settings/defaults/project_settings/unreal.json @@ -1,6 +1,5 @@ { "project_setup": { - "dev_mode": true, - "install_unreal_python_engine": false + "dev_mode": true } } \ No newline at end of file diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json b/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json index 0a59cab5106..e70c0da708a 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json @@ -14,6 +14,39 @@ "type": "text" } }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "maya-dirmap", + "label": "Maya Directory Mapping", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "dict", + "key": "paths", + "children": [ + { + "type": "list", + "object_type": "text", + "key": "source-path", + "label": "Source Path" + }, + { + "type": "list", + "object_type": "text", + "key": "destination-path", + "label": "Destination Path" + } + ] + } + ] + }, { "type": "schema", "name": "schema_maya_create" From 7a7d44e628f212293ef0a4d71ee8885420944d7d Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Tue, 3 Aug 2021 16:15:09 +0200 Subject: [PATCH 2/5] better error handling --- openpype/hosts/maya/api/__init__.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/api/__init__.py b/openpype/hosts/maya/api/__init__.py index 027fa871e8a..7af22e2ca85 100644 --- a/openpype/hosts/maya/api/__init__.py +++ b/openpype/hosts/maya/api/__init__.py @@ -30,19 +30,25 @@ def install(): project_settings = get_project_settings(os.getenv("AVALON_PROJECT")) mapping = project_settings["maya"]["maya-dirmap"]["paths"] or {} - if mapping.get("source-path") and project_settings["maya"]["maya-dirmap"]["enabled"] is True: + mapping_enabled = project_settings["maya"]["maya-dirmap"]["enabled"] + if mapping.get("source-path") and mapping_enabled is True: log.info("Processing directory mapping ...") cmds.dirmap(en=True) for k, sp in enumerate(mapping["source-path"]): try: print("{} -> {}".format(sp, mapping["destination-path"][k])) - cmds.dirmap(m=[sp, mapping["destination-path"][k]]) - cmds.dirmap(m=[mapping["destination-path"][k], sp]) + cmds.dirmap(m=(sp, mapping["destination-path"][k])) + cmds.dirmap(m=(mapping["destination-path"][k], sp)) except IndexError: # missing corresponding destination path log.error(("invalid dirmap mapping, missing corresponding" " destination directory.")) break + except RuntimeError: + log.error("invalid path {} -> {}, mapping not registered".format( + sp, mapping["destination-path"][k] + )) + continue pyblish.register_plugin_path(PUBLISH_PATH) avalon.register_plugin_path(avalon.Loader, LOAD_PATH) From 0a9c335f9078f77c39c1efbf810ae0554b701797 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Tue, 3 Aug 2021 16:32:10 +0200 Subject: [PATCH 3/5] =?UTF-8?q?=E2=86=A9=EF=B8=8F=20backward=20compatibili?= =?UTF-8?q?ty?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- openpype/hosts/maya/api/__init__.py | 56 ++++++++++++++++++----------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/openpype/hosts/maya/api/__init__.py b/openpype/hosts/maya/api/__init__.py index 7af22e2ca85..9219da407f8 100644 --- a/openpype/hosts/maya/api/__init__.py +++ b/openpype/hosts/maya/api/__init__.py @@ -29,26 +29,8 @@ def install(): from openpype.settings import get_project_settings project_settings = get_project_settings(os.getenv("AVALON_PROJECT")) - mapping = project_settings["maya"]["maya-dirmap"]["paths"] or {} - mapping_enabled = project_settings["maya"]["maya-dirmap"]["enabled"] - if mapping.get("source-path") and mapping_enabled is True: - log.info("Processing directory mapping ...") - cmds.dirmap(en=True) - for k, sp in enumerate(mapping["source-path"]): - try: - print("{} -> {}".format(sp, mapping["destination-path"][k])) - cmds.dirmap(m=(sp, mapping["destination-path"][k])) - cmds.dirmap(m=(mapping["destination-path"][k], sp)) - except IndexError: - # missing corresponding destination path - log.error(("invalid dirmap mapping, missing corresponding" - " destination directory.")) - break - except RuntimeError: - log.error("invalid path {} -> {}, mapping not registered".format( - sp, mapping["destination-path"][k] - )) - continue + # process path mapping + process_dirmap(project_settings) pyblish.register_plugin_path(PUBLISH_PATH) avalon.register_plugin_path(avalon.Loader, LOAD_PATH) @@ -77,6 +59,40 @@ def install(): avalon.data["familiesStateToggled"] = ["imagesequence"] +def process_dirmap(project_settings): + # type: (dict) -> None + """Go through all paths in Settings and set them using `dirmap`. + + Args: + project_settings (dict): Settings for current project. + + """ + if not project_settings["maya"].get("maya-dirmap"): + return + mapping = project_settings["maya"]["maya-dirmap"]["paths"] or {} + mapping_enabled = project_settings["maya"]["maya-dirmap"]["enabled"] + if not mapping or not mapping_enabled: + return + if mapping.get("source-path") and mapping_enabled is True: + log.info("Processing directory mapping ...") + cmds.dirmap(en=True) + for k, sp in enumerate(mapping["source-path"]): + try: + print("{} -> {}".format(sp, mapping["destination-path"][k])) + cmds.dirmap(m=(sp, mapping["destination-path"][k])) + cmds.dirmap(m=(mapping["destination-path"][k], sp)) + except IndexError: + # missing corresponding destination path + log.error(("invalid dirmap mapping, missing corresponding" + " destination directory.")) + break + except RuntimeError: + log.error("invalid path {} -> {}, mapping not registered".format( + sp, mapping["destination-path"][k] + )) + continue + + def uninstall(): pyblish.deregister_plugin_path(PUBLISH_PATH) avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH) From 066765427e622fb4ef7988a6bc0828e5cda4a5ce Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Tue, 3 Aug 2021 16:54:06 +0200 Subject: [PATCH 4/5] =?UTF-8?q?add=20documentation=20=F0=9F=A7=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- website/docs/admin_hosts_maya.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/website/docs/admin_hosts_maya.md b/website/docs/admin_hosts_maya.md index 5e0aa153458..7a928483bb0 100644 --- a/website/docs/admin_hosts_maya.md +++ b/website/docs/admin_hosts_maya.md @@ -94,4 +94,9 @@ You can add your custom tools menu into Maya by extending definitions in **Maya :::note Work in progress This is still work in progress. Menu definition will be handled more friendly with widgets and not raw json. -::: \ No newline at end of file +::: + +## Multiplatform path mapping +You can configure path mapping using Maya `dirmap` command. This will add bi-directional mapping between +list of paths specified in **Settings**. You can find it in **Settings -> Project Settings -> Maya -> Maya Directory Mapping** +![Dirmap settings](assets/maya-admin_dirmap_settings.png) From af6282953f7952c9c682364cee11e5122c193093 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Tue, 3 Aug 2021 16:55:04 +0200 Subject: [PATCH 5/5] =?UTF-8?q?add=20image=20=F0=9F=96=BC=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../docs/assets/maya-admin_dirmap_settings.png | Bin 0 -> 15234 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 website/docs/assets/maya-admin_dirmap_settings.png diff --git a/website/docs/assets/maya-admin_dirmap_settings.png b/website/docs/assets/maya-admin_dirmap_settings.png new file mode 100644 index 0000000000000000000000000000000000000000..9d5780dfc815a49832bc33ea70f444adf38b3c20 GIT binary patch literal 15234 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 H#(w_?-F&;- literal 0 HcmV?d00001