mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 16:34:53 +01:00
228 lines
6.4 KiB
Python
228 lines
6.4 KiB
Python
"""
|
|
Basic avalon integration
|
|
"""
|
|
import os
|
|
import sys
|
|
import logging
|
|
import contextlib
|
|
|
|
import pyblish.api
|
|
|
|
from openpype.lib import Logger
|
|
from openpype.pipeline import (
|
|
register_loader_plugin_path,
|
|
register_creator_plugin_path,
|
|
register_inventory_action_path,
|
|
deregister_loader_plugin_path,
|
|
deregister_creator_plugin_path,
|
|
deregister_inventory_action_path,
|
|
AVALON_CONTAINER_ID,
|
|
)
|
|
from openpype.hosts.fusion import FUSION_HOST_DIR
|
|
|
|
log = Logger.get_logger(__name__)
|
|
|
|
PLUGINS_DIR = os.path.join(FUSION_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")
|
|
|
|
|
|
class CompLogHandler(logging.Handler):
|
|
def emit(self, record):
|
|
entry = self.format(record)
|
|
comp = get_current_comp()
|
|
if comp:
|
|
comp.Print(entry)
|
|
|
|
|
|
def install():
|
|
"""Install fusion-specific functionality of avalon-core.
|
|
|
|
This is where you install menus and register families, data
|
|
and loaders into fusion.
|
|
|
|
It is called automatically when installing via
|
|
`openpype.pipeline.install_host(openpype.hosts.fusion.api)`
|
|
|
|
See the Maya equivalent for inspiration on how to implement this.
|
|
|
|
"""
|
|
# Remove all handlers associated with the root logger object, because
|
|
# that one sometimes logs as "warnings" incorrectly.
|
|
for handler in logging.root.handlers[:]:
|
|
logging.root.removeHandler(handler)
|
|
|
|
# Attach default logging handler that prints to active comp
|
|
logger = logging.getLogger()
|
|
formatter = logging.Formatter(fmt="%(message)s\n")
|
|
handler = CompLogHandler()
|
|
handler.setFormatter(formatter)
|
|
logger.addHandler(handler)
|
|
logger.setLevel(logging.DEBUG)
|
|
|
|
log.info("openpype.hosts.fusion installed")
|
|
|
|
pyblish.api.register_host("fusion")
|
|
pyblish.api.register_plugin_path(PUBLISH_PATH)
|
|
log.info("Registering Fusion plug-ins..")
|
|
|
|
register_loader_plugin_path(LOAD_PATH)
|
|
register_creator_plugin_path(CREATE_PATH)
|
|
register_inventory_action_path(INVENTORY_PATH)
|
|
|
|
pyblish.api.register_callback(
|
|
"instanceToggled", on_pyblish_instance_toggled
|
|
)
|
|
|
|
|
|
def uninstall():
|
|
"""Uninstall all that was installed
|
|
|
|
This is where you undo everything that was done in `install()`.
|
|
That means, removing menus, deregistering families and data
|
|
and everything. It should be as though `install()` was never run,
|
|
because odds are calling this function means the user is interested
|
|
in re-installing shortly afterwards. If, for example, he has been
|
|
modifying the menu or registered families.
|
|
|
|
"""
|
|
pyblish.api.deregister_host("fusion")
|
|
pyblish.api.deregister_plugin_path(PUBLISH_PATH)
|
|
log.info("Deregistering Fusion plug-ins..")
|
|
|
|
deregister_loader_plugin_path(LOAD_PATH)
|
|
deregister_creator_plugin_path(CREATE_PATH)
|
|
deregister_inventory_action_path(INVENTORY_PATH)
|
|
|
|
pyblish.api.deregister_callback(
|
|
"instanceToggled", on_pyblish_instance_toggled
|
|
)
|
|
|
|
|
|
def on_pyblish_instance_toggled(instance, new_value, old_value):
|
|
"""Toggle saver tool passthrough states on instance toggles."""
|
|
comp = instance.context.data.get("currentComp")
|
|
if not comp:
|
|
return
|
|
|
|
savers = [tool for tool in instance if
|
|
getattr(tool, "ID", None) == "Saver"]
|
|
if not savers:
|
|
return
|
|
|
|
# Whether instances should be passthrough based on new value
|
|
passthrough = not new_value
|
|
with comp_lock_and_undo_chunk(comp,
|
|
undo_queue_name="Change instance "
|
|
"active state"):
|
|
for tool in savers:
|
|
attrs = tool.GetAttrs()
|
|
current = attrs["TOOLB_PassThrough"]
|
|
if current != passthrough:
|
|
tool.SetAttrs({"TOOLB_PassThrough": passthrough})
|
|
|
|
|
|
def ls():
|
|
"""List containers from active Fusion scene
|
|
|
|
This is the host-equivalent of api.ls(), but instead of listing
|
|
assets on disk, it lists assets already loaded in Fusion; once loaded
|
|
they are called 'containers'
|
|
|
|
Yields:
|
|
dict: container
|
|
|
|
"""
|
|
|
|
comp = get_current_comp()
|
|
tools = comp.GetToolList(False, "Loader").values()
|
|
|
|
for tool in tools:
|
|
container = parse_container(tool)
|
|
if container:
|
|
yield container
|
|
|
|
|
|
def imprint_container(tool,
|
|
name,
|
|
namespace,
|
|
context,
|
|
loader=None):
|
|
"""Imprint a Loader with metadata
|
|
|
|
Containerisation enables a tracking of version, author and origin
|
|
for loaded assets.
|
|
|
|
Arguments:
|
|
tool (object): The node in Fusion to imprint as container, usually a
|
|
Loader.
|
|
name (str): Name of resulting assembly
|
|
namespace (str): Namespace under which to host container
|
|
context (dict): Asset information
|
|
loader (str, optional): Name of loader used to produce this container.
|
|
|
|
Returns:
|
|
None
|
|
|
|
"""
|
|
|
|
data = [
|
|
("schema", "openpype:container-2.0"),
|
|
("id", AVALON_CONTAINER_ID),
|
|
("name", str(name)),
|
|
("namespace", str(namespace)),
|
|
("loader", str(loader)),
|
|
("representation", str(context["representation"]["_id"])),
|
|
]
|
|
|
|
for key, value in data:
|
|
tool.SetData("avalon.{}".format(key), value)
|
|
|
|
|
|
def parse_container(tool):
|
|
"""Returns imprinted container data of a tool
|
|
|
|
This reads the imprinted data from `imprint_container`.
|
|
|
|
"""
|
|
|
|
data = tool.GetData('avalon')
|
|
if not isinstance(data, dict):
|
|
return
|
|
|
|
# If not all required data return the empty container
|
|
required = ['schema', 'id', 'name',
|
|
'namespace', 'loader', 'representation']
|
|
if not all(key in data for key in required):
|
|
return
|
|
|
|
container = {key: data[key] for key in required}
|
|
|
|
# Store the tool's name
|
|
container["objectName"] = tool.Name
|
|
|
|
# Store reference to the tool object
|
|
container["_tool"] = tool
|
|
|
|
return container
|
|
|
|
|
|
def get_current_comp():
|
|
"""Hack to get current comp in this session"""
|
|
fusion = getattr(sys.modules["__main__"], "fusion", None)
|
|
return fusion.CurrentComp if fusion else None
|
|
|
|
|
|
@contextlib.contextmanager
|
|
def comp_lock_and_undo_chunk(comp, undo_queue_name="Script CMD"):
|
|
"""Lock comp and open an undo chunk during the context"""
|
|
try:
|
|
comp.Lock()
|
|
comp.StartUndo(undo_queue_name)
|
|
yield
|
|
finally:
|
|
comp.Unlock()
|
|
comp.EndUndo()
|