moved maya implementation to openpype

This commit is contained in:
Jakub Trllo 2022-02-03 18:44:27 +01:00
parent df24aab3e9
commit f94f636ff2
52 changed files with 1707 additions and 569 deletions

View file

@ -5,9 +5,7 @@ def add_implementation_envs(env, _app):
# Add requirements to PYTHONPATH
pype_root = os.environ["OPENPYPE_REPOS_ROOT"]
new_python_paths = [
os.path.join(pype_root, "openpype", "hosts", "maya", "startup"),
os.path.join(pype_root, "repos", "avalon-core", "setup", "maya"),
os.path.join(pype_root, "tools", "mayalookassigner")
os.path.join(pype_root, "openpype", "hosts", "maya", "startup")
]
old_python_path = env.get("PYTHONPATH") or ""
for path in old_python_path.split(os.pathsep):

View file

@ -1,233 +1,90 @@
import os
import logging
import weakref
"""Public API
from maya import utils, cmds
Anything that isn't defined here is INTERNAL and unreliable for external use.
from avalon import api as avalon
from avalon import pipeline
from avalon.maya import suspended_refresh
from avalon.maya.pipeline import IS_HEADLESS
from openpype.tools.utils import host_tools
from pyblish import api as pyblish
from openpype.lib import any_outdated
import openpype.hosts.maya
from openpype.hosts.maya.lib import copy_workspace_mel
from openpype.lib.path_tools import HostDirmap
from . import menu, lib
"""
log = logging.getLogger("openpype.hosts.maya")
from .pipeline import (
install,
uninstall,
HOST_DIR = os.path.dirname(os.path.abspath(openpype.hosts.maya.__file__))
PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory")
ls,
containerise,
lock,
unlock,
is_locked,
lock_ignored,
)
from .plugin import (
Creator,
Loader
)
from .workio import (
open_file,
save_file,
current_file,
has_unsaved_changes,
file_extensions,
work_root
)
from .lib import (
export_alembic,
lsattr,
lsattrs,
read,
apply_shaders,
without_extension,
maintained_selection,
suspended_refresh,
unique_name,
unique_namespace,
)
def install():
from openpype.settings import get_project_settings
__all__ = [
"install",
"uninstall",
project_settings = get_project_settings(os.getenv("AVALON_PROJECT"))
# process path mapping
dirmap_processor = MayaDirmap("maya", project_settings)
dirmap_processor.process_dirmap()
"Creator",
"Loader",
pyblish.register_plugin_path(PUBLISH_PATH)
avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
avalon.register_plugin_path(avalon.Creator, CREATE_PATH)
avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH)
log.info(PUBLISH_PATH)
menu.install()
"ls",
log.info("Installing callbacks ... ")
avalon.on("init", on_init)
"lock",
"unlock",
"is_locked",
"lock_ignored",
# Callbacks below are not required for headless mode, the `init` however
# is important to load referenced Alembics correctly at rendertime.
if IS_HEADLESS:
log.info("Running in headless mode, skipping Maya "
"save/open/new callback installation..")
return
# Workfiles API
"open_file",
"save_file",
"current_file",
"has_unsaved_changes",
"file_extensions",
"work_root",
avalon.on("save", on_save)
avalon.on("open", on_open)
avalon.on("new", on_new)
avalon.before("save", on_before_save)
avalon.on("taskChanged", on_task_changed)
avalon.on("before.workfile.save", before_workfile_save)
# Utility functions
"export_alembic",
"lsattr",
"lsattrs",
"read",
log.info("Setting default family states for loader..")
avalon.data["familiesStateToggled"] = ["imagesequence"]
"unique_name",
"unique_namespace",
"apply_shaders",
"without_extension",
"maintained_selection",
"suspended_refresh",
def uninstall():
pyblish.deregister_plugin_path(PUBLISH_PATH)
avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH)
avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH)
avalon.deregister_plugin_path(avalon.InventoryAction, INVENTORY_PATH)
]
menu.uninstall()
def on_init(_):
avalon.logger.info("Running callback on init..")
def safe_deferred(fn):
"""Execute deferred the function in a try-except"""
def _fn():
"""safely call in deferred callback"""
try:
fn()
except Exception as exc:
print(exc)
try:
utils.executeDeferred(_fn)
except Exception as exc:
print(exc)
# Force load Alembic so referenced alembics
# work correctly on scene open
cmds.loadPlugin("AbcImport", quiet=True)
cmds.loadPlugin("AbcExport", quiet=True)
# Force load objExport plug-in (requested by artists)
cmds.loadPlugin("objExport", quiet=True)
from .customize import (
override_component_mask_commands,
override_toolbox_ui
)
safe_deferred(override_component_mask_commands)
launch_workfiles = os.environ.get("WORKFILES_STARTUP")
if launch_workfiles:
safe_deferred(host_tools.show_workfiles)
if not IS_HEADLESS:
safe_deferred(override_toolbox_ui)
def on_before_save(return_code, _):
"""Run validation for scene's FPS prior to saving"""
return lib.validate_fps()
def on_save(_):
"""Automatically add IDs to new nodes
Any transform of a mesh, without an existing ID, is given one
automatically on file save.
"""
avalon.logger.info("Running callback on save..")
# # Update current task for the current scene
# update_task_from_path(cmds.file(query=True, sceneName=True))
# Generate ids of the current context on nodes in the scene
nodes = lib.get_id_required_nodes(referenced_nodes=False)
for node, new_id in lib.generate_ids(nodes):
lib.set_id(node, new_id, overwrite=False)
def on_open(_):
"""On scene open let's assume the containers have changed."""
from Qt import QtWidgets
from openpype.widgets import popup
cmds.evalDeferred(
"from openpype.hosts.maya.api import lib;"
"lib.remove_render_layer_observer()")
cmds.evalDeferred(
"from openpype.hosts.maya.api import lib;"
"lib.add_render_layer_observer()")
cmds.evalDeferred(
"from openpype.hosts.maya.api import lib;"
"lib.add_render_layer_change_observer()")
# # Update current task for the current scene
# update_task_from_path(cmds.file(query=True, sceneName=True))
# Validate FPS after update_task_from_path to
# ensure it is using correct FPS for the asset
lib.validate_fps()
lib.fix_incompatible_containers()
if any_outdated():
log.warning("Scene has outdated content.")
# Find maya main window
top_level_widgets = {w.objectName(): w for w in
QtWidgets.QApplication.topLevelWidgets()}
parent = top_level_widgets.get("MayaWindow", None)
if parent is None:
log.info("Skipping outdated content pop-up "
"because Maya window can't be found.")
else:
# Show outdated pop-up
def _on_show_inventory():
host_tools.show_scene_inventory(parent=parent)
dialog = popup.Popup(parent=parent)
dialog.setWindowTitle("Maya scene has outdated content")
dialog.setMessage("There are outdated containers in "
"your Maya scene.")
dialog.on_show.connect(_on_show_inventory)
dialog.show()
def on_new(_):
"""Set project resolution and fps when create a new file"""
avalon.logger.info("Running callback on new..")
with suspended_refresh():
cmds.evalDeferred(
"from openpype.hosts.maya.api import lib;"
"lib.remove_render_layer_observer()")
cmds.evalDeferred(
"from openpype.hosts.maya.api import lib;"
"lib.add_render_layer_observer()")
cmds.evalDeferred(
"from openpype.hosts.maya.api import lib;"
"lib.add_render_layer_change_observer()")
lib.set_context_settings()
def on_task_changed(*args):
"""Wrapped function of app initialize and maya's on task changed"""
# Run
with suspended_refresh():
lib.set_context_settings()
lib.update_content_on_context_change()
msg = " project: {}\n asset: {}\n task:{}".format(
avalon.Session["AVALON_PROJECT"],
avalon.Session["AVALON_ASSET"],
avalon.Session["AVALON_TASK"]
)
lib.show_message(
"Context was changed",
("Context was changed to:\n{}".format(msg)),
)
def before_workfile_save(event):
workdir_path = event.workdir_path
if workdir_path:
copy_workspace_mel(workdir_path)
class MayaDirmap(HostDirmap):
def on_enable_dirmap(self):
cmds.dirmap(en=True)
def dirmap_routine(self, source_path, destination_path):
cmds.dirmap(m=(source_path, destination_path))
cmds.dirmap(m=(destination_path, source_path))
# Backwards API compatibility
open = open_file
save = save_file

View file

@ -2,7 +2,7 @@
from __future__ import absolute_import
import pyblish.api
from avalon import io
from openpype.api import get_errored_instances_from_context
@ -72,8 +72,7 @@ class GenerateUUIDsOnInvalidAction(pyblish.api.Action):
nodes (list): all nodes to regenerate ids on
"""
from openpype.hosts.maya.api import lib
import avalon.io as io
from . import lib
asset = instance.data['asset']
asset_id = io.find_one({"name": asset, "type": "asset"},

View file

@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
"""OpenPype script commands to be used directly in Maya."""
from maya import cmds
from avalon import api, io
class ToolWindows:
@ -51,3 +53,135 @@ def edit_shader_definitions():
window = ShaderDefinitionsEditor(parent=main_window)
ToolWindows.set_window("shader_definition_editor", window)
window.show()
def reset_frame_range():
"""Set frame range to current asset"""
# Set FPS first
fps = {15: 'game',
24: 'film',
25: 'pal',
30: 'ntsc',
48: 'show',
50: 'palf',
60: 'ntscf',
23.98: '23.976fps',
23.976: '23.976fps',
29.97: '29.97fps',
47.952: '47.952fps',
47.95: '47.952fps',
59.94: '59.94fps',
44100: '44100fps',
48000: '48000fps'
}.get(float(api.Session.get("AVALON_FPS", 25)), "pal")
cmds.currentUnit(time=fps)
# Set frame start/end
asset_name = api.Session["AVALON_ASSET"]
asset = io.find_one({"name": asset_name, "type": "asset"})
frame_start = asset["data"].get("frameStart")
frame_end = asset["data"].get("frameEnd")
# Backwards compatibility
if frame_start is None or frame_end is None:
frame_start = asset["data"].get("edit_in")
frame_end = asset["data"].get("edit_out")
if frame_start is None or frame_end is None:
cmds.warning("No edit information found for %s" % asset_name)
return
handles = asset["data"].get("handles") or 0
handle_start = asset["data"].get("handleStart")
if handle_start is None:
handle_start = handles
handle_end = asset["data"].get("handleEnd")
if handle_end is None:
handle_end = handles
frame_start -= int(handle_start)
frame_end += int(handle_end)
cmds.playbackOptions(minTime=frame_start)
cmds.playbackOptions(maxTime=frame_end)
cmds.playbackOptions(animationStartTime=frame_start)
cmds.playbackOptions(animationEndTime=frame_end)
cmds.playbackOptions(minTime=frame_start)
cmds.playbackOptions(maxTime=frame_end)
cmds.currentTime(frame_start)
cmds.setAttr("defaultRenderGlobals.startFrame", frame_start)
cmds.setAttr("defaultRenderGlobals.endFrame", frame_end)
def _resolution_from_document(doc):
if not doc or "data" not in doc:
print("Entered document is not valid. \"{}\"".format(str(doc)))
return None
resolution_width = doc["data"].get("resolutionWidth")
resolution_height = doc["data"].get("resolutionHeight")
# Backwards compatibility
if resolution_width is None or resolution_height is None:
resolution_width = doc["data"].get("resolution_width")
resolution_height = doc["data"].get("resolution_height")
# Make sure both width and heigh are set
if resolution_width is None or resolution_height is None:
cmds.warning(
"No resolution information found for \"{}\"".format(doc["name"])
)
return None
return int(resolution_width), int(resolution_height)
def reset_resolution():
# Default values
resolution_width = 1920
resolution_height = 1080
# Get resolution from asset
asset_name = api.Session["AVALON_ASSET"]
asset_doc = io.find_one({"name": asset_name, "type": "asset"})
resolution = _resolution_from_document(asset_doc)
# Try get resolution from project
if resolution is None:
# TODO go through visualParents
print((
"Asset \"{}\" does not have set resolution."
" Trying to get resolution from project"
).format(asset_name))
project_doc = io.find_one({"type": "project"})
resolution = _resolution_from_document(project_doc)
if resolution is None:
msg = "Using default resolution {}x{}"
else:
resolution_width, resolution_height = resolution
msg = "Setting resolution to {}x{}"
print(msg.format(resolution_width, resolution_height))
# set for different renderers
# arnold, vray, redshift, renderman
renderer = cmds.getAttr("defaultRenderGlobals.currentRenderer").lower()
# handle various renderman names
if renderer.startswith("renderman"):
renderer = "renderman"
# default attributes are usable for Arnold, Renderman and Redshift
width_attr_name = "defaultResolution.width"
height_attr_name = "defaultResolution.height"
# Vray has its own way
if renderer == "vray":
width_attr_name = "vraySettings.width"
height_attr_name = "vraySettings.height"
cmds.setAttr(width_attr_name, resolution_width)
cmds.setAttr(height_attr_name, resolution_height)

View file

@ -8,10 +8,9 @@ from functools import partial
import maya.cmds as mc
import maya.mel as mel
from avalon.maya import pipeline
from openpype.api import resources
from openpype.tools.utils import host_tools
from .lib import get_main_window
log = logging.getLogger(__name__)
@ -76,6 +75,7 @@ def override_component_mask_commands():
def override_toolbox_ui():
"""Add custom buttons in Toolbox as replacement for Maya web help icon."""
icons = resources.get_resource("icons")
parent_widget = get_main_window()
# Ensure the maya web icon on toolbox exists
web_button = "ToolBox|MainToolboxLayout|mayaWebButton"
@ -115,7 +115,7 @@ def override_toolbox_ui():
label="Work Files",
image=os.path.join(icons, "workfiles.png"),
command=lambda: host_tools.show_workfiles(
parent=pipeline._parent
parent=parent_widget
),
width=icon_size,
height=icon_size,
@ -130,7 +130,7 @@ def override_toolbox_ui():
label="Loader",
image=os.path.join(icons, "loader.png"),
command=lambda: host_tools.show_loader(
parent=pipeline._parent, use_context=True
parent=parent_widget, use_context=True
),
width=icon_size,
height=icon_size,
@ -145,7 +145,7 @@ def override_toolbox_ui():
label="Inventory",
image=os.path.join(icons, "inventory.png"),
command=lambda: host_tools.show_scene_inventory(
parent=pipeline._parent
parent=parent_widget
),
width=icon_size,
height=icon_size,

View file

@ -1,7 +1,8 @@
"""Standalone helper functions"""
import re
import os
import sys
import re
import platform
import uuid
import math
@ -18,16 +19,19 @@ import bson
from maya import cmds, mel
import maya.api.OpenMaya as om
from avalon import api, maya, io, pipeline
import avalon.maya.lib
import avalon.maya.interactive
from avalon import api, io, pipeline
from openpype import lib
from openpype.api import get_anatomy_settings
from .commands import reset_frame_range
self = sys.modules[__name__]
self._parent = None
log = logging.getLogger(__name__)
IS_HEADLESS = not hasattr(cmds, "about") or cmds.about(batch=True)
ATTRIBUTE_DICT = {"int": {"attributeType": "long"},
"str": {"dataType": "string"},
"unicode": {"dataType": "string"},
@ -100,6 +104,155 @@ FLOAT_FPS = {23.98, 23.976, 29.97, 47.952, 59.94}
RENDERLIKE_INSTANCE_FAMILIES = ["rendering", "vrayscene"]
def get_main_window():
"""Acquire Maya's main window"""
from Qt import QtWidgets
if self._parent is None:
self._parent = {
widget.objectName(): widget
for widget in QtWidgets.QApplication.topLevelWidgets()
}["MayaWindow"]
return self._parent
@contextlib.contextmanager
def suspended_refresh():
"""Suspend viewport refreshes"""
try:
cmds.refresh(suspend=True)
yield
finally:
cmds.refresh(suspend=False)
@contextlib.contextmanager
def maintained_selection():
"""Maintain selection during context
Example:
>>> scene = cmds.file(new=True, force=True)
>>> node = cmds.createNode("transform", name="Test")
>>> cmds.select("persp")
>>> with maintained_selection():
... cmds.select("Test", replace=True)
>>> "Test" in cmds.ls(selection=True)
False
"""
previous_selection = cmds.ls(selection=True)
try:
yield
finally:
if previous_selection:
cmds.select(previous_selection,
replace=True,
noExpand=True)
else:
cmds.select(clear=True)
def unique_name(name, format="%02d", namespace="", prefix="", suffix=""):
"""Return unique `name`
The function takes into consideration an optional `namespace`
and `suffix`. The suffix is included in evaluating whether a
name exists - such as `name` + "_GRP" - but isn't included
in the returned value.
If a namespace is provided, only names within that namespace
are considered when evaluating whether the name is unique.
Arguments:
format (str, optional): The `name` is given a number, this determines
how this number is formatted. Defaults to a padding of 2.
E.g. my_name01, my_name02.
namespace (str, optional): Only consider names within this namespace.
suffix (str, optional): Only consider names with this suffix.
Example:
>>> name = cmds.createNode("transform", name="MyName")
>>> cmds.objExists(name)
True
>>> unique = unique_name(name)
>>> cmds.objExists(unique)
False
"""
iteration = 1
unique = prefix + (name + format % iteration) + suffix
while cmds.objExists(namespace + ":" + unique):
iteration += 1
unique = prefix + (name + format % iteration) + suffix
if suffix:
return unique[:-len(suffix)]
return unique
def unique_namespace(namespace, format="%02d", prefix="", suffix=""):
"""Return unique namespace
Similar to :func:`unique_name` but evaluating namespaces
as opposed to object names.
Arguments:
namespace (str): Name of namespace to consider
format (str, optional): Formatting of the given iteration number
suffix (str, optional): Only consider namespaces with this suffix.
"""
iteration = 1
unique = prefix + (namespace + format % iteration) + suffix
# The `existing` set does not just contain the namespaces but *all* nodes
# within "current namespace". We need all because the namespace could
# also clash with a node name. To be truly unique and valid one needs to
# check against all.
existing = set(cmds.namespaceInfo(listNamespace=True))
while unique in existing:
iteration += 1
unique = prefix + (namespace + format % iteration) + suffix
return unique
def read(node):
"""Return user-defined attributes from `node`"""
data = dict()
for attr in cmds.listAttr(node, userDefined=True) or list():
try:
value = cmds.getAttr(node + "." + attr, asString=True)
except RuntimeError:
# For Message type attribute or others that have connections,
# take source node name as value.
source = cmds.listConnections(node + "." + attr,
source=True,
destination=False)
source = cmds.ls(source, long=True) or [None]
value = source[0]
except ValueError:
# Some attributes cannot be read directly,
# such as mesh and color attributes. These
# are considered non-essential to this
# particular publishing pipeline.
value = None
data[attr] = value
return data
def _get_mel_global(name):
"""Return the value of a mel global variable"""
return mel.eval("$%s = $%s;" % (name, name))
@ -280,6 +433,73 @@ def shape_from_element(element):
return node
def export_alembic(nodes,
file,
frame_range=None,
write_uv=True,
write_visibility=True,
attribute_prefix=None):
"""Wrap native MEL command with limited set of arguments
Arguments:
nodes (list): Long names of nodes to cache
file (str): Absolute path to output destination
frame_range (tuple, optional): Start- and end-frame of cache,
default to current animation range.
write_uv (bool, optional): Whether or not to include UVs,
default to True
write_visibility (bool, optional): Turn on to store the visibility
state of objects in the Alembic file. Otherwise, all objects are
considered visible, default to True
attribute_prefix (str, optional): Include all user-defined
attributes with this prefix.
"""
if frame_range is None:
frame_range = (
cmds.playbackOptions(query=True, ast=True),
cmds.playbackOptions(query=True, aet=True)
)
options = [
("file", file),
("frameRange", "%s %s" % frame_range),
] + [("root", mesh) for mesh in nodes]
if isinstance(attribute_prefix, string_types):
# Include all attributes prefixed with "mb"
# TODO(marcus): This would be a good candidate for
# external registration, so that the developer
# doesn't have to edit this function to modify
# the behavior of Alembic export.
options.append(("attrPrefix", str(attribute_prefix)))
if write_uv:
options.append(("uvWrite", ""))
if write_visibility:
options.append(("writeVisibility", ""))
# Generate MEL command
mel_args = list()
for key, value in options:
mel_args.append("-{0} {1}".format(key, value))
mel_args_string = " ".join(mel_args)
mel_cmd = "AbcExport -j \"{0}\"".format(mel_args_string)
# For debuggability, put the string passed to MEL in the Script editor.
print("mel.eval('%s')" % mel_cmd)
return mel.eval(mel_cmd)
def collect_animation_data(fps=False):
"""Get the basic animation data
@ -305,6 +525,256 @@ def collect_animation_data(fps=False):
return data
def imprint(node, data):
"""Write `data` to `node` as userDefined attributes
Arguments:
node (str): Long name of node
data (dict): Dictionary of key/value pairs
Example:
>>> from maya import cmds
>>> def compute():
... return 6
...
>>> cube, generator = cmds.polyCube()
>>> imprint(cube, {
... "regularString": "myFamily",
... "computedValue": lambda: compute()
... })
...
>>> cmds.getAttr(cube + ".computedValue")
6
"""
for key, value in data.items():
if callable(value):
# Support values evaluated at imprint
value = value()
if isinstance(value, bool):
add_type = {"attributeType": "bool"}
set_type = {"keyable": False, "channelBox": True}
elif isinstance(value, string_types):
add_type = {"dataType": "string"}
set_type = {"type": "string"}
elif isinstance(value, int):
add_type = {"attributeType": "long"}
set_type = {"keyable": False, "channelBox": True}
elif isinstance(value, float):
add_type = {"attributeType": "double"}
set_type = {"keyable": False, "channelBox": True}
elif isinstance(value, (list, tuple)):
add_type = {"attributeType": "enum", "enumName": ":".join(value)}
set_type = {"keyable": False, "channelBox": True}
value = 0 # enum default
else:
raise TypeError("Unsupported type: %r" % type(value))
cmds.addAttr(node, longName=key, **add_type)
cmds.setAttr(node + "." + key, value, **set_type)
def serialise_shaders(nodes):
"""Generate a shader set dictionary
Arguments:
nodes (list): Absolute paths to nodes
Returns:
dictionary of (shader: id) pairs
Schema:
{
"shader1": ["id1", "id2"],
"shader2": ["id3", "id1"]
}
Example:
{
"Bazooka_Brothers01_:blinn4SG": [
"f9520572-ac1d-11e6-b39e-3085a99791c9.f[4922:5001]",
"f9520572-ac1d-11e6-b39e-3085a99791c9.f[4587:4634]",
"f9520572-ac1d-11e6-b39e-3085a99791c9.f[1120:1567]",
"f9520572-ac1d-11e6-b39e-3085a99791c9.f[4251:4362]"
],
"lambert2SG": [
"f9520571-ac1d-11e6-9dbb-3085a99791c9"
]
}
"""
valid_nodes = cmds.ls(
nodes,
long=True,
recursive=True,
showType=True,
objectsOnly=True,
type="transform"
)
meshes_by_id = {}
for mesh in valid_nodes:
shapes = cmds.listRelatives(valid_nodes[0],
shapes=True,
fullPath=True) or list()
if shapes:
shape = shapes[0]
if not cmds.nodeType(shape):
continue
try:
id_ = cmds.getAttr(mesh + ".mbID")
if id_ not in meshes_by_id:
meshes_by_id[id_] = list()
meshes_by_id[id_].append(mesh)
except ValueError:
continue
meshes_by_shader = dict()
for id_, mesh in meshes_by_id.items():
shape = cmds.listRelatives(mesh,
shapes=True,
fullPath=True) or list()
for shader in cmds.listConnections(shape,
type="shadingEngine") or list():
# Objects in this group are those that haven't got
# any shaders. These are expected to be managed
# elsewhere, such as by the default model loader.
if shader == "initialShadingGroup":
continue
if shader not in meshes_by_shader:
meshes_by_shader[shader] = list()
shaded = cmds.sets(shader, query=True) or list()
meshes_by_shader[shader].extend(shaded)
shader_by_id = {}
for shader, shaded in meshes_by_shader.items():
if shader not in shader_by_id:
shader_by_id[shader] = list()
for mesh in shaded:
# Enable shader assignment to faces.
name = mesh.split(".f[")[0]
transform = name
if cmds.objectType(transform) == "mesh":
transform = cmds.listRelatives(name, parent=True)[0]
try:
id_ = cmds.getAttr(transform + ".mbID")
shader_by_id[shader].append(mesh.replace(name, id_))
except KeyError:
continue
# Remove duplicates
shader_by_id[shader] = list(set(shader_by_id[shader]))
return shader_by_id
def lsattr(attr, value=None):
"""Return nodes matching `key` and `value`
Arguments:
attr (str): Name of Maya attribute
value (object, optional): Value of attribute. If none
is provided, return all nodes with this attribute.
Example:
>> lsattr("id", "myId")
["myNode"]
>> lsattr("id")
["myNode", "myOtherNode"]
"""
if value is None:
return cmds.ls("*.%s" % attr,
recursive=True,
objectsOnly=True,
long=True)
return lsattrs({attr: value})
def lsattrs(attrs):
"""Return nodes with the given attribute(s).
Arguments:
attrs (dict): Name and value pairs of expected matches
Example:
>> # Return nodes with an `age` of five.
>> lsattr({"age": "five"})
>> # Return nodes with both `age` and `color` of five and blue.
>> lsattr({"age": "five", "color": "blue"})
Return:
list: matching nodes.
"""
dep_fn = om.MFnDependencyNode()
dag_fn = om.MFnDagNode()
selection_list = om.MSelectionList()
first_attr = next(iter(attrs))
try:
selection_list.add("*.{0}".format(first_attr),
searchChildNamespaces=True)
except RuntimeError as exc:
if str(exc).endswith("Object does not exist"):
return []
matches = set()
for i in range(selection_list.length()):
node = selection_list.getDependNode(i)
if node.hasFn(om.MFn.kDagNode):
fn_node = dag_fn.setObject(node)
full_path_names = [path.fullPathName()
for path in fn_node.getAllPaths()]
else:
fn_node = dep_fn.setObject(node)
full_path_names = [fn_node.name()]
for attr in attrs:
try:
plug = fn_node.findPlug(attr, True)
if plug.asString() != attrs[attr]:
break
except RuntimeError:
break
else:
matches.update(full_path_names)
return list(matches)
@contextlib.contextmanager
def without_extension():
"""Use cmds.file with defaultExtensions=False"""
previous_setting = cmds.file(defaultExtensions=True, query=True)
try:
cmds.file(defaultExtensions=False)
yield
finally:
cmds.file(defaultExtensions=previous_setting)
@contextlib.contextmanager
def attribute_values(attr_values):
"""Remaps node attributes to values during context.
@ -736,7 +1206,7 @@ def namespaced(namespace, new=True):
"""
original = cmds.namespaceInfo(cur=True, absoluteName=True)
if new:
namespace = avalon.maya.lib.unique_namespace(namespace)
namespace = unique_namespace(namespace)
cmds.namespace(add=namespace)
try:
@ -1408,7 +1878,7 @@ def assign_look_by_version(nodes, version_id):
raise RuntimeError("Could not find LookLoader, this is a bug")
# Reference the look file
with maya.maintained_selection():
with maintained_selection():
container_node = pipeline.load(Loader, look_representation)
# Get container members
@ -1947,7 +2417,7 @@ def set_context_settings():
reset_scene_resolution()
# Set frame range.
avalon.maya.interactive.reset_frame_range()
reset_frame_range()
# Set colorspace
set_colorspace()
@ -2386,7 +2856,7 @@ def get_attr_in_layer(attr, layer):
def fix_incompatible_containers():
"""Return whether the current scene has any outdated content"""
host = avalon.api.registered_host()
host = api.registered_host()
for container in host.ls():
loader = container['loader']

View file

@ -1,58 +1,144 @@
import sys
import os
import logging
from Qt import QtWidgets, QtGui
import maya.utils
import maya.cmds as cmds
from avalon.maya import pipeline
import avalon.api
from openpype.api import BuildWorkfile
from openpype.settings import get_project_settings
from openpype.tools.utils import host_tools
from openpype.hosts.maya.api import lib
from .lib import get_main_window, IS_HEADLESS
from .commands import reset_frame_range
log = logging.getLogger(__name__)
MENU_NAME = "op_maya_menu"
def _get_menu(menu_name=None):
"""Return the menu instance if it currently exists in Maya"""
if menu_name is None:
menu_name = pipeline._menu
menu_name = MENU_NAME
widgets = {w.objectName(): w for w in QtWidgets.QApplication.allWidgets()}
return widgets.get(menu_name)
def deferred():
def add_build_workfiles_item():
# Add build first workfile
cmds.menuItem(divider=True, parent=pipeline._menu)
def install():
if cmds.about(batch=True):
log.info("Skipping openpype.menu initialization in batch mode..")
return
def deferred():
from avalon.tools import publish
parent_widget = get_main_window()
cmds.menu(
MENU_NAME,
label=avalon.api.Session["AVALON_LABEL"],
tearOff=True,
parent="MayaWindow"
)
# Create context menu
context_label = "{}, {}".format(
avalon.api.Session["AVALON_ASSET"],
avalon.api.Session["AVALON_TASK"]
)
cmds.menuItem(
"currentContext",
label=context_label,
parent=MENU_NAME,
enable=False
)
cmds.setParent("..", menu=True)
cmds.menuItem(divider=True)
# Create default items
cmds.menuItem(
"Create...",
command=lambda *args: host_tools.show_creator(parent=parent_widget)
)
cmds.menuItem(
"Load...",
command=lambda *args: host_tools.show_loader(
parent=parent_widget,
use_context=True
)
)
cmds.menuItem(
"Publish...",
command=lambda *args: host_tools.show_publish(parent=parent_widget),
image=publish.ICON
)
cmds.menuItem(
"Manage...",
command=lambda *args: host_tools.show_scene_inventory(
parent=parent_widget
)
)
cmds.menuItem(
"Library...",
command=lambda *args: host_tools.show_library_loader(
parent=parent_widget
)
)
cmds.menuItem(divider=True)
cmds.menuItem(
"Work Files...",
command=lambda *args: host_tools.show_workfiles(
parent=parent_widget
),
)
cmds.menuItem(
"Reset Frame Range",
command=reset_frame_range
)
cmds.menuItem(
"Reset Resolution",
command=lib.reset_scene_resolution
)
cmds.menuItem(
"Set Colorspace",
command=lib.set_colorspace,
)
cmds.menuItem(divider=True, parent=MENU_NAME)
cmds.menuItem(
"Build First Workfile",
parent=pipeline._menu,
parent=MENU_NAME,
command=lambda *args: BuildWorkfile().process()
)
def add_look_assigner_item():
cmds.menuItem(
"Look assigner",
parent=pipeline._menu,
"Look assigner...",
command=lambda *args: host_tools.show_look_assigner(
pipeline._parent
parent_widget
)
)
def add_experimental_item():
cmds.menuItem(
"Experimental tools...",
parent=pipeline._menu,
command=lambda *args: host_tools.show_experimental_tools_dialog(
pipeline._parent
parent_widget
)
)
cmds.setParent(MENU_NAME, menu=True)
def add_scripts_menu():
try:
@ -82,124 +168,13 @@ def deferred():
# apply configuration
studio_menu.build_from_configuration(studio_menu, config)
def modify_workfiles():
# Find the pipeline menu
top_menu = _get_menu()
# Try to find workfile tool action in the menu
workfile_action = None
for action in top_menu.actions():
if action.text() == "Work Files":
workfile_action = action
break
# Add at the top of menu if "Work Files" action was not found
after_action = ""
if workfile_action:
# Use action's object name for `insertAfter` argument
after_action = workfile_action.objectName()
# Insert action to menu
cmds.menuItem(
"Work Files",
parent=pipeline._menu,
command=lambda *args: host_tools.show_workfiles(pipeline._parent),
insertAfter=after_action
)
# Remove replaced action
if workfile_action:
top_menu.removeAction(workfile_action)
def modify_resolution():
# Find the pipeline menu
top_menu = _get_menu()
# Try to find resolution tool action in the menu
resolution_action = None
for action in top_menu.actions():
if action.text() == "Reset Resolution":
resolution_action = action
break
# Add at the top of menu if "Work Files" action was not found
after_action = ""
if resolution_action:
# Use action's object name for `insertAfter` argument
after_action = resolution_action.objectName()
# Insert action to menu
cmds.menuItem(
"Reset Resolution",
parent=pipeline._menu,
command=lambda *args: lib.reset_scene_resolution(),
insertAfter=after_action
)
# Remove replaced action
if resolution_action:
top_menu.removeAction(resolution_action)
def remove_project_manager():
top_menu = _get_menu()
# Try to find "System" menu action in the menu
system_menu = None
for action in top_menu.actions():
if action.text() == "System":
system_menu = action
break
if system_menu is None:
return
# Try to find "Project manager" action in "System" menu
project_manager_action = None
for action in system_menu.menu().children():
if hasattr(action, "text") and action.text() == "Project Manager":
project_manager_action = action
break
# Remove "Project manager" action if was found
if project_manager_action is not None:
system_menu.menu().removeAction(project_manager_action)
def add_colorspace():
# Find the pipeline menu
top_menu = _get_menu()
# Try to find workfile tool action in the menu
workfile_action = None
for action in top_menu.actions():
if action.text() == "Reset Resolution":
workfile_action = action
break
# Add at the top of menu if "Work Files" action was not found
after_action = ""
if workfile_action:
# Use action's object name for `insertAfter` argument
after_action = workfile_action.objectName()
# Insert action to menu
cmds.menuItem(
"Set Colorspace",
parent=pipeline._menu,
command=lambda *args: lib.set_colorspace(),
insertAfter=after_action
)
log.info("Attempting to install scripts menu ...")
# add_scripts_menu()
add_build_workfiles_item()
add_look_assigner_item()
add_experimental_item()
modify_workfiles()
modify_resolution()
remove_project_manager()
add_colorspace()
add_scripts_menu()
# Allow time for uninstallation to finish.
# We use Maya's executeDeferred instead of QTimer.singleShot
# so that it only gets called after Maya UI has initialized too.
# This is crucial with Maya 2020+ which initializes without UI
# first as a QCoreApplication
maya.utils.executeDeferred(deferred)
cmds.evalDeferred(add_scripts_menu, lowestPriority=True)
def uninstall():
@ -214,18 +189,27 @@ def uninstall():
log.error(e)
def install():
if cmds.about(batch=True):
log.info("Skipping openpype.menu initialization in batch mode..")
return
# Allow time for uninstallation to finish.
cmds.evalDeferred(deferred, lowestPriority=True)
def popup():
"""Pop-up the existing menu near the mouse cursor."""
menu = _get_menu()
cursor = QtGui.QCursor()
point = cursor.pos()
menu.exec_(point)
def update_menu_task_label():
"""Update the task label in Avalon menu to current session"""
if IS_HEADLESS:
return
object_name = "{}|currentContext".format(MENU_NAME)
if not cmds.menuItem(object_name, query=True, exists=True):
log.warning("Can't find menuItem: {}".format(object_name))
return
label = "{}, {}".format(
avalon.api.Session["AVALON_ASSET"],
avalon.api.Session["AVALON_TASK"]
)
cmds.menuItem(object_name, edit=True, label=label)

View file

@ -0,0 +1,594 @@
import os
import sys
import errno
import logging
import contextlib
from maya import utils, cmds, OpenMaya
import maya.api.OpenMaya as om
import pyblish.api
import avalon.api
from avalon.lib import find_submodule
from avalon.pipeline import AVALON_CONTAINER_ID
import openpype.hosts.maya
from openpype.tools.utils import host_tools
from openpype.lib import any_outdated
from openpype.lib.path_tools import HostDirmap
from openpype.hosts.maya.lib import copy_workspace_mel
from . import menu, lib
log = logging.getLogger("openpype.hosts.maya")
HOST_DIR = os.path.dirname(os.path.abspath(openpype.hosts.maya.__file__))
PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory")
AVALON_CONTAINERS = ":AVALON_CONTAINERS"
self = sys.modules[__name__]
self._ignore_lock = False
self._events = {}
def install():
from openpype.settings import get_project_settings
project_settings = get_project_settings(os.getenv("AVALON_PROJECT"))
# process path mapping
dirmap_processor = MayaDirmap("maya", project_settings)
dirmap_processor.process_dirmap()
pyblish.api.register_plugin_path(PUBLISH_PATH)
pyblish.api.register_host("mayabatch")
pyblish.api.register_host("mayapy")
pyblish.api.register_host("maya")
avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH)
avalon.api.register_plugin_path(avalon.api.Creator, CREATE_PATH)
avalon.api.register_plugin_path(avalon.api.InventoryAction, INVENTORY_PATH)
log.info(PUBLISH_PATH)
log.info("Installing callbacks ... ")
avalon.api.on("init", on_init)
# Callbacks below are not required for headless mode, the `init` however
# is important to load referenced Alembics correctly at rendertime.
if lib.IS_HEADLESS:
log.info(("Running in headless mode, skipping Maya "
"save/open/new callback installation.."))
return
_set_project()
_register_callbacks()
menu.install()
avalon.api.on("save", on_save)
avalon.api.on("open", on_open)
avalon.api.on("new", on_new)
avalon.api.before("save", on_before_save)
avalon.api.on("taskChanged", on_task_changed)
avalon.api.on("before.workfile.save", before_workfile_save)
log.info("Setting default family states for loader..")
avalon.api.data["familiesStateToggled"] = ["imagesequence"]
def _set_project():
"""Sets the maya project to the current Session's work directory.
Returns:
None
"""
workdir = avalon.api.Session["AVALON_WORKDIR"]
try:
os.makedirs(workdir)
except OSError as e:
# An already existing working directory is fine.
if e.errno == errno.EEXIST:
pass
else:
raise
cmds.workspace(workdir, openWorkspace=True)
def _register_callbacks():
for handler, event in self._events.copy().items():
if event is None:
continue
try:
OpenMaya.MMessage.removeCallback(event)
self._events[handler] = None
except RuntimeError as e:
log.info(e)
self._events[_on_scene_save] = OpenMaya.MSceneMessage.addCallback(
OpenMaya.MSceneMessage.kBeforeSave, _on_scene_save
)
self._events[_before_scene_save] = OpenMaya.MSceneMessage.addCheckCallback(
OpenMaya.MSceneMessage.kBeforeSaveCheck, _before_scene_save
)
self._events[_on_scene_new] = OpenMaya.MSceneMessage.addCallback(
OpenMaya.MSceneMessage.kAfterNew, _on_scene_new
)
self._events[_on_maya_initialized] = OpenMaya.MSceneMessage.addCallback(
OpenMaya.MSceneMessage.kMayaInitialized, _on_maya_initialized
)
self._events[_on_scene_open] = OpenMaya.MSceneMessage.addCallback(
OpenMaya.MSceneMessage.kAfterOpen, _on_scene_open
)
log.info("Installed event handler _on_scene_save..")
log.info("Installed event handler _before_scene_save..")
log.info("Installed event handler _on_scene_new..")
log.info("Installed event handler _on_maya_initialized..")
log.info("Installed event handler _on_scene_open..")
def _on_maya_initialized(*args):
avalon.api.emit("init", args)
if cmds.about(batch=True):
log.warning("Running batch mode ...")
return
# Keep reference to the main Window, once a main window exists.
lib.get_main_window()
def _on_scene_new(*args):
avalon.api.emit("new", args)
def _on_scene_save(*args):
avalon.api.emit("save", args)
def _on_scene_open(*args):
avalon.api.emit("open", args)
def _before_scene_save(return_code, client_data):
# Default to allowing the action. Registered
# callbacks can optionally set this to False
# in order to block the operation.
OpenMaya.MScriptUtil.setBool(return_code, True)
avalon.api.emit("before_save", [return_code, client_data])
def uninstall():
pyblish.api.deregister_plugin_path(PUBLISH_PATH)
pyblish.api.deregister_host("mayabatch")
pyblish.api.deregister_host("mayapy")
pyblish.api.deregister_host("maya")
avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH)
avalon.api.deregister_plugin_path(avalon.api.Creator, CREATE_PATH)
avalon.api.deregister_plugin_path(avalon.api.InventoryAction, INVENTORY_PATH)
menu.uninstall()
def lock():
"""Lock scene
Add an invisible node to your Maya scene with the name of the
current file, indicating that this file is "locked" and cannot
be modified any further.
"""
if not cmds.objExists("lock"):
with lib.maintained_selection():
cmds.createNode("objectSet", name="lock")
cmds.addAttr("lock", ln="basename", dataType="string")
# Permanently hide from outliner
cmds.setAttr("lock.verticesOnlySet", True)
fname = cmds.file(query=True, sceneName=True)
basename = os.path.basename(fname)
cmds.setAttr("lock.basename", basename, type="string")
def unlock():
"""Permanently unlock a locked scene
Doesn't throw an error if scene is already unlocked.
"""
try:
cmds.delete("lock")
except ValueError:
pass
def is_locked():
"""Query whether current scene is locked"""
fname = cmds.file(query=True, sceneName=True)
basename = os.path.basename(fname)
if self._ignore_lock:
return False
try:
return cmds.getAttr("lock.basename") == basename
except ValueError:
return False
@contextlib.contextmanager
def lock_ignored():
"""Context manager for temporarily ignoring the lock of a scene
The purpose of this function is to enable locking a scene and
saving it with the lock still in place.
Example:
>>> with lock_ignored():
... pass # Do things without lock
"""
self._ignore_lock = True
try:
yield
finally:
self._ignore_lock = False
def parse_container(container):
"""Return the container node's full container data.
Args:
container (str): A container node name.
Returns:
dict: The container schema data for this container node.
"""
data = lib.read(container)
# Backwards compatibility pre-schemas for containers
data["schema"] = data.get("schema", "openpype:container-1.0")
# Append transient data
data["objectName"] = container
return data
def _ls():
"""Yields Avalon container node names.
Used by `ls()` to retrieve the nodes and then query the full container's
data.
Yields:
str: Avalon container node name (objectSet)
"""
def _maya_iterate(iterator):
"""Helper to iterate a maya iterator"""
while not iterator.isDone():
yield iterator.thisNode()
iterator.next()
ids = {AVALON_CONTAINER_ID,
# Backwards compatibility
"pyblish.mindbender.container"}
# Iterate over all 'set' nodes in the scene to detect whether
# they have the avalon container ".id" attribute.
fn_dep = om.MFnDependencyNode()
iterator = om.MItDependencyNodes(om.MFn.kSet)
for mobject in _maya_iterate(iterator):
if mobject.apiTypeStr != "kSet":
# Only match by exact type
continue
fn_dep.setObject(mobject)
if not fn_dep.hasAttribute("id"):
continue
plug = fn_dep.findPlug("id", True)
value = plug.asString()
if value in ids:
yield fn_dep.name()
def ls():
"""Yields containers from active Maya scene
This is the host-equivalent of api.ls(), but instead of listing
assets on disk, it lists assets already loaded in Maya; once loaded
they are called 'containers'
Yields:
dict: container
"""
container_names = _ls()
has_metadata_collector = False
config_host = find_submodule(avalon.api.registered_config(), "maya")
if hasattr(config_host, "collect_container_metadata"):
has_metadata_collector = True
for container in sorted(container_names):
data = parse_container(container)
# Collect custom data if attribute is present
if has_metadata_collector:
metadata = config_host.collect_container_metadata(container)
data.update(metadata)
yield data
def containerise(name,
namespace,
nodes,
context,
loader=None,
suffix="CON"):
"""Bundle `nodes` into an assembly and imprint it with metadata
Containerisation enables a tracking of version, author and origin
for loaded assets.
Arguments:
name (str): Name of resulting assembly
namespace (str): Namespace under which to host container
nodes (list): Long names of nodes to containerise
context (dict): Asset information
loader (str, optional): Name of loader used to produce this container.
suffix (str, optional): Suffix of container, defaults to `_CON`.
Returns:
container (str): Name of container assembly
"""
container = cmds.sets(nodes, name="%s_%s_%s" % (namespace, name, suffix))
data = [
("schema", "openpype:container-2.0"),
("id", AVALON_CONTAINER_ID),
("name", name),
("namespace", namespace),
("loader", str(loader)),
("representation", context["representation"]["_id"]),
]
for key, value in data:
if not value:
continue
if isinstance(value, (int, float)):
cmds.addAttr(container, longName=key, attributeType="short")
cmds.setAttr(container + "." + key, value)
else:
cmds.addAttr(container, longName=key, dataType="string")
cmds.setAttr(container + "." + key, value, type="string")
main_container = cmds.ls(AVALON_CONTAINERS, type="objectSet")
if not main_container:
main_container = cmds.sets(empty=True, name=AVALON_CONTAINERS)
# Implement #399: Maya 2019+ hide AVALON_CONTAINERS on creation..
if cmds.attributeQuery("hiddenInOutliner",
node=main_container,
exists=True):
cmds.setAttr(main_container + ".hiddenInOutliner", True)
else:
main_container = main_container[0]
cmds.sets(container, addElement=main_container)
# Implement #399: Maya 2019+ hide containers in outliner
if cmds.attributeQuery("hiddenInOutliner",
node=container,
exists=True):
cmds.setAttr(container + ".hiddenInOutliner", True)
return container
def on_init(_):
log.info("Running callback on init..")
def safe_deferred(fn):
"""Execute deferred the function in a try-except"""
def _fn():
"""safely call in deferred callback"""
try:
fn()
except Exception as exc:
print(exc)
try:
utils.executeDeferred(_fn)
except Exception as exc:
print(exc)
# Force load Alembic so referenced alembics
# work correctly on scene open
cmds.loadPlugin("AbcImport", quiet=True)
cmds.loadPlugin("AbcExport", quiet=True)
# Force load objExport plug-in (requested by artists)
cmds.loadPlugin("objExport", quiet=True)
from .customize import (
override_component_mask_commands,
override_toolbox_ui
)
safe_deferred(override_component_mask_commands)
launch_workfiles = os.environ.get("WORKFILES_STARTUP")
if launch_workfiles:
safe_deferred(host_tools.show_workfiles)
if not lib.IS_HEADLESS:
safe_deferred(override_toolbox_ui)
def on_before_save(return_code, _):
"""Run validation for scene's FPS prior to saving"""
return lib.validate_fps()
def on_save(_):
"""Automatically add IDs to new nodes
Any transform of a mesh, without an existing ID, is given one
automatically on file save.
"""
log.info("Running callback on save..")
# # Update current task for the current scene
# update_task_from_path(cmds.file(query=True, sceneName=True))
# Generate ids of the current context on nodes in the scene
nodes = lib.get_id_required_nodes(referenced_nodes=False)
for node, new_id in lib.generate_ids(nodes):
lib.set_id(node, new_id, overwrite=False)
def on_open(_):
"""On scene open let's assume the containers have changed."""
from Qt import QtWidgets
from openpype.widgets import popup
cmds.evalDeferred(
"from openpype.hosts.maya.api import lib;"
"lib.remove_render_layer_observer()")
cmds.evalDeferred(
"from openpype.hosts.maya.api import lib;"
"lib.add_render_layer_observer()")
cmds.evalDeferred(
"from openpype.hosts.maya.api import lib;"
"lib.add_render_layer_change_observer()")
# # Update current task for the current scene
# update_task_from_path(cmds.file(query=True, sceneName=True))
# Validate FPS after update_task_from_path to
# ensure it is using correct FPS for the asset
lib.validate_fps()
lib.fix_incompatible_containers()
if any_outdated():
log.warning("Scene has outdated content.")
# Find maya main window
top_level_widgets = {w.objectName(): w for w in
QtWidgets.QApplication.topLevelWidgets()}
parent = top_level_widgets.get("MayaWindow", None)
if parent is None:
log.info("Skipping outdated content pop-up "
"because Maya window can't be found.")
else:
# Show outdated pop-up
def _on_show_inventory():
host_tools.show_scene_inventory(parent=parent)
dialog = popup.Popup(parent=parent)
dialog.setWindowTitle("Maya scene has outdated content")
dialog.setMessage("There are outdated containers in "
"your Maya scene.")
dialog.on_show.connect(_on_show_inventory)
dialog.show()
def on_new(_):
"""Set project resolution and fps when create a new file"""
log.info("Running callback on new..")
with lib.suspended_refresh():
cmds.evalDeferred(
"from openpype.hosts.maya.api import lib;"
"lib.remove_render_layer_observer()")
cmds.evalDeferred(
"from openpype.hosts.maya.api import lib;"
"lib.add_render_layer_observer()")
cmds.evalDeferred(
"from openpype.hosts.maya.api import lib;"
"lib.add_render_layer_change_observer()")
lib.set_context_settings()
def on_task_changed(*args):
"""Wrapped function of app initialize and maya's on task changed"""
# Run
menu.update_menu_task_label()
workdir = avalon.api.Session["AVALON_WORKDIR"]
if os.path.exists(workdir):
log.info("Updating Maya workspace for task change to %s", workdir)
_set_project()
# Set Maya fileDialog's start-dir to /scenes
frule_scene = cmds.workspace(fileRuleEntry="scene")
cmds.optionVar(stringValue=("browserLocationmayaBinaryscene",
workdir + "/" + frule_scene))
else:
log.warning((
"Can't set project for new context because path does not exist: {}"
).format(workdir))
with lib.suspended_refresh():
lib.set_context_settings()
lib.update_content_on_context_change()
msg = " project: {}\n asset: {}\n task:{}".format(
avalon.api.Session["AVALON_PROJECT"],
avalon.api.Session["AVALON_ASSET"],
avalon.api.Session["AVALON_TASK"]
)
lib.show_message(
"Context was changed",
("Context was changed to:\n{}".format(msg)),
)
def before_workfile_save(event):
workdir_path = event.workdir_path
if workdir_path:
copy_workspace_mel(workdir_path)
class MayaDirmap(HostDirmap):
def on_enable_dirmap(self):
cmds.dirmap(en=True)
def dirmap_routine(self, source_path, destination_path):
cmds.dirmap(m=(source_path, destination_path))
cmds.dirmap(m=(destination_path, source_path))

View file

@ -1,8 +1,14 @@
import os
from maya import cmds
from avalon import api
from avalon.vendor import qargparse
import avalon.maya
from openpype.api import PypeCreatorMixin
from .pipeline import containerise
from . import lib
def get_reference_node(members, log=None):
"""Get the reference node from the container members
@ -14,8 +20,6 @@ def get_reference_node(members, log=None):
"""
from maya import cmds
# Collect the references without .placeHolderList[] attributes as
# unique entries (objects only) and skipping the sharedReferenceNode.
references = set()
@ -61,8 +65,6 @@ def get_reference_node_parents(ref):
list: The upstream parent reference nodes.
"""
from maya import cmds
parent = cmds.referenceQuery(ref,
referenceNode=True,
parent=True)
@ -75,11 +77,25 @@ def get_reference_node_parents(ref):
return parents
class Creator(PypeCreatorMixin, avalon.maya.Creator):
pass
class Creator(PypeCreatorMixin, api.Creator):
def process(self):
nodes = list()
with lib.undo_chunk():
if (self.options or {}).get("useSelection"):
nodes = cmds.ls(selection=True)
instance = cmds.sets(nodes, name=self.name)
lib.imprint(instance, self.data)
return instance
class ReferenceLoader(api.Loader):
class Loader(api.Loader):
hosts = ["maya"]
class ReferenceLoader(Loader):
"""A basic ReferenceLoader for Maya
This will implement the basic behavior for a loader to inherit from that
@ -117,11 +133,6 @@ class ReferenceLoader(api.Loader):
namespace=None,
options=None
):
import os
from avalon.maya import lib
from avalon.maya.pipeline import containerise
assert os.path.exists(self.fname), "%s does not exist." % self.fname
asset = context['asset']
@ -182,8 +193,6 @@ class ReferenceLoader(api.Loader):
def update(self, container, representation):
import os
from maya import cmds
node = container["objectName"]

View file

@ -9,8 +9,10 @@ import six
from maya import cmds
from avalon import api, io
from avalon.maya.lib import unique_namespace
from openpype.hosts.maya.api.lib import matrix_equals
from openpype.hosts.maya.api.lib import (
matrix_equals,
unique_namespace
)
log = logging.getLogger("PackageLoader")
@ -239,7 +241,7 @@ def get_contained_containers(container):
"""
import avalon.schema
from avalon.maya.pipeline import parse_container
from .pipeline import parse_container
# Get avalon containers in this package setdress container
containers = []

View file

@ -0,0 +1,67 @@
"""Host API required Work Files tool"""
import os
from maya import cmds
from avalon import api
def file_extensions():
return api.HOST_WORKFILE_EXTENSIONS["maya"]
def has_unsaved_changes():
return cmds.file(query=True, modified=True)
def save_file(filepath):
cmds.file(rename=filepath)
ext = os.path.splitext(filepath)[1]
if ext == ".mb":
file_type = "mayaBinary"
else:
file_type = "mayaAscii"
cmds.file(save=True, type=file_type)
def open_file(filepath):
return cmds.file(filepath, open=True, force=True)
def current_file():
current_filepath = cmds.file(query=True, sceneName=True)
if not current_filepath:
return None
return current_filepath
def work_root(session):
work_dir = session["AVALON_WORKDIR"]
scene_dir = None
# Query scene file rule from workspace.mel if it exists in WORKDIR
# We are parsing the workspace.mel manually as opposed to temporarily
# setting the Workspace in Maya in a context manager since Maya had a
# tendency to crash on frequently changing the workspace when this
# function was called many times as one scrolled through Work Files assets.
workspace_mel = os.path.join(work_dir, "workspace.mel")
if os.path.exists(workspace_mel):
scene_rule = 'workspace -fr "scene" '
# We need to use builtins as `open` is overridden by the workio API
open_file = __builtins__["open"]
with open_file(workspace_mel, "r") as f:
for line in f:
if line.strip().startswith(scene_rule):
# remainder == "rule";
remainder = line[len(scene_rule):]
# scene_dir == rule
scene_dir = remainder.split('"')[1]
else:
# We can't query a workspace that does not exist
# so we return similar to what we do in other hosts.
scene_dir = session.get("AVALON_SCENEDIR")
if scene_dir:
return os.path.join(work_dir, scene_dir)
else:
return work_dir

View file

@ -1,4 +1,9 @@
from avalon import api, io
import json
from avalon import api, io, pipeline
from openpype.hosts.maya.api.lib import (
maintained_selection,
apply_shaders
)
class ImportModelRender(api.InventoryAction):
@ -49,10 +54,8 @@ class ImportModelRender(api.InventoryAction):
Returns:
None
"""
import json
from maya import cmds
from avalon import maya, io, pipeline
from openpype.hosts.maya.api import lib
# Get representations of shader file and relationships
look_repr = io.find_one({
@ -77,7 +80,7 @@ class ImportModelRender(api.InventoryAction):
json_file = pipeline.get_representation_path_from_context(context)
# Import the look file
with maya.maintained_selection():
with maintained_selection():
shader_nodes = cmds.file(maya_file,
i=True, # import
returnNewNodes=True)
@ -89,4 +92,4 @@ class ImportModelRender(api.InventoryAction):
relationships = json.load(f)
# Assign relationships
lib.apply_shaders(relationships, shader_nodes, nodes)
apply_shaders(relationships, shader_nodes, nodes)

View file

@ -17,7 +17,7 @@ class AbcLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
def process_reference(self, context, name, namespace, data):
import maya.cmds as cmds
from avalon import maya
from openpype.hosts.maya.api.lib import unique_namespace
cmds.loadPlugin("AbcImport.mll", quiet=True)
# Prevent identical alembic nodes from being shared
@ -27,9 +27,11 @@ class AbcLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
# Assuming name is subset name from the animation, we split the number
# suffix from the name to ensure the namespace is unique
name = name.split("_")[0]
namespace = maya.unique_namespace("{}_".format(name),
format="%03d",
suffix="_abc")
namespace = unique_namespace(
"{}_".format(name),
format="%03d",
suffix="_abc"
)
# hero_001 (abc)
# asset_counter{optional}

View file

@ -3,6 +3,10 @@
"""
from avalon import api
from openpype.hosts.maya.api.lib import (
maintained_selection,
unique_namespace
)
class SetFrameRangeLoader(api.Loader):
@ -98,22 +102,19 @@ class ImportMayaLoader(api.Loader):
def load(self, context, name=None, namespace=None, data=None):
import maya.cmds as cmds
from avalon import maya
from avalon.maya import lib
choice = self.display_warning()
if choice is False:
return
asset = context['asset']
namespace = namespace or lib.unique_namespace(
namespace = namespace or unique_namespace(
asset["name"] + "_",
prefix="_" if asset["name"][0].isdigit() else "",
suffix="_",
)
with maya.maintained_selection():
with maintained_selection():
cmds.file(self.fname,
i=True,
preserveReferences=True,

View file

@ -1,9 +1,15 @@
import os
import clique
from avalon import api
from openpype.api import get_project_settings
import openpype.hosts.maya.api.plugin
from openpype.hosts.maya.api.plugin import get_reference_node
import os
from openpype.api import get_project_settings
import clique
from openpype.hosts.maya.api.lib import (
maintained_selection,
unique_namespace
)
from openpype.hosts.maya.api.pipeline import containerise
class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
@ -20,7 +26,6 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
def process_reference(self, context, name, namespace, options):
import maya.cmds as cmds
from avalon import maya
import pymel.core as pm
version = context['version']
@ -35,7 +40,7 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
except ValueError:
family = "ass"
with maya.maintained_selection():
with maintained_selection():
groupName = "{}:{}".format(namespace, name)
path = self.fname
@ -95,8 +100,6 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
self.update(container, representation)
def update(self, container, representation):
import os
from maya import cmds
import pymel.core as pm
@ -175,8 +178,6 @@ class AssStandinLoader(api.Loader):
def load(self, context, name, namespace, options):
import maya.cmds as cmds
import avalon.maya.lib as lib
from avalon.maya.pipeline import containerise
import mtoa.ui.arnoldmenu
import pymel.core as pm
@ -188,7 +189,7 @@ class AssStandinLoader(api.Loader):
frameStart = version_data.get("frameStart", None)
asset = context['asset']['name']
namespace = namespace or lib.unique_namespace(
namespace = namespace or unique_namespace(
asset + "_",
prefix="_" if asset[0].isdigit() else "",
suffix="_",

View file

@ -13,11 +13,11 @@ class AssemblyLoader(api.Loader):
def load(self, context, name, namespace, data):
from avalon.maya.pipeline import containerise
from avalon.maya import lib
from openpype.hosts.maya.api.pipeline import containerise
from openpype.hosts.maya.api.lib import unique_namespace
asset = context['asset']['name']
namespace = namespace or lib.unique_namespace(
namespace = namespace or unique_namespace(
asset + "_",
prefix="_" if asset[0].isdigit() else "",
suffix="_",
@ -25,9 +25,11 @@ class AssemblyLoader(api.Loader):
from openpype.hosts.maya.api import setdress
containers = setdress.load_package(filepath=self.fname,
name=name,
namespace=namespace)
containers = setdress.load_package(
filepath=self.fname,
name=name,
namespace=namespace
)
self[:] = containers

View file

@ -1,7 +1,7 @@
from avalon import api, io
from avalon.maya.pipeline import containerise
from avalon.maya import lib
from maya import cmds, mel
from avalon import api, io
from openpype.hosts.maya.api.pipeline import containerise
from openpype.hosts.maya.api.lib import unique_namespace
class AudioLoader(api.Loader):
@ -27,7 +27,7 @@ class AudioLoader(api.Loader):
)
asset = context["asset"]["name"]
namespace = namespace or lib.unique_namespace(
namespace = namespace or unique_namespace(
asset + "_",
prefix="_" if asset[0].isdigit() else "",
suffix="_",

View file

@ -17,11 +17,11 @@ class GpuCacheLoader(api.Loader):
def load(self, context, name, namespace, data):
import maya.cmds as cmds
import avalon.maya.lib as lib
from avalon.maya.pipeline import containerise
from openpype.hosts.maya.api.pipeline import containerise
from openpype.hosts.maya.api.lib import unique_namespace
asset = context['asset']['name']
namespace = namespace or lib.unique_namespace(
namespace = namespace or unique_namespace(
asset + "_",
prefix="_" if asset[0].isdigit() else "",
suffix="_",

View file

@ -1,8 +1,9 @@
from avalon import api, io
from avalon.maya.pipeline import containerise
from avalon.maya import lib
from Qt import QtWidgets, QtCore
from avalon import api, io
from openpype.hosts.maya.api.pipeline import containerise
from openpype.hosts.maya.api.lib import unique_namespace
from maya import cmds
@ -88,7 +89,7 @@ class ImagePlaneLoader(api.Loader):
new_nodes = []
image_plane_depth = 1000
asset = context['asset']['name']
namespace = namespace or lib.unique_namespace(
namespace = namespace or unique_namespace(
asset + "_",
prefix="_" if asset[0].isdigit() else "",
suffix="_",

View file

@ -1,13 +1,15 @@
# -*- coding: utf-8 -*-
"""Look loader."""
import openpype.hosts.maya.api.plugin
from avalon import api, io
import json
import openpype.hosts.maya.api.lib
from collections import defaultdict
from openpype.widgets.message_window import ScrollMessageBox
from Qt import QtWidgets
from avalon import api, io
import openpype.hosts.maya.api.plugin
from openpype.hosts.maya.api import lib
from openpype.widgets.message_window import ScrollMessageBox
from openpype.hosts.maya.api.plugin import get_reference_node
@ -36,9 +38,8 @@ class LookLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
"""
import maya.cmds as cmds
from avalon import maya
with maya.maintained_selection():
with lib.maintained_selection():
nodes = cmds.file(self.fname,
namespace=namespace,
reference=True,
@ -140,9 +141,7 @@ class LookLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
cmds.file(cr=reference_node) # cleanReference
# reapply shading groups from json representation on orig nodes
openpype.hosts.maya.api.lib.apply_shaders(json_data,
shader_nodes,
orig_nodes)
lib.apply_shaders(json_data, shader_nodes, orig_nodes)
msg = ["During reference update some edits failed.",
"All successful edits were kept intact.\n",
@ -159,8 +158,8 @@ class LookLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
# region compute lookup
nodes_by_id = defaultdict(list)
for n in nodes:
nodes_by_id[openpype.hosts.maya.api.lib.get_id(n)].append(n)
openpype.hosts.maya.api.lib.apply_attributes(attributes, nodes_by_id)
nodes_by_id[lib.get_id(n)].append(n)
lib.apply_attributes(attributes, nodes_by_id)
# Update metadata
cmds.setAttr("{}.representation".format(node),

View file

@ -1,11 +1,18 @@
# -*- coding: utf-8 -*-
"""Loader for Redshift proxy."""
from avalon.maya import lib
import os
import clique
import maya.cmds as cmds
from avalon import api
from openpype.api import get_project_settings
import os
import maya.cmds as cmds
import clique
from openpype.hosts.maya.api.lib import (
namespaced,
maintained_selection,
unique_namespace
)
from openpype.hosts.maya.api.pipeline import containerise
class RedshiftProxyLoader(api.Loader):
@ -21,17 +28,13 @@ class RedshiftProxyLoader(api.Loader):
def load(self, context, name=None, namespace=None, options=None):
"""Plugin entry point."""
from avalon.maya.pipeline import containerise
from openpype.hosts.maya.api.lib import namespaced
try:
family = context["representation"]["context"]["family"]
except ValueError:
family = "redshiftproxy"
asset_name = context['asset']["name"]
namespace = namespace or lib.unique_namespace(
namespace = namespace or unique_namespace(
asset_name + "_",
prefix="_" if asset_name[0].isdigit() else "",
suffix="_",
@ -40,7 +43,7 @@ class RedshiftProxyLoader(api.Loader):
# Ensure Redshift for Maya is loaded.
cmds.loadPlugin("redshift4maya", quiet=True)
with lib.maintained_selection():
with maintained_selection():
cmds.namespace(addNamespace=namespace)
with namespaced(namespace, new=False):
nodes, group_node = self.create_rs_proxy(

View file

@ -1,9 +1,10 @@
import openpype.hosts.maya.api.plugin
from avalon import api, maya
from maya import cmds
import os
from maya import cmds
from avalon import api
from openpype.api import get_project_settings
from openpype.lib import get_creator_by_name
import openpype.hosts.maya.api.plugin
from openpype.hosts.maya.api.lib import maintained_selection
class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
@ -32,7 +33,6 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
def process_reference(self, context, name, namespace, options):
import maya.cmds as cmds
from avalon import maya
import pymel.core as pm
try:
@ -44,7 +44,7 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
# True by default to keep legacy behaviours
attach_to_root = options.get("attach_to_root", True)
with maya.maintained_selection():
with maintained_selection():
cmds.loadPlugin("AbcImport.mll", quiet=True)
nodes = cmds.file(self.fname,
namespace=namespace,
@ -149,7 +149,7 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
# Create the animation instance
creator_plugin = get_creator_by_name(self.animation_creator_name)
with maya.maintained_selection():
with maintained_selection():
cmds.select([output, controls] + roots, noExpand=True)
api.create(
creator_plugin,

View file

@ -11,8 +11,8 @@ import six
import sys
from avalon import api
from avalon.maya import lib
from openpype.hosts.maya.api import lib as pypelib
from openpype.hosts.maya.api import lib
from openpype.hosts.maya.api.pipeline import containerise
from maya import cmds
import maya.app.renderSetup.model.renderSetup as renderSetup
@ -31,7 +31,6 @@ class RenderSetupLoader(api.Loader):
def load(self, context, name, namespace, data):
"""Load RenderSetup settings."""
from avalon.maya.pipeline import containerise
# from openpype.hosts.maya.api.lib import namespaced
@ -83,7 +82,7 @@ class RenderSetupLoader(api.Loader):
def update(self, container, representation):
"""Update RenderSetup setting by overwriting existing settings."""
pypelib.show_message(
lib.show_message(
"Render setup update",
"Render setup setting will be overwritten by new version. All "
"setting specified by user not included in loaded version "

View file

@ -1,7 +1,8 @@
from avalon import api
import os
from avalon import api
from openpype.api import get_project_settings
class LoadVDBtoRedShift(api.Loader):
"""Load OpenVDB in a Redshift Volume Shape"""
@ -15,8 +16,8 @@ class LoadVDBtoRedShift(api.Loader):
def load(self, context, name=None, namespace=None, data=None):
from maya import cmds
import avalon.maya.lib as lib
from avalon.maya.pipeline import containerise
from openpype.hosts.maya.api.pipeline import containerise
from openpype.hosts.maya.api.lib import unique_namespace
try:
family = context["representation"]["context"]["family"]
@ -45,7 +46,7 @@ class LoadVDBtoRedShift(api.Loader):
asset = context['asset']
asset_name = asset["name"]
namespace = namespace or lib.unique_namespace(
namespace = namespace or unique_namespace(
asset_name + "_",
prefix="_" if asset_name[0].isdigit() else "",
suffix="_",

View file

@ -1,6 +1,6 @@
import os
from avalon import api
from openpype.api import get_project_settings
import os
from maya import cmds
@ -80,8 +80,8 @@ class LoadVDBtoVRay(api.Loader):
def load(self, context, name, namespace, data):
import avalon.maya.lib as lib
from avalon.maya.pipeline import containerise
from openpype.hosts.maya.api.lib import unique_namespace
from openpype.hosts.maya.api.pipeline import containerise
assert os.path.exists(self.fname), (
"Path does not exist: %s" % self.fname
@ -111,7 +111,7 @@ class LoadVDBtoVRay(api.Loader):
asset = context['asset']
asset_name = asset["name"]
namespace = namespace or lib.unique_namespace(
namespace = namespace or unique_namespace(
asset_name + "_",
prefix="_" if asset_name[0].isdigit() else "",
suffix="_",

View file

@ -9,9 +9,14 @@ import os
import maya.cmds as cmds
from avalon.maya import lib
from avalon import api, io
from openpype.api import get_project_settings
from openpype.hosts.maya.api.lib import (
maintained_selection,
namespaced,
unique_namespace
)
from openpype.hosts.maya.api.pipeline import containerise
class VRayProxyLoader(api.Loader):
@ -36,8 +41,6 @@ class VRayProxyLoader(api.Loader):
options (dict): Optional loader options.
"""
from avalon.maya.pipeline import containerise
from openpype.hosts.maya.api.lib import namespaced
try:
family = context["representation"]["context"]["family"]
@ -48,7 +51,7 @@ class VRayProxyLoader(api.Loader):
self.fname = self._get_abc(context["version"]["_id"]) or self.fname
asset_name = context['asset']["name"]
namespace = namespace or lib.unique_namespace(
namespace = namespace or unique_namespace(
asset_name + "_",
prefix="_" if asset_name[0].isdigit() else "",
suffix="_",
@ -57,7 +60,7 @@ class VRayProxyLoader(api.Loader):
# Ensure V-Ray for Maya is loaded.
cmds.loadPlugin("vrayformaya", quiet=True)
with lib.maintained_selection():
with maintained_selection():
cmds.namespace(addNamespace=namespace)
with namespaced(namespace, new=False):
nodes, group_node = self.create_vray_proxy(

View file

@ -1,8 +1,13 @@
from avalon.maya import lib
from avalon import api
from openpype.api import config
import os
import maya.cmds as cmds
from avalon import api
from openpype.api import get_project_settings
from openpype.hosts.maya.api.lib import (
maintained_selection,
namespaced,
unique_namespace
)
from openpype.hosts.maya.api.pipeline import containerise
class VRaySceneLoader(api.Loader):
@ -18,8 +23,6 @@ class VRaySceneLoader(api.Loader):
def load(self, context, name, namespace, data):
from avalon.maya.pipeline import containerise
from openpype.hosts.maya.lib import namespaced
try:
family = context["representation"]["context"]["family"]
@ -27,7 +30,7 @@ class VRaySceneLoader(api.Loader):
family = "vrayscene_layer"
asset_name = context['asset']["name"]
namespace = namespace or lib.unique_namespace(
namespace = namespace or unique_namespace(
asset_name + "_",
prefix="_" if asset_name[0].isdigit() else "",
suffix="_",
@ -36,7 +39,7 @@ class VRaySceneLoader(api.Loader):
# Ensure V-Ray for Maya is loaded.
cmds.loadPlugin("vrayformaya", quiet=True)
with lib.maintained_selection():
with maintained_selection():
cmds.namespace(addNamespace=namespace)
with namespaced(namespace, new=False):
nodes, group_node = self.create_vray_scene(name,
@ -47,8 +50,8 @@ class VRaySceneLoader(api.Loader):
return
# colour the group node
presets = config.get_presets(project=os.environ['AVALON_PROJECT'])
colors = presets['plugins']['maya']['load']['colors']
presets = get_project_settings(os.environ['AVALON_PROJECT'])
colors = presets['maya']['load']['colors']
c = colors.get(family)
if c is not None:
cmds.setAttr("{0}.useOutlinerColor".format(group_node), 1)

View file

@ -3,14 +3,14 @@ import json
import re
import glob
from collections import defaultdict
from pprint import pprint
from maya import cmds
from avalon import api, io
from avalon.maya import lib as avalon_lib, pipeline
from openpype.hosts.maya.api import lib
from openpype.api import get_project_settings
from pprint import pprint
from openpype.hosts.maya.api import lib
from openpype.hosts.maya.api.pipeline import containerise
class YetiCacheLoader(api.Loader):
@ -75,11 +75,13 @@ class YetiCacheLoader(api.Loader):
self[:] = nodes
return pipeline.containerise(name=name,
namespace=namespace,
nodes=nodes,
context=context,
loader=self.__class__.__name__)
return containerise(
name=name,
namespace=namespace,
nodes=nodes,
context=context,
loader=self.__class__.__name__
)
def remove(self, container):
@ -239,9 +241,11 @@ class YetiCacheLoader(api.Loader):
asset_name = "{}_".format(asset)
prefix = "_" if asset_name[0].isdigit()else ""
namespace = avalon_lib.unique_namespace(asset_name,
prefix=prefix,
suffix="_")
namespace = lib.unique_namespace(
asset_name,
prefix=prefix,
suffix="_"
)
return namespace

View file

@ -25,7 +25,6 @@ class YetiRigLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
self, context, name=None, namespace=None, options=None):
import maya.cmds as cmds
from avalon import maya
# get roots of selected hierarchies
selected_roots = []
@ -53,7 +52,7 @@ class YetiRigLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
scene_lookup[cb_id] = node
# load rig
with maya.maintained_selection():
with lib.maintained_selection():
nodes = cmds.file(self.fname,
namespace=namespace,
reference=True,

View file

@ -2,7 +2,7 @@ from collections import defaultdict
import pyblish.api
from maya import cmds, mel
from avalon import maya as avalon
from openpype.hosts.maya import api
from openpype.hosts.maya.api import lib
# TODO : Publish of assembly: -unique namespace for all assets, VALIDATOR!
@ -30,7 +30,7 @@ class CollectAssembly(pyblish.api.InstancePlugin):
def process(self, instance):
# Find containers
containers = avalon.ls()
containers = api.ls()
# Get all content from the instance
instance_lookup = set(cmds.ls(instance, type="transform", long=True))

View file

@ -49,7 +49,7 @@ import maya.app.renderSetup.model.renderSetup as renderSetup
import pyblish.api
from avalon import maya, api
from avalon import api
from openpype.hosts.maya.api.lib_renderproducts import get as get_layer_render_products # noqa: E501
from openpype.hosts.maya.api import lib
@ -409,7 +409,7 @@ class CollectMayaRender(pyblish.api.ContextPlugin):
dict: only overrides with values
"""
attributes = maya.read(render_globals)
attributes = lib.read(render_globals)
options = {"renderGlobals": {}}
options["renderGlobals"]["Priority"] = attributes["priority"]

View file

@ -2,9 +2,12 @@ import os
from maya import cmds
import avalon.maya
import openpype.api
from openpype.hosts.maya.api.lib import extract_alembic
from openpype.hosts.maya.api.lib import (
extract_alembic,
suspended_refresh,
maintained_selection
)
class ExtractAnimation(openpype.api.Extractor):
@ -71,8 +74,8 @@ class ExtractAnimation(openpype.api.Extractor):
# Since Maya 2017 alembic supports multiple uv sets - write them.
options["writeUVSets"] = True
with avalon.maya.suspended_refresh():
with avalon.maya.maintained_selection():
with suspended_refresh():
with maintained_selection():
cmds.select(nodes, noExpand=True)
extract_alembic(file=path,
startFrame=float(start),

View file

@ -1,9 +1,9 @@
import os
import avalon.maya
import openpype.api
from maya import cmds
from openpype.hosts.maya.api.lib import maintained_selection
class ExtractAssStandin(openpype.api.Extractor):
@ -30,7 +30,7 @@ class ExtractAssStandin(openpype.api.Extractor):
# Write out .ass file
self.log.info("Writing: '%s'" % file_path)
with avalon.maya.maintained_selection():
with maintained_selection():
self.log.info("Writing: {}".format(instance.data["setMembers"]))
cmds.select(instance.data["setMembers"], noExpand=True)

View file

@ -1,10 +1,10 @@
import os
from maya import cmds
import contextlib
import avalon.maya
from maya import cmds
import openpype.api
from openpype.hosts.maya.api.lib import maintained_selection
class ExtractAssProxy(openpype.api.Extractor):
@ -54,7 +54,7 @@ class ExtractAssProxy(openpype.api.Extractor):
noIntermediate=True)
self.log.info(members)
with avalon.maya.maintained_selection():
with maintained_selection():
with unparent(members[0]):
cmds.select(members, noExpand=True)
cmds.file(path,

View file

@ -2,9 +2,7 @@ import os
from maya import cmds
import avalon.maya
import openpype.api
from openpype.hosts.maya.api import lib
@ -54,7 +52,7 @@ class ExtractCameraAlembic(openpype.api.Extractor):
path = os.path.join(dir_path, filename)
# Perform alembic extraction
with avalon.maya.maintained_selection():
with lib.maintained_selection():
cmds.select(camera, replace=True, noExpand=True)
# Enforce forward slashes for AbcExport because we're
@ -86,7 +84,7 @@ class ExtractCameraAlembic(openpype.api.Extractor):
job_str += " -attr {0}".format(attr)
with lib.evaluation("off"):
with avalon.maya.suspended_refresh():
with lib.suspended_refresh():
cmds.AbcExport(j=job_str, verbose=False)
if "representations" not in instance.data:

View file

@ -5,7 +5,6 @@ import itertools
from maya import cmds
import avalon.maya
import openpype.api
from openpype.hosts.maya.api import lib
@ -157,9 +156,9 @@ class ExtractCameraMayaScene(openpype.api.Extractor):
path = os.path.join(dir_path, filename)
# Perform extraction
with avalon.maya.maintained_selection():
with lib.maintained_selection():
with lib.evaluation("off"):
with avalon.maya.suspended_refresh():
with lib.suspended_refresh():
if bake_to_worldspace:
self.log.info(
"Performing camera bakes: {}".format(transform))

View file

@ -3,12 +3,12 @@ import os
from maya import cmds # noqa
import maya.mel as mel # noqa
from openpype.hosts.maya.api.lib import root_parent
import pyblish.api
import avalon.maya
import openpype.api
from openpype.hosts.maya.api.lib import (
root_parent,
maintained_selection
)
class ExtractFBX(openpype.api.Extractor):
@ -205,13 +205,13 @@ class ExtractFBX(openpype.api.Extractor):
# Export
if "unrealStaticMesh" in instance.data["families"]:
with avalon.maya.maintained_selection():
with maintained_selection():
with root_parent(members):
self.log.info("Un-parenting: {}".format(members))
cmds.select(members, r=1, noExpand=True)
mel.eval('FBXExport -f "{}" -s'.format(path))
else:
with avalon.maya.maintained_selection():
with maintained_selection():
cmds.select(members, r=1, noExpand=True)
mel.eval('FBXExport -f "{}" -s'.format(path))

View file

@ -11,8 +11,7 @@ from collections import OrderedDict
from maya import cmds # noqa
import pyblish.api
import avalon.maya
from avalon import io, api
from avalon import io
import openpype.api
from openpype.hosts.maya.api import lib
@ -239,7 +238,7 @@ class ExtractLook(openpype.api.Extractor):
# getting incorrectly remapped. (LKD-17, PLN-101)
with no_workspace_dir():
with lib.attribute_values(remap):
with avalon.maya.maintained_selection():
with lib.maintained_selection():
cmds.select(sets, noExpand=True)
cmds.file(
maya_path,

View file

@ -4,8 +4,8 @@ import os
from maya import cmds
import avalon.maya
import openpype.api
from openpype.hosts.maya.api.lib import maintained_selection
class ExtractMayaSceneRaw(openpype.api.Extractor):
@ -59,7 +59,7 @@ class ExtractMayaSceneRaw(openpype.api.Extractor):
# Perform extraction
self.log.info("Performing extraction ...")
with avalon.maya.maintained_selection():
with maintained_selection():
cmds.select(members, noExpand=True)
cmds.file(path,
force=True,

View file

@ -4,7 +4,6 @@ import os
from maya import cmds
import avalon.maya
import openpype.api
from openpype.hosts.maya.api import lib
@ -74,7 +73,7 @@ class ExtractModel(openpype.api.Extractor):
polygonObject=1):
with lib.shader(members,
shadingEngine="initialShadingGroup"):
with avalon.maya.maintained_selection():
with lib.maintained_selection():
cmds.select(members, noExpand=True)
cmds.file(path,
force=True,

View file

@ -2,9 +2,12 @@ import os
from maya import cmds
import avalon.maya
import openpype.api
from openpype.hosts.maya.api.lib import extract_alembic
from openpype.hosts.maya.api.lib import (
extract_alembic,
suspended_refresh,
maintained_selection
)
class ExtractAlembic(openpype.api.Extractor):
@ -70,8 +73,8 @@ class ExtractAlembic(openpype.api.Extractor):
# Since Maya 2017 alembic supports multiple uv sets - write them.
options["writeUVSets"] = True
with avalon.maya.suspended_refresh():
with avalon.maya.maintained_selection():
with suspended_refresh():
with maintained_selection():
cmds.select(nodes, noExpand=True)
extract_alembic(file=path,
startFrame=start,

View file

@ -2,11 +2,11 @@
"""Redshift Proxy extractor."""
import os
import avalon.maya
import openpype.api
from maya import cmds
import openpype.api
from openpype.hosts.maya.api.lib import maintained_selection
class ExtractRedshiftProxy(openpype.api.Extractor):
"""Extract the content of the instance to a redshift proxy file."""
@ -54,7 +54,7 @@ class ExtractRedshiftProxy(openpype.api.Extractor):
# Write out rs file
self.log.info("Writing: '%s'" % file_path)
with avalon.maya.maintained_selection():
with maintained_selection():
cmds.select(instance.data["setMembers"], noExpand=True)
cmds.file(file_path,
pr=False,

View file

@ -4,8 +4,8 @@ import os
from maya import cmds
import avalon.maya
import openpype.api
from openpype.hosts.maya.api.lib import maintained_selection
class ExtractRig(openpype.api.Extractor):
@ -40,7 +40,7 @@ class ExtractRig(openpype.api.Extractor):
# Perform extraction
self.log.info("Performing extraction ...")
with avalon.maya.maintained_selection():
with maintained_selection():
cmds.select(instance, noExpand=True)
cmds.file(path,
force=True,

View file

@ -1,10 +1,10 @@
import os
import avalon.maya
import openpype.api
from maya import cmds
import openpype.api
from openpype.hosts.maya.api.lib import maintained_selection
class ExtractVRayProxy(openpype.api.Extractor):
"""Extract the content of the instance to a vrmesh file
@ -41,7 +41,7 @@ class ExtractVRayProxy(openpype.api.Extractor):
# Write out vrmesh file
self.log.info("Writing: '%s'" % file_path)
with avalon.maya.maintained_selection():
with maintained_selection():
cmds.select(instance.data["setMembers"], noExpand=True)
cmds.vrayCreateProxy(exportType=1,
dir=staging_dir,

View file

@ -3,9 +3,9 @@
import os
import re
import avalon.maya
import openpype.api
from openpype.hosts.maya.api.render_setup_tools import export_in_rs_layer
from openpype.hosts.maya.api.lib import maintained_selection
from maya import cmds
@ -57,7 +57,7 @@ class ExtractVrayscene(openpype.api.Extractor):
# Write out vrscene file
self.log.info("Writing: '%s'" % file_path)
with avalon.maya.maintained_selection():
with maintained_selection():
if "*" not in instance.data["setMembers"]:
self.log.info(
"Exporting: {}".format(instance.data["setMembers"]))

View file

@ -2,8 +2,11 @@ import os
from maya import cmds
import avalon.maya
import openpype.api
from openpype.hosts.maya.api.lib import (
suspended_refresh,
maintained_selection
)
class ExtractXgenCache(openpype.api.Extractor):
@ -32,8 +35,8 @@ class ExtractXgenCache(openpype.api.Extractor):
filename = "{name}.abc".format(**instance.data)
path = os.path.join(parent_dir, filename)
with avalon.maya.suspended_refresh():
with avalon.maya.maintained_selection():
with suspended_refresh():
with maintained_selection():
command = (
'-file '
+ path

View file

@ -7,9 +7,8 @@ import contextlib
from maya import cmds
import avalon.maya.lib as lib
import openpype.api
import openpype.hosts.maya.api.lib as maya
from openpype.hosts.maya.api import lib
@contextlib.contextmanager

View file

@ -2,10 +2,9 @@ from maya import cmds
import pyblish.api
from avalon import maya
import openpype.api
import openpype.hosts.maya.api.action
from openpype.hosts.maya.api.lib import maintained_selection
class ValidateCycleError(pyblish.api.InstancePlugin):
@ -26,7 +25,7 @@ class ValidateCycleError(pyblish.api.InstancePlugin):
@classmethod
def get_invalid(cls, instance):
with maya.maintained_selection():
with maintained_selection():
cmds.select(instance[:], noExpand=True)
plugs = cmds.cycleCheck(all=False, # check selection only
list=True)

View file

@ -3,7 +3,7 @@ from maya import cmds
import pyblish.api
import openpype.api
import openpype.hosts.maya.api.action
from avalon import maya
from openpype.hosts.maya.api.lib import maintained_selection
class ValidateMeshArnoldAttributes(pyblish.api.InstancePlugin):
@ -67,7 +67,7 @@ class ValidateMeshArnoldAttributes(pyblish.api.InstancePlugin):
@classmethod
def repair(cls, instance):
with maya.maintained_selection():
with maintained_selection():
with pc.UndoChunk():
temp_transform = pc.polyCube()[0]

View file

@ -3,7 +3,6 @@ from maya import cmds
import pyblish.api
import openpype.api
import openpype.hosts.maya.api.action
from avalon import maya
from openpype.hosts.maya.api import lib

View file

@ -5,8 +5,6 @@ import openpype.api
import openpype.hosts.maya.api.action
from openpype.hosts.maya.api import lib
from avalon.maya import maintained_selection
class ValidateShapeZero(pyblish.api.Validator):
"""Shape components may not have any "tweak" values
@ -51,7 +49,7 @@ class ValidateShapeZero(pyblish.api.Validator):
if not invalid_shapes:
return
with maintained_selection():
with lib.maintained_selection():
with lib.tool("selectSuperContext"):
for shape in invalid_shapes:
cmds.polyCollapseTweaks(shape)

View file

@ -1,8 +1,12 @@
import os
import avalon.api
from openpype.api import get_project_settings
from openpype.hosts.maya import api
import openpype.hosts.maya.api.lib as mlib
from maya import cmds
avalon.api.install(api)
print("starting OpenPype usersetup")