Merge branch 'develop' into max_tweaks

This commit is contained in:
Sponge96 2024-02-14 08:55:24 +00:00 committed by GitHub
commit 09dfae0b93
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
177 changed files with 1647 additions and 2840 deletions

102
.github/pr-glob-labeler.yml vendored Normal file
View file

@ -0,0 +1,102 @@
# Add type: unittest label if any changes in tests folders
'type: unittest':
- '*/*tests*/**/*'
# any changes in documentation structure
'type: documentation':
- '*/**/*website*/**/*'
- '*/**/*docs*/**/*'
# hosts triage
'host: Nuke':
- '*/**/*nuke*'
- '*/**/*nuke*/**/*'
'host: Photoshop':
- '*/**/*photoshop*'
- '*/**/*photoshop*/**/*'
'host: Harmony':
- '*/**/*harmony*'
- '*/**/*harmony*/**/*'
'host: UE':
- '*/**/*unreal*'
- '*/**/*unreal*/**/*'
'host: Houdini':
- '*/**/*houdini*'
- '*/**/*houdini*/**/*'
'host: Maya':
- '*/**/*maya*'
- '*/**/*maya*/**/*'
'host: Resolve':
- '*/**/*resolve*'
- '*/**/*resolve*/**/*'
'host: Blender':
- '*/**/*blender*'
- '*/**/*blender*/**/*'
'host: Hiero':
- '*/**/*hiero*'
- '*/**/*hiero*/**/*'
'host: Fusion':
- '*/**/*fusion*'
- '*/**/*fusion*/**/*'
'host: Flame':
- '*/**/*flame*'
- '*/**/*flame*/**/*'
'host: TrayPublisher':
- '*/**/*traypublisher*'
- '*/**/*traypublisher*/**/*'
'host: 3dsmax':
- '*/**/*max*'
- '*/**/*max*/**/*'
'host: TV Paint':
- '*/**/*tvpaint*'
- '*/**/*tvpaint*/**/*'
'host: CelAction':
- '*/**/*celaction*'
- '*/**/*celaction*/**/*'
'host: After Effects':
- '*/**/*aftereffects*'
- '*/**/*aftereffects*/**/*'
'host: Substance Painter':
- '*/**/*substancepainter*'
- '*/**/*substancepainter*/**/*'
# modules triage
'module: Deadline':
- '*/**/*deadline*'
- '*/**/*deadline*/**/*'
'module: RoyalRender':
- '*/**/*royalrender*'
- '*/**/*royalrender*/**/*'
'module: Sitesync':
- '*/**/*sync_server*'
- '*/**/*sync_server*/**/*'
'module: Ftrack':
- '*/**/*ftrack*'
- '*/**/*ftrack*/**/*'
'module: Shotgrid':
- '*/**/*shotgrid*'
- '*/**/*shotgrid*/**/*'
'module: Kitsu':
- '*/**/*kitsu*'
- '*/**/*kitsu*/**/*'

View file

@ -7,3 +7,6 @@ AYON_CORE_ROOT = os.path.dirname(os.path.abspath(__file__))
PACKAGE_DIR = AYON_CORE_ROOT
PLUGINS_DIR = os.path.join(AYON_CORE_ROOT, "plugins")
AYON_SERVER_ENABLED = True
# Indicate if AYON entities should be used instead of OpenPype entities
USE_AYON_ENTITIES = False

View file

@ -31,7 +31,7 @@ AYON addons should contain separated logic of specific kind of implementation, s
- addon must implement `get_plugin_paths` which must return dictionary with possible keys `"publish"`, `"load"`, `"create"` or `"actions"`
- each key may contain list or string with a path to directory with plugins
## ITrayModule
## ITrayAddon
- addon has more logic when used in a tray
- it is possible that addon can be used only in the tray
- abstract methods
@ -46,7 +46,7 @@ AYON addons should contain separated logic of specific kind of implementation, s
- if addon has logic only in tray or for both then should be checking for `tray_initialized` attribute to decide how should handle situations
### ITrayService
- inherits from `ITrayModule` and implements `tray_menu` method for you
- inherits from `ITrayAddon` and implements `tray_menu` method for you
- adds action to submenu "Services" in tray widget menu with icon and label
- abstract attribute `label`
- label shown in menu
@ -57,7 +57,7 @@ AYON addons should contain separated logic of specific kind of implementation, s
- these states must be set by addon itself `set_service_running` is default state on initialization
### ITrayAction
- inherits from `ITrayModule` and implements `tray_menu` method for you
- inherits from `ITrayAddon` and implements `tray_menu` method for you
- adds action to tray widget menu with label
- abstract attribute `label`
- label shown in menu
@ -89,4 +89,4 @@ AYON addons should contain separated logic of specific kind of implementation, s
### TrayAddonsManager
- inherits from `AddonsManager`
- has specific implementation for Pype Tray tool and handle `ITrayModule` methods
- has specific implementation for Pype Tray tool and handle `ITrayAddon` methods

View file

@ -15,7 +15,7 @@ method to convert 'click_wrap' object to 'click' object.
Before
```python
import click
from ayon_core.modules import AYONAddon
from ayon_core.addon import AYONAddon
class ExampleAddon(AYONAddon):
@ -40,7 +40,7 @@ def mycommand(arg1, arg2):
Now
```
from ayon_core import click_wrap
from ayon_core.modules import AYONAddon
from ayon_core.addon import AYONAddon
class ExampleAddon(AYONAddon):
@ -72,7 +72,7 @@ Added small enhancements:
Example:
```python
from ayon_core import click_wrap
from ayon_core.modules import AYONAddon
from ayon_core.addon import AYONAddon
class ExampleAddon(AYONAddon):

View file

@ -92,8 +92,8 @@ class HostDirmap(object):
self.on_enable_dirmap()
for k, sp in enumerate(mapping["source-path"]):
dst = mapping["destination-path"][k]
for k, sp in enumerate(mapping["source_path"]):
dst = mapping["destination_path"][k]
try:
# add trailing slash if missing
sp = os.path.join(sp, '')
@ -116,7 +116,7 @@ class HostDirmap(object):
continue
def get_mappings(self):
"""Get translation from source-path to destination-path.
"""Get translation from source_path to destination_path.
It checks if Site Sync is enabled and user chose to use local
site, in that case configuration in Local Settings takes precedence
@ -138,8 +138,8 @@ class HostDirmap(object):
if (
not mapping
or not mapping.get("destination-path")
or not mapping.get("source-path")
or not mapping.get("destination_path")
or not mapping.get("source_path")
):
return {}
self.log.info("Processing directory mapping ...")
@ -154,7 +154,7 @@ class HostDirmap(object):
in Local Settings.
Returns:
dict : { "source-path": [XXX], "destination-path": [YYYY]}
dict : { "source_path": [XXX], "destination_path": [YYYY]}
"""
project_name = self.project_name
@ -210,13 +210,13 @@ class HostDirmap(object):
continue
if os.path.isdir(active_site_dir):
if "destination-path" not in mapping:
mapping["destination-path"] = []
mapping["destination-path"].append(active_site_dir)
if "destination_path" not in mapping:
mapping["destination_path"] = []
mapping["destination_path"].append(active_site_dir)
if "source-path" not in mapping:
mapping["source-path"] = []
mapping["source-path"].append(remote_site_dir)
if "source_path" not in mapping:
mapping["source_path"] = []
mapping["source_path"].append(remote_site_dir)
self.log.debug("local sync mapping:: {}".format(mapping))
return mapping

View file

@ -49,7 +49,6 @@ class HostBase(object):
Todo:
- move content of 'install_host' as method of this class
- register host object
- install legacy_io
- install global plugin paths
- store registered plugin paths to this object
- handle current context (project, asset, task)
@ -133,8 +132,6 @@ class HostBase(object):
can be opened multiple workfiles at one moment and change of context
can't be caught properly.
Default implementation returns values from 'legacy_io.Session'.
Returns:
Dict[str, Union[str, None]]: Context with 3 keys 'project_name',
'asset_name' and 'task_name'. All of them can be 'None'.

View file

@ -1,13 +1,10 @@
from ayon_core.modules import OpenPypeModule, IHostAddon
from ayon_core.addon import AYONAddon, IHostAddon
class AfterEffectsAddon(OpenPypeModule, IHostAddon):
class AfterEffectsAddon(AYONAddon, IHostAddon):
name = "aftereffects"
host_name = "aftereffects"
def initialize(self, module_settings):
self.enabled = True
def add_implementation_envs(self, env, _app):
"""Modify environments to contain all required for implementation."""
defaults = {

View file

@ -15,9 +15,8 @@ from wsrpc_aiohttp import (
from qtpy import QtCore
from ayon_core.lib import Logger
from ayon_core.tests.lib import is_in_tests
from ayon_core.pipeline import install_host, legacy_io
from ayon_core.lib import Logger, is_in_tests
from ayon_core.pipeline import install_host
from ayon_core.addon import AddonsManager
from ayon_core.tools.utils import host_tools, get_ayon_qt_app
from ayon_core.tools.adobe_webserver.app import WebServerTool
@ -298,13 +297,10 @@ class AfterEffectsRoute(WebSocketRoute):
log.info("Setting context change")
log.info("project {} asset {} ".format(project, asset))
if project:
legacy_io.Session["AVALON_PROJECT"] = project
os.environ["AVALON_PROJECT"] = project
if asset:
legacy_io.Session["AVALON_ASSET"] = asset
os.environ["AVALON_ASSET"] = asset
if task:
legacy_io.Session["AVALON_TASK"] = task
os.environ["AVALON_TASK"] = task
async def read(self):

View file

@ -1,16 +1,13 @@
import os
from ayon_core.modules import OpenPypeModule, IHostAddon
from ayon_core.addon import AYONAddon, IHostAddon
BLENDER_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
class BlenderAddon(OpenPypeModule, IHostAddon):
class BlenderAddon(AYONAddon, IHostAddon):
name = "blender"
host_name = "blender"
def initialize(self, module_settings):
self.enabled = True
def add_implementation_envs(self, env, _app):
"""Modify environments to contain all required for implementation."""
# Prepare path to implementation script

View file

@ -19,7 +19,6 @@ from ayon_core.host import (
from ayon_core.client import get_asset_by_name
from ayon_core.pipeline import (
schema,
legacy_io,
get_current_project_name,
get_current_asset_name,
register_loader_plugin_path,
@ -380,7 +379,7 @@ def _on_task_changed():
# `directory` attribute, so it opens in that directory (does it?).
# https://docs.blender.org/api/blender2.8/bpy.types.Operator.html#calling-a-file-selector
# https://docs.blender.org/api/blender2.8/bpy.types.WindowManager.html#bpy.types.WindowManager.fileselect_add
workdir = legacy_io.Session["AVALON_WORKDIR"]
workdir = os.getenv("AVALON_WORKDIR")
log.debug("New working directory: %s", workdir)

View file

@ -1,9 +1,11 @@
import os
import json
import clique
import pyblish.api
import bpy
import pyblish.api
from ayon_core.pipeline import publish
from ayon_core.hosts.blender.api import capture
from ayon_core.hosts.blender.api.lib import maintained_time
@ -23,6 +25,8 @@ class ExtractPlayblast(publish.Extractor, publish.OptionalPyblishPluginMixin):
optional = True
order = pyblish.api.ExtractorOrder + 0.01
presets = "{}"
def process(self, instance):
if not self.is_active(instance.data):
return
@ -59,8 +63,7 @@ class ExtractPlayblast(publish.Extractor, publish.OptionalPyblishPluginMixin):
self.log.debug(f"Outputting images to {path}")
project_settings = instance.context.data["project_settings"]["blender"]
presets = project_settings["publish"]["ExtractPlayblast"]["presets"]
presets = json.loads(self.presets)
preset = presets.get("default")
preset.update({
"camera": camera,

View file

@ -1,5 +1,6 @@
import os
import glob
import json
import pyblish.api
from ayon_core.pipeline import publish
@ -21,7 +22,7 @@ class ExtractThumbnail(publish.Extractor):
hosts = ["blender"]
families = ["review"]
order = pyblish.api.ExtractorOrder + 0.01
presets = {}
presets = "{}"
def process(self, instance):
self.log.debug("Extracting capture..")
@ -44,7 +45,8 @@ class ExtractThumbnail(publish.Extractor):
family = instance.data.get("family")
isolate = instance.data("isolate", None)
preset = self.presets.get(family, {})
presets = json.loads(self.presets)
preset = presets.get(family, {})
preset.update({
"camera": camera,

View file

@ -1,16 +1,13 @@
import os
from ayon_core.modules import OpenPypeModule, IHostAddon
from ayon_core.addon import AYONAddon, IHostAddon
CELACTION_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
class CelactionAddon(OpenPypeModule, IHostAddon):
class CelactionAddon(AYONAddon, IHostAddon):
name = "celaction"
host_name = "celaction"
def initialize(self, module_settings):
self.enabled = True
def get_launch_hook_paths(self, app):
if app.host_name != self.host_name:
return []

View file

@ -1,16 +1,13 @@
import os
from ayon_core.modules import OpenPypeModule, IHostAddon
from ayon_core.addon import AYONAddon, IHostAddon
HOST_DIR = os.path.dirname(os.path.abspath(__file__))
class FlameAddon(OpenPypeModule, IHostAddon):
class FlameAddon(AYONAddon, IHostAddon):
name = "flame"
host_name = "flame"
def initialize(self, module_settings):
self.enabled = True
def add_implementation_envs(self, env, _app):
# Add requirements to DL_PYTHON_HOOK_PATH
env["DL_PYTHON_HOOK_PATH"] = os.path.join(HOST_DIR, "startup")

View file

@ -50,17 +50,28 @@ class LoadClipBatch(opfapi.ClipLoader):
version_name = version.get("name", None)
colorspace = self.get_colorspace(context)
# TODO remove '{folder[name]}' and '{product[name]}' replacement
clip_name_template = (
self.clip_name_template
.replace("{folder[name]}", "{asset}")
.replace("{product[name]}", "{subset}")
)
layer_rename_template = (
self.layer_rename_template
.replace("{folder[name]}", "{asset}")
.replace("{product[name]}", "{subset}")
)
# in case output is not in context replace key to representation
if not context["representation"]["context"].get("output"):
self.clip_name_template = self.clip_name_template.replace(
clip_name_template = clip_name_template.replace(
"output", "representation")
self.layer_rename_template = self.layer_rename_template.replace(
layer_rename_template = layer_rename_template.replace(
"output", "representation")
formatting_data = deepcopy(context["representation"]["context"])
formatting_data["batch"] = self.batch.name.get_value()
clip_name = StringTemplate(self.clip_name_template).format(
clip_name = StringTemplate(clip_name_template).format(
formatting_data)
# convert colorspace with ocio to flame mapping
@ -86,7 +97,7 @@ class LoadClipBatch(opfapi.ClipLoader):
"path": path.replace("\\", "/"),
"colorspace": colorspace,
"version": "v{:0>3}".format(version_name),
"layer_rename_template": self.layer_rename_template,
"layer_rename_template": layer_rename_template,
"layer_rename_patterns": self.layer_rename_patterns,
"context_data": formatting_data
}

View file

@ -1,6 +1,5 @@
import os
import re
import tempfile
from copy import deepcopy
import pyblish.api
@ -15,12 +14,12 @@ from ayon_core.pipeline.editorial import (
import flame
class ExtractSubsetResources(publish.Extractor):
class ExtractProductResources(publish.Extractor):
"""
Extractor for transcoding files from Flame clip
"""
label = "Extract subset resources"
label = "Extract product resources"
order = pyblish.api.ExtractorOrder
families = ["clip"]
hosts = ["flame"]
@ -47,7 +46,7 @@ class ExtractSubsetResources(publish.Extractor):
hide_ui_on_process = True
# settings
export_presets_mapping = {}
export_presets_mapping = []
def process(self, instance):
if not self.keep_original_representation:
@ -146,15 +145,21 @@ class ExtractSubsetResources(publish.Extractor):
# append staging dir for later cleanup
instance.context.data["cleanupFullPaths"].append(staging_dir)
export_presets_mapping = {}
for preset_mapping in deepcopy(self.export_presets_mapping):
name = preset_mapping.pop("name")
export_presets_mapping[name] = preset_mapping
# add default preset type for thumbnail and reviewable video
# update them with settings and override in case the same
# are found in there
_preset_keys = [k.split('_')[0] for k in self.export_presets_mapping]
_preset_keys = [k.split('_')[0] for k in export_presets_mapping]
export_presets = {
k: v for k, v in deepcopy(self.default_presets).items()
k: v
for k, v in deepcopy(self.default_presets).items()
if k not in _preset_keys
}
export_presets.update(self.export_presets_mapping)
export_presets.update(export_presets_mapping)
if not instance.data.get("versionData"):
instance.data["versionData"] = {}

View file

@ -1,6 +1,6 @@
import os
import re
from ayon_core.modules import OpenPypeModule, IHostAddon
from ayon_core.addon import AYONAddon, IHostAddon
from ayon_core.lib import Logger
FUSION_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
@ -48,13 +48,10 @@ def get_fusion_version(app_name):
)
class FusionAddon(OpenPypeModule, IHostAddon):
class FusionAddon(AYONAddon, IHostAddon):
name = "fusion"
host_name = "fusion"
def initialize(self, module_settings):
self.enabled = True
def get_launch_hook_paths(self, app):
if app.host_name != self.host_name:
return []

View file

@ -11,7 +11,6 @@ from ayon_core.lib import (
EnumDef,
)
from ayon_core.pipeline import (
legacy_io,
Creator,
CreatedInstance
)
@ -136,7 +135,7 @@ class GenericCreateSaver(Creator):
ext = data["creator_attributes"]["image_format"]
# Subset change detected
workdir = os.path.normpath(legacy_io.Session["AVALON_WORKDIR"])
workdir = os.path.normpath(os.getenv("AVALON_WORKDIR"))
formatting_data.update({
"workdir": workdir,
"frame": "0" * frame_padding,
@ -148,7 +147,16 @@ class GenericCreateSaver(Creator):
})
# build file path to render
filepath = self.temp_rendering_path_template.format(**formatting_data)
# TODO make sure the keys are available in 'formatting_data'
temp_rendering_path_template = (
self.temp_rendering_path_template
.replace("{product[name]}", "{subset}")
.replace("{product[type]}", "{family}")
.replace("{folder[name]}", "{asset}")
.replace("{task[name]}", "{task}")
)
filepath = temp_rendering_path_template.format(**formatting_data)
comp = get_current_comp()
tool["Clip"] = comp.ReverseMapPath(os.path.normpath(filepath))

View file

@ -1,16 +1,13 @@
import os
from ayon_core.modules import OpenPypeModule, IHostAddon
from ayon_core.addon import AYONAddon, IHostAddon
HARMONY_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
class HarmonyAddon(OpenPypeModule, IHostAddon):
class HarmonyAddon(AYONAddon, IHostAddon):
name = "harmony"
host_name = "harmony"
def initialize(self, module_settings):
self.enabled = True
def add_implementation_envs(self, env, _app):
"""Modify environments to contain all required for implementation."""
openharmony_path = os.path.join(

View file

@ -52,7 +52,7 @@ Because Harmony projects are directories, this integration uses `.zip` as work f
### Show Workfiles on launch
You can show the Workfiles app when Harmony launches by setting environment variable `AVALON_HARMONY_WORKFILES_ON_LAUNCH=1`.
You can show the Workfiles app when Harmony launches by setting environment variable `AYON_HARMONY_WORKFILES_ON_LAUNCH=1`.
## Developing

View file

@ -349,7 +349,7 @@ function start() {
/** hostname or ip of server - should be localhost */
var host = '127.0.0.1';
/** port of the server */
var port = parseInt(System.getenv('AVALON_HARMONY_PORT'));
var port = parseInt(System.getenv('AYON_HARMONY_PORT'));
// Attach the client to the QApplication to preserve.
var app = QCoreApplication.instance();

View file

@ -189,14 +189,14 @@ def launch(application_path, *args):
install_host(harmony)
ProcessContext.port = random.randrange(49152, 65535)
os.environ["AVALON_HARMONY_PORT"] = str(ProcessContext.port)
os.environ["AYON_HARMONY_PORT"] = str(ProcessContext.port)
ProcessContext.application_path = application_path
# Launch Harmony.
setup_startup_scripts()
check_libs()
if not os.environ.get("AVALON_HARMONY_WORKFILES_ON_LAUNCH", False):
if not os.environ.get("AYON_HARMONY_WORKFILES_ON_LAUNCH", False):
open_empty_workfile()
return

View file

@ -1,17 +1,14 @@
import os
import platform
from ayon_core.modules import OpenPypeModule, IHostAddon
from ayon_core.addon import AYONAddon, IHostAddon
HIERO_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
class HieroAddon(OpenPypeModule, IHostAddon):
class HieroAddon(AYONAddon, IHostAddon):
name = "hiero"
host_name = "hiero"
def initialize(self, module_settings):
self.enabled = True
def add_implementation_envs(self, env, _app):
# Add requirements to HIERO_PLUGIN_PATH
new_hiero_paths = [

View file

@ -1,16 +1,13 @@
import os
from ayon_core.modules import OpenPypeModule, IHostAddon
from ayon_core.addon import AYONAddon, IHostAddon
HOUDINI_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
class HoudiniAddon(OpenPypeModule, IHostAddon):
class HoudiniAddon(AYONAddon, IHostAddon):
name = "houdini"
host_name = "houdini"
def initialize(self, module_settings):
self.enabled = True
def add_implementation_envs(self, env, _app):
# Add requirements to HOUDINI_PATH and HOUDINI_MENU_PATH
startup_path = os.path.join(HOUDINI_HOST_DIR, "startup")

View file

@ -6,6 +6,7 @@ import re
import uuid
import logging
import json
from contextlib import contextmanager
import six

View file

@ -7,7 +7,7 @@ from qtpy import QtWidgets, QtCore, QtGui
from ayon_core import style
from ayon_core.client import get_asset_by_name
from ayon_core.pipeline import legacy_io, get_current_project_name
from ayon_core.pipeline import get_current_project_name
from ayon_core.tools.utils.assets_widget import SingleSelectAssetsWidget
from pxr import Sdf
@ -27,7 +27,8 @@ class SelectAssetDialog(QtWidgets.QWidget):
self.setWindowTitle("Pick Asset")
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Popup)
assets_widget = SingleSelectAssetsWidget(legacy_io, parent=self)
assets_widget = SingleSelectAssetsWidget(self)
assets_widget.set_project_name(get_current_project_name(), False)
layout = QtWidgets.QHBoxLayout(self)
layout.addWidget(assets_widget)

View file

@ -1,17 +1,14 @@
# -*- coding: utf-8 -*-
import os
from ayon_core.modules import OpenPypeModule, IHostAddon
from ayon_core.addon import AYONAddon, IHostAddon
MAX_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
class MaxAddon(OpenPypeModule, IHostAddon):
class MaxAddon(AYONAddon, IHostAddon):
name = "max"
host_name = "max"
def initialize(self, module_settings):
self.enabled = True
def add_implementation_envs(self, env, _app):
# Remove auto screen scale factor for Qt
# - let 3dsmax decide it's value

View file

@ -59,6 +59,9 @@ class MaxHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
rt.callbacks.addScript(rt.Name('filePostOpen'),
lib.check_colorspace)
rt.callbacks.addScript(rt.Name('postWorkspaceChange'),
self._deferred_menu_creation)
def workfile_has_unsaved_changes(self):
return rt.getSaveRequired()

View file

@ -155,7 +155,9 @@ class ExtractPointCloud(publish.Extractor):
custom_attr_list = []
attr_settings = self.settings["attribute"]
for key, value in attr_settings.items():
for attr in attr_settings:
key = attr["name"]
value = attr["value"]
custom_attr = "{0}.PRTChannels_{1}=True".format(operator,
value)
self.log.debug(

View file

@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
"""Validator for Attributes."""
import json
from pyblish.api import ContextPlugin, ValidatorOrder
from pymxs import runtime as rt
@ -61,9 +63,13 @@ class ValidateAttributes(OptionalPyblishPluginMixin,
@classmethod
def get_invalid(cls, context):
attributes = (
context.data["project_settings"]["max"]["publish"]
["ValidateAttributes"]["attributes"]
attributes = json.loads(
context.data
["project_settings"]
["max"]
["publish"]
["ValidateAttributes"]
["attributes"]
)
if not attributes:
return
@ -112,9 +118,13 @@ class ValidateAttributes(OptionalPyblishPluginMixin,
@classmethod
def repair(cls, context):
attributes = (
context.data["project_settings"]["max"]["publish"]
["ValidateAttributes"]["attributes"]
attributes = json.loads(
context.data
["project_settings"]
["max"]
["publish"]
["ValidateAttributes"]
["attributes"]
)
invalid_attributes = cls.get_invalid(context)
for attrs in invalid_attributes:

View file

@ -25,7 +25,7 @@ class ValidateLoadedPlugin(OptionalPyblishPluginMixin,
optional = True
actions = [RepairAction]
family_plugins_mapping = {}
family_plugins_mapping = []
@classmethod
def get_invalid(cls, instance):
@ -34,6 +34,12 @@ class ValidateLoadedPlugin(OptionalPyblishPluginMixin,
if not family_plugins_mapping:
return
# Backward compatibility - settings did have 'product_types'
if "product_types" in family_plugins_mapping:
family_plugins_mapping["families"] = family_plugins_mapping.pop(
"product_types"
)
invalid = []
# Find all plug-in requirements for current instance
instance_families = {instance.data["family"]}
@ -47,7 +53,9 @@ class ValidateLoadedPlugin(OptionalPyblishPluginMixin,
if not mapping:
return
match_families = {fam.strip() for fam in mapping["families"]}
match_families = {
fam.strip() for fam in mapping["families"]
}
has_match = "*" in match_families or match_families.intersection(
instance_families)

View file

@ -59,7 +59,9 @@ class ValidatePointCloud(pyblish.api.InstancePlugin):
event_name = sub_anim.name
opt = "${0}.{1}.export_particles".format(sel.name,
event_name)
for key, value in attr_settings.items():
for attr in attr_settings:
key = attr["name"]
value = attr["value"]
custom_attr = "{0}.PRTChannels_{1}".format(opt,
value)
try:

View file

@ -1,16 +1,13 @@
import os
from ayon_core.modules import OpenPypeModule, IHostAddon
from ayon_core.addon import AYONAddon, IHostAddon
MAYA_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
class MayaAddon(OpenPypeModule, IHostAddon):
class MayaAddon(AYONAddon, IHostAddon):
name = "maya"
host_name = "maya"
def initialize(self, module_settings):
self.enabled = True
def add_implementation_envs(self, env, _app):
# Add requirements to PYTHONPATH
new_python_paths = [

View file

@ -329,7 +329,7 @@ def generate_capture_preset(instance, camera, path,
# Update preset with current panel setting
# if override_viewport_options is turned off
if not capture_preset["Viewport Options"]["override_viewport_options"]:
if not capture_preset["ViewportOptions"]["override_viewport_options"]:
panel_preset = capture.parse_view(preset["panel"])
panel_preset.pop("camera")
preset.update(panel_preset)
@ -2937,14 +2937,15 @@ def load_capture_preset(data):
options.update(data["Generic"])
options.update(data["Resolution"])
camera_options.update(data['Camera Options'])
camera_options.update(data["CameraOptions"])
viewport_options.update(data["Renderer"])
# DISPLAY OPTIONS
disp_options = {}
for key, value in data['Display Options'].items():
if key.startswith('background'):
for key, value in data["DisplayOptions"].items():
if key.startswith("background"):
# Convert background, backgroundTop, backgroundBottom colors
if len(value) == 4:
# Ignore alpha + convert RGB to float
value = [
@ -2956,7 +2957,7 @@ def load_capture_preset(data):
elif key == "displayGradient":
disp_options[key] = value
options['display_options'] = disp_options
options["display_options"] = disp_options
# Viewport Options has a mixture of Viewport2 Options and Viewport Options
# to pass along to capture. So we'll need to differentiate between the two
@ -2981,7 +2982,7 @@ def load_capture_preset(data):
"motionBlurShutterOpenFraction",
"lineAAEnable"
}
for key, value in data['Viewport Options'].items():
for key, value in data["ViewportOptions"].items():
# There are some keys we want to ignore
if key in {"override_viewport_options", "high_quality"}:
@ -3140,119 +3141,6 @@ def fix_incompatible_containers():
"ReferenceLoader", type="string")
def _null(*args):
pass
class shelf():
'''A simple class to build shelves in maya. Since the build method is empty,
it should be extended by the derived class to build the necessary shelf
elements. By default it creates an empty shelf called "customShelf".'''
###########################################################################
'''This is an example shelf.'''
# class customShelf(_shelf):
# def build(self):
# self.addButon(label="button1")
# self.addButon("button2")
# self.addButon("popup")
# p = cmds.popupMenu(b=1)
# self.addMenuItem(p, "popupMenuItem1")
# self.addMenuItem(p, "popupMenuItem2")
# sub = self.addSubMenu(p, "subMenuLevel1")
# self.addMenuItem(sub, "subMenuLevel1Item1")
# sub2 = self.addSubMenu(sub, "subMenuLevel2")
# self.addMenuItem(sub2, "subMenuLevel2Item1")
# self.addMenuItem(sub2, "subMenuLevel2Item2")
# self.addMenuItem(sub, "subMenuLevel1Item2")
# self.addMenuItem(p, "popupMenuItem3")
# self.addButon("button3")
# customShelf()
###########################################################################
def __init__(self, name="customShelf", iconPath="", preset={}):
self.name = name
self.iconPath = iconPath
self.labelBackground = (0, 0, 0, 0)
self.labelColour = (.9, .9, .9)
self.preset = preset
self._cleanOldShelf()
cmds.setParent(self.name)
self.build()
def build(self):
'''This method should be overwritten in derived classes to actually
build the shelf elements. Otherwise, nothing is added to the shelf.'''
for item in self.preset['items']:
if not item.get('command'):
item['command'] = self._null
if item['type'] == 'button':
self.addButon(item['name'],
command=item['command'],
icon=item['icon'])
if item['type'] == 'menuItem':
self.addMenuItem(item['parent'],
item['name'],
command=item['command'],
icon=item['icon'])
if item['type'] == 'subMenu':
self.addMenuItem(item['parent'],
item['name'],
command=item['command'],
icon=item['icon'])
def addButon(self, label, icon="commandButton.png",
command=_null, doubleCommand=_null):
'''
Adds a shelf button with the specified label, command,
double click command and image.
'''
cmds.setParent(self.name)
if icon:
icon = os.path.join(self.iconPath, icon)
print(icon)
cmds.shelfButton(width=37, height=37, image=icon, label=label,
command=command, dcc=doubleCommand,
imageOverlayLabel=label, olb=self.labelBackground,
olc=self.labelColour)
def addMenuItem(self, parent, label, command=_null, icon=""):
'''
Adds a shelf button with the specified label, command,
double click command and image.
'''
if icon:
icon = os.path.join(self.iconPath, icon)
print(icon)
return cmds.menuItem(p=parent, label=label, c=command, i="")
def addSubMenu(self, parent, label, icon=None):
'''
Adds a sub menu item with the specified label and icon to
the specified parent popup menu.
'''
if icon:
icon = os.path.join(self.iconPath, icon)
print(icon)
return cmds.menuItem(p=parent, label=label, i=icon, subMenu=1)
def _cleanOldShelf(self):
'''
Checks if the shelf exists and empties it if it does
or creates it if it does not.
'''
if cmds.shelfLayout(self.name, ex=1):
if cmds.shelfLayout(self.name, q=1, ca=1):
for each in cmds.shelfLayout(self.name, q=1, ca=1):
cmds.deleteUI(each)
else:
cmds.shelfLayout(self.name, p="ShelfLayout")
def update_content_on_context_change():
"""
This will update scene content to match new asset on context change
@ -4059,10 +3947,10 @@ def get_capture_preset(task_name, task_type, subset, project_settings, log):
Args:
task_name (str): Task name.
take_type (str): Task type.
task_type (str): Task type.
subset (str): Subset name.
project_settings (dict): Project settings.
log (object): Logging object.
log (logging.Logger): Logging object.
"""
capture_preset = None
filtering_criteria = {
@ -4091,8 +3979,18 @@ def get_capture_preset(task_name, task_type, subset, project_settings, log):
"Falling back to deprecated Extract Playblast capture preset "
"because no new style playblast profiles are defined."
)
capture_preset = plugin_settings["capture_preset"]
capture_preset = plugin_settings.get("capture_preset")
if capture_preset:
# Create deepcopy of preset as we'll change the values
capture_preset = copy.deepcopy(capture_preset)
viewport_options = capture_preset["ViewportOptions"]
# Change 'list' to 'dict' for 'capture.py'
viewport_options["pluginObjects"] = {
item["name"]: item["value"]
for item in viewport_options["pluginObjects"]
}
return capture_preset or {}

View file

@ -46,7 +46,7 @@ class RenderSettings(object):
project_settings = get_project_settings(
get_current_project_name()
)
render_settings = project_settings["maya"]["RenderSettings"]
render_settings = project_settings["maya"]["render_settings"]
image_prefixes = {
"vray": render_settings["vray_renderer"]["image_prefix"],
"arnold": render_settings["arnold_renderer"]["image_prefix"],
@ -82,12 +82,12 @@ class RenderSettings(object):
try:
aov_separator = self._aov_chars[(
self._project_settings["maya"]
["RenderSettings"]
["render_settings"]
["aov_separator"]
)]
except KeyError:
aov_separator = "_"
reset_frame = self._project_settings["maya"]["RenderSettings"]["reset_current_frame"] # noqa
reset_frame = self._project_settings["maya"]["render_settings"]["reset_current_frame"] # noqa
if reset_frame:
start_frame = cmds.getAttr("defaultRenderGlobals.startFrame")
@ -131,7 +131,7 @@ class RenderSettings(object):
import maya.mel as mel # noqa: F401
createOptions()
render_settings = self._project_settings["maya"]["RenderSettings"]
render_settings = self._project_settings["maya"]["render_settings"]
arnold_render_presets = render_settings["arnold_renderer"] # noqa
# Force resetting settings and AOV list to avoid having to deal with
# AOV checking logic, for now.
@ -180,7 +180,7 @@ class RenderSettings(object):
from maya import cmds # noqa: F401
import maya.mel as mel # noqa: F401
render_settings = self._project_settings["maya"]["RenderSettings"]
render_settings = self._project_settings["maya"]["render_settings"]
redshift_render_presets = render_settings["redshift_renderer"]
remove_aovs = render_settings["remove_aovs"]
@ -239,7 +239,7 @@ class RenderSettings(object):
rman_render_presets = (
self._project_settings
["maya"]
["RenderSettings"]
["render_settings"]
["renderman_renderer"]
)
display_filters = rman_render_presets["display_filters"]
@ -304,7 +304,7 @@ class RenderSettings(object):
settings = cmds.ls(type="VRaySettingsNode")
node = settings[0] if settings else cmds.createNode("VRaySettingsNode")
render_settings = self._project_settings["maya"]["RenderSettings"]
render_settings = self._project_settings["maya"]["render_settings"]
vray_render_presets = render_settings["vray_renderer"]
# vrayRenderElement
remove_aovs = render_settings["remove_aovs"]
@ -390,7 +390,8 @@ class RenderSettings(object):
import maya.mel as mel # noqa: F401
for item in additional_attribs:
attribute, value = item
attribute = item["attribute"]
value = item["value"]
attribute = str(attribute) # ensure str conversion from settings
attribute_type = cmds.getAttr(attribute, type=True)
if attribute_type in {"long", "bool"}:

View file

@ -9,7 +9,8 @@ import maya.cmds as cmds
from ayon_core.pipeline import (
get_current_asset_name,
get_current_task_name
get_current_task_name,
registered_host
)
from ayon_core.pipeline.workfile import BuildWorkfile
from ayon_core.tools.utils import host_tools
@ -21,8 +22,10 @@ from .workfile_template_builder import (
create_placeholder,
update_placeholder,
build_workfile_template,
update_workfile_template,
update_workfile_template
)
from ayon_core.tools.workfile_template_build import open_template_ui
from .workfile_template_builder import MayaTemplateBuilder
log = logging.getLogger(__name__)
@ -167,16 +170,6 @@ def install(project_settings):
tearOff=True,
parent=MENU_NAME
)
cmds.menuItem(
"Create Placeholder",
parent=builder_menu,
command=create_placeholder
)
cmds.menuItem(
"Update Placeholder",
parent=builder_menu,
command=update_placeholder
)
cmds.menuItem(
"Build Workfile from template",
parent=builder_menu,
@ -187,6 +180,27 @@ def install(project_settings):
parent=builder_menu,
command=update_workfile_template
)
cmds.menuItem(
divider=True,
parent=builder_menu
)
cmds.menuItem(
"Open Template",
parent=builder_menu,
command=lambda *args: open_template_ui(
MayaTemplateBuilder(registered_host()), get_main_window()
),
)
cmds.menuItem(
"Create Placeholder",
parent=builder_menu,
command=create_placeholder
)
cmds.menuItem(
"Update Placeholder",
parent=builder_menu,
command=update_placeholder
)
cmds.setParent(MENU_NAME, menu=True)

View file

@ -26,7 +26,6 @@ from ayon_core.lib import (
emit_event
)
from ayon_core.pipeline import (
legacy_io,
get_current_project_name,
register_loader_plugin_path,
register_inventory_action_path,
@ -247,7 +246,7 @@ def _set_project():
None
"""
workdir = legacy_io.Session["AVALON_WORKDIR"]
workdir = os.getenv("AVALON_WORKDIR")
try:
os.makedirs(workdir)
@ -629,7 +628,7 @@ def on_task_changed():
# Run
menu.update_menu_task_label()
workdir = legacy_io.Session["AVALON_WORKDIR"]
workdir = os.getenv("AVALON_WORKDIR")
if os.path.exists(workdir):
log.info("Updating Maya workspace for task change to %s", workdir)
_set_project()
@ -678,7 +677,7 @@ def workfile_save_before_xgen(event):
import xgenm
current_work_dir = legacy_io.Session["AVALON_WORKDIR"].replace("\\", "/")
current_work_dir = os.getenv("AVALON_WORKDIR").replace("\\", "/")
expected_work_dir = event.data["workdir_path"].replace("\\", "/")
if current_work_dir == expected_work_dir:
return

View file

@ -22,6 +22,7 @@ from ayon_core.pipeline import (
LegacyCreator,
LoaderPlugin,
get_representation_path,
get_current_project_name,
)
from ayon_core.pipeline.load import LoadError
from ayon_core.client import get_asset_by_name
@ -585,6 +586,39 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase):
project_name)
def get_load_color_for_family(family, settings=None):
"""Get color for family from settings.
Args:
family (str): Family name.
settings (Optional[dict]): Settings dictionary.
Returns:
Union[tuple[float, float, float], None]: RGB color.
"""
if settings is None:
settings = get_project_settings(get_current_project_name())
colors = settings["maya"]["load"]["colors"]
color = colors.get(family)
if not color:
return None
if len(color) == 3:
red, green, blue = color
elif len(color) == 4:
red, green, blue, _ = color
else:
raise ValueError("Invalid color definition {}".format(str(color)))
if type(red, int):
red = red / 255.0
green = green / 255.0
blue = blue / 255.0
return red, green, blue
class Loader(LoaderPlugin):
hosts = ["maya"]
@ -611,33 +645,38 @@ class Loader(LoaderPlugin):
options["attach_to_root"] = True
custom_naming = self.load_settings[loader_key]
if not custom_naming['namespace']:
if not custom_naming["namespace"]:
raise LoadError("No namespace specified in "
"Maya ReferenceLoader settings")
elif not custom_naming['group_name']:
elif not custom_naming["group_name"]:
self.log.debug("No custom group_name, no group will be created.")
options["attach_to_root"] = False
asset = context['asset']
subset = context['subset']
asset = context["asset"]
subset = context["subset"]
family = (
subset["data"].get("family")
or subset["data"]["families"][0]
)
formatting_data = {
"asset_name": asset['name'],
"asset_type": asset['type'],
"asset_name": asset["name"],
"asset_type": asset["type"],
"folder": {
"name": asset["name"],
},
"subset": subset['name'],
"family": (
subset['data'].get('family') or
subset['data']['families'][0]
)
"subset": subset["name"],
"product": {
"name": subset["name"],
"type": family,
},
"family": family
}
custom_namespace = custom_naming['namespace'].format(
custom_namespace = custom_naming["namespace"].format(
**formatting_data
)
custom_group_name = custom_naming['group_name'].format(
custom_group_name = custom_naming["group_name"].format(
**formatting_data
)
@ -937,7 +976,7 @@ class ReferenceLoader(Loader):
"""
settings = get_project_settings(project_name)
use_env_var_as_root = (settings["maya"]
["maya-dirmap"]
["maya_dirmap"]
["use_env_var_as_root"])
if use_env_var_as_root:
anatomy = Anatomy(project_name)

View file

@ -35,7 +35,7 @@ class CreateRenderlayer(plugin.RenderlayerCreator):
@classmethod
def apply_settings(cls, project_settings):
cls.render_settings = project_settings["maya"]["RenderSettings"]
cls.render_settings = project_settings["maya"]["render_settings"]
def create(self, subset_name, instance_data, pre_create_data):
# Only allow a single render instance to exist

View file

@ -75,7 +75,7 @@ class CreateReview(plugin.MayaCreator):
"review_width": preset["Resolution"]["width"],
"review_height": preset["Resolution"]["height"],
"isolate": preset["Generic"]["isolate_view"],
"imagePlane": preset["Viewport Options"]["imagePlane"],
"imagePlane": preset["ViewportOptions"]["imagePlane"],
"panZoom": preset["Generic"]["pan_zoom"]
}
for key, value in mapping.items():

View file

@ -23,7 +23,7 @@ class CreateVRayScene(plugin.RenderlayerCreator):
@classmethod
def apply_settings(cls, project_settings):
cls.render_settings = project_settings["maya"]["RenderSettings"]
cls.render_settings = project_settings["maya"]["render_settings"]
def create(self, subset_name, instance_data, pre_create_data):
# Only allow a single render instance to exist

View file

@ -6,7 +6,6 @@ import maya.cmds as cmds
from ayon_core.settings import get_project_settings
from ayon_core.pipeline import (
load,
legacy_io,
get_representation_path
)
from ayon_core.hosts.maya.api.lib import (
@ -16,6 +15,7 @@ from ayon_core.hosts.maya.api.lib import (
convert_to_maya_fps
)
from ayon_core.hosts.maya.api.pipeline import containerise
from ayon_core.hosts.maya.api.plugin import get_load_color_for_family
def is_sequence(files):
@ -26,11 +26,6 @@ def is_sequence(files):
return sequence
def get_current_session_fps():
session_fps = float(legacy_io.Session.get('AVALON_FPS', 25))
return convert_to_maya_fps(session_fps)
class ArnoldStandinLoader(load.LoaderPlugin):
"""Load as Arnold standin"""
@ -72,11 +67,12 @@ class ArnoldStandinLoader(load.LoaderPlugin):
# Set color.
settings = get_project_settings(context["project"]["name"])
color = settings['maya']['load']['colors'].get('ass')
color = get_load_color_for_family("ass", settings)
if color is not None:
red, green, blue = color
cmds.setAttr(root + ".useOutlinerColor", True)
cmds.setAttr(
root + ".outlinerColor", color[0], color[1], color[2]
root + ".outlinerColor", red, green, blue
)
with maintained_selection():
@ -99,7 +95,7 @@ class ArnoldStandinLoader(load.LoaderPlugin):
sequence = is_sequence(os.listdir(os.path.dirname(repre_path)))
cmds.setAttr(standin_shape + ".useFrameExtension", sequence)
fps = float(version["data"].get("fps"))or get_current_session_fps()
fps = float(version["data"].get("fps")) or 25
cmds.setAttr(standin_shape + ".abcFPS", fps)
nodes = [root, standin, standin_shape]

View file

@ -9,6 +9,7 @@ from ayon_core.pipeline import (
get_representation_path
)
from ayon_core.settings import get_project_settings
from ayon_core.hosts.maya.api.plugin import get_load_color_for_family
class GpuCacheLoader(load.LoaderPlugin):
@ -39,13 +40,12 @@ class GpuCacheLoader(load.LoaderPlugin):
project_name = context["project"]["name"]
settings = get_project_settings(project_name)
colors = settings['maya']['load']['colors']
c = colors.get('model')
if c is not None:
color = get_load_color_for_family("model", settings)
if color is not None:
red, green, blue = color
cmds.setAttr(root + ".useOutlinerColor", 1)
cmds.setAttr(
root + ".outlinerColor",
(float(c[0]) / 255), (float(c[1]) / 255), (float(c[2]) / 255)
root + ".outlinerColor", red, green, blue
)
# Create transform with shape

View file

@ -16,6 +16,7 @@ from ayon_core.hosts.maya.api.lib import (
unique_namespace
)
from ayon_core.hosts.maya.api.pipeline import containerise
from ayon_core.hosts.maya.api.plugin import get_load_color_for_family
class RedshiftProxyLoader(load.LoaderPlugin):
@ -59,12 +60,13 @@ class RedshiftProxyLoader(load.LoaderPlugin):
# colour the group node
project_name = context["project"]["name"]
settings = get_project_settings(project_name)
colors = settings['maya']['load']['colors']
c = colors.get(family)
if c is not None:
color = get_load_color_for_family(family, settings)
if color is not None:
red, green, blue = color
cmds.setAttr("{0}.useOutlinerColor".format(group_node), 1)
cmds.setAttr("{0}.outlinerColor".format(group_node),
c[0], c[1], c[2])
cmds.setAttr(
"{0}.outlinerColor".format(group_node), red, green, blue
)
return containerise(
name=name,

View file

@ -1,4 +1,3 @@
import os
import difflib
import contextlib
@ -6,7 +5,7 @@ from maya import cmds
import qargparse
from ayon_core.settings import get_project_settings
import ayon_core.hosts.maya.api.plugin
from ayon_core.hosts.maya.api import plugin
from ayon_core.hosts.maya.api.lib import (
maintained_selection,
get_container_members,
@ -87,7 +86,7 @@ def preserve_modelpanel_cameras(container, log=None):
cmds.modelPanel(panel, edit=True, camera=new_camera)
class ReferenceLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader):
class ReferenceLoader(plugin.ReferenceLoader):
"""Reference file"""
families = ["model",
@ -185,14 +184,16 @@ class ReferenceLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader):
"{}.displayHandle".format(group_name), display_handle
)
colors = settings['maya']['load']['colors']
c = colors.get(family)
if c is not None:
color = plugin.get_load_color_for_family(family, settings)
if color is not None:
red, green, blue = color
cmds.setAttr("{}.useOutlinerColor".format(group_name), 1)
cmds.setAttr("{}.outlinerColor".format(group_name),
(float(c[0]) / 255),
(float(c[1]) / 255),
(float(c[2]) / 255))
cmds.setAttr(
"{}.outlinerColor".format(group_name),
red,
green,
blue
)
cmds.setAttr(
"{}.displayHandle".format(group_name), display_handle

View file

@ -5,6 +5,7 @@ from ayon_core.pipeline import (
load,
get_representation_path
)
from ayon_core.hosts.maya.api.plugin import get_load_color_for_family
# TODO aiVolume doesn't automatically set velocity fps correctly, set manual?
@ -50,16 +51,11 @@ class LoadVDBtoArnold(load.LoaderPlugin):
project_name = context["project"]["name"]
settings = get_project_settings(project_name)
colors = settings['maya']['load']['colors']
c = colors.get(family)
if c is not None:
color = get_load_color_for_family(family, settings)
if color is not None:
red, green, blue = color
cmds.setAttr(root + ".useOutlinerColor", 1)
cmds.setAttr(root + ".outlinerColor",
(float(c[0]) / 255),
(float(c[1]) / 255),
(float(c[2]) / 255)
)
cmds.setAttr(root + ".outlinerColor", red, green, blue)
# Create VRayVolumeGrid
grid_node = cmds.createNode("aiVolume",

View file

@ -5,6 +5,7 @@ from ayon_core.pipeline import (
load,
get_representation_path
)
from ayon_core.hosts.maya.api.plugin import get_load_color_for_family
class LoadVDBtoRedShift(load.LoaderPlugin):
@ -69,16 +70,11 @@ class LoadVDBtoRedShift(load.LoaderPlugin):
project_name = context["project"]["name"]
settings = get_project_settings(project_name)
colors = settings['maya']['load']['colors']
c = colors.get(family)
if c is not None:
color = get_load_color_for_family(family, settings)
if color is not None:
red, green, blue = color
cmds.setAttr(root + ".useOutlinerColor", 1)
cmds.setAttr(root + ".outlinerColor",
(float(c[0])/255),
(float(c[1])/255),
(float(c[2])/255)
)
cmds.setAttr(root + ".outlinerColor", red, green, blue)
# Create VR
volume_node = cmds.createNode("RedshiftVolumeShape",

View file

@ -5,6 +5,7 @@ from ayon_core.pipeline import (
load,
get_representation_path
)
from ayon_core.hosts.maya.api.plugin import get_load_color_for_family
from maya import cmds
@ -129,15 +130,11 @@ class LoadVDBtoVRay(load.LoaderPlugin):
project_name = context["project"]["name"]
settings = get_project_settings(project_name)
colors = settings['maya']['load']['colors']
c = colors.get(family)
if c is not None:
color = get_load_color_for_family(family, settings)
if color is not None:
red, green, blue = color
cmds.setAttr(root + ".useOutlinerColor", 1)
cmds.setAttr(root + ".outlinerColor",
float(c[0]) / 255,
float(c[1]) / 255,
float(c[2]) / 255)
cmds.setAttr(root + ".outlinerColor", red, green, blue)
# Create VRayVolumeGrid
grid_node = cmds.createNode("VRayVolumeGrid",

View file

@ -22,6 +22,7 @@ from ayon_core.hosts.maya.api.lib import (
unique_namespace
)
from ayon_core.hosts.maya.api.pipeline import containerise
from ayon_core.hosts.maya.api.plugin import get_load_color_for_family
class VRayProxyLoader(load.LoaderPlugin):
@ -80,15 +81,12 @@ class VRayProxyLoader(load.LoaderPlugin):
# colour the group node
project_name = context["project"]["name"]
settings = get_project_settings(project_name)
colors = settings['maya']['load']['colors']
c = colors.get(family)
if c is not None:
color = get_load_color_for_family(family, settings)
if color is not None:
red, green, blue = color
cmds.setAttr("{0}.useOutlinerColor".format(group_node), 1)
cmds.setAttr(
"{0}.outlinerColor".format(group_node),
(float(c[0]) / 255),
(float(c[1]) / 255),
(float(c[2]) / 255)
"{0}.outlinerColor".format(group_node), red, green, blue
)
return containerise(

View file

@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
import os
import maya.cmds as cmds # noqa
from ayon_core.settings import get_project_settings
from ayon_core.pipeline import (
@ -12,6 +11,7 @@ from ayon_core.hosts.maya.api.lib import (
unique_namespace
)
from ayon_core.hosts.maya.api.pipeline import containerise
from ayon_core.hosts.maya.api.plugin import get_load_color_for_family
class VRaySceneLoader(load.LoaderPlugin):
@ -58,14 +58,12 @@ class VRaySceneLoader(load.LoaderPlugin):
# colour the group node
project_name = context["project"]["name"]
settings = get_project_settings(project_name)
colors = settings['maya']['load']['colors']
c = colors.get(family)
if c is not None:
color = get_load_color_for_family(family, settings)
if color is not None:
red, green, blue = color
cmds.setAttr("{0}.useOutlinerColor".format(root_node), 1)
cmds.setAttr("{0}.outlinerColor".format(root_node),
(float(c[0])/255),
(float(c[1])/255),
(float(c[2])/255)
cmds.setAttr(
"{0}.outlinerColor".format(root_node), red, green, blue
)
return containerise(

View file

@ -13,6 +13,7 @@ from ayon_core.pipeline import (
)
from ayon_core.hosts.maya.api import lib
from ayon_core.hosts.maya.api.pipeline import containerise
from ayon_core.hosts.maya.api.plugin import get_load_color_for_family
# Do not reset these values on update but only apply on first load
@ -81,16 +82,11 @@ class YetiCacheLoader(load.LoaderPlugin):
project_name = context["project"]["name"]
settings = get_project_settings(project_name)
colors = settings['maya']['load']['colors']
c = colors.get(family)
if c is not None:
color = get_load_color_for_family(family, settings)
if color is not None:
red, green, blue = color
cmds.setAttr(group_node + ".useOutlinerColor", 1)
cmds.setAttr(group_node + ".outlinerColor",
(float(c[0])/255),
(float(c[1])/255),
(float(c[2])/255)
)
cmds.setAttr(group_node + ".outlinerColor", red, green, blue)
nodes.append(group_node)

View file

@ -1,11 +1,10 @@
import maya.cmds as cmds
from ayon_core.settings import get_current_project_settings
import ayon_core.hosts.maya.api.plugin
from ayon_core.hosts.maya.api import plugin
from ayon_core.hosts.maya.api import lib
class YetiRigLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader):
class YetiRigLoader(plugin.ReferenceLoader):
"""This loader will load Yeti rig."""
families = ["yetiRig"]
@ -41,14 +40,12 @@ class YetiRigLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader):
groupName=group_name
)
settings = get_current_project_settings()
colors = settings["maya"]["load"]["colors"]
c = colors.get("yetiRig")
if c is not None:
color = plugin.get_load_color_for_family("yetiRig")
if color is not None:
red, green, blue = color
cmds.setAttr(group_name + ".useOutlinerColor", 1)
cmds.setAttr(
group_name + ".outlinerColor",
(float(c[0]) / 255), (float(c[1]) / 255), (float(c[2]) / 255)
group_name + ".outlinerColor", red, green, blue
)
self[:] = nodes

View file

@ -39,7 +39,7 @@ class CollectReview(pyblish.api.InstancePlugin):
if display_lights == "project_settings":
settings = instance.context.data["project_settings"]
settings = settings["maya"]["publish"]["ExtractPlayblast"]
settings = settings["capture_preset"]["Viewport Options"]
settings = settings["capture_preset"]["ViewportOptions"]
display_lights = settings["displayLights"]
# Collect camera focal length.

View file

@ -1,13 +1,8 @@
# -*- coding: utf-8 -*-
"""Collect Vray Scene and prepare it for extraction and publishing."""
import re
import maya.app.renderSetup.model.renderSetup as renderSetup
from maya import cmds
import pyblish.api
from ayon_core.pipeline import legacy_io
from ayon_core.lib import get_formatted_current_time
from ayon_core.hosts.maya.api import lib

View file

@ -1,4 +1,5 @@
import os
import json
from maya import cmds
@ -21,7 +22,7 @@ class ExtractCameraAlembic(publish.Extractor,
label = "Extract Camera (Alembic)"
hosts = ["maya"]
families = ["camera", "matchmove"]
bake_attributes = []
bake_attributes = "[]"
def process(self, instance):
@ -95,11 +96,12 @@ class ExtractCameraAlembic(publish.Extractor,
job_str += ' -file "{0}"'.format(path)
bake_attributes = json.loads(self.bake_attributes)
# bake specified attributes in preset
assert isinstance(self.bake_attributes, (list, tuple)), (
assert isinstance(bake_attributes, list), (
"Attributes to bake must be specified as a list"
)
for attr in self.bake_attributes:
for attr in bake_attributes:
self.log.debug("Adding {} attribute".format(attr))
job_str += " -attr {0}".format(attr)

View file

@ -112,9 +112,11 @@ class ExtractCameraMayaScene(publish.Extractor,
def process(self, instance):
"""Plugin entry point."""
# get settings
ext_mapping = (
instance.context.data["project_settings"]["maya"]["ext_mapping"]
)
maya_settings = instance.context.data["project_settings"]["maya"]
ext_mapping = {
item["name"]: item["value"]
for item in maya_settings["ext_mapping"]
}
if ext_mapping:
self.log.debug("Looking in settings for scene type ...")
# use extension mapping for first family found

View file

@ -37,9 +37,11 @@ class ExtractImportReference(publish.Extractor,
if not self.is_active(instance.data):
return
ext_mapping = (
instance.context.data["project_settings"]["maya"]["ext_mapping"]
)
maya_settings = instance.context.data["project_settings"]["maya"]
ext_mapping = {
item["name"]: item["value"]
for item in maya_settings["ext_mapping"]
}
if ext_mapping:
self.log.debug("Looking in settings for scene type ...")
# use extension mapping for first family found

View file

@ -431,9 +431,11 @@ class ExtractLook(publish.Extractor):
project settings.
"""
ext_mapping = (
instance.context.data["project_settings"]["maya"]["ext_mapping"]
)
maya_settings = instance.context.data["project_settings"]["maya"]
ext_mapping = {
item["name"]: item["value"]
for item in maya_settings["ext_mapping"]
}
if ext_mapping:
self.log.debug("Looking in settings for scene type ...")
# use extension mapping for first family found

View file

@ -43,9 +43,11 @@ class ExtractMayaSceneRaw(publish.Extractor, AYONPyblishPluginMixin):
def process(self, instance):
"""Plugin entry point."""
ext_mapping = (
instance.context.data["project_settings"]["maya"]["ext_mapping"]
)
maya_settings = instance.context.data["project_settings"]["maya"]
ext_mapping = {
item["name"]: item["value"]
for item in maya_settings["ext_mapping"]
}
if ext_mapping:
self.log.debug("Looking in settings for scene type ...")
# use extension mapping for first family found

View file

@ -35,9 +35,11 @@ class ExtractModel(publish.Extractor,
if not self.is_active(instance.data):
return
ext_mapping = (
instance.context.data["project_settings"]["maya"]["ext_mapping"]
)
maya_settings = instance.context.data["project_settings"]["maya"]
ext_mapping = {
item["name"]: item["value"]
for item in maya_settings["ext_mapping"]
}
if ext_mapping:
self.log.debug("Looking in settings for scene type ...")
# use extension mapping for first family found

View file

@ -18,9 +18,11 @@ class ExtractRig(publish.Extractor):
def process(self, instance):
"""Plugin entry point."""
ext_mapping = (
instance.context.data["project_settings"]["maya"]["ext_mapping"]
)
maya_settings = instance.context.data["project_settings"]["maya"]
ext_mapping = {
item["name"]: item["value"]
for item in maya_settings["ext_mapping"]
}
if ext_mapping:
self.log.debug("Looking in settings for scene type ...")
# use extension mapping for first family found

View file

@ -100,9 +100,11 @@ class ExtractYetiRig(publish.Extractor):
def process(self, instance):
"""Plugin entry point."""
ext_mapping = (
instance.context.data["project_settings"]["maya"]["ext_mapping"]
)
maya_settings = instance.context.data["project_settings"]["maya"]
ext_mapping = {
item["name"]: item["value"]
for item in maya_settings["ext_mapping"]
}
if ext_mapping:
self.log.debug("Looking in settings for scene type ...")
# use extension mapping for first family found

View file

@ -1,3 +1,4 @@
import json
from collections import defaultdict
import pyblish.api
@ -23,19 +24,19 @@ class ValidateAttributes(pyblish.api.InstancePlugin,
"""
order = ValidateContentsOrder
label = "Attributes"
label = "Validate Attributes"
hosts = ["maya"]
actions = [RepairAction]
optional = True
attributes = None
attributes = "{}"
def process(self, instance):
if not self.is_active(instance.data):
return
# Check for preset existence.
if not self.attributes:
if not self.get_attributes_data():
return
invalid = self.get_invalid(instance, compute=True)
@ -44,6 +45,10 @@ class ValidateAttributes(pyblish.api.InstancePlugin,
"Found attributes with invalid values: {}".format(invalid)
)
@classmethod
def get_attributes_data(cls):
return json.loads(cls.attributes)
@classmethod
def get_invalid(cls, instance, compute=False):
if compute:
@ -55,21 +60,22 @@ class ValidateAttributes(pyblish.api.InstancePlugin,
def get_invalid_attributes(cls, instance):
invalid_attributes = []
attributes_data = cls.get_attributes_data()
# Filter families.
families = [instance.data["family"]]
families += instance.data.get("families", [])
families = set(families) & set(cls.attributes.keys())
families = set(families) & set(attributes_data.keys())
if not families:
return []
# Get all attributes to validate.
attributes = defaultdict(dict)
for family in families:
if family not in cls.attributes:
if family not in attributes_data:
# No attributes to validate for family
continue
for preset_attr, preset_value in cls.attributes[family].items():
for preset_attr, preset_value in attributes_data[family].items():
node_name, attribute_name = preset_attr.split(".", 1)
attributes[node_name][attribute_name] = preset_value

View file

@ -39,7 +39,7 @@ class ValidateFrameRange(pyblish.api.InstancePlugin,
"yeticache"]
optional = True
actions = [RepairAction]
exclude_families = []
exclude_product_types = []
def process(self, instance):
if not self.is_active(instance.data):
@ -73,7 +73,9 @@ class ValidateFrameRange(pyblish.api.InstancePlugin,
# compare with data on instance
errors = []
if [ef for ef in self.exclude_families
# QUESTION shouldn't this be just:
# 'if instance.data["family"] in self.exclude_product_types:'
if [ef for ef in self.exclude_product_types
if instance.data["family"] in ef]:
return
if (inst_start != frame_start_handle):

View file

@ -12,7 +12,6 @@ import ayon_core.hosts.maya.api.action
from ayon_core.client.mongo import OpenPypeMongoConnection
from ayon_core.hosts.maya.api.shader_definition_editor import (
DEFINITION_FILENAME)
from ayon_core.pipeline import legacy_io
from ayon_core.pipeline.publish import (
OptionalPyblishPluginMixin, PublishValidationError, ValidateContentsOrder)

View file

@ -3,7 +3,6 @@ import pyblish.api
import ayon_core.hosts.maya.api.action
from ayon_core.client import get_assets
from ayon_core.hosts.maya.api import lib
from ayon_core.pipeline import legacy_io
from ayon_core.pipeline.publish import (
PublishValidationError, ValidatePipelineOrder)

View file

@ -30,14 +30,18 @@ class ValidatePluginPathAttributes(pyblish.api.InstancePlugin):
def get_invalid(cls, instance):
invalid = list()
file_attrs = cls.attribute
file_attrs = {
item["name"]: item["value"]
for item in cls.attribute
}
if not file_attrs:
return invalid
# Consider only valid node types to avoid "Unknown object type" warning
all_node_types = set(cmds.allNodeTypes())
node_types = [
key for key in file_attrs.keys()
key
for key in file_attrs.keys()
if key in all_node_types
]

View file

@ -55,12 +55,15 @@ class ValidateRenderImageRule(pyblish.api.InstancePlugin):
if staging_dir:
cls.log.debug(
"Staging dir found: \"{}\". Ignoring setting from "
"`project_settings/maya/RenderSettings/"
"`project_settings/maya/render_settings/"
"default_render_image_folder`.".format(staging_dir)
)
return staging_dir
return instance.context.data.get('project_settings')\
.get('maya') \
.get('RenderSettings') \
.get('default_render_image_folder')
return (
instance.context.data
["project_settings"]
["maya"]
["render_settings"]
["default_render_image_folder"]
)

View file

@ -2,7 +2,6 @@ import pyblish.api
import ayon_core.hosts.maya.api.action
from ayon_core.client import get_subset_by_name
from ayon_core.pipeline import legacy_io
from ayon_core.pipeline.publish import PublishValidationError

View file

@ -265,7 +265,7 @@ class ValidateRenderSettings(pyblish.api.InstancePlugin):
# load validation definitions from settings
settings_lights_flag = instance.context.data["project_settings"].get(
"maya", {}).get(
"RenderSettings", {}).get(
"render_settings", {}).get(
"enable_all_lights", False)
instance_lights_flag = instance.data.get("renderSetupIncludeLights")
@ -281,6 +281,8 @@ class ValidateRenderSettings(pyblish.api.InstancePlugin):
# if so, compare its value from the one required.
for data in cls.get_nodes(instance, renderer):
for node in data["nodes"]:
# Why is captured 'PublishValidationError'? How it can be
# raised by 'cmds.getAttr(...)'?
try:
render_value = cmds.getAttr(
"{}.{}".format(node, data["attribute"])
@ -310,11 +312,16 @@ class ValidateRenderSettings(pyblish.api.InstancePlugin):
@classmethod
def get_nodes(cls, instance, renderer):
maya_settings = instance.context.data["project_settings"]["maya"]
renderer_key = "{}_render_attributes".format(renderer)
validation_settings = (
maya_settings["publish"]["ValidateRenderSettings"].get(
"{}_render_attributes".format(renderer)
) or []
)
renderer_key
)
) or []
validation_settings = [
(item["type"], item["value"])
for item in validation_settings
]
result = []
for attr, values in OrderedDict(validation_settings).items():
values = [convert_to_int_or_float(v) for v in values if v]

View file

@ -7,6 +7,7 @@ from ayon_core.hosts.maya.api import lib
from ayon_core.pipeline.publish import (
RepairAction,
ValidateContentsOrder,
PublishValidationError
)
@ -38,7 +39,8 @@ class ValidateRigJointsHidden(pyblish.api.InstancePlugin):
invalid = self.get_invalid(instance)
if invalid:
raise ValueError("Visible joints found: {0}".format(invalid))
raise PublishValidationError(
"Visible joints found: {0}".format(invalid))
@classmethod
def repair(cls, instance):

View file

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
"""Plugin for validating naming conventions."""
import json
from maya import cmds
import pyblish.api
@ -35,29 +36,37 @@ class ValidateTransformNamingSuffix(pyblish.api.InstancePlugin,
"""
order = ValidateContentsOrder
hosts = ['maya']
families = ['model']
hosts = ["maya"]
families = ["model"]
optional = True
label = 'Suffix Naming Conventions'
label = "Suffix Naming Conventions"
actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
SUFFIX_NAMING_TABLE = {"mesh": ["_GEO", "_GES", "_GEP", "_OSD"],
"nurbsCurve": ["_CRV"],
"nurbsSurface": ["_NRB"],
"locator": ["_LOC"],
"group": ["_GRP"]}
SUFFIX_NAMING_TABLE = json.dumps({
"mesh": ["_GEO", "_GES", "_GEP", "_OSD"],
"nurbsCurve": ["_CRV"],
"nurbsSurface": ["_NRB"],
"locator": ["_LOC"],
"group": ["_GRP"]
})
ALLOW_IF_NOT_IN_SUFFIX_TABLE = True
@classmethod
def get_table_for_invalid(cls):
ss = []
for k, v in cls.SUFFIX_NAMING_TABLE.items():
ss.append(" - <b>{}</b>: {}".format(k, ", ".join(v)))
suffix_naming_table = json.loads(cls.SUFFIX_NAMING_TABLE)
ss = [
" - <b>{}</b>: {}".format(k, ", ".join(v))
for k, v in suffix_naming_table.items()
]
return "<br>".join(ss)
@staticmethod
def is_valid_name(node_name, shape_type,
SUFFIX_NAMING_TABLE, ALLOW_IF_NOT_IN_SUFFIX_TABLE):
def is_valid_name(
node_name,
shape_type,
suffix_naming_table,
allow_if_not_in_suffix_table
):
"""Return whether node's name is correct.
The correctness for a transform's suffix is dependent on what
@ -70,18 +79,18 @@ class ValidateTransformNamingSuffix(pyblish.api.InstancePlugin,
Args:
node_name (str): Node name.
shape_type (str): Type of node.
SUFFIX_NAMING_TABLE (dict): Mapping dict for suffixes.
ALLOW_IF_NOT_IN_SUFFIX_TABLE (dict): Filter dict.
suffix_naming_table (dict): Mapping dict for suffixes.
allow_if_not_in_suffix_table (bool): Default output.
"""
if shape_type not in SUFFIX_NAMING_TABLE:
return ALLOW_IF_NOT_IN_SUFFIX_TABLE
else:
suffices = SUFFIX_NAMING_TABLE[shape_type]
for suffix in suffices:
if node_name.endswith(suffix):
return True
return False
if shape_type not in suffix_naming_table:
return allow_if_not_in_suffix_table
suffices = suffix_naming_table[shape_type]
for suffix in suffices:
if node_name.endswith(suffix):
return True
return False
@classmethod
def get_invalid(cls, instance):
@ -91,9 +100,10 @@ class ValidateTransformNamingSuffix(pyblish.api.InstancePlugin,
instance (:class:`pyblish.api.Instance`): published instance.
"""
transforms = cmds.ls(instance, type='transform', long=True)
transforms = cmds.ls(instance, type="transform", long=True)
invalid = []
suffix_naming_table = json.loads(cls.SUFFIX_NAMING_TABLE)
for transform in transforms:
shapes = cmds.listRelatives(transform,
shapes=True,
@ -101,9 +111,12 @@ class ValidateTransformNamingSuffix(pyblish.api.InstancePlugin,
noIntermediate=True)
shape_type = cmds.nodeType(shapes[0]) if shapes else "group"
if not cls.is_valid_name(transform, shape_type,
cls.SUFFIX_NAMING_TABLE,
cls.ALLOW_IF_NOT_IN_SUFFIX_TABLE):
if not cls.is_valid_name(
transform,
shape_type,
suffix_naming_table,
cls.ALLOW_IF_NOT_IN_SUFFIX_TABLE
):
invalid.append(transform)
return invalid

View file

@ -5,8 +5,6 @@ import re
import pyblish.api
import ayon_core.hosts.maya.api.action
from ayon_core.pipeline import legacy_io
from ayon_core.settings import get_project_settings
from ayon_core.pipeline.publish import (
ValidateContentsOrder,
OptionalPyblishPluginMixin,

View file

@ -46,24 +46,5 @@ if bool(int(os.environ.get(key, "0"))):
lowestPriority=True
)
# Build a shelf.
shelf_preset = settings['maya'].get('project_shelf')
if shelf_preset:
icon_path = os.path.join(
os.environ['OPENPYPE_PROJECT_SCRIPTS'],
project_name,
"icons")
icon_path = os.path.abspath(icon_path)
for i in shelf_preset['imports']:
import_string = "from {} import {}".format(project_name, i)
print(import_string)
exec(import_string)
cmds.evalDeferred(
"mlib.shelf(name=shelf_preset['name'], iconPath=icon_path,"
" preset=shelf_preset)"
)
print("Finished OpenPype usersetup.")

View file

@ -1,17 +1,14 @@
import os
import platform
from ayon_core.modules import OpenPypeModule, IHostAddon
from ayon_core.addon import AYONAddon, IHostAddon
NUKE_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
class NukeAddon(OpenPypeModule, IHostAddon):
class NukeAddon(AYONAddon, IHostAddon):
name = "nuke"
host_name = "nuke"
def initialize(self, module_settings):
self.enabled = True
def add_implementation_envs(self, env, _app):
# Add requirements to NUKE_PATH
new_nuke_paths = [

View file

@ -21,10 +21,12 @@ from ayon_core.pipeline import (
AVALON_CONTAINER_ID,
get_current_asset_name,
get_current_task_name,
registered_host,
)
from ayon_core.pipeline.workfile import BuildWorkfile
from ayon_core.tools.utils import host_tools
from ayon_core.hosts.nuke import NUKE_ROOT_DIR
from ayon_core.tools.workfile_template_build import open_template_ui
from .command import viewer_update_and_undo_stop
from .lib import (
@ -55,6 +57,7 @@ from .workfile_template_builder import (
build_workfile_template,
create_placeholder,
update_placeholder,
NukeTemplateBuilder,
)
from .workio import (
open_file,
@ -176,7 +179,7 @@ def add_nuke_callbacks():
nuke.addOnScriptLoad(WorkfileSettings().set_context_settings)
if nuke_settings["nuke-dirmap"]["enabled"]:
if nuke_settings["nuke_dirmap"]["enabled"]:
log.info("Added Nuke's dir-mapping callback ...")
# Add dirmap for file paths.
nuke.addFilenameFilter(dirmap_file_name_filter)
@ -313,7 +316,7 @@ def _install_menu():
lambda: BuildWorkfile().process()
)
menu_template = menu.addMenu("Template Builder") # creating template menu
menu_template = menu.addMenu("Template Builder")
menu_template.addCommand(
"Build Workfile from template",
lambda: build_workfile_template()
@ -321,6 +324,12 @@ def _install_menu():
if not ASSIST:
menu_template.addSeparator()
menu_template.addCommand(
"Open template",
lambda: open_template_ui(
NukeTemplateBuilder(registered_host()), get_main_window()
)
)
menu_template.addCommand(
"Create Place Holder",
lambda: create_placeholder()

View file

@ -7,7 +7,7 @@ from ayon_core.pipeline.workfile.workfile_template_builder import (
LoadPlaceholderItem,
CreatePlaceholderItem,
PlaceholderLoadMixin,
PlaceholderCreateMixin
PlaceholderCreateMixin,
)
from ayon_core.tools.workfile_template_build import (
WorkfileBuildPlaceholderDialog,

View file

@ -1,16 +1,13 @@
import os
from ayon_core.modules import OpenPypeModule, IHostAddon
from ayon_core.addon import AYONAddon, IHostAddon
PHOTOSHOP_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
class PhotoshopAddon(OpenPypeModule, IHostAddon):
class PhotoshopAddon(AYONAddon, IHostAddon):
name = "photoshop"
host_name = "photoshop"
def initialize(self, module_settings):
self.enabled = True
def add_implementation_envs(self, env, _app):
"""Modify environments to contain all required for implementation."""
defaults = {

View file

@ -3,12 +3,11 @@ import sys
import contextlib
import traceback
from ayon_core.lib import env_value_to_bool, Logger
from ayon_core.lib import env_value_to_bool, Logger, is_in_tests
from ayon_core.addon import AddonsManager
from ayon_core.pipeline import install_host
from ayon_core.tools.utils import host_tools
from ayon_core.tools.utils import get_ayon_qt_app
from ayon_core.tests.lib import is_in_tests
from .launch_logic import ProcessLauncher, stub

View file

@ -17,12 +17,11 @@ import os
import pyblish.api
from ayon_core.pipeline import legacy_io
from openpype_modules.webpublisher.lib import (
get_batch_asset_task_info,
parse_json
)
from ayon_core.tests.lib import is_in_tests
from ayon_core.lib import is_in_tests
class CollectBatchData(pyblish.api.ContextPlugin):
@ -71,8 +70,6 @@ class CollectBatchData(pyblish.api.ContextPlugin):
os.environ["AVALON_ASSET"] = asset_name
os.environ["AVALON_TASK"] = task_name
legacy_io.Session["AVALON_ASSET"] = asset_name
legacy_io.Session["AVALON_TASK"] = task_name
context.data["asset"] = asset_name
context.data["task"] = task_name

View file

@ -3,10 +3,9 @@ import re
import pyblish.api
from ayon_core.lib import prepare_template_data
from ayon_core.lib import prepare_template_data, is_in_tests
from ayon_core.hosts.photoshop import api as photoshop
from ayon_core.settings import get_project_settings
from ayon_core.tests.lib import is_in_tests
class CollectColorCodedInstances(pyblish.api.ContextPlugin):
@ -29,9 +28,8 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin):
Identifier:
id (str): "pyblish.avalon.instance"
"""
order = pyblish.api.CollectorOrder + 0.100
label = "Instances"
label = "Collect Color-coded Instances"
order = pyblish.api.CollectorOrder
hosts = ["photoshop"]
targets = ["automated"]
@ -42,7 +40,7 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin):
# flattened template cannot
subset_template_name = ""
create_flatten_image = "no"
flatten_subset_template = ""
flatten_product_name_template = ""
def process(self, context):
self.log.info("CollectColorCodedInstances")
@ -124,12 +122,12 @@ class CollectColorCodedInstances(pyblish.api.ContextPlugin):
if self.create_flatten_image != "no" and publishable_layers:
self.log.debug("create_flatten_image")
if not self.flatten_subset_template:
if not self.flatten_product_name_template:
self.log.warning("No template for flatten image")
return
fill_pairs.pop("layer")
subset = self.flatten_subset_template.format(
subset = self.flatten_product_name_template.format(
**prepare_template_data(fill_pairs))
first_layer = publishable_layers[0] # dummy layer

View file

@ -6,24 +6,17 @@ Provides:
instance -> family ("review")
"""
import os
import pyblish.api
from ayon_core.pipeline.create import get_subset_name
class CollectReview(pyblish.api.ContextPlugin):
"""Adds review to families for instances marked to be reviewable.
"""
label = "Collect Review"
label = "Review"
hosts = ["photoshop"]
order = pyblish.api.CollectorOrder + 0.1
publish = True
def process(self, context):
for instance in context:
creator_attributes = instance.data["creator_attributes"]

View file

@ -1,17 +1,14 @@
import os
from ayon_core.modules import OpenPypeModule, IHostAddon
from ayon_core.addon import AYONAddon, IHostAddon
from .utils import RESOLVE_ROOT_DIR
class ResolveAddon(OpenPypeModule, IHostAddon):
class ResolveAddon(AYONAddon, IHostAddon):
name = "resolve"
host_name = "resolve"
def initialize(self, module_settings):
self.enabled = True
def get_launch_hook_paths(self, app):
if app.host_name != self.host_name:
return []

View file

@ -33,7 +33,7 @@ def ensure_installed_host():
def launch_menu():
print("Launching Resolve OpenPype menu..")
print("Launching Resolve AYON menu..")
ensure_installed_host()
ayon_core.hosts.resolve.api.launch_pype_menu()
@ -54,7 +54,7 @@ def main():
else:
log.info("No last workfile set to open. Skipping..")
# Launch OpenPype menu
# Launch AYON menu
from ayon_core.settings import get_project_settings
from ayon_core.pipeline.context_tools import get_current_project_name
project_name = get_current_project_name()
@ -62,7 +62,7 @@ def main():
settings = get_project_settings(project_name)
if settings.get("resolve", {}).get("launch_openpype_menu_on_start", True):
log.info("Launching OpenPype menu..")
log.info("Launching AYON menu..")
launch_menu()

View file

@ -1,16 +1,13 @@
import os
from ayon_core.modules import OpenPypeModule, IHostAddon
from ayon_core.addon import AYONAddon, IHostAddon
SUBSTANCE_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
class SubstanceAddon(OpenPypeModule, IHostAddon):
class SubstanceAddon(AYONAddon, IHostAddon):
name = "substancepainter"
host_name = "substancepainter"
def initialize(self, module_settings):
self.enabled = True
def add_implementation_envs(self, env, _app):
# Add requirements to SUBSTANCE_PAINTER_PLUGINS_PATH
plugin_path = os.path.join(SUBSTANCE_HOST_DIR, "deploy")

View file

@ -240,33 +240,34 @@ class SubstanceHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
def _install_shelves(self, project_settings):
shelves = project_settings["substancepainter"].get("shelves", {})
shelves = project_settings["substancepainter"].get("shelves", [])
if not shelves:
return
# Prepare formatting data if we detect any path which might have
# template tokens like {asset} in there.
formatting_data = {}
has_formatting_entries = any("{" in path for path in shelves.values())
has_formatting_entries = any("{" in item["value"] for item in shelves)
if has_formatting_entries:
project_name = self.get_current_project_name()
asset_name = self.get_current_asset_name()
task_name = self.get_current_asset_name()
system_settings = get_system_settings()
formatting_data = get_template_data_with_names(project_name,
asset_name,
task_name,
system_settings)
formatting_data = get_template_data_with_names(
project_name, asset_name, task_name, system_settings
)
anatomy = Anatomy(project_name)
formatting_data["root"] = anatomy.roots
for name, path in shelves.items():
shelf_name = None
for shelve_item in shelves:
# Allow formatting with anatomy for the paths
path = shelve_item["value"]
if "{" in path:
path = StringTemplate.format_template(path, formatting_data)
name = shelve_item["name"]
shelf_name = None
try:
shelf_name = lib.load_shelf(path, name=name)
except ValueError as exc:

View file

@ -2,9 +2,9 @@ import os
from ayon_core.lib import get_ayon_launcher_args
from ayon_core.lib.execute import run_detached_process
from ayon_core.modules import (
from ayon_core.addon import (
click_wrap,
OpenPypeModule,
AYONAddon,
ITrayAction,
IHostAddon,
)
@ -12,13 +12,12 @@ from ayon_core.modules import (
TRAYPUBLISH_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
class TrayPublishAddon(OpenPypeModule, IHostAddon, ITrayAction):
class TrayPublishAddon(AYONAddon, IHostAddon, ITrayAction):
label = "Publisher"
name = "traypublisher"
host_name = "traypublisher"
def initialize(self, modules_settings):
self.enabled = True
def initialize(self, settings):
self.publish_paths = [
os.path.join(TRAYPUBLISH_ROOT_DIR, "plugins", "publish")
]
@ -36,7 +35,7 @@ class TrayPublishAddon(OpenPypeModule, IHostAddon, ITrayAction):
def run_traypublisher(self):
args = get_ayon_launcher_args(
"module", self.name, "launch"
"addon", self.name, "launch"
)
run_detached_process(args)

View file

@ -16,25 +16,31 @@ class ShotMetadataSolver:
NO_DECOR_PATERN = re.compile(r"\{([a-z]*?)\}")
# presets
clip_name_tokenizer = None
shot_rename = True
shot_hierarchy = None
shot_add_tasks = None
def __init__(self, logger):
self.clip_name_tokenizer = []
self.shot_rename = {
"enabled": False,
"shot_rename_template": "",
}
self.shot_hierarchy = {
"enabled": False,
"parents": [],
"parents_path": "",
}
self.shot_add_tasks = []
self.log = logger
def __init__(
def update_data(
self,
clip_name_tokenizer,
shot_rename,
shot_hierarchy,
shot_add_tasks,
logger
shot_add_tasks
):
self.clip_name_tokenizer = clip_name_tokenizer
self.shot_rename = shot_rename
self.shot_hierarchy = shot_hierarchy
self.shot_add_tasks = shot_add_tasks
self.log = logger
def _rename_template(self, data):
"""Shot renaming function
@ -86,7 +92,9 @@ class ShotMetadataSolver:
search_text = parent_name + clip_name
for token_key, pattern in self.clip_name_tokenizer.items():
for clip_name_item in self.clip_name_tokenizer:
token_key = clip_name_item["name"]
pattern = clip_name_item["regex"]
p = re.compile(pattern)
match = p.findall(search_text)
if not match:
@ -137,11 +145,11 @@ class ShotMetadataSolver:
))
_parent_tokens_type = {
parent_token["name"]: parent_token["type"]
parent_token["name"]: parent_token["parent_type"]
for parent_token in hierarchy_parents
}
for _index, _parent in enumerate(
shot_hierarchy["parents_path"].split("/")
shot_hierarchy["parents_path"].split("/")
):
# format parent token with value which is formatted
try:
@ -262,22 +270,22 @@ class ShotMetadataSolver:
"""
tasks_to_add = {}
project_tasks = project_doc["config"]["tasks"]
for task_name, task_data in self.shot_add_tasks.items():
_task_data = deepcopy(task_data)
project_task_types = project_doc["config"]["tasks"]
for task_item in self.shot_add_tasks:
task_name = task_item["name"]
task_type = task_item["task_type"]
# check if task type in project task types
if _task_data["type"] in project_tasks.keys():
tasks_to_add[task_name] = _task_data
else:
if task_type not in project_task_types.keys():
raise KeyError(
"Missing task type `{}` for `{}` is not"
" existing in `{}``".format(
_task_data["type"],
task_type,
task_name,
list(project_tasks.keys())
list(project_task_types.keys())
)
)
tasks_to_add[task_name] = {"type": task_type}
return tasks_to_add

View file

@ -7,7 +7,6 @@ import pyblish.api
from ayon_core.pipeline import (
register_creator_plugin_path,
legacy_io,
)
from ayon_core.host import HostBase, IPublishHost
@ -24,7 +23,6 @@ class TrayPublisherHost(HostBase, IPublishHost):
def install(self):
os.environ["AVALON_APP"] = self.name
legacy_io.Session["AVALON_APP"] = self.name
pyblish.api.register_host("traypublisher")
pyblish.api.register_plugin_path(PUBLISH_PATH)
@ -43,8 +41,6 @@ class TrayPublisherHost(HostBase, IPublishHost):
# TODO Deregister project specific plugins and register new project
# plugins
os.environ["AVALON_PROJECT"] = project_name
legacy_io.Session["AVALON_PROJECT"] = project_name
legacy_io.install()
HostContext.set_project_name(project_name)

View file

@ -311,7 +311,7 @@ class SettingsCreator(TrayPublishCreator):
@classmethod
def from_settings(cls, item_data):
identifier = item_data["identifier"]
family = item_data["family"]
family = item_data["product_type"]
if not identifier:
identifier = "settings_{}".format(family)
return type(

View file

@ -174,46 +174,42 @@ Supporting publishing new shots to project
or updating already created. Publishing will create OTIO file.
"""
icon = "fa.file"
product_type_presets = []
def __init__(
self, project_settings, *args, **kwargs
):
super(EditorialSimpleCreator, self).__init__(
project_settings, *args, **kwargs
)
def __init__(self, *args, **kwargs):
self._shot_metadata_solver = ShotMetadataSolver(self.log)
super(EditorialSimpleCreator, self).__init__(*args, **kwargs)
def apply_settings(self, project_settings):
editorial_creators = deepcopy(
project_settings["traypublisher"]["editorial_creators"]
)
# get this creator settings by identifier
self._creator_settings = editorial_creators.get(self.identifier)
creator_settings = editorial_creators.get(self.identifier)
clip_name_tokenizer = self._creator_settings["clip_name_tokenizer"]
shot_rename = self._creator_settings["shot_rename"]
shot_hierarchy = self._creator_settings["shot_hierarchy"]
shot_add_tasks = self._creator_settings["shot_add_tasks"]
self._shot_metadata_solver = ShotMetadataSolver(
clip_name_tokenizer,
shot_rename,
shot_hierarchy,
shot_add_tasks,
self.log
self._shot_metadata_solver.update_data(
creator_settings["clip_name_tokenizer"],
creator_settings["shot_rename"],
creator_settings["shot_hierarchy"],
creator_settings["shot_add_tasks"]
)
# try to set main attributes from settings
if self._creator_settings.get("default_variants"):
self.default_variants = self._creator_settings["default_variants"]
self.product_type_presets = creator_settings["product_type_presets"]
default_variants = creator_settings.get("default_variants")
if default_variants:
self.default_variants = default_variants
def create(self, subset_name, instance_data, pre_create_data):
allowed_family_presets = self._get_allowed_family_presets(
allowed_product_type_presets = self._get_allowed_product_type_presets(
pre_create_data)
product_types = {
item["product_type"]
for item in self.product_type_presets
}
clip_instance_properties = {
k: v for k, v in pre_create_data.items()
k: v
for k, v in pre_create_data.items()
if k != "sequence_filepath_data"
if k not in [
i["family"] for i in self._creator_settings["family_presets"]
]
if k not in product_types
}
asset_name = instance_data["folderPath"]
@ -255,7 +251,7 @@ or updating already created. Publishing will create OTIO file.
otio_timeline,
media_path,
clip_instance_properties,
allowed_family_presets,
allowed_product_type_presets,
os.path.basename(seq_path),
first_otio_timeline
)
@ -355,7 +351,7 @@ or updating already created. Publishing will create OTIO file.
otio_timeline,
media_path,
instance_data,
family_presets,
product_type_presets,
sequence_file_name,
first_otio_timeline=None
):
@ -365,7 +361,7 @@ or updating already created. Publishing will create OTIO file.
otio_timeline (otio.Timeline): otio timeline object
media_path (str): media file path string
instance_data (dict): clip instance data
family_presets (list): list of dict settings subset presets
product_type_presets (list): list of dict settings subset presets
"""
tracks = [
@ -411,17 +407,17 @@ or updating already created. Publishing will create OTIO file.
"instance_id": None
}
for _fpreset in family_presets:
for product_type_preset in product_type_presets:
# exclude audio family if no audio stream
if (
_fpreset["family"] == "audio"
product_type_preset["product_type"] == "audio"
and not media_data.get("audio")
):
continue
instance = self._make_subset_instance(
otio_clip,
_fpreset,
product_type_preset,
deepcopy(base_instance_data),
parenting_data
)
@ -533,7 +529,7 @@ or updating already created. Publishing will create OTIO file.
def _make_subset_instance(
self,
otio_clip,
preset,
product_type_preset,
instance_data,
parenting_data
):
@ -541,16 +537,16 @@ or updating already created. Publishing will create OTIO file.
Args:
otio_clip (otio.Clip): otio clip object
preset (dict): single family preset
product_type_preset (dict): single family preset
instance_data (dict): instance data
parenting_data (dict): shot instance parent data
Returns:
CreatedInstance: creator instance object
"""
family = preset["family"]
family = product_type_preset["product_type"]
label = self._make_subset_naming(
preset,
product_type_preset,
instance_data
)
instance_data["label"] = label
@ -569,11 +565,11 @@ or updating already created. Publishing will create OTIO file.
else:
# add review family if defined
instance_data.update({
"outputFileType": preset["output_file_type"],
"outputFileType": product_type_preset["output_file_type"],
"parent_instance_id": parenting_data["instance_id"],
"creator_attributes": {
"parent_instance": parenting_data["instance_label"],
"add_review_family": preset.get("review")
"add_review_family": product_type_preset.get("review")
}
})
@ -585,15 +581,11 @@ or updating already created. Publishing will create OTIO file.
return c_instance
def _make_subset_naming(
self,
preset,
instance_data
):
def _make_subset_naming(self, product_type_preset, instance_data):
""" Subset name maker
Args:
preset (dict): single preset item
product_type_preset (dict): single preset item
instance_data (dict): instance data
Returns:
@ -602,10 +594,10 @@ or updating already created. Publishing will create OTIO file.
asset_name = instance_data["creator_attributes"]["folderPath"]
variant_name = instance_data["variant"]
family = preset["family"]
family = product_type_preset["product_type"]
# get variant name from preset or from inheritance
_variant_name = preset.get("variant") or variant_name
_variant_name = product_type_preset.get("variant") or variant_name
# subset name
subset_name = "{}{}".format(
@ -763,7 +755,7 @@ or updating already created. Publishing will create OTIO file.
"sourceOut": int(source_out)
}
def _get_allowed_family_presets(self, pre_create_data):
def _get_allowed_product_type_presets(self, pre_create_data):
""" Filter out allowed family presets.
Args:
@ -773,10 +765,11 @@ or updating already created. Publishing will create OTIO file.
list: lit of dict with preset items
"""
return [
{"family": "shot"},
{"product_type": "shot"},
*[
preset for preset in self._creator_settings["family_presets"]
if pre_create_data[preset["family"]]
preset
for preset in self.product_type_presets
if pre_create_data[preset["product_type"]]
]
]
@ -853,8 +846,8 @@ or updating already created. Publishing will create OTIO file.
]
# add variants swithers
attr_defs.extend(
BoolDef(_var["family"], label=_var["family"])
for _var in self._creator_settings["family_presets"]
BoolDef(item["product_type"], label=item["product_type"])
for item in self.product_type_presets
)
attr_defs.append(UISeparatorDef())

View file

@ -1,5 +1,5 @@
import os
from ayon_core.modules import OpenPypeModule, IHostAddon
from ayon_core.addon import AYONAddon, IHostAddon
TVPAINT_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
@ -12,13 +12,10 @@ def get_launch_script_path():
)
class TVPaintAddon(OpenPypeModule, IHostAddon):
class TVPaintAddon(AYONAddon, IHostAddon):
name = "tvpaint"
host_name = "tvpaint"
def initialize(self, module_settings):
self.enabled = True
def add_implementation_envs(self, env, _app):
"""Modify environments to contain all required for implementation."""

View file

@ -13,7 +13,6 @@ from ayon_core.hosts.tvpaint import TVPAINT_ROOT_DIR
from ayon_core.settings import get_current_project_settings
from ayon_core.lib import register_event_callback
from ayon_core.pipeline import (
legacy_io,
register_loader_plugin_path,
register_creator_plugin_path,
AVALON_CONTAINER_ID,
@ -66,11 +65,10 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
def install(self):
"""Install TVPaint-specific functionality."""
log.info("OpenPype - Installing TVPaint integration")
legacy_io.install()
log.info("AYON - Installing TVPaint integration")
# Create workdir folder if does not exist yet
workdir = legacy_io.Session["AVALON_WORKDIR"]
workdir = os.getenv("AVALON_WORKDIR")
if not os.path.exists(workdir):
os.makedirs(workdir)

View file

@ -4,7 +4,6 @@ import tempfile
import pyblish.api
from ayon_core.pipeline import legacy_io
from ayon_core.hosts.tvpaint.api.lib import (
execute_george,
execute_george_through_file,
@ -90,7 +89,6 @@ class CollectWorkfileData(pyblish.api.ContextPlugin):
("AVALON_TASK", "task_name")
)
for env_key, key in key_map:
legacy_io.Session[env_key] = workfile_context[key]
os.environ[env_key] = workfile_context[key]
self.log.info("Context changed to: {}".format(workfile_context))

View file

@ -1,17 +1,14 @@
import os
import re
from ayon_core.modules import IHostAddon, OpenPypeModule
from ayon_core.addon import AYONAddon, IHostAddon
UNREAL_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
class UnrealAddon(OpenPypeModule, IHostAddon):
class UnrealAddon(AYONAddon, IHostAddon):
name = "unreal"
host_name = "unreal"
def initialize(self, module_settings):
self.enabled = True
def get_global_environments(self):
return {
"AYON_UNREAL_ROOT": UNREAL_ROOT_DIR,

View file

@ -158,6 +158,7 @@ from .ayon_info import (
is_running_from_build,
is_staging_enabled,
is_dev_mode_enabled,
is_in_tests,
)
@ -229,6 +230,8 @@ __all__ = [
"IniSettingRegistry",
"JSONSettingRegistry",
"AYONSecureRegistry",
"AYONSettingsRegistry",
"OpenPypeSecureRegistry",
"OpenPypeSettingsRegistry",
"get_local_site_id",
@ -271,6 +274,7 @@ __all__ = [
"terminal",
"get_datetime_data",
"get_timestamp",
"get_formatted_current_time",
"Logger",
@ -278,6 +282,7 @@ __all__ = [
"is_running_from_build",
"is_staging_enabled",
"is_dev_mode_enabled",
"is_in_tests",
"requests_get",
"requests_post"

View file

@ -230,29 +230,25 @@ class ApplicationGroup:
self.manager = manager
self._data = data
self.enabled = data.get("enabled", True)
self.label = data.get("label") or None
self.icon = data.get("icon") or None
self._environment = data.get("environment") or {}
self.enabled = data["enabled"]
self.label = data["label"] or None
self.icon = data["icon"] or None
env = {}
try:
env = json.loads(data["environment"])
except Exception:
pass
self._environment = env
host_name = data.get("host_name", None)
host_name = data["host_name"] or None
self.is_host = host_name is not None
self.host_name = host_name
variants = data.get("variants") or {}
key_label_mapping = variants.pop(M_DYNAMIC_KEY_LABEL, {})
for variant_name, variant_data in variants.items():
if variant_name in METADATA_KEYS:
continue
if "variant_label" not in variant_data:
variant_label = key_label_mapping.get(variant_name)
if variant_label:
variant_data["variant_label"] = variant_label
variants[variant_name] = Application(
variant_name, variant_data, self
)
settings_variants = data["variants"]
variants = {}
for variant_data in settings_variants:
app_variant = Application(variant_data, self)
variants[app_variant.name] = app_variant
self.variants = variants
@ -274,62 +270,56 @@ class Application:
Object by itself does nothing special.
Args:
name (str): Specific version (or variant) of application.
e.g. "maya2020", "nuke11.3", etc.
data (dict): Data for the version containing information about
executables, variant label or if is enabled.
Only required key is `executables`.
group (ApplicationGroup): App group object that created the application
and under which application belongs.
"""
def __init__(self, name, data, group):
self.name = name
self.group = group
def __init__(self, data, group):
self._data = data
name = data["name"]
label = data["label"] or name
enabled = False
if group.enabled:
enabled = data.get("enabled", True)
self.enabled = enabled
self.use_python_2 = data.get("use_python_2", False)
self.label = data.get("variant_label") or name
self.full_name = "/".join((group.name, name))
if group.label:
full_label = " ".join((group.label, self.label))
full_label = " ".join((group.label, label))
else:
full_label = self.label
self.full_label = full_label
self._environment = data.get("environment") or {}
full_label = label
env = {}
try:
env = json.loads(data["environment"])
except Exception:
pass
arguments = data.get("arguments")
arguments = data["arguments"]
if isinstance(arguments, dict):
arguments = arguments.get(platform.system().lower())
if not arguments:
arguments = []
_executables = data["executables"].get(platform.system().lower(), [])
executables = [
ApplicationExecutable(executable)
for executable in _executables
]
self.group = group
self.name = name
self.label = label
self.enabled = enabled
self.use_python_2 = data.get("use_python_2", False)
self.full_name = "/".join((group.name, name))
self.full_label = full_label
self.arguments = arguments
if "executables" not in data:
self.executables = [
UndefinedApplicationExecutable()
]
return
_executables = data["executables"]
if isinstance(_executables, dict):
_executables = _executables.get(platform.system().lower())
if not _executables:
_executables = []
executables = []
for executable in _executables:
executables.append(ApplicationExecutable(executable))
self.executables = executables
self._environment = env
def __repr__(self):
return "<{}> - {}".format(self.__class__.__name__, self.full_name)
@ -384,12 +374,12 @@ class ApplicationManager:
"""Load applications and tools and store them by their full name.
Args:
system_settings (dict): Preloaded system settings. When passed manager
studio_settings (dict): Preloaded studio settings. When passed manager
will always use these values. Gives ability to create manager
using different settings.
"""
def __init__(self, system_settings=None):
def __init__(self, studio_settings=None):
self.log = Logger.get_logger(self.__class__.__name__)
self.app_groups = {}
@ -397,16 +387,16 @@ class ApplicationManager:
self.tool_groups = {}
self.tools = {}
self._system_settings = system_settings
self._studio_settings = studio_settings
self.refresh()
def set_system_settings(self, system_settings):
def set_studio_settings(self, studio_settings):
"""Ability to change init system settings.
This will trigger refresh of manager.
"""
self._system_settings = system_settings
self._studio_settings = studio_settings
self.refresh()
@ -417,72 +407,30 @@ class ApplicationManager:
self.tool_groups.clear()
self.tools.clear()
if self._system_settings is not None:
settings = copy.deepcopy(self._system_settings)
if self._studio_settings is not None:
settings = copy.deepcopy(self._studio_settings)
else:
settings = get_system_settings(
clear_metadata=False, exclude_locals=False
)
all_app_defs = {}
applications_addon_settings = settings["applications"]
# Prepare known applications
app_defs = settings["applications"]
additional_apps = {}
app_defs = applications_addon_settings["applications"]
additional_apps = app_defs.pop("additional_apps")
app_defs.update(additional_apps)
for group_name, variant_defs in app_defs.items():
if group_name in METADATA_KEYS:
continue
if group_name == "additional_apps":
additional_apps = variant_defs
else:
all_app_defs[group_name] = variant_defs
# Prepare additional applications
# - First find dynamic keys that can be used as labels of group
dynamic_keys = {}
for group_name, variant_defs in additional_apps.items():
if group_name == M_DYNAMIC_KEY_LABEL:
dynamic_keys = variant_defs
break
# Add additional apps to known applications
for group_name, variant_defs in additional_apps.items():
if group_name in METADATA_KEYS:
continue
# Determine group label
label = variant_defs.get("label")
if not label:
# Look for label set in dynamic labels
label = dynamic_keys.get(group_name)
if not label:
label = group_name
variant_defs["label"] = label
all_app_defs[group_name] = variant_defs
for group_name, variant_defs in all_app_defs.items():
if group_name in METADATA_KEYS:
continue
group = ApplicationGroup(group_name, variant_defs, self)
self.app_groups[group_name] = group
for app in group:
self.applications[app.full_name] = app
tools_definitions = settings["tools"]["tool_groups"]
tool_label_mapping = tools_definitions.pop(M_DYNAMIC_KEY_LABEL, {})
for tool_group_name, tool_group_data in tools_definitions.items():
if not tool_group_name or tool_group_name in METADATA_KEYS:
continue
tool_group_label = (
tool_label_mapping.get(tool_group_name) or tool_group_name
)
group = EnvironmentToolGroup(
tool_group_name, tool_group_label, tool_group_data, self
)
self.tool_groups[tool_group_name] = group
tools_definitions = applications_addon_settings["tool_groups"]
for tool_group_data in tools_definitions:
group = EnvironmentToolGroup(tool_group_data, self)
self.tool_groups[group.name] = group
for tool in group:
self.tools[tool.full_name] = tool
@ -571,30 +519,31 @@ class EnvironmentToolGroup:
are same.
Args:
name (str): Name of the tool group.
data (dict): Group's information with it's variants.
data (dict): Group information with variants.
manager (ApplicationManager): Manager that creates the group.
"""
def __init__(self, name, label, data, manager):
def __init__(self, data, manager):
name = data["name"]
label = data["label"]
self.name = name
self.label = label
self._data = data
self.manager = manager
self._environment = data["environment"]
variants = data.get("variants") or {}
label_by_key = variants.pop(M_DYNAMIC_KEY_LABEL, {})
environment = {}
try:
environment = json.loads(data["environment"])
except Exception:
pass
self._environment = environment
variants = data.get("variants") or []
variants_by_name = {}
for variant_name, variant_data in variants.items():
if variant_name in METADATA_KEYS:
continue
variant_label = label_by_key.get(variant_name) or variant_name
tool = EnvironmentTool(
variant_name, variant_label, variant_data, self
)
variants_by_name[variant_name] = tool
for variant_data in variants:
tool = EnvironmentTool(variant_data, self)
variants_by_name[tool.name] = tool
self.variants = variants_by_name
def __repr__(self):
@ -615,23 +564,25 @@ class EnvironmentTool:
Structure of tool information.
Args:
name (str): Name of the tool.
variant_data (dict): Variant data with environments and
host and app variant filters.
group (str): Name of group which wraps tool.
group (EnvironmentToolGroup): Name of group which wraps tool.
"""
def __init__(self, name, label, variant_data, group):
def __init__(self, variant_data, group):
# Backwards compatibility 3.9.1 - 3.9.2
# - 'variant_data' contained only environments but contain also host
# and application variant filters
host_names = variant_data.get("host_names", [])
app_variants = variant_data.get("app_variants", [])
name = variant_data["name"]
label = variant_data["label"]
host_names = variant_data["host_names"]
app_variants = variant_data["app_variants"]
if "environment" in variant_data:
environment = variant_data["environment"]
else:
environment = variant_data
environment = {}
try:
environment = json.loads(variant_data["environment"])
except Exception:
pass
self.host_names = host_names
self.app_variants = app_variants

View file

@ -38,6 +38,16 @@ def is_staging_enabled():
return os.getenv("AYON_USE_STAGING") == "1"
def is_in_tests():
"""Process is running in automatic tests mode.
Returns:
bool: True if running in tests.
"""
return os.environ.get("AYON_IN_TESTS") == "1"
def is_dev_mode_enabled():
"""Dev mode is enabled in AYON.

View file

@ -38,8 +38,8 @@ class AYONSecureRegistry:
Registry should be used for private data that should be available only for
user.
All passed registry names will have added prefix `OpenPype/` to easier
identify which data were created by OpenPype.
All passed registry names will have added prefix `AYON/` to easier
identify which data were created by AYON.
Args:
name(str): Name of registry used as identifier for data.

View file

@ -7,11 +7,10 @@ from datetime import datetime
from ayon_core.lib import (
env_value_to_bool,
collect_frames,
is_in_tests,
)
from ayon_core.pipeline import legacy_io
from openpype_modules.deadline import abstract_submit_deadline
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo
from ayon_core.tests.lib import is_in_tests
@attr.s
@ -84,13 +83,17 @@ class AfterEffectsSubmitDeadline(
"AVALON_PROJECT",
"AVALON_ASSET",
"AVALON_TASK",
"AVALON_WORKDIR",
"AVALON_APP_NAME",
"AYON_LOG_NO_COLORS",
"IS_TEST"
]
environment = dict({key: os.environ[key] for key in keys
if key in os.environ}, **legacy_io.Session)
environment = {
key: os.environ[key]
for key in keys
if key in os.environ
}
for key in keys:
value = environment.get(key)
if value:

Some files were not shown because too many files have changed in this diff Show more