Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

Maya: Move implementation to OpenPype #2649

Merged
merged 2 commits into from
Feb 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions openpype/hosts/maya/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
324 changes: 91 additions & 233 deletions openpype/hosts/maya/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,233 +1,91 @@
import os
import logging
import weakref

from maya import utils, cmds

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

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


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.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()

log.info("Installing callbacks ... ")
avalon.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 IS_HEADLESS:
log.info("Running in headless mode, skipping Maya "
"save/open/new callback installation..")
return

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)

log.info("Setting default family states for loader..")
avalon.data["familiesStateToggled"] = ["imagesequence"]


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))
"""Public API
Anything that isn't defined here is INTERNAL and unreliable for external use.
"""

from .pipeline import (
install,
uninstall,

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,
)


__all__ = [
"install",
"uninstall",

"ls",
"containerise",

"lock",
"unlock",
"is_locked",
"lock_ignored",

"Creator",
"Loader",

# Workfiles API
"open_file",
"save_file",
"current_file",
"has_unsaved_changes",
"file_extensions",
"work_root",

# Utility functions
"export_alembic",
"lsattr",
"lsattrs",
"read",

"unique_name",
"unique_namespace",

"apply_shaders",
"without_extension",
"maintained_selection",
"suspended_refresh",

]

# Backwards API compatibility
open = open_file
save = save_file
5 changes: 2 additions & 3 deletions openpype/hosts/maya/api/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -72,8 +72,7 @@ def _update_id_attribute(self, instance, nodes):
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"},
Expand Down
Loading