From 51beef8192a435edcd2e0c4b29802f161ab755f6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 25 Aug 2025 11:22:22 +0200 Subject: [PATCH] handle the actions --- .../ayon_core/tools/loader/models/actions.py | 84 ++++++++------ .../ayon_core/tools/loader/models/sitesync.py | 2 +- client/ayon_core/tools/loader/ui/window.py | 103 +++++++++++++++++- 3 files changed, 154 insertions(+), 35 deletions(-) diff --git a/client/ayon_core/tools/loader/models/actions.py b/client/ayon_core/tools/loader/models/actions.py index e6ac328f92..d3d053ae85 100644 --- a/client/ayon_core/tools/loader/models/actions.py +++ b/client/ayon_core/tools/loader/models/actions.py @@ -124,12 +124,13 @@ class LoaderActionsModel: self, plugin_identifier: str, identifier: str, - options: dict[str, Any], project_name: str, entity_ids: set[str], entity_type: str, selected_ids: set[str], selected_entity_type: str, + options: dict[str, Any], + form_values: dict[str, Any], ): """Trigger action by identifier. @@ -142,17 +143,23 @@ class LoaderActionsModel: Args: plugin_identifier (str): Plugin identifier. identifier (str): Action identifier. - options (dict[str, Any]): Loader option values. project_name (str): Project name. entity_ids (set[str]): Entity ids on action item. entity_type (str): Entity type on action item. selected_ids (set[str]): Selected entity ids. selected_entity_type (str): Selected entity type. + options (dict[str, Any]): Loader option values. + form_values (dict[str, Any]): Form values. """ event_data = { "plugin_identifier": plugin_identifier, "identifier": identifier, + "project_name": project_name, + "entity_ids": list(entity_ids), + "entity_type": entity_type, + "selected_ids": list(selected_ids), + "selected_entity_type": selected_entity_type, "id": uuid.uuid4().hex, } self._controller.emit_event( @@ -162,9 +169,10 @@ class LoaderActionsModel: ) if plugin_identifier != LOADER_PLUGIN_ID: # TODO fill error infor if any happens - error_info = [] + result = None + crashed = False try: - self._loader_actions.execute_action( + result = self._loader_actions.execute_action( plugin_identifier, identifier, entity_ids, @@ -174,37 +182,47 @@ class LoaderActionsModel: selected_ids, selected_entity_type, ), - {}, + form_values, ) except Exception: + crashed = True self._log.warning( f"Failed to execute action '{identifier}'", exc_info=True, ) - else: - loader = self._get_loader_by_identifier( - project_name, identifier - ) - if entity_type == "version": - error_info = self._trigger_version_loader( - loader, - options, - project_name, - entity_ids, - ) - elif entity_type == "representation": - error_info = self._trigger_representation_loader( - loader, - options, - project_name, - entity_ids, - ) - else: - raise NotImplementedError( - f"Invalid entity type '{entity_type}' to trigger action item" - ) + event_data["result"] = result + event_data["crashed"] = crashed + self._controller.emit_event( + "loader.action.finished", + event_data, + ACTIONS_MODEL_SENDER, + ) + return + + loader = self._get_loader_by_identifier( + project_name, identifier + ) + + if entity_type == "version": + error_info = self._trigger_version_loader( + loader, + options, + project_name, + entity_ids, + ) + elif entity_type == "representation": + error_info = self._trigger_representation_loader( + loader, + options, + project_name, + entity_ids, + ) + else: + raise NotImplementedError( + f"Invalid entity type '{entity_type}' to trigger action item" + ) event_data["error_info"] = error_info self._controller.emit_event( @@ -334,8 +352,8 @@ class LoaderActionsModel: label=label, icon=self._get_action_icon(loader), tooltip=self._get_action_tooltip(loader), - options=loader.get_options(contexts), order=loader.order, + options=loader.get_options(contexts), ) def _get_loaders(self, project_name): @@ -783,11 +801,11 @@ class LoaderActionsModel: action.identifier, action.entity_ids, action.entity_type, - label, - action.icon, - None, # action.tooltip, - None, # action.options, - action.order, + label=label, + icon=action.icon, + tooltip=None, # action.tooltip, + order=action.order, + options=None, # action.options, )) return items diff --git a/client/ayon_core/tools/loader/models/sitesync.py b/client/ayon_core/tools/loader/models/sitesync.py index 67da36cd53..ced4ac5d05 100644 --- a/client/ayon_core/tools/loader/models/sitesync.py +++ b/client/ayon_core/tools/loader/models/sitesync.py @@ -493,10 +493,10 @@ class SiteSyncModel: "color": "#999999" }, tooltip=tooltip, - options={}, order=1, entity_ids=representation_ids, entity_type="representation", + options={}, ) def _add_site(self, project_name, repre_entity, site_name, product_type): diff --git a/client/ayon_core/tools/loader/ui/window.py b/client/ayon_core/tools/loader/ui/window.py index df5beb708f..71679213e5 100644 --- a/client/ayon_core/tools/loader/ui/window.py +++ b/client/ayon_core/tools/loader/ui/window.py @@ -1,18 +1,24 @@ from __future__ import annotations +from typing import Optional + from qtpy import QtWidgets, QtCore, QtGui from ayon_core.resources import get_ayon_icon_filepath from ayon_core.style import load_stylesheet +from ayon_core.pipeline.actions import LoaderActionResult from ayon_core.tools.utils import ( PlaceholderLineEdit, + MessageOverlayObject, ErrorMessageBox, ThumbnailPainterWidget, RefreshButton, GoToCurrentButton, + ProjectsCombobox, + get_qt_icon, ) +from ayon_core.tools.attribute_defs import AttributeDefinitionsDialog from ayon_core.tools.utils.lib import center_window -from ayon_core.tools.utils import ProjectsCombobox from ayon_core.tools.common_models import StatusItem from ayon_core.tools.loader.abstract import ProductTypeItem from ayon_core.tools.loader.control import LoaderController @@ -141,6 +147,8 @@ class LoaderWindow(QtWidgets.QWidget): if controller is None: controller = LoaderController() + overlay_object = MessageOverlayObject(self) + main_splitter = QtWidgets.QSplitter(self) context_splitter = QtWidgets.QSplitter(main_splitter) @@ -294,6 +302,12 @@ class LoaderWindow(QtWidgets.QWidget): "controller.reset.finished", self._on_controller_reset_finish, ) + controller.register_event_callback( + "loader.action.finished", + self._on_loader_action_finished, + ) + + self._overlay_object = overlay_object self._group_dialog = ProductGroupDialog(controller, self) @@ -406,6 +420,20 @@ class LoaderWindow(QtWidgets.QWidget): if self._reset_on_show: self.refresh() + def _show_toast_message( + self, + message: str, + success: bool = True, + message_id: Optional[str] = None, + ): + message_type = None + if not success: + message_type = "error" + + self._overlay_object.add_message( + message, message_type, message_id=message_id + ) + def _show_group_dialog(self): project_name = self._projects_combobox.get_selected_project_name() if not project_name: @@ -494,6 +522,79 @@ class LoaderWindow(QtWidgets.QWidget): box = LoadErrorMessageBox(error_info, self) box.show() + def _on_loader_action_finished(self, event): + crashed = event["crashed"] + if crashed: + self._show_toast_message( + "Action failed", + success=False, + ) + return + + result: Optional[LoaderActionResult] = event["result"] + if result is None: + return + + if result.message: + self._show_toast_message( + result.message, result.success + ) + + if result.form is None: + return + + form = result.form + dialog = AttributeDefinitionsDialog( + form.fields, + title=form.title, + parent=self, + ) + if result.form_values: + dialog.set_values(result.form_values) + submit_label = form.submit_label + submit_icon = form.submit_icon + cancel_label = form.cancel_label + cancel_icon = form.cancel_icon + + if submit_icon: + submit_icon = get_qt_icon(submit_icon) + if cancel_icon: + cancel_icon = get_qt_icon(cancel_icon) + + if submit_label: + dialog.set_submit_label(submit_label) + else: + dialog.set_submit_visible(False) + + if submit_icon: + dialog.set_submit_icon(submit_icon) + + if cancel_label: + dialog.set_cancel_label(cancel_label) + else: + dialog.set_cancel_visible(False) + + if cancel_icon: + dialog.set_cancel_icon(cancel_icon) + + dialog.setMinimumSize(300, 140) + result = dialog.exec_() + if result != QtWidgets.QDialog.Accepted: + return + + form_data = dialog.get_values() + self._controller.trigger_action_item( + event["plugin_identifier"], + event["identifier"], + event["project_name"], + event["entity_ids"], + event["entity_type"], + event["selected_ids"], + event["selected_entity_type"], + {}, + form_data, + ) + def _on_project_selection_changed(self, event): self._selected_project_name = event["project_name"] self._update_filters()