implemented launcher action class

This commit is contained in:
Jakub Trllo 2024-06-10 14:53:01 +02:00
parent f95a8eed0b
commit 67dd1d0721
3 changed files with 189 additions and 1 deletions

View file

@ -0,0 +1,147 @@
import copy
import ayon_api
from ayon_core import resources
from ayon_core.lib import Logger, NestedCacheItem
from ayon_core.settings import get_studio_settings, get_project_settings
from ayon_core.pipeline.actions import LauncherAction
from .exceptions import (
ApplicationExecutableNotFound,
ApplicationLaunchFailed,
)
class ApplicationAction(LauncherAction):
"""Action to launch an application.
Application action based on 'ApplicationManager' system.
Handling of applications in launcher is not ideal and should be completely
redone from scratch. This is just a temporary solution to keep backwards
compatibility with AYON launcher.
Todos:
Move handling of errors to frontend.
"""
# Application object
application = None
# Action attributes
name = None
label = None
label_variant = None
group = None
icon = None
color = None
order = 0
data = {}
project_settings = {}
project_entities = {}
_log = None
# --- For compatibility for combinations of new and old ayon-core ---
project_settings_cache = NestedCacheItem(
levels=1, default_factory=dict, lifetime=20
)
project_entities_cache = NestedCacheItem(
levels=1, default_factory=dict, lifetime=20
)
@classmethod
def _app_get_project_settings(cls, selection):
project_name = selection.project_name
if project_name in ApplicationAction.project_settings:
return ApplicationAction.project_settings[project_name]
if hasattr(selection, "get_project_settings"):
return selection.get_project_settings()
cache = ApplicationAction.project_settings_cache[project_name]
if not cache.is_valid:
if project_name:
settings = get_project_settings(project_name)
else:
settings = get_studio_settings()
cache.update_data(settings)
return copy.deepcopy(cache.get_data())
@classmethod
def _app_get_project_entity(cls, selection):
project_name = selection.project_name
if project_name in ApplicationAction.project_entities:
return ApplicationAction.project_entities[project_name]
if hasattr(selection, "get_project_settings"):
return selection.get_project_entity()
cache = ApplicationAction.project_entities_cache[project_name]
if not cache.is_valid:
project_entity = None
if project_name:
project_entity = ayon_api.get_project(project_name)
cache.update_data(project_entity)
return copy.deepcopy(cache.get_data())
@property
def log(self):
if self._log is None:
self._log = Logger.get_logger(self.__class__.__name__)
return self._log
def is_compatible(self, selection):
if not selection.is_task_selected:
return False
project_entity = self._app_get_project_entity(selection)
apps = project_entity["attrib"].get("applications")
if not apps or self.application.full_name not in apps:
return False
project_settings = self._app_get_project_settings(selection)
only_available = project_settings["applications"]["only_available"]
if only_available and not self.application.find_executable():
return False
return True
def _show_message_box(self, title, message, details=None):
from qtpy import QtWidgets, QtGui
from ayon_core import style
dialog = QtWidgets.QMessageBox()
icon = QtGui.QIcon(resources.get_ayon_icon_filepath())
dialog.setWindowIcon(icon)
dialog.setStyleSheet(style.load_stylesheet())
dialog.setWindowTitle(title)
dialog.setText(message)
if details:
dialog.setDetailedText(details)
dialog.exec_()
def process(self, selection, **kwargs):
"""Process the full Application action"""
try:
self.application.launch(
project_name=selection.project_name,
folder_path=selection.folder_path,
task_name=selection.task_name,
**self.data
)
except ApplicationExecutableNotFound as exc:
details = exc.details
msg = exc.msg
log_msg = str(msg)
if details:
log_msg += "\n" + details
self.log.warning(log_msg)
self._show_message_box(
"Application executable not found", msg, details
)
except ApplicationLaunchFailed as exc:
msg = str(exc)
self.log.warning(msg, exc_info=True)
self._show_message_box("Application launch failed", msg)

View file

@ -162,6 +162,47 @@ class ApplicationsAddon(AYONAddon, IPluginPaths):
server_url, "addons", self.name, self.version, "icons", icon_name
])
def get_applications_action_classes(self):
"""Get application action classes for launcher tool.
This method should be used only by launcher tool. Please do not use it
in other places as its implementation is not optimal, and might
change or be removed.
Returns:
list[ApplicationAction]: List of application action classes.
"""
from .action import ApplicationAction
actions = []
manager = self.get_applications_manager()
for full_name, application in manager.applications.items():
if not application.enabled:
continue
icon = self.get_app_icon_path(application.icon)
action = type(
"app_{}".format(full_name),
(ApplicationAction,),
{
"identifier": "application.{}".format(full_name),
"application": application,
"name": application.name,
"label": application.group.label,
"label_variant": application.label,
"group": None,
"icon": icon,
"color": getattr(application, "color", None),
"order": getattr(application, "order", None) or 0,
"data": {}
}
)
actions.append(action)
return actions
def launch_application(
self, app_name, project_name, folder_path, task_name
):

View file

@ -156,7 +156,7 @@ class ApplicationManager:
Args:
app_name (str): Name of application that should be launched.
**data (dict): Any additional data. Data may be used during
**data (Any): Any additional data. Data may be used during
preparation to store objects usable in multiple places.
Raises: