From dfe057fc307519148cac7f7fa8cbe8f71f6296ef Mon Sep 17 00:00:00 2001 From: Aleks Berland Date: Mon, 25 Aug 2025 20:44:16 -0400 Subject: [PATCH 1/2] Implement task display order settings and sorting in TasksQtModel and TasksProxyModel --- client/ayon_core/tools/utils/tasks_widget.py | 63 ++++++++++++++++++++ server/settings/tools.py | 20 +++++++ 2 files changed, 83 insertions(+) diff --git a/client/ayon_core/tools/utils/tasks_widget.py b/client/ayon_core/tools/utils/tasks_widget.py index 744eb6060a..5ba8d3f44d 100644 --- a/client/ayon_core/tools/utils/tasks_widget.py +++ b/client/ayon_core/tools/utils/tasks_widget.py @@ -7,6 +7,7 @@ from ayon_core.style import ( get_disabled_entity_icon_color, get_default_entity_icon_color, ) +from ayon_core.settings import get_project_settings from .views import DeselectableTreeView from .lib import RefreshThread, get_qt_icon @@ -16,6 +17,7 @@ ITEM_ID_ROLE = QtCore.Qt.UserRole + 1 PARENT_ID_ROLE = QtCore.Qt.UserRole + 2 ITEM_NAME_ROLE = QtCore.Qt.UserRole + 3 TASK_TYPE_ROLE = QtCore.Qt.UserRole + 4 +TASK_SORT_ROLE = QtCore.Qt.UserRole + 5 class TasksQtModel(QtGui.QStandardItemModel): @@ -51,6 +53,9 @@ class TasksQtModel(QtGui.QStandardItemModel): self._last_folder_id = None self._refresh_threads = {} + + # Sorting configuration + self._task_display_order = "anatomy" # "anatomy" | "alphabetical" self._current_refresh_thread = None # Initial state @@ -176,6 +181,26 @@ class TasksQtModel(QtGui.QStandardItemModel): self._is_refreshing = True self._last_project_name = project_name self._last_folder_id = folder_id + # Load tools configuration for task display order + try: + if project_name: + settings = get_project_settings(project_name) + tools_settings = settings.get("core", {}).get("tools", {}) + # Prefer new Tools Display section + display_settings = tools_settings.get("display", {}) + if "task_display_order" in display_settings: + self._task_display_order = display_settings.get( + "task_display_order", "anatomy" + ) + else: + # Fallback to legacy location if present + menu_settings = tools_settings.get("ayon_menu", {}) + self._task_display_order = menu_settings.get( + "task_display_order", "anatomy" + ) + except Exception: + # Keep default if settings are not available in this context + self._task_display_order = "anatomy" if not folder_id: self._add_invalid_selection_item() self._current_refresh_thread = None @@ -261,6 +286,11 @@ class TasksQtModel(QtGui.QStandardItemModel): task_type_item.name: task_type_item for task_type_item in task_type_items } + # Build order mapping from project anatomy task type list order + task_type_order_by_name = { + task_type_item.name: index + for index, task_type_item in enumerate(task_type_items) + } task_type_icon_cache = {} new_items = [] new_names = set() @@ -283,6 +313,15 @@ class TasksQtModel(QtGui.QStandardItemModel): item.setData(name, ITEM_NAME_ROLE) item.setData(task_item.id, ITEM_ID_ROLE) item.setData(task_item.task_type, TASK_TYPE_ROLE) + # Store sorting order based on selected display order + sort_value = None + if self._task_display_order == "alphabetical": + # Use lowercased label for consistent alphabetical sorting + sort_value = task_item.full_label.lower() + else: + # Default: project anatomy task type order + sort_value = task_type_order_by_name.get(task_item.task_type) + item.setData(sort_value, TASK_SORT_ROLE) item.setData(task_item.parent_id, PARENT_ID_ROLE) item.setData(icon, QtCore.Qt.DecorationRole) @@ -368,6 +407,30 @@ class TasksProxyModel(QtCore.QSortFilterProxyModel): return False return super().filterAcceptsRow(row, parent_index) + def lessThan(self, left, right): + # Sort by TASK_SORT_ROLE if present + left_value = left.data(TASK_SORT_ROLE) + right_value = right.data(TASK_SORT_ROLE) + + if left_value == right_value: + return super().lessThan(left, right) + + # If values are strings, compare lexicographically + if isinstance(left_value, str) or isinstance(right_value, str): + left_str = "" if left_value is None else str(left_value) + right_str = "" if right_value is None else str(right_value) + return left_str < right_str + + # Otherwise treat as numeric order; None goes last + if right_value is None: + return True + if left_value is None: + return False + try: + return left_value < right_value + except Exception: + return super().lessThan(left, right) + class TasksWidget(QtWidgets.QWidget): """Tasks widget. diff --git a/server/settings/tools.py b/server/settings/tools.py index 815ef40f8e..9d73f64715 100644 --- a/server/settings/tools.py +++ b/server/settings/tools.py @@ -167,6 +167,19 @@ class AYONMenuModel(BaseSettingsModel): ) +class ToolsDisplayModel(BaseSettingsModel): + _layout = "expanded" + task_display_order: str = SettingsField( + "anatomy", + title="Task Display Order", + description="Order in which tasks are displayed in tools", + enum_resolver=lambda: [ + {"value": "anatomy", "label": "Anatomy Order"}, + {"value": "alphabetical", "label": "Alphabetical"}, + ], + ) + + class WorkfilesToolModel(BaseSettingsModel): workfile_template_profiles: list[WorkfileTemplateProfile] = SettingsField( default_factory=list, @@ -373,6 +386,10 @@ class GlobalToolsModel(BaseSettingsModel): default_factory=AYONMenuModel, title="AYON Menu" ) + display: ToolsDisplayModel = SettingsField( + default_factory=ToolsDisplayModel, + title="Tools Display" + ) creator: CreatorToolModel = SettingsField( default_factory=CreatorToolModel, title="Creator" @@ -395,6 +412,9 @@ DEFAULT_TOOLS_VALUES = { "ayon_menu": { "version_up_current_workfile": False }, + "display": { + "task_display_order": "anatomy" + }, "creator": { "product_types_smart_select": [ { From aaefad12bb1ebf4578b9fb380453e75158f9ecf1 Mon Sep 17 00:00:00 2001 From: Aleks Berland <2853462+FuzzkingCool@users.noreply.github.com> Date: Thu, 30 Oct 2025 11:40:48 -0400 Subject: [PATCH 2/2] Update client/ayon_core/tools/utils/tasks_widget.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- client/ayon_core/tools/utils/tasks_widget.py | 24 ++++++-------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/client/ayon_core/tools/utils/tasks_widget.py b/client/ayon_core/tools/utils/tasks_widget.py index 7e11ee3414..3c511620e9 100644 --- a/client/ayon_core/tools/utils/tasks_widget.py +++ b/client/ayon_core/tools/utils/tasks_widget.py @@ -413,24 +413,14 @@ class TasksProxyModel(QtCore.QSortFilterProxyModel): left_value = left.data(TASK_SORT_ROLE) right_value = right.data(TASK_SORT_ROLE) - if left_value == right_value: - return super().lessThan(left, right) - - # If values are strings, compare lexicographically - if isinstance(left_value, str) or isinstance(right_value, str): - left_str = "" if left_value is None else str(left_value) - right_str = "" if right_value is None else str(right_value) - return left_str < right_str - - # Otherwise treat as numeric order; None goes last - if right_value is None: - return True - if left_value is None: - return False - try: + if left_value != right_value: + if left_value is None: + return False + if right_value is None: + return True return left_value < right_value - except Exception: - return super().lessThan(left, right) + + return super().lessThan(left, right) class TasksWidget(QtWidgets.QWidget):