Merge branch 'develop' into enhancement/OP-7630_DCC-connection-timeout

This commit is contained in:
Jakub Trllo 2024-02-19 15:44:20 +01:00
commit 0c24c12075
206 changed files with 1980 additions and 3202 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

@ -788,6 +788,7 @@ class AddonsManager:
addon_classes.append(modules_item)
aliased_names = []
for addon_cls in addon_classes:
name = addon_cls.__name__
if issubclass(addon_cls, OpenPypeModule):
@ -807,6 +808,13 @@ class AddonsManager:
self._addons.append(addon)
self._addons_by_id[addon.id] = addon
self._addons_by_name[addon.name] = addon
# NOTE This will be removed with release 1.0.0 of ayon-core
# please use carefully.
# Gives option to use alias name for addon for cases when
# name in OpenPype was not the same as in AYON.
name_alias = getattr(addon, "openpype_alias", None)
if name_alias:
aliased_names.append((name_alias, addon))
enabled_str = "X"
if not addon.enabled:
enabled_str = " "
@ -822,6 +830,17 @@ class AddonsManager:
exc_info=True
)
for item in aliased_names:
name_alias, addon = item
if name_alias not in self._addons_by_name:
self._addons_by_name[name_alias] = addon
continue
self.log.warning(
"Alias name '{}' of addon '{}' is already assigned.".format(
name_alias, addon.name
)
)
if self._report is not None:
report[self._report_total_key] = time.time() - time_start
self._report["Initialization"] = report

View file

@ -73,6 +73,20 @@ class Commands:
import pyblish.api
import pyblish.util
# Fix older jobs
for src_key, dst_key in (
("AVALON_PROJECT", "AYON_PROJECT_NAME"),
("AVALON_ASSET", "AYON_FOLDER_PATH"),
("AVALON_TASK", "AYON_TASK_NAME"),
("AVALON_WORKDIR", "AYON_WORKDIR"),
("AVALON_APP_NAME", "AYON_APP_NAME"),
("AVALON_APP", "AYON_HOST_NAME"),
):
if src_key in os.environ and dst_key not in os.environ:
os.environ[dst_key] = os.environ[src_key]
# Remove old keys, so we're sure they're not used
os.environ.pop(src_key, None)
log = Logger.get_logger("CLI-publish")
install_ayon_plugins()
@ -87,7 +101,7 @@ class Commands:
if not any(paths):
raise RuntimeError("No publish paths specified")
app_full_name = os.getenv("AVALON_APP_NAME")
app_full_name = os.getenv("AYON_APP_NAME")
if app_full_name:
context = get_global_context()
env = get_app_environments_for_context(

View file

@ -21,7 +21,7 @@ class CreateWorkdirExtraFolders(PreLaunchHook):
return
env = self.data.get("env") or {}
workdir = env.get("AVALON_WORKDIR")
workdir = env.get("AYON_WORKDIR")
if not workdir or not os.path.exists(workdir):
return

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)
@ -107,7 +106,7 @@ class HostBase(object):
Union[str, None]: Current project name.
"""
return os.environ.get("AVALON_PROJECT")
return os.environ.get("AYON_PROJECT_NAME")
def get_current_asset_name(self):
"""
@ -115,7 +114,7 @@ class HostBase(object):
Union[str, None]: Current asset name.
"""
return os.environ.get("AVALON_ASSET")
return os.environ.get("AYON_FOLDER_PATH")
def get_current_task_name(self):
"""
@ -123,7 +122,7 @@ class HostBase(object):
Union[str, None]: Current task name.
"""
return os.environ.get("AVALON_TASK")
return os.environ.get("AYON_TASK_NAME")
def get_current_context(self):
"""Get current context information.
@ -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

@ -234,7 +234,7 @@ class IWorkfileHost:
str: Path to new workdir.
"""
return session["AVALON_WORKDIR"]
return session["AYON_WORKDIR"]
# --- Deprecated method names ---
def file_extensions(self):

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,14 +297,11 @@ 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
os.environ["AYON_PROJECT_NAME"] = project
if asset:
legacy_io.Session["AVALON_ASSET"] = asset
os.environ["AVALON_ASSET"] = asset
os.environ["AYON_FOLDER_PATH"] = asset
if task:
legacy_io.Session["AVALON_TASK"] = task
os.environ["AVALON_TASK"] = task
os.environ["AYON_TASK_NAME"] = task
async def read(self):
log.debug("aftereffects.read client calls server server calls "

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,
@ -273,7 +272,7 @@ def set_resolution(data):
def on_new():
project = os.environ.get("AVALON_PROJECT")
project = os.environ.get("AYON_PROJECT_NAME")
settings = get_project_settings(project).get("blender")
set_resolution_startup = settings.get("set_resolution_startup")
@ -294,7 +293,7 @@ def on_new():
def on_open():
project = os.environ.get("AVALON_PROJECT")
project = os.environ.get("AYON_PROJECT_NAME")
settings = get_project_settings(project).get("blender")
set_resolution_startup = settings.get("set_resolution_startup")
@ -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("AYON_WORKDIR")
log.debug("New working directory: %s", workdir)

View file

@ -82,7 +82,7 @@ def file_extensions() -> List[str]:
def work_root(session: dict) -> str:
"""Return the default root to browse for work files."""
work_dir = session["AVALON_WORKDIR"]
work_dir = session["AYON_WORKDIR"]
scene_dir = session.get("AVALON_SCENEDIR")
if scene_dir:
return str(Path(work_dir, scene_dir))

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

@ -34,4 +34,4 @@ def current_file():
def work_root(session):
return os.path.normpath(session["AVALON_WORKDIR"]).replace("\\", "/")
return os.path.normpath(session["AYON_WORKDIR"]).replace("\\", "/")

View file

@ -70,7 +70,7 @@ class LoadClip(opfapi.ClipLoader):
self.log.info("Loading with colorspace: `{}`".format(colorspace))
# create workfile path
workfile_dir = os.environ["AVALON_WORKDIR"]
workfile_dir = os.environ["AYON_WORKDIR"]
openclip_dir = os.path.join(
workfile_dir, clip_name
)

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
@ -69,7 +80,7 @@ class LoadClipBatch(opfapi.ClipLoader):
self.log.info("Loading with colorspace: `{}`".format(colorspace))
# create workfile path
workfile_dir = options.get("workdir") or os.environ["AVALON_WORKDIR"]
workfile_dir = options.get("workdir") or os.environ["AYON_WORKDIR"]
openclip_dir = os.path.join(
workfile_dir, clip_name
)
@ -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

@ -22,7 +22,7 @@ def get_fusion_version(app_name):
The function is triggered by the prelaunch hooks to get the fusion version.
`app_name` is obtained by prelaunch hooks from the
`launch_context.env.get("AVALON_APP_NAME")`.
`launch_context.env.get("AYON_APP_NAME")`.
To get a correct Fusion version, a version number should be present
in the `applications/fusion/variants` key

View file

@ -135,7 +135,7 @@ class FusionHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
return current_filepath
def work_root(self, session):
work_dir = session["AVALON_WORKDIR"]
work_dir = session["AYON_WORKDIR"]
scene_dir = session.get("AVALON_SCENEDIR")
if scene_dir:
return os.path.join(work_dir, scene_dir)

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("AYON_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

@ -131,7 +131,7 @@ class FusionCopyPrefsPrelaunch(PreLaunchHook):
) = self.get_copy_fusion_prefs_settings()
# Get launched application context and return correct app version
app_name = self.launch_context.env.get("AVALON_APP_NAME")
app_name = self.launch_context.env.get("AYON_APP_NAME")
app_version = get_fusion_version(app_name)
if app_version is None:
version_names = ", ".join(str(x) for x in FUSION_VERSIONS_DICT)

View file

@ -28,7 +28,7 @@ class FusionPrelaunch(PreLaunchHook):
def execute(self):
# making sure python 3 is installed at provided path
# Py 3.3-3.10 for Fusion 18+ or Py 3.6 for Fu 16-17
app_data = self.launch_context.env.get("AVALON_APP_NAME")
app_data = self.launch_context.env.get("AYON_APP_NAME")
app_version = get_fusion_version(app_data)
if not app_version:
raise ApplicationLaunchFailed(

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

@ -74,4 +74,4 @@ def current_file():
def work_root(session):
return os.path.normpath(session["AVALON_WORKDIR"]).replace("\\", "/")
return os.path.normpath(session["AYON_WORKDIR"]).replace("\\", "/")

View file

@ -77,7 +77,7 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin):
expected_settings.pop("resolutionWidth")
expected_settings.pop("resolutionHeight")
if (any(re.search(pattern, os.getenv('AVALON_TASK'))
if (any(re.search(pattern, os.getenv('AYON_TASK_NAME'))
for pattern in self.skip_timelines_check)):
self.log.info("Skipping frames check because of "
"task name and pattern {}".format(

View file

@ -70,4 +70,4 @@ def current_file():
def work_root(session):
return os.path.normpath(session["AVALON_WORKDIR"]).replace("\\", "/")
return os.path.normpath(session["AYON_WORKDIR"]).replace("\\", "/")

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

@ -10,7 +10,7 @@ class SetPath(PreLaunchHook):
launch_types = {LaunchTypes.local}
def execute(self):
workdir = self.launch_context.env.get("AVALON_WORKDIR", "")
workdir = self.launch_context.env.get("AYON_WORKDIR", "")
if not workdir:
self.log.warning("BUG: Workdir is not filled.")
return

View file

@ -63,9 +63,8 @@ class MaxHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
rt.callbacks.addScript(rt.Name('postWorkspaceChange'),
self._deferred_menu_creation)
def has_unsaved_changes(self):
# TODO: how to get it from 3dsmax?
return True
def workfile_has_unsaved_changes(self):
return rt.getSaveRequired()
def get_workfile_extensions(self):
return [".max"]

View file

@ -10,7 +10,7 @@ class SetPath(PreLaunchHook):
launch_types = {LaunchTypes.local}
def execute(self):
workdir = self.launch_context.env.get("AVALON_WORKDIR", "")
workdir = self.launch_context.env.get("AYON_WORKDIR", "")
if not workdir:
self.log.warning("BUG: Workdir is not filled.")
return

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,11 +1,9 @@
import pyblish.api
import os
from ayon_core.pipeline import registered_host
class SaveCurrentScene(pyblish.api.ContextPlugin):
"""Save current scene
"""
"""Save current scene"""
label = "Save current file"
order = pyblish.api.ExtractorOrder - 0.49
@ -13,9 +11,13 @@ class SaveCurrentScene(pyblish.api.ContextPlugin):
families = ["maxrender", "workfile"]
def process(self, context):
from pymxs import runtime as rt
folder = rt.maxFilePath
file = rt.maxFileName
current = os.path.join(folder, file)
assert context.data["currentFile"] == current
rt.saveMaxFile(current)
host = registered_host()
current_file = host.get_current_workfile()
assert context.data["currentFile"] == current_file
if host.workfile_has_unsaved_changes():
self.log.info(f"Saving current file: {current_file}")
host.save_workfile(current_file)
else:
self.log.debug("No unsaved changes, skipping file save..")

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

@ -8,5 +8,8 @@
local pythonpath = systemTools.getEnvVariable "MAX_PYTHONPATH"
systemTools.setEnvVariable "PYTHONPATH" pythonpath
/*opens the create menu on startup to ensure users are presented with a useful default view.*/
max create mode
python.ExecuteFile startup
)

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("AYON_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("AYON_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("AYON_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 isinstance(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

@ -12,7 +12,7 @@ import gridfs
DEFINITION_FILENAME = "{}/maya/shader_definition.txt".format(
os.getenv("AVALON_PROJECT"))
os.getenv("AYON_PROJECT_NAME"))
class ShaderDefinitionsEditor(QtWidgets.QWidget):

View file

@ -35,7 +35,7 @@ def current_file():
def work_root(session):
work_dir = session["AVALON_WORKDIR"]
work_dir = session["AYON_WORKDIR"]
scene_dir = None
# Query scene file rule from workspace.mel if it exists in WORKDIR

View file

@ -12,7 +12,7 @@ class PreCopyMel(PreLaunchHook):
def execute(self):
project_doc = self.data["project_doc"]
workdir = self.launch_context.env.get("AVALON_WORKDIR")
workdir = self.launch_context.env.get("AYON_WORKDIR")
if not workdir:
self.log.warning("BUG: Workdir is not filled.")
return

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

@ -8,13 +8,12 @@ publishing on farm.
Requires:
instance -> families
instance -> setMembers
instance -> asset
context -> currentFile
context -> workspaceDir
context -> user
session -> AVALON_ASSET
Optional:
Provides:

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

@ -38,7 +38,7 @@ if explicit_plugins_loading["enabled"]:
key = "AYON_OPEN_WORKFILE_POST_INITIALIZATION"
if bool(int(os.environ.get(key, "0"))):
def _log_and_open():
path = os.environ["AVALON_LAST_WORKFILE"]
path = os.environ["AYON_LAST_WORKFILE"]
print("Opening \"{}\"".format(path))
cmds.file(path, open=True, force=True)
cmds.evalDeferred(
@ -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

@ -120,7 +120,7 @@ def deprecated(new_destination):
class Context:
main_window = None
context_action_item = None
project_name = os.getenv("AVALON_PROJECT")
project_name = os.getenv("AYON_PROJECT_NAME")
# Workfile related code
workfiles_launched = False
workfiles_tool_timer = None
@ -2605,7 +2605,7 @@ Reopening Nuke should synchronize these paths and resolve any discrepancies.
def set_favorites(self):
from .utils import set_context_favorites
work_dir = os.getenv("AVALON_WORKDIR")
work_dir = os.getenv("AYON_WORKDIR")
asset = get_current_asset_name()
favorite_items = OrderedDict()
@ -2953,7 +2953,7 @@ def process_workfile_builder():
create_fv_on = workfile_builder.get("create_first_version") or None
builder_on = workfile_builder.get("builder_on_start") or None
last_workfile_path = os.environ.get("AVALON_LAST_WORKFILE")
last_workfile_path = os.environ.get("AYON_LAST_WORKFILE")
# generate first version in file not existing and feature is enabled
if create_fv_on and not os.path.exists(last_workfile_path):
@ -3203,7 +3203,7 @@ class DirmapCache:
@classmethod
def project_name(cls):
if cls._project_name is None:
cls._project_name = os.getenv("AVALON_PROJECT")
cls._project_name = os.getenv("AYON_PROJECT_NAME")
return cls._project_name
@classmethod

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["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

@ -68,7 +68,7 @@ def current_file():
def work_root(session):
work_dir = session["AVALON_WORKDIR"]
work_dir = session["AYON_WORKDIR"]
scene_dir = session.get("AVALON_SCENEDIR")
if scene_dir:
path = os.path.join(work_dir, scene_dir)

View file

@ -112,7 +112,7 @@ class WriteNodeKnobSettingPanel(nukescripts.PythonPanel):
for write_node in write_selected_nodes:
# data for mapping the path
data = {
"work": os.getenv("AVALON_WORKDIR"),
"work": os.getenv("AYON_WORKDIR"),
"subset": write_node["name"].value(),
"frame": "#" * frame_padding,
"ext": ext

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

@ -62,7 +62,7 @@ class PhotoshopHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
return None
def work_root(self, session):
return os.path.normpath(session["AVALON_WORKDIR"]).replace("\\", "/")
return os.path.normpath(session["AYON_WORKDIR"]).replace("\\", "/")
def open_workfile(self, filepath):
lib.stub().open(filepath)

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):
@ -53,10 +52,10 @@ class CollectBatchData(pyblish.api.ContextPlugin):
assert os.path.exists(batch_dir), \
"Folder {} doesn't exist".format(batch_dir)
project_name = os.environ.get("AVALON_PROJECT")
project_name = os.environ.get("AYON_PROJECT_NAME")
if project_name is None:
raise AssertionError(
"Environment `AVALON_PROJECT` was not found."
"Environment `AYON_PROJECT_NAME` was not found."
"Could not set project `root` which may cause issues."
)
@ -69,10 +68,8 @@ class CollectBatchData(pyblish.api.ContextPlugin):
batch_data["context"]
)
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
os.environ["AYON_FOLDER_PATH"] = asset_name
os.environ["AYON_TASK_NAME"] = 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

@ -79,7 +79,7 @@ def open_file(filepath):
def current_file():
pm = get_project_manager()
file_ext = file_extensions()[0]
workdir_path = os.getenv("AVALON_WORKDIR")
workdir_path = os.getenv("AYON_WORKDIR")
project = pm.GetCurrentProject()
project_name = project.GetName()
file_name = project_name + file_ext
@ -93,4 +93,4 @@ def current_file():
def work_root(session):
return os.path.normpath(session["AVALON_WORKDIR"]).replace("\\", "/")
return os.path.normpath(session["AYON_WORKDIR"]).replace("\\", "/")

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

@ -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
@ -23,8 +22,7 @@ class TrayPublisherHost(HostBase, IPublishHost):
name = "traypublisher"
def install(self):
os.environ["AVALON_APP"] = self.name
legacy_io.Session["AVALON_APP"] = self.name
os.environ["AYON_HOST_NAME"] = self.name
pyblish.api.register_host("traypublisher")
pyblish.api.register_plugin_path(PUBLISH_PATH)
@ -42,9 +40,7 @@ class TrayPublisherHost(HostBase, IPublishHost):
def set_project_name(self, project_name):
# 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()
os.environ["AYON_PROJECT_NAME"] = project_name
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

@ -8,7 +8,7 @@ log = Logger.get_logger(__name__)
def initialize():
from ayon_core.hosts.traypublisher.api.plugin import SettingsCreator
project_name = os.environ["AVALON_PROJECT"]
project_name = os.environ["AYON_PROJECT_NAME"]
project_settings = get_project_settings(project_name)
simple_creators = project_settings["traypublisher"]["simple_creators"]

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("AYON_WORKDIR")
if not os.path.exists(workdir):
os.makedirs(workdir)
@ -157,7 +155,7 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
return execute_george(george_script)
def work_root(self, session):
return session["AVALON_WORKDIR"]
return session["AYON_WORKDIR"]
def get_current_workfile(self):
return execute_george("tv_GetProjectName")
@ -176,7 +174,7 @@ class TVPaintHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
# Setup project settings if its the template that's launched.
# TODO also check for template creation when it's possible to define
# templates
last_workfile = os.environ.get("AVALON_LAST_WORKFILE")
last_workfile = os.environ.get("AYON_LAST_WORKFILE")
if not last_workfile or os.path.exists(last_workfile):
return

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