Merge branch 'develop' into AY-7812_source-resolution-keys

This commit is contained in:
Petr Kalis 2025-06-25 17:44:53 +02:00 committed by GitHub
commit 8156d0bfcb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 359 additions and 161 deletions

View file

@ -235,6 +235,30 @@ def version(build):
print(os.environ["AYON_VERSION"])
@main_cli.command()
@click.option(
"--project",
type=str,
help="Project name",
required=True)
def create_project_structure(
project,
):
"""Create project folder structure as defined in setting
`ayon+settings://core/project_folder_structure`
Args:
project (str): The name of the project for which you
want to create its additional folder structure.
"""
from ayon_core.pipeline.project_folders import create_project_folders
print(f">>> Creating project folder structure for project '{project}'.")
create_project_folders(project)
def _set_global_environments() -> None:
"""Set global AYON environments."""
# First resolve general environment

View file

@ -829,35 +829,47 @@ HintedLineEditButton {
}
/* Launcher specific stylesheets */
ActionsView[mode="icon"] {
ActionsView {
/* font size can't be set on items */
font-size: 9pt;
font-size: 8pt;
border: 0px;
padding: 0px;
margin: 0px;
}
ActionsView[mode="icon"]::item {
ActionsView::item {
padding-top: 8px;
padding-bottom: 4px;
border: 0px;
border-radius: 0.3em;
border-radius: 5px;
}
ActionsView[mode="icon"]::item:hover {
ActionsView::item:hover {
color: {color:font-hover};
background: #424A57;
}
ActionsView[mode="icon"]::icon {}
ActionsView::icon {}
ActionMenuPopup #GroupLabel {
padding: 5px;
color: #ffffff;
}
ActionMenuPopup #ShadowFrame {
border-radius: 5px;
background: rgba(12, 13, 24, 0.5);
}
ActionMenuPopup #Wrapper {
border-radius: 0.3em;
border-radius: 5px;
background: #353B46;
}
ActionMenuPopup ActionsView[mode="icon"] {
ActionMenuPopup ActionsView {
background: transparent;
border: none;
margin: 4px;
}
#IconView[mode="icon"] {

View file

@ -16,12 +16,12 @@ from ayon_core.lib.attribute_definitions import (
from ayon_core.tools.flickcharm import FlickCharm
from ayon_core.tools.utils import (
get_qt_icon,
PixmapLabel,
)
from ayon_core.tools.attribute_defs import AttributeDefinitionsDialog
from ayon_core.tools.launcher.abstract import WebactionContext
ANIMATION_LEN = 7
SHADOW_FRAME_MARGINS = (1, 1, 1, 1)
ACTION_ID_ROLE = QtCore.Qt.UserRole + 1
ACTION_TYPE_ROLE = QtCore.Qt.UserRole + 2
@ -51,13 +51,9 @@ def _variant_label_sort_getter(action_item):
# --- Replacement for QAction for action variants ---
class LauncherSettingsLabel(PixmapLabel):
class LauncherSettingsLabel(QtWidgets.QWidget):
_settings_icon = None
def __init__(self, parent):
icon = self._get_settings_icon()
super().__init__(icon.pixmap(64, 64), parent)
@classmethod
def _get_settings_icon(cls):
if cls._settings_icon is None:
@ -67,24 +63,52 @@ class LauncherSettingsLabel(PixmapLabel):
})
return cls._settings_icon
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.setRenderHints(
QtGui.QPainter.Antialiasing
| QtGui.QPainter.SmoothPixmapTransform
)
rect = event.rect()
size = min(rect.height(), rect.width())
pix_rect = QtCore.QRect(
rect.x(), rect.y(),
size, size
)
pix = self._get_settings_icon().pixmap(size, size)
painter.drawPixmap(pix_rect, pix)
painter.end()
class ActionOverlayWidget(QtWidgets.QFrame):
config_requested = QtCore.Signal(str)
def __init__(self, item_id, parent):
super().__init__(parent)
self._item_id = item_id
settings_icon = LauncherSettingsLabel(self)
settings_icon.setToolTip("Right click for options")
settings_icon.setVisible(False)
main_layout = QtWidgets.QGridLayout(self)
main_layout.setContentsMargins(5, 5, 0, 0)
main_layout.addWidget(settings_icon, 0, 0)
main_layout.setColumnStretch(1, 1)
main_layout.setRowStretch(1, 1)
main_layout.setColumnStretch(0, 1)
main_layout.setColumnStretch(1, 5)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground, True)
self._settings_icon = settings_icon
def enterEvent(self, event):
super().enterEvent(event)
self._settings_icon.setVisible(True)
def leaveEvent(self, event):
super().leaveEvent(event)
self._settings_icon.setVisible(False)
class ActionsQtModel(QtGui.QStandardItemModel):
@ -138,6 +162,12 @@ class ActionsQtModel(QtGui.QStandardItemModel):
def get_item_by_id(self, action_id):
return self._items_by_id.get(action_id)
def get_index_by_id(self, action_id):
item = self.get_item_by_id(action_id)
if item is not None:
return self.indexFromItem(item)
return QtCore.QModelIndex()
def get_group_item_by_action_id(self, action_id):
item = self._items_by_id.get(action_id)
if item is not None:
@ -222,7 +252,7 @@ class ActionsQtModel(QtGui.QStandardItemModel):
item.setFlags(QtCore.Qt.ItemIsEnabled)
item.setData(label, QtCore.Qt.DisplayRole)
# item.setData(label, QtCore.Qt.ToolTipRole)
item.setData(label, QtCore.Qt.ToolTipRole)
item.setData(icon, QtCore.Qt.DecorationRole)
item.setData(is_group, ACTION_IS_GROUP_ROLE)
item.setData(has_configs, ACTION_HAS_CONFIGS_ROLE)
@ -295,8 +325,8 @@ class ActionMenuPopupModel(QtGui.QStandardItemModel):
item = QtGui.QStandardItem()
item.setFlags(QtCore.Qt.ItemIsEnabled)
# item.setData(action_item.full_label, QtCore.Qt.ToolTipRole)
item.setData(action_item.full_label, QtCore.Qt.DisplayRole)
item.setData(action_item.variant_label, QtCore.Qt.DisplayRole)
item.setData(action_item.full_label, QtCore.Qt.ToolTipRole)
item.setData(icon, QtCore.Qt.DecorationRole)
item.setData(action_item.identifier, ACTION_ID_ROLE)
item.setData(
@ -344,8 +374,24 @@ class ActionMenuPopupModel(QtGui.QStandardItemModel):
class ActionMenuPopup(QtWidgets.QWidget):
"""Popup widget for group varaints.
The popup is handling most of the layout and showing of the items
manually.
There 4 parts:
1. Shadow - semi transparent black widget used as shadow.
2. Background - painted over the shadow with blur effect. All
other items are painted over.
3. Label - show group label and positioned manually at the top
of the popup.
4. View - View with variant action items. View is positioned
and resized manually according to the items in the group and then
animated using mask region.
"""
action_triggered = QtCore.Signal(str)
config_requested = QtCore.Signal(str)
config_requested = QtCore.Signal(str, QtCore.QPoint)
def __init__(self, parent):
super().__init__(parent)
@ -363,16 +409,34 @@ class ActionMenuPopup(QtWidgets.QWidget):
expand_anim.setDuration(60)
expand_anim.setEasingCurve(QtCore.QEasingCurve.InOutQuad)
sh_l, sh_t, sh_r, sh_b = SHADOW_FRAME_MARGINS
group_label = QtWidgets.QLabel("|", self)
group_label.setObjectName("GroupLabel")
# View with actions
view = ActionsView(self)
view.setGridSize(QtCore.QSize(75, 80))
view.setIconSize(QtCore.QSize(32, 32))
view.move(QtCore.QPoint(3, 3))
view.move(sh_l, sh_t)
view.stackUnder(group_label)
# Background draw
bg_frame = QtWidgets.QFrame(self)
bg_frame.setObjectName("ShadowFrame")
bg_frame.stackUnder(view)
wrapper = QtWidgets.QFrame(self)
wrapper.setObjectName("Wrapper")
wrapper.stackUnder(view)
effect = QtWidgets.QGraphicsBlurEffect(wrapper)
effect.setBlurRadius(3.0)
wrapper.setGraphicsEffect(effect)
bg_layout = QtWidgets.QVBoxLayout(bg_frame)
bg_layout.setContentsMargins(sh_l, sh_t, sh_r, sh_b)
bg_layout.addWidget(wrapper)
model = ActionMenuPopupModel()
proxy_model = ActionsProxyModel()
@ -387,10 +451,12 @@ class ActionMenuPopup(QtWidgets.QWidget):
expand_anim.finished.connect(self._on_expand_finish)
view.clicked.connect(self._on_clicked)
view.config_requested.connect(self.config_requested)
view.config_requested.connect(self._on_configs_trigger)
self._group_label = group_label
self._view = view
self._wrapper = wrapper
self._bg_frame = bg_frame
self._effect = effect
self._model = model
self._proxy_model = proxy_model
@ -417,7 +483,8 @@ class ActionMenuPopup(QtWidgets.QWidget):
super().leaveEvent(event)
self._close_timer.start()
def show_items(self, action_id, action_items, pos):
def show_items(self, group_label, action_id, action_items, pos):
self._group_label.setText(group_label)
if not action_items:
if self._showed:
self._close_timer.start()
@ -426,70 +493,82 @@ class ActionMenuPopup(QtWidgets.QWidget):
self._close_timer.stop()
update_position = False
if action_id != self._current_id:
update_position = True
self.setGeometry(pos.x(), pos.y(), 1, 1)
self._current_id = action_id
self._update_items(action_items)
# Make sure is visible
if not self._showed:
update_position = True
self.show()
if not update_position:
self.raise_()
return
# Set geometry to position
# - first make sure widget changes from '_update_items'
# are recalculated
app = QtWidgets.QApplication.instance()
app.processEvents()
items_count, size, target_size = self._get_size_hint()
items_count, start_size, target_size = self._get_size_hint()
self._model.fill_to_count(items_count)
label_sh = self._group_label.sizeHint()
label_width, label_height = label_sh.width(), label_sh.height()
window = self.screen()
window_geo = window.geometry()
_target_x = pos.x() + target_size.width()
_target_y = pos.y() + target_size.height() + label_height
right_to_left = (
pos.x() + target_size.width() > window_geo.right()
or pos.y() + target_size.height() > window_geo.bottom()
_target_x > window_geo.right()
or _target_y > window_geo.bottom()
)
pos_x = pos.x() - 5
pos_y = pos.y() - 4
wrap_x = wrap_y = 0
sh_l, sh_t, sh_r, sh_b = SHADOW_FRAME_MARGINS
viewport_offset = self._view.viewport().geometry().topLeft()
pos_x = pos.x() - (sh_l + viewport_offset.x() + 2)
pos_y = pos.y() - (sh_t + viewport_offset.y() + 1)
bg_x = bg_y = 0
sort_order = QtCore.Qt.DescendingOrder
if right_to_left:
sort_order = QtCore.Qt.AscendingOrder
size_diff = target_size - size
size_diff = target_size - start_size
pos_x -= size_diff.width()
pos_y -= size_diff.height()
wrap_x = size_diff.width()
wrap_y = size_diff.height()
bg_x = size_diff.width()
bg_y = size_diff.height() - label_height
wrap_geo = QtCore.QRect(
wrap_x, wrap_y, size.width(), size.height()
bg_geo = QtCore.QRect(
bg_x, bg_y,
start_size.width(), start_size.height() + label_height
)
label_pos_x = sh_l
label_pos_y = bg_y + sh_t
if label_width < start_size.width():
label_pos_x = bg_x + (start_size.width() - label_width) // 2
if self._expand_anim.state() == QtCore.QAbstractAnimation.Running:
self._expand_anim.stop()
self._first_anim_frame = True
self._right_to_left = right_to_left
self._proxy_model.sort(0, sort_order)
self.setUpdatesEnabled(False)
self._view.setMask(wrap_geo)
self._view.setMask(
bg_geo.adjusted(sh_l, sh_t, -sh_r, -sh_b)
)
self._view.setMinimumWidth(target_size.width())
self._view.setMaximumWidth(target_size.width())
self._wrapper.setGeometry(wrap_geo)
self._view.setMinimumHeight(target_size.height())
self._view.move(sh_l, sh_t + label_height)
self.setGeometry(
pos_x, pos_y,
target_size.width(), target_size.height()
pos_x, pos_y - label_height,
target_size.width(), target_size.height() + label_height
)
self._bg_frame.setGeometry(bg_geo)
self._group_label.move(label_pos_x, label_pos_y)
self.setUpdatesEnabled(True)
self._expand_anim.updateCurrentTime(0)
self._expand_anim.setStartValue(size)
self._expand_anim.setStartValue(start_size)
self._expand_anim.setEndValue(target_size)
self._expand_anim.start()
@ -511,20 +590,37 @@ class ActionMenuPopup(QtWidgets.QWidget):
self._expand_anim.stop()
return
wrapper_geo = self._wrapper.geometry()
wrapper_geo.setWidth(value.width())
wrapper_geo.setHeight(value.height())
bg_geo = self._bg_frame.geometry()
label_sh = self._group_label.sizeHint()
label_width, label_height = label_sh.width(), label_sh.height()
if self._right_to_left:
geo = self.geometry()
popup_geo = self.geometry()
diff_size = popup_geo.size() - value
pos = QtCore.QPoint(
geo.width() - value.width(),
geo.height() - value.height(),
diff_size.width(), diff_size.height() - label_height
)
wrapper_geo.setTopLeft(pos)
self._view.setMask(wrapper_geo)
self._wrapper.setGeometry(wrapper_geo)
bg_geo.moveTopLeft(pos)
bg_geo.setWidth(value.width())
bg_geo.setHeight(value.height() + label_height)
label_width = self._group_label.sizeHint().width()
bgeo_tl = bg_geo.topLeft()
sh_l, sh_t, sh_r, sh_b = SHADOW_FRAME_MARGINS
label_pos_x = sh_l
if label_width < value.width():
label_pos_x = bgeo_tl.x() + (value.width() - label_width) // 2
self.setUpdatesEnabled(False)
self._view.setMask(
bg_geo.adjusted(sh_l, sh_t, -sh_r, -sh_b)
)
self._group_label.move(label_pos_x, sh_t)
self._bg_frame.setGeometry(bg_geo)
self.setUpdatesEnabled(True)
def _on_expand_finish(self):
# Make sure that size is recalculated if src and targe size is same
@ -547,16 +643,25 @@ class ActionMenuPopup(QtWidgets.QWidget):
if rows == 1:
cols = row_count
m_l, m_t, m_r, m_b = (3, 3, 1, 1)
# QUESTION how to get the margins from Qt?
border = 2 * 1
viewport_geo = self._view.viewport().geometry()
viewport_offset = viewport_geo.topLeft()
# QUESTION how to get the bottom and right margins from Qt?
vp_lr = viewport_offset.x()
vp_tb = viewport_offset.y()
m_l, m_t, m_r, m_b = (
s_m + vp_m
for s_m, vp_m in zip(
SHADOW_FRAME_MARGINS,
(vp_lr, vp_tb, vp_lr, vp_tb)
)
)
single_width = (
grid_size.width()
+ self._view.horizontalOffset() + border + m_l + m_r + 1
+ self._view.horizontalOffset() + m_l + m_r + 1
)
single_height = (
grid_size.height()
+ self._view.verticalOffset() + border + m_b + m_t + 1
+ self._view.verticalOffset() + m_b + m_t + 1
)
total_width = single_width
total_height = single_height
@ -586,14 +691,13 @@ class ActionMenuPopup(QtWidgets.QWidget):
self.action_triggered.emit(action_id)
self.close()
def _on_configs_trigger(self, action_id):
self.config_requested.emit(action_id)
def _on_configs_trigger(self, action_id, center_pos):
self.config_requested.emit(action_id, center_pos)
self.close()
class ActionDelegate(QtWidgets.QStyledItemDelegate):
_cached_extender = {}
_cached_extender_base_pix = None
_extender_icon = None
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -653,31 +757,18 @@ class ActionDelegate(QtWidgets.QStyledItemDelegate):
painter.restore()
@classmethod
def _get_extender_pixmap(cls, size):
pix = cls._cached_extender.get(size)
if pix is not None:
return pix
base_pix = cls._cached_extender_base_pix
if base_pix is None:
icon = get_qt_icon({
def _get_extender_pixmap(cls):
if cls._extender_icon is None:
cls._extender_icon = get_qt_icon({
"type": "material-symbols",
"name": "more_horiz",
})
base_pix = icon.pixmap(64, 64)
cls._cached_extender_base_pix = base_pix
pix = base_pix.scaled(
size, size,
QtCore.Qt.KeepAspectRatio,
QtCore.Qt.SmoothTransformation
)
cls._cached_extender[size] = pix
return pix
return cls._extender_icon
def paint(self, painter, option, index):
painter.setRenderHints(
QtGui.QPainter.Antialiasing
| QtGui.QPainter.TextAntialiasing
| QtGui.QPainter.SmoothPixmapTransform
)
@ -690,20 +781,15 @@ class ActionDelegate(QtWidgets.QStyledItemDelegate):
return
grid_size = option.widget.gridSize()
x_offset = int(
(grid_size.width() / 2)
- (option.rect.width() / 2)
)
item_x = option.rect.x() - x_offset
tenth_size = int(grid_size.width() / 10)
extender_size = int(tenth_size * 2.4)
extender_rect = option.rect.adjusted(5, 5, 0, 0)
extender_size = grid_size.width() // 6
extender_rect.setWidth(extender_size)
extender_rect.setHeight(extender_size)
extender_x = item_x + tenth_size
extender_y = option.rect.y() + tenth_size
pix = self._get_extender_pixmap(extender_size)
painter.drawPixmap(extender_x, extender_y, pix)
icon = self._get_extender_pixmap()
pix = icon.pixmap(extender_size, extender_size)
painter.drawPixmap(extender_rect, pix)
class ActionsProxyModel(QtCore.QSortFilterProxyModel):
@ -739,12 +825,10 @@ class ActionsProxyModel(QtCore.QSortFilterProxyModel):
class ActionsView(QtWidgets.QListView):
action_triggered = QtCore.Signal(str)
config_requested = QtCore.Signal(str)
config_requested = QtCore.Signal(str, QtCore.QPoint)
def __init__(self, parent):
super().__init__(parent)
self.setProperty("mode", "icon")
self.setViewMode(QtWidgets.QListView.IconMode)
self.setResizeMode(QtWidgets.QListView.Adjust)
self.setSelectionMode(QtWidgets.QListView.NoSelection)
@ -773,18 +857,6 @@ class ActionsView(QtWidgets.QListView):
self._overlay_widgets = []
self._flick = flick
self._delegate = delegate
self._popup_widget = None
def mouseMoveEvent(self, event):
"""Handle mouse move event."""
super().mouseMoveEvent(event)
# Update hover state for the item under mouse
index = self.indexAt(event.pos())
if index.isValid() and index.data(ACTION_IS_GROUP_ROLE):
self._show_group_popup(index)
elif self._popup_widget is not None:
self._popup_widget.close()
def _on_context_menu(self, point):
"""Creates menu to force skip opening last workfile."""
@ -792,34 +864,9 @@ class ActionsView(QtWidgets.QListView):
if not index.isValid():
return
action_id = index.data(ACTION_ID_ROLE)
self.config_requested.emit(action_id)
def _get_popup_widget(self):
if self._popup_widget is None:
popup_widget = ActionMenuPopup(self)
popup_widget.action_triggered.connect(self.action_triggered)
popup_widget.config_requested.connect(self.config_requested)
self._popup_widget = popup_widget
return self._popup_widget
def _show_group_popup(self, index):
action_id = index.data(ACTION_ID_ROLE)
model = self.model()
while hasattr(model, "sourceModel"):
model = model.sourceModel()
if not hasattr(model, "get_group_items"):
return
action_items = model.get_group_items(action_id)
rect = self.visualRect(index)
pos = self.mapToGlobal(rect.topLeft())
popup_widget = self._get_popup_widget()
popup_widget.show_items(
action_id, action_items, pos
)
global_center = self.mapToGlobal(rect.center())
self.config_requested.emit(action_id, global_center)
def update_on_refresh(self):
viewport = self.viewport()
@ -837,9 +884,6 @@ class ActionsView(QtWidgets.QListView):
if has_configs:
item_id = index.data(ACTION_ID_ROLE)
widget = ActionOverlayWidget(item_id, viewport)
widget.config_requested.connect(
self.config_requested
)
overlay_widgets.append(widget)
self.setIndexWidget(index, widget)
@ -877,8 +921,7 @@ class ActionsWidget(QtWidgets.QWidget):
animation_timer.timeout.connect(self._on_animation)
view.clicked.connect(self._on_clicked)
view.action_triggered.connect(self._trigger_action)
view.config_requested.connect(self._on_config_request)
view.config_requested.connect(self._show_config_dialog)
model.refreshed.connect(self._on_model_refresh)
self._animated_items = set()
@ -888,6 +931,8 @@ class ActionsWidget(QtWidgets.QWidget):
self._model = model
self._proxy_model = proxy_model
self._popup_widget = None
self._set_row_height(1)
def refresh(self):
@ -974,10 +1019,32 @@ class ActionsWidget(QtWidgets.QWidget):
return
is_group = index.data(ACTION_IS_GROUP_ROLE)
if is_group:
return
action_id = index.data(ACTION_ID_ROLE)
self._trigger_action(action_id, index)
if is_group:
self._show_group_popup(index)
else:
self._trigger_action(action_id, index)
def _get_popup_widget(self):
if self._popup_widget is None:
popup_widget = ActionMenuPopup(self)
popup_widget.action_triggered.connect(self._trigger_action)
popup_widget.config_requested.connect(self._show_config_dialog)
self._popup_widget = popup_widget
return self._popup_widget
def _show_group_popup(self, index):
action_id = index.data(ACTION_ID_ROLE)
group_label = index.data(QtCore.Qt.DisplayRole)
action_items = self._model.get_group_items(action_id)
rect = self._view.visualRect(index)
pos = self.mapToGlobal(rect.topLeft())
popup_widget = self._get_popup_widget()
popup_widget.show_items(
group_label, action_id, action_items, pos
)
def _trigger_action(self, action_id, index=None):
project_name = self._model.get_selected_project_name()
@ -1011,10 +1078,7 @@ class ActionsWidget(QtWidgets.QWidget):
if index is not None:
self._start_animation(index)
def _on_config_request(self, action_id):
self._show_config_dialog(action_id)
def _show_config_dialog(self, action_id):
def _show_config_dialog(self, action_id, center_point):
action_item = self._model.get_action_item_by_id(action_id)
config_fields = self._model.get_action_config_fields(action_id)
if not config_fields:
@ -1040,11 +1104,31 @@ class ActionsWidget(QtWidgets.QWidget):
"Cancel",
)
dialog.set_values(values)
dialog.show()
self._center_dialog(dialog, center_point)
result = dialog.exec_()
if result == QtWidgets.QDialog.Accepted:
new_values = dialog.get_values()
self._controller.set_action_config_values(context, new_values)
@staticmethod
def _center_dialog(dialog, target_center_pos):
dialog_geo = dialog.geometry()
dialog_geo.moveCenter(target_center_pos)
screen = dialog.screen()
screen_geo = screen.availableGeometry()
if screen_geo.left() > dialog_geo.left():
dialog_geo.moveLeft(screen_geo.left())
elif screen_geo.right() < dialog_geo.right():
dialog_geo.moveRight(screen_geo.right())
if screen_geo.top() > dialog_geo.top():
dialog_geo.moveTop(screen_geo.top())
elif screen_geo.bottom() < dialog_geo.bottom():
dialog_geo.moveBottom(screen_geo.bottom())
dialog.move(dialog_geo.topLeft())
def _create_attrs_dialog(
self,
config_fields,

View file

@ -683,7 +683,7 @@ class CreateWidget(QtWidgets.QWidget):
options = list(self._current_creator_variant_hints)
if options:
options.append("---")
options.extend(variant_hints)
options.extend(sorted(variant_hints))
# Add hints to actions
self._variant_widget.set_options(options)

View file

@ -37,6 +37,7 @@ mdx-gh-links = "^0.4"
pymdown-extensions = "^10.14.3"
mike = "^2.1.3"
mkdocstrings-shell = "^1.0.2"
nxtools = "^1.6"
[tool.poetry.group.test.dependencies]
attrs = "^25.0.0"

View file

@ -1,6 +1,15 @@
from typing import Any
from ayon_server.addons import BaseServerAddon
from ayon_server.actions import (
ActionExecutor,
ExecuteResponseModel,
SimpleActionManifest,
)
try:
from ayon_server.logging import logger
except ImportError:
from nxtools import logging as logger
from .settings import (
CoreSettings,
@ -26,3 +35,67 @@ class CoreAddon(BaseServerAddon):
return await super().convert_settings_overrides(
source_version, overrides
)
async def get_simple_actions(
self,
project_name: str | None = None,
variant: str = "production",
) -> list[SimpleActionManifest]:
"""Return a list of simple actions provided by the addon"""
output = []
if project_name:
# Add 'Create Project Folder Structure' action to folders.
output.append(
SimpleActionManifest(
identifier="core.createprojectstructure",
label="Create Project Folder Structure",
icon={
"type": "material-symbols",
"name": "create_new_folder",
},
order=100,
entity_type="project",
entity_subtypes=None,
allow_multiselection=False,
)
)
return output
async def execute_action(
self,
executor: ActionExecutor,
) -> ExecuteResponseModel:
"""Execute webactions."""
project_name = executor.context.project_name
if executor.identifier == "core.createprojectstructure":
if not project_name:
logger.error(
f"Can't execute {executor.identifier} because"
" of missing project name."
)
# Works since AYON server 1.8.3
if hasattr(executor, "get_simple_response"):
return await executor.get_simple_response(
"Missing project name", success=False
)
return
args = [
"create-project-structure", "--project", project_name,
]
# Works since AYON server 1.8.3
if hasattr(executor, "get_launcher_response"):
return await executor.get_launcher_response(args)
return await executor.get_launcher_action_response(args)
logger.debug(f"Unknown action: {executor.identifier}")
# Works since AYON server 1.8.3
if hasattr(executor, "get_simple_response"):
return await executor.get_simple_response(
"Unknown action", success=False
)

View file

@ -106,7 +106,7 @@ class FallbackProductModel(BaseSettingsModel):
fallback_type: str = SettingsField(
title="Fallback config type",
enum_resolver=_fallback_ocio_config_profile_types,
conditionalEnum=True,
conditional_enum=True,
default="builtin_path",
description=(
"Type of config which needs to be used in case published "
@ -162,7 +162,7 @@ class CoreImageIOConfigProfilesModel(BaseSettingsModel):
type: str = SettingsField(
title="Profile type",
enum_resolver=_ocio_config_profile_types,
conditionalEnum=True,
conditional_enum=True,
default="builtin_path",
section="---",
)
@ -319,6 +319,10 @@ class CoreSettings(BaseSettingsModel):
"{}",
widget="textarea",
title="Project folder structure",
description=(
"Defines project folders to create on disk"
" for 'Create project folders' action."
),
section="---"
)
project_environments: str = SettingsField(

View file

@ -340,7 +340,7 @@ class ResizeModel(BaseSettingsModel):
title="Type",
description="Type of resizing",
enum_resolver=lambda: _resize_types_enum,
conditionalEnum=True,
conditional_enum=True,
default="source"
)
@ -373,7 +373,7 @@ class ExtractThumbnailOIIODefaultsModel(BaseSettingsModel):
title="Type",
description="Transcoding type",
enum_resolver=lambda: _thumbnail_oiio_transcoding_type,
conditionalEnum=True,
conditional_enum=True,
default="colorspace"
)
@ -476,7 +476,7 @@ class ExtractOIIOTranscodeOutputModel(BaseSettingsModel):
"colorspace",
title="Transcoding type",
enum_resolver=_extract_oiio_transcoding_type,
conditionalEnum=True,
conditional_enum=True,
description=(
"Select the transcoding type for your output, choosing either "
"*Colorspace* or *Display&View* transform."