remove applications action logic

This commit is contained in:
Jakub Trllo 2025-04-03 11:49:04 +02:00
parent ddad178dd6
commit 172fc27fa0
4 changed files with 8 additions and 341 deletions

View file

@ -307,22 +307,6 @@ class AbstractLauncherFrontEnd(AbstractLauncherCommon):
"""
pass
@abstractmethod
def set_application_force_not_open_workfile(
self, project_name, folder_id, task_id, action_ids, enabled
):
"""This is application action related to force not open last workfile.
Args:
project_name (Union[str, None]): Project name.
folder_id (Union[str, None]): Folder id.
task_id (Union[str, None]): Task id.
action_ids (Iterable[str]): Action identifiers.
enabled (bool): New value of force not open workfile.
"""
pass
@abstractmethod
def refresh(self):
"""Refresh everything, models, ui etc.

View file

@ -135,12 +135,7 @@ class BaseLauncherController(
return self._actions_model.get_action_items(
project_name, folder_id, task_id)
def set_application_force_not_open_workfile(
self, project_name, folder_id, task_id, action_ids, enabled
):
self._actions_model.set_application_force_not_open_workfile(
project_name, folder_id, task_id, action_ids, enabled
)
def trigger_action(self, project_name, folder_id, task_id, identifier):
self._actions_model.trigger_action(

View file

@ -1,117 +1,12 @@
import os
from ayon_core import resources
from ayon_core.lib import Logger, AYONSettingsRegistry
from ayon_core.addon import AddonsManager
from ayon_core.pipeline.actions import (
discover_launcher_actions,
LauncherAction,
LauncherActionSelection,
register_launcher_action_path,
)
from ayon_core.pipeline.workfile import should_use_last_workfile_on_launch
try:
# Available since applications addon 0.2.4
from ayon_applications.action import ApplicationAction
except ImportError:
# Backwards compatibility from 0.3.3 (24/06/10)
# TODO: Remove in future releases
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
@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.project_entities[selection.project_name]
apps = project_entity["attrib"].get("applications")
if not apps or self.application.full_name not in apps:
return False
project_settings = self.project_settings[selection.project_name]
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"""
from ayon_applications import (
ApplicationExecutableNotFound,
ApplicationLaunchFailed,
)
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)
# class Action:
@ -160,9 +55,6 @@ class ActionItem:
action if it has same 'label' and have set 'variant_label'.
icon (dict[str, str]): Icon definition.
order (int): Action ordering.
is_application (bool): Is action application action.
force_not_open_workfile (bool): Force not open workfile. Application
related.
full_label (Optional[str]): Full label, if not set it is generated
from 'label' and 'variant_label'.
"""
@ -174,8 +66,6 @@ class ActionItem:
variant_label,
icon,
order,
is_application,
force_not_open_workfile,
full_label=None
):
self.identifier = identifier
@ -183,8 +73,6 @@ class ActionItem:
self.variant_label = variant_label
self.icon = icon
self.order = order
self.is_application = is_application
self.force_not_open_workfile = force_not_open_workfile
self._full_label = full_label
def copy(self):
@ -206,8 +94,6 @@ class ActionItem:
"variant_label": self.variant_label,
"icon": self.icon,
"order": self.order,
"is_application": self.is_application,
"force_not_open_workfile": self.force_not_open_workfile,
"full_label": self._full_label,
}
@ -264,8 +150,6 @@ class ActionsModel:
controller (AbstractLauncherBackend): Controller instance.
"""
_not_open_workfile_reg_key = "force_not_open_workfile"
def __init__(self, controller):
self._controller = controller
@ -275,8 +159,6 @@ class ActionsModel:
self._actions = None
self._action_items = {}
self._launcher_tool_reg = AYONSettingsRegistry("launcher_tool")
self._addons_manager = None
@property
@ -294,34 +176,6 @@ class ActionsModel:
self._get_action_objects()
self._controller.emit_event("actions.refresh.finished")
def _should_start_last_workfile(
self,
project_name,
task_id,
identifier,
host_name,
not_open_workfile_actions
):
if identifier in not_open_workfile_actions:
return not not_open_workfile_actions[identifier]
task_name = None
task_type = None
if task_id is not None:
task_entity = self._controller.get_task_entity(
project_name, task_id
)
task_name = task_entity["name"]
task_type = task_entity["taskType"]
output = should_use_last_workfile_on_launch(
project_name,
host_name,
task_name,
task_type
)
return output
def get_action_items(self, project_name, folder_id, task_id):
"""Get actions for project.
@ -332,46 +186,18 @@ class ActionsModel:
Returns:
list[ActionItem]: List of actions.
"""
not_open_workfile_actions = self._get_no_last_workfile_for_context(
project_name, folder_id, task_id)
selection = self._prepare_selection(project_name, folder_id, task_id)
output = []
action_items = self._get_action_items(project_name)
for identifier, action in self._get_action_objects().items():
if not action.is_compatible(selection):
continue
if action.is_compatible(selection):
output.append(action_items[identifier])
action_item = action_items[identifier]
# Handling of 'force_not_open_workfile' for applications
if action_item.is_application:
action_item = action_item.copy()
start_last_workfile = self._should_start_last_workfile(
project_name,
task_id,
identifier,
action.application.host_name,
not_open_workfile_actions
)
action_item.force_not_open_workfile = (
not start_last_workfile
)
output.append(action_item)
return output
def set_application_force_not_open_workfile(
self, project_name, folder_id, task_id, action_ids, enabled
):
no_workfile_reg_data = self._get_no_last_workfile_reg_data()
project_data = no_workfile_reg_data.setdefault(project_name, {})
folder_data = project_data.setdefault(folder_id, {})
task_data = folder_data.setdefault(task_id, {})
for action_id in action_ids:
task_data[action_id] = enabled
self._launcher_tool_reg.set_item(
self._not_open_workfile_reg_key, no_workfile_reg_data
)
def trigger_action(self, project_name, folder_id, task_id, identifier):
selection = self._prepare_selection(project_name, folder_id, task_id)
@ -390,18 +216,6 @@ class ActionsModel:
"full_label": action_label,
}
)
if isinstance(action, ApplicationAction):
per_action = self._get_no_last_workfile_for_context(
project_name, folder_id, task_id
)
start_last_workfile = self._should_start_last_workfile(
project_name,
task_id,
identifier,
action.application.host_name,
per_action
)
action.data["start_last_workfile"] = start_last_workfile
action.process(selection)
except Exception as exc:
@ -424,27 +238,6 @@ class ActionsModel:
self._addons_manager = AddonsManager()
return self._addons_manager
def _get_no_last_workfile_reg_data(self):
try:
no_workfile_reg_data = self._launcher_tool_reg.get_item(
self._not_open_workfile_reg_key)
except ValueError:
no_workfile_reg_data = {}
self._launcher_tool_reg.set_item(
self._not_open_workfile_reg_key, no_workfile_reg_data)
return no_workfile_reg_data
def _get_no_last_workfile_for_context(
self, project_name, folder_id, task_id
):
not_open_workfile_reg_data = self._get_no_last_workfile_reg_data()
return (
not_open_workfile_reg_data
.get(project_name, {})
.get(folder_id, {})
.get(task_id, {})
)
def _prepare_selection(self, project_name, folder_id, task_id):
project_entity = None
if project_name:
@ -470,7 +263,6 @@ class ActionsModel:
register_launcher_action_path(path)
self._discovered_actions = (
discover_launcher_actions()
+ self._get_applications_action_classes()
)
return self._discovered_actions
@ -498,10 +290,9 @@ class ActionsModel:
action_items = {}
for identifier, action in self._get_action_objects().items():
is_application = isinstance(action, ApplicationAction)
# Backwards compatibility from 0.3.3 (24/06/10)
# TODO: Remove in future releases
if is_application and hasattr(action, "project_settings"):
if hasattr(action, "project_settings"):
action.project_entities[project_name] = project_entity
action.project_settings[project_name] = project_settings
@ -515,45 +306,7 @@ class ActionsModel:
variant_label,
icon,
action.order,
is_application,
False
)
action_items[identifier] = item
self._action_items[project_name] = action_items
return action_items
def _get_applications_action_classes(self):
addons_manager = self._get_addons_manager()
applications_addon = addons_manager.get_enabled_addon("applications")
if hasattr(applications_addon, "get_applications_action_classes"):
return applications_addon.get_applications_action_classes()
# Backwards compatibility from 0.3.3 (24/06/10)
# TODO: Remove in future releases
actions = []
if applications_addon is None:
return actions
manager = applications_addon.get_applications_manager()
for full_name, application in manager.applications.items():
if not application.enabled:
continue
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": application.icon,
"color": getattr(application, "color", None),
"order": getattr(application, "order", None) or 0,
"data": {}
}
)
actions.append(action)
return actions

View file

@ -11,12 +11,10 @@ from .resources import get_options_image_path
ANIMATION_LEN = 7
ACTION_ID_ROLE = QtCore.Qt.UserRole + 1
ACTION_IS_APPLICATION_ROLE = QtCore.Qt.UserRole + 2
ACTION_IS_GROUP_ROLE = QtCore.Qt.UserRole + 3
ACTION_SORT_ROLE = QtCore.Qt.UserRole + 4
ANIMATION_START_ROLE = QtCore.Qt.UserRole + 5
ANIMATION_STATE_ROLE = QtCore.Qt.UserRole + 6
FORCE_NOT_OPEN_WORKFILE_ROLE = QtCore.Qt.UserRole + 7
ACTION_IS_GROUP_ROLE = QtCore.Qt.UserRole + 2
ACTION_SORT_ROLE = QtCore.Qt.UserRole + 3
ANIMATION_START_ROLE = QtCore.Qt.UserRole + 4
ANIMATION_STATE_ROLE = QtCore.Qt.UserRole + 5
def _variant_label_sort_getter(action_item):
@ -144,11 +142,6 @@ class ActionsQtModel(QtGui.QStandardItemModel):
item.setData(icon, QtCore.Qt.DecorationRole)
item.setData(is_group, ACTION_IS_GROUP_ROLE)
item.setData(action_item.order, ACTION_SORT_ROLE)
item.setData(
action_item.is_application, ACTION_IS_APPLICATION_ROLE)
item.setData(
action_item.force_not_open_workfile,
FORCE_NOT_OPEN_WORKFILE_ROLE)
items_by_id[action_item.identifier] = item
action_items_by_id[action_item.identifier] = action_item
@ -263,13 +256,6 @@ class ActionDelegate(QtWidgets.QStyledItemDelegate):
super(ActionDelegate, self).paint(painter, option, index)
if index.data(FORCE_NOT_OPEN_WORKFILE_ROLE):
rect = QtCore.QRectF(
option.rect.x(), option.rect.y() + option.rect.height(), 5, 5)
painter.setPen(QtCore.Qt.NoPen)
painter.setBrush(QtGui.QColor(200, 0, 0))
painter.drawEllipse(rect)
if not index.data(ACTION_IS_GROUP_ROLE):
return
@ -360,14 +346,11 @@ class ActionsWidget(QtWidgets.QWidget):
animation_timer.timeout.connect(self._on_animation)
view.clicked.connect(self._on_clicked)
view.customContextMenuRequested.connect(self._on_context_menu)
model.refreshed.connect(self._on_model_refresh)
self._animated_items = set()
self._animation_timer = animation_timer
self._context_menu = None
self._flick = flick
self._view = view
self._model = model
@ -416,54 +399,6 @@ class ActionsWidget(QtWidgets.QWidget):
self._animated_items.add(action_id)
self._animation_timer.start()
def _on_context_menu(self, point):
"""Creates menu to force skip opening last workfile."""
index = self._view.indexAt(point)
if not index.isValid():
return
if not index.data(ACTION_IS_APPLICATION_ROLE):
return
menu = QtWidgets.QMenu(self._view)
checkbox = QtWidgets.QCheckBox(
"Skip opening last workfile.", menu)
if index.data(FORCE_NOT_OPEN_WORKFILE_ROLE):
checkbox.setChecked(True)
action_id = index.data(ACTION_ID_ROLE)
is_group = index.data(ACTION_IS_GROUP_ROLE)
if is_group:
action_items = self._model.get_group_items(action_id)
else:
action_items = [self._model.get_action_item_by_id(action_id)]
action_ids = {action_item.identifier for action_item in action_items}
checkbox.stateChanged.connect(
lambda: self._on_checkbox_changed(
action_ids, checkbox.isChecked()
)
)
action = QtWidgets.QWidgetAction(menu)
action.setDefaultWidget(checkbox)
menu.addAction(action)
self._context_menu = menu
global_point = self.mapToGlobal(point)
menu.exec_(global_point)
self._context_menu = None
def _on_checkbox_changed(self, action_ids, is_checked):
if self._context_menu is not None:
self._context_menu.close()
project_name = self._model.get_selected_project_name()
folder_id = self._model.get_selected_folder_id()
task_id = self._model.get_selected_task_id()
self._controller.set_application_force_not_open_workfile(
project_name, folder_id, task_id, action_ids, is_checked)
self._model.refresh()
def _on_clicked(self, index):
if not index or not index.isValid():
return