moved remaining plugins/actions from avalon into openpype

This commit is contained in:
Jakub Trllo 2022-03-18 13:15:48 +01:00
parent 7fa905a5e3
commit d16f8f1384
4 changed files with 327 additions and 1 deletions

View file

@ -41,6 +41,22 @@ from .publish import (
OpenPypePyblishPluginMixin
)
from .actions import (
LauncherAction,
InventoryAction,
discover_launcher_actions,
register_launcher_action,
register_launcher_action_path,
discover_inventory_actions,
register_inventory_action,
register_inventory_action_path,
deregister_inventory_action,
deregister_inventory_action_path,
)
__all__ = (
"attribute_definitions",
@ -82,5 +98,19 @@ __all__ = (
"PublishValidationError",
"PublishXmlValidationError",
"KnownPublishError",
"OpenPypePyblishPluginMixin"
"OpenPypePyblishPluginMixin",
# --- Plugins ---
"LauncherAction",
"InventoryAction",
"discover_launcher_actions",
"register_launcher_action",
"register_launcher_action_path",
"discover_inventory_actions",
"register_inventory_action",
"register_inventory_action_path",
"deregister_inventory_action",
"deregister_inventory_action_path",
)

View file

@ -0,0 +1,148 @@
import os
import copy
import logging
class LauncherAction(object):
"""A custom action available"""
name = None
label = None
icon = None
color = None
order = 0
log = logging.getLogger("LauncherAction")
log.propagate = True
def is_compatible(self, session):
"""Return whether the class is compatible with the Session."""
return True
def process(self, session, **kwargs):
pass
class InventoryAction(object):
"""A custom action for the scene inventory tool
If registered the action will be visible in the Right Mouse Button menu
under the submenu "Actions".
"""
label = None
icon = None
color = None
order = 0
log = logging.getLogger("InventoryAction")
log.propagate = True
@staticmethod
def is_compatible(container):
"""Override function in a custom class
This method is specifically used to ensure the action can operate on
the container.
Args:
container(dict): the data of a loaded asset, see host.ls()
Returns:
bool
"""
return bool(container.get("objectName"))
def process(self, containers):
"""Override function in a custom class
This method will receive all containers even those which are
incompatible. It is advised to create a small filter along the lines
of this example:
valid_containers = filter(self.is_compatible(c) for c in containers)
The return value will need to be a True-ish value to trigger
the data_changed signal in order to refresh the view.
You can return a list of container names to trigger GUI to select
treeview items.
You can return a dict to carry extra GUI options. For example:
{
"objectNames": [container names...],
"options": {"mode": "toggle",
"clear": False}
}
Currently workable GUI options are:
- clear (bool): Clear current selection before selecting by action.
Default `True`.
- mode (str): selection mode, use one of these:
"select", "deselect", "toggle". Default is "select".
Args:
containers (list): list of dictionaries
Return:
bool, list or dict
"""
return True
# Launcher action
def discover_launcher_actions():
import avalon.api
return avalon.api.discover(LauncherAction)
def register_launcher_action(plugin):
import avalon.api
return avalon.api.register_plugin(LauncherAction, plugin)
def register_launcher_action_path(path):
import avalon.api
return avalon.api.register_plugin_path(LauncherAction, path)
# Inventory action
def discover_inventory_actions():
import avalon.api
actions = avalon.api.discover(InventoryAction)
filtered_actions = []
for action in actions:
if action is not InventoryAction:
print("DISCOVERED", action)
filtered_actions.append(action)
else:
print("GOT SOURCE")
return filtered_actions
def register_inventory_action(plugin):
import avalon.api
return avalon.api.register_plugin(InventoryAction, plugin)
def deregister_inventory_action(plugin):
import avalon.api
avalon.api.deregister_plugin(InventoryAction, plugin)
def register_inventory_action_path(path):
import avalon.api
return avalon.api.register_plugin_path(InventoryAction, path)
def deregister_inventory_action_path(path):
import avalon.api
return avalon.api.deregister_plugin_path(InventoryAction, path)

View file

@ -127,4 +127,5 @@ def register_loader_plugin_path(path):
def deregister_loader_plugin(plugin):
import avalon.api
avalon.api.deregister_plugin(LoaderPlugin, plugin)

View file

@ -0,0 +1,147 @@
import os
import copy
import logging
log = logging.getLogger(__name__)
def get_thumbnail_binary(thumbnail_entity, thumbnail_type, dbcon=None):
if not thumbnail_entity:
return
resolvers = discover_thumbnail_resolvers()
resolvers = sorted(resolvers, key=lambda cls: cls.priority)
if dbcon is None:
from avalon import io
dbcon = io
for Resolver in resolvers:
available_types = Resolver.thumbnail_types
if (
thumbnail_type not in available_types
and "*" not in available_types
and (
isinstance(available_types, (list, tuple))
and len(available_types) == 0
)
):
continue
try:
instance = Resolver(dbcon)
result = instance.process(thumbnail_entity, thumbnail_type)
if result:
return result
except Exception:
log.warning("Resolver {0} failed durring process.".format(
Resolver.__class__.__name__, exc_info=True
))
class ThumbnailResolver(object):
"""Determine how to get data from thumbnail entity.
"priority" - determines the order of processing in `get_thumbnail_binary`,
lower number is processed earlier.
"thumbnail_types" - it is expected that thumbnails will be used in more
more than one level, there is only ["thumbnail"] type at the moment
of creating this docstring but it is expected to add "ico" and "full"
in future.
"""
priority = 100
thumbnail_types = ["*"]
def __init__(self, dbcon):
self._log = None
self.dbcon = dbcon
@property
def log(self):
if self._log is None:
self._log = logging.getLogger(self.__class__.__name__)
return self._log
def process(self, thumbnail_entity, thumbnail_type):
pass
class TemplateResolver(ThumbnailResolver):
priority = 90
def process(self, thumbnail_entity, thumbnail_type):
if not os.environ.get("AVALON_THUMBNAIL_ROOT"):
return
template = thumbnail_entity["data"].get("template")
if not template:
self.log.debug("Thumbnail entity does not have set template")
return
project = self.dbcon.find_one(
{"type": "project"},
{
"name": True,
"data.code": True
}
)
template_data = copy.deepcopy(
thumbnail_entity["data"].get("template_data") or {}
)
template_data.update({
"_id": str(thumbnail_entity["_id"]),
"thumbnail_type": thumbnail_type,
"thumbnail_root": os.environ.get("AVALON_THUMBNAIL_ROOT"),
"project": {
"name": project["name"],
"code": project["data"].get("code")
}
})
try:
filepath = os.path.normpath(template.format(**template_data))
except KeyError:
self.log.warning((
"Missing template data keys for template <{0}> || Data: {1}"
).format(template, str(template_data)))
return
if not os.path.exists(filepath):
self.log.warning("File does not exist \"{0}\"".format(filepath))
return
with open(filepath, "rb") as _file:
content = _file.read()
return content
class BinaryThumbnail(ThumbnailResolver):
def process(self, thumbnail_entity, thumbnail_type):
return thumbnail_entity["data"].get("binary_data")
# Thumbnail resolvers
def discover_thumbnail_resolvers():
import avalon.api
return avalon.api.discover(ThumbnailResolver)
def register_thumbnail_resolver(plugin):
import avalon.api
return avalon.api.register_plugin(ThumbnailResolver, plugin)
def register_thumbnail_resolver_path(path):
import avalon.api
return avalon.api.register_plugin_path(ThumbnailResolver, path)
register_thumbnail_resolver(TemplateResolver)
register_thumbnail_resolver(BinaryThumbnail)