Merge branch 'develop' into enhancement/Houdini_menu_help_submenu

This commit is contained in:
Kayla Man 2024-02-21 13:12:43 +08:00 committed by GitHub
commit 1afbf8e571
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
227 changed files with 2181 additions and 4258 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
@ -181,6 +181,10 @@ class HostDirmap(object):
exclude_locals=False,
cached=False)
# TODO implement
# Dirmap is dependent on 'get_site_local_overrides' which
# is not implemented in AYON. The mapping should be received
# from sitesync addon.
active_overrides = get_site_local_overrides(
project_name, active_site)
remote_overrides = get_site_local_overrides(
@ -210,13 +214,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

@ -194,13 +194,13 @@ class RenderCreator(Creator):
name into created subset name.
Position of composition name could be set in
`project_settings/global/tools/creator/subset_name_profiles` with some
form of '{composition}' placeholder.
`project_settings/global/tools/creator/product_name_profiles` with
some form of '{composition}' placeholder.
Composition name will be used implicitly if multiple composition should
be handled at same time.
If {composition} placeholder is not us 'subset_name_profiles'
If {composition} placeholder is not us 'product_name_profiles'
composition name will be capitalized and set at the end of subset name
if necessary.

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

@ -2,7 +2,7 @@ import hou
import pyblish.api
from ayon_core.hosts.houdini.api import lib
import ayon_core.hosts.houdini.api.usd as hou_usdlib
import ayon_core.lib.usdlib as usdlib
from ayon_core.pipeline import usdlib
class CollectInstancesUsdLayered(pyblish.api.ContextPlugin):

View file

@ -5,7 +5,7 @@ from ayon_core.client import (
get_asset_by_name,
get_asset_name_identifier,
)
import ayon_core.lib.usdlib as usdlib
from ayon_core.pipeline import usdlib
class CollectUsdBootstrap(pyblish.api.InstancePlugin):

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

@ -38,25 +38,6 @@ class ToolWindows:
cls._windows[tool] = window
def edit_shader_definitions():
from qtpy import QtWidgets
from ayon_core.hosts.maya.api.shader_definition_editor import (
ShaderDefinitionsEditor
)
from ayon_core.tools.utils import qt_app_context
top_level_widgets = QtWidgets.QApplication.topLevelWidgets()
main_window = next(widget for widget in top_level_widgets
if widget.objectName() == "MayaWindow")
with qt_app_context():
window = ToolWindows.get_window("shader_definition_editor")
if not window:
window = ShaderDefinitionsEditor(parent=main_window)
ToolWindows.set_window("shader_definition_editor", window)
window.show()
def _resolution_from_document(doc):
if not doc or "data" not in doc:
print("Entered document is not valid. \"{}\"".format(str(doc)))

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

@ -1,176 +0,0 @@
# -*- coding: utf-8 -*-
"""Editor for shader definitions.
Shader names are stored as simple text file over GridFS in mongodb.
"""
import os
from qtpy import QtWidgets, QtCore, QtGui
from ayon_core.client.mongo import OpenPypeMongoConnection
from ayon_core import resources
import gridfs
DEFINITION_FILENAME = "{}/maya/shader_definition.txt".format(
os.getenv("AVALON_PROJECT"))
class ShaderDefinitionsEditor(QtWidgets.QWidget):
"""Widget serving as simple editor for shader name definitions."""
# name of the file used to store definitions
def __init__(self, parent=None):
super(ShaderDefinitionsEditor, self).__init__(parent)
self._mongo = OpenPypeMongoConnection.get_mongo_client()
self._gridfs = gridfs.GridFS(
self._mongo[os.getenv("OPENPYPE_DATABASE_NAME")])
self._editor = None
self._original_content = self._read_definition_file()
self.setObjectName("shaderDefinitionEditor")
self.setWindowTitle("OpenPype shader name definition editor")
icon = QtGui.QIcon(resources.get_ayon_icon_filepath())
self.setWindowIcon(icon)
self.setWindowFlags(QtCore.Qt.Window)
self.setParent(parent)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.resize(750, 500)
self._setup_ui()
self._reload()
def _setup_ui(self):
"""Setup UI of Widget."""
layout = QtWidgets.QVBoxLayout(self)
label = QtWidgets.QLabel()
label.setText("Put shader names here - one name per line:")
layout.addWidget(label)
self._editor = QtWidgets.QPlainTextEdit()
self._editor.setStyleSheet("border: none;")
layout.addWidget(self._editor)
btn_layout = QtWidgets.QHBoxLayout()
save_btn = QtWidgets.QPushButton("Save")
save_btn.clicked.connect(self._save)
reload_btn = QtWidgets.QPushButton("Reload")
reload_btn.clicked.connect(self._reload)
exit_btn = QtWidgets.QPushButton("Exit")
exit_btn.clicked.connect(self._close)
btn_layout.addWidget(reload_btn)
btn_layout.addWidget(save_btn)
btn_layout.addWidget(exit_btn)
layout.addLayout(btn_layout)
def _read_definition_file(self, file=None):
"""Read definition file from database.
Args:
file (gridfs.grid_file.GridOut, Optional): File to read. If not
set, new query will be issued to find it.
Returns:
str: Content of the file or empty string if file doesn't exist.
"""
content = ""
if not file:
file = self._gridfs.find_one(
{"filename": DEFINITION_FILENAME})
if not file:
print(">>> [SNDE]: nothing in database yet")
return content
content = file.read()
file.close()
return content
def _write_definition_file(self, content, force=False):
"""Write content as definition to file in database.
Before file is written, check is made if its content has not
changed. If is changed, warning is issued to user if he wants
it to overwrite. Note: GridFs doesn't allow changing file content.
You need to delete existing file and create new one.
Args:
content (str): Content to write.
Raises:
ContentException: If file is changed in database while
editor is running.
"""
file = self._gridfs.find_one(
{"filename": DEFINITION_FILENAME})
if file:
content_check = self._read_definition_file(file)
if content == content_check:
print(">>> [SNDE]: content not changed")
return
if self._original_content != content_check:
if not force:
raise ContentException("Content changed")
print(">>> [SNDE]: overwriting data")
file.close()
self._gridfs.delete(file._id)
file = self._gridfs.new_file(
filename=DEFINITION_FILENAME,
content_type='text/plain',
encoding='utf-8')
file.write(content)
file.close()
QtCore.QTimer.singleShot(200, self._reset_style)
self._editor.setStyleSheet("border: 1px solid #33AF65;")
self._original_content = content
def _reset_style(self):
"""Reset editor style back.
Used to visually indicate save.
"""
self._editor.setStyleSheet("border: none;")
def _close(self):
self.hide()
def closeEvent(self, event):
event.ignore()
self.hide()
def _reload(self):
print(">>> [SNDE]: reloading")
self._set_content(self._read_definition_file())
def _save(self):
try:
self._write_definition_file(content=self._editor.toPlainText())
except ContentException:
# content has changed meanwhile
print(">>> [SNDE]: content has changed")
self._show_overwrite_warning()
def _set_content(self, content):
self._editor.setPlainText(content)
def _show_overwrite_warning(self):
reply = QtWidgets.QMessageBox.question(
self,
"Warning",
("Content you are editing was changed meanwhile in database.\n"
"Please, reload and solve the conflict."),
QtWidgets.QMessageBox.OK)
if reply == QtWidgets.QMessageBox.OK:
# do nothing
pass
class ContentException(Exception):
"""This is risen during save if file is changed in database."""
pass

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

@ -1,162 +0,0 @@
# -*- coding: utf-8 -*-
"""Validate model nodes names."""
import os
import platform
import re
import gridfs
import pyblish.api
from maya import cmds
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)
class ValidateModelName(pyblish.api.InstancePlugin,
OptionalPyblishPluginMixin):
"""Validate name of model
starts with (somename)_###_(materialID)_GEO
materialID must be present in list
padding number doesn't have limit
"""
optional = True
order = ValidateContentsOrder
hosts = ["maya"]
families = ["model"]
label = "Model Name"
actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction]
material_file = None
database_file = DEFINITION_FILENAME
@classmethod
def get_invalid(cls, instance):
"""Get invalid nodes."""
use_db = cls.database
def is_group(group_name):
"""Find out if supplied transform is group or not."""
try:
children = cmds.listRelatives(group_name, children=True)
for child in children:
if not cmds.ls(child, transforms=True):
return False
return True
except Exception:
return False
invalid = []
content_instance = instance.data.get("setMembers", None)
if not content_instance:
cls.log.error("Instance has no nodes!")
return True
pass
# validate top level group name
assemblies = cmds.ls(content_instance, assemblies=True, long=True)
if len(assemblies) != 1:
cls.log.error("Must have exactly one top group")
return assemblies or True
top_group = assemblies[0]
regex = cls.top_level_regex
r = re.compile(regex)
m = r.match(top_group)
project_name = instance.context.data["projectName"]
current_asset_name = instance.context.data["asset"]
if m is None:
cls.log.error("invalid name on: {}".format(top_group))
cls.log.error("name doesn't match regex {}".format(regex))
invalid.append(top_group)
else:
if "asset" in r.groupindex:
if m.group("asset") != current_asset_name:
cls.log.error("Invalid asset name in top level group.")
return top_group
if "subset" in r.groupindex:
if m.group("subset") != instance.data.get("subset"):
cls.log.error("Invalid subset name in top level group.")
return top_group
if "project" in r.groupindex:
if m.group("project") != project_name:
cls.log.error("Invalid project name in top level group.")
return top_group
descendants = cmds.listRelatives(content_instance,
allDescendents=True,
fullPath=True) or []
descendants = cmds.ls(descendants, noIntermediate=True, long=True)
trns = cmds.ls(descendants, long=False, type='transform')
# filter out groups
filtered = [node for node in trns if not is_group(node)]
# load shader list file as utf-8
shaders = []
if not use_db:
material_file = cls.material_file[platform.system().lower()]
if material_file:
if os.path.isfile(material_file):
shader_file = open(material_file, "r")
shaders = shader_file.readlines()
shader_file.close()
else:
cls.log.error("Missing shader name definition file.")
return True
else:
client = OpenPypeMongoConnection.get_mongo_client()
fs = gridfs.GridFS(client[os.getenv("OPENPYPE_DATABASE_NAME")])
shader_file = fs.find_one({"filename": cls.database_file})
if not shader_file:
cls.log.error("Missing shader name definition in database.")
return True
shaders = shader_file.read().splitlines()
shader_file.close()
# strip line endings from list
shaders = [s.rstrip() for s in shaders if s.rstrip()]
# compile regex for testing names
regex = cls.regex
r = re.compile(regex)
for obj in filtered:
cls.log.debug("testing: {}".format(obj))
m = r.match(obj)
if m is None:
cls.log.error("invalid name on: {}".format(obj))
invalid.append(obj)
else:
# if we have shader files and shader named group is in
# regex, test this group against names in shader file
if "shader" in r.groupindex and shaders:
try:
if not m.group('shader') in shaders:
cls.log.error(
"invalid materialID on: {0} ({1})".format(
obj, m.group('shader')))
invalid.append(obj)
except IndexError:
# shader named group doesn't match
cls.log.error(
"shader group doesn't match: {}".format(obj))
invalid.append(obj)
return invalid
def process(self, instance):
"""Plugin entry point."""
if not self.is_active(instance.data):
return
invalid = self.get_invalid(instance)
if invalid:
raise PublishValidationError(
"Model naming is invalid. See the log.")

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

@ -493,7 +493,7 @@ def get_colorspace_from_node(node):
def get_review_presets_config():
settings = get_current_project_settings()
review_profiles = (
settings["global"]
settings["core"]
["publish"]
["ExtractReview"]
["profiles"]
@ -1348,7 +1348,9 @@ def _remove_old_knobs(node):
def exposed_write_knobs(settings, plugin_name, instance_node):
exposed_knobs = settings["nuke"]["create"][plugin_name]["exposed_knobs"]
exposed_knobs = settings["nuke"]["create"][plugin_name].get(
"exposed_knobs", []
)
if exposed_knobs:
instance_node.addKnob(nuke.Text_Knob('', 'Write Knobs'))
write_node = nuke.allNodes(group=instance_node, filter="Write")[0]

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

@ -65,7 +65,7 @@ class ValidateExposedKnobs(
group_node = instance.data["transientData"]["node"]
nuke_settings = instance.context.data["project_settings"]["nuke"]
create_settings = nuke_settings["create"][plugin]
exposed_knobs = create_settings["exposed_knobs"]
exposed_knobs = create_settings.get("exposed_knobs", [])
unexposed_knobs = []
for knob in exposed_knobs:
if knob not in group_node.knobs():

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

@ -209,8 +209,8 @@ class ImageCreator(Creator):
'Use layer name in subset' will explicitly add layer name into subset
name. Position of this name is configurable in
`project_settings/global/tools/creator/subset_name_profiles`.
If layer placeholder ({layer}) is not used in `subset_name_profiles`
`project_settings/global/tools/creator/product_name_profiles`.
If layer placeholder ({layer}) is not used in `product_name_profiles`
but layer name should be used (set explicitly in UI or implicitly if
multiple images should be created), it is added in capitalized form
as a suffix to subset name.

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("\\", "/")

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