From 3782105fc0c7b45edc11b32f3a09f26c2d542347 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 27 Jan 2025 17:36:17 +0100 Subject: [PATCH 01/17] EnumDef allows placeholder to be set --- client/ayon_core/lib/attribute_definitions.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/lib/attribute_definitions.py b/client/ayon_core/lib/attribute_definitions.py index e8327a45b6..e750c00a27 100644 --- a/client/ayon_core/lib/attribute_definitions.py +++ b/client/ayon_core/lib/attribute_definitions.py @@ -554,12 +554,18 @@ class EnumDef(AbstractAttrDef): """ type = "enum" + type_attributes = [ + "multiselection", + "placeholder", + ] + def __init__( self, key: str, items: "EnumItemsInputType", default: "Union[str, List[Any]]" = None, multiselection: Optional[bool] = False, + placeholder: Optional[str] = None, **kwargs ): if not items: @@ -587,6 +593,7 @@ class EnumDef(AbstractAttrDef): self.items: List["EnumItemDict"] = items self._item_values: Set[Any] = item_values_set self.multiselection: bool = multiselection + self.placeholder: Optional[str] = placeholder def convert_value(self, value): if not self.multiselection: @@ -612,7 +619,6 @@ class EnumDef(AbstractAttrDef): def serialize(self): data = super().serialize() data["items"] = copy.deepcopy(self.items) - data["multiselection"] = self.multiselection return data @staticmethod From 29dbc8b8cf50cd72825d61964d17e601733c47d0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 27 Jan 2025 17:37:14 +0100 Subject: [PATCH 02/17] set the placeholder on widgets --- client/ayon_core/tools/attribute_defs/widgets.py | 6 +++++- .../tools/utils/multiselection_combobox.py | 4 ++-- client/ayon_core/tools/utils/widgets.py | 13 ++++++++++--- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/tools/attribute_defs/widgets.py b/client/ayon_core/tools/attribute_defs/widgets.py index 93f63730f5..635855863c 100644 --- a/client/ayon_core/tools/attribute_defs/widgets.py +++ b/client/ayon_core/tools/attribute_defs/widgets.py @@ -640,11 +640,15 @@ class EnumAttrWidget(_BaseAttrDefWidget): return self.attr_def.multiselection def _ui_init(self): + placeholder = self.attr_def.placeholder if self.multiselection: - input_widget = MultiSelectionComboBox(self) + input_widget = MultiSelectionComboBox( + self, placeholder=placeholder + ) else: input_widget = CustomTextComboBox(self) + input_widget.set_placeholder(placeholder) combo_delegate = QtWidgets.QStyledItemDelegate(input_widget) input_widget.setItemDelegate(combo_delegate) self._combo_delegate = combo_delegate diff --git a/client/ayon_core/tools/utils/multiselection_combobox.py b/client/ayon_core/tools/utils/multiselection_combobox.py index 34361fca17..b90838267b 100644 --- a/client/ayon_core/tools/utils/multiselection_combobox.py +++ b/client/ayon_core/tools/utils/multiselection_combobox.py @@ -61,7 +61,7 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): self._block_mouse_release_timer = QtCore.QTimer(self, singleShot=True) self._initial_mouse_pos = None self._separator = separator - self._placeholder_text = placeholder + self._placeholder_text = placeholder or "" delegate = ComboItemDelegate(self) self.setItemDelegate(delegate) @@ -74,7 +74,7 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): return self._placeholder_text def set_placeholder_text(self, text): - self._placeholder_text = text + self._placeholder_text = text or "" self._update_size_hint() def set_custom_text(self, text): diff --git a/client/ayon_core/tools/utils/widgets.py b/client/ayon_core/tools/utils/widgets.py index 4c2b418c41..8d5a11b811 100644 --- a/client/ayon_core/tools/utils/widgets.py +++ b/client/ayon_core/tools/utils/widgets.py @@ -54,7 +54,7 @@ class ComboBox(QtWidgets.QComboBox): """ def __init__(self, *args, **kwargs): - super(ComboBox, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) delegate = QtWidgets.QStyledItemDelegate() self.setItemDelegate(delegate) self.setFocusPolicy(QtCore.Qt.StrongFocus) @@ -63,7 +63,7 @@ class ComboBox(QtWidgets.QComboBox): def wheelEvent(self, event): if self.hasFocus(): - return super(ComboBox, self).wheelEvent(event) + return super().wheelEvent(event) class CustomTextComboBox(ComboBox): @@ -71,7 +71,14 @@ class CustomTextComboBox(ComboBox): def __init__(self, *args, **kwargs): self._custom_text = None - super(CustomTextComboBox, self).__init__(*args, **kwargs) + self._placeholder = placeholder + super().__init__(*args, **kwargs) + + def set_placeholder(self, placeholder: Optional[str]): + if placeholder == self._placeholder: + return + self.lineEdit().setPlaceholderText(placeholder) + self.repaint() def set_custom_text(self, text=None): if self._custom_text != text: From 4c7176c7521827c4f48f2694ba3337d2fe0bbcf7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 27 Jan 2025 17:40:03 +0100 Subject: [PATCH 03/17] remove placeholder from single selection combobox --- client/ayon_core/tools/attribute_defs/widgets.py | 4 +--- client/ayon_core/tools/utils/widgets.py | 7 ------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/client/ayon_core/tools/attribute_defs/widgets.py b/client/ayon_core/tools/attribute_defs/widgets.py index 635855863c..201fd5be48 100644 --- a/client/ayon_core/tools/attribute_defs/widgets.py +++ b/client/ayon_core/tools/attribute_defs/widgets.py @@ -640,15 +640,13 @@ class EnumAttrWidget(_BaseAttrDefWidget): return self.attr_def.multiselection def _ui_init(self): - placeholder = self.attr_def.placeholder if self.multiselection: input_widget = MultiSelectionComboBox( - self, placeholder=placeholder + self, placeholder=self.attr_def.placeholder ) else: input_widget = CustomTextComboBox(self) - input_widget.set_placeholder(placeholder) combo_delegate = QtWidgets.QStyledItemDelegate(input_widget) input_widget.setItemDelegate(combo_delegate) self._combo_delegate = combo_delegate diff --git a/client/ayon_core/tools/utils/widgets.py b/client/ayon_core/tools/utils/widgets.py index 8d5a11b811..059a06648b 100644 --- a/client/ayon_core/tools/utils/widgets.py +++ b/client/ayon_core/tools/utils/widgets.py @@ -71,15 +71,8 @@ class CustomTextComboBox(ComboBox): def __init__(self, *args, **kwargs): self._custom_text = None - self._placeholder = placeholder super().__init__(*args, **kwargs) - def set_placeholder(self, placeholder: Optional[str]): - if placeholder == self._placeholder: - return - self.lineEdit().setPlaceholderText(placeholder) - self.repaint() - def set_custom_text(self, text=None): if self._custom_text != text: self._custom_text = text From a312c463842bbeae1053054503f80168faa7abd3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 27 Jan 2025 17:40:14 +0100 Subject: [PATCH 04/17] added placeholder to docstring --- client/ayon_core/lib/attribute_definitions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/ayon_core/lib/attribute_definitions.py b/client/ayon_core/lib/attribute_definitions.py index e750c00a27..c901e0f03e 100644 --- a/client/ayon_core/lib/attribute_definitions.py +++ b/client/ayon_core/lib/attribute_definitions.py @@ -550,6 +550,8 @@ class EnumDef(AbstractAttrDef): passed items or list of values for multiselection. multiselection (Optional[bool]): If True, multiselection is allowed. Output is list of selected items. + placeholder (Optional[str]): Placeholder for UI purposes, only for + multiselection enumeration. """ type = "enum" From e13de28a24c2a223fe4903421f802a4ba7fcef08 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 5 Feb 2025 11:24:07 +0100 Subject: [PATCH 05/17] draw placeholder with disabled color --- client/ayon_core/tools/utils/multiselection_combobox.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/utils/multiselection_combobox.py b/client/ayon_core/tools/utils/multiselection_combobox.py index b90838267b..e8bc688234 100644 --- a/client/ayon_core/tools/utils/multiselection_combobox.py +++ b/client/ayon_core/tools/utils/multiselection_combobox.py @@ -208,7 +208,8 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): draw_text = False if draw_text: option.currentText = combotext - option.palette.setCurrentColorGroup(QtGui.QPalette.Disabled) + # Draw text as disabled -> to mimic placeholder color + option.state &= ~QtWidgets.QStyle.State_Enabled painter.drawControl(QtWidgets.QStyle.CE_ComboBoxLabel, option) return From 71454b6fa4e31ce08028b08b813974c942f0ece6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 5 Feb 2025 11:54:56 +0100 Subject: [PATCH 06/17] usa same placeholder color as elsewhere --- .../tools/utils/multiselection_combobox.py | 65 ++++++++++++------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/client/ayon_core/tools/utils/multiselection_combobox.py b/client/ayon_core/tools/utils/multiselection_combobox.py index e8bc688234..7a8c4c2fcc 100644 --- a/client/ayon_core/tools/utils/multiselection_combobox.py +++ b/client/ayon_core/tools/utils/multiselection_combobox.py @@ -1,5 +1,7 @@ from qtpy import QtCore, QtGui, QtWidgets +from ayon_core.style import get_objected_colors + from .lib import ( checkstate_int_to_enum, checkstate_enum_to_int, @@ -49,11 +51,12 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): item_spacing = 5 item_bg_color = QtGui.QColor("#31424e") + _placeholder_color = None def __init__( self, parent=None, placeholder="", separator=", ", **kwargs ): - super(MultiSelectionComboBox, self).__init__(parent=parent, **kwargs) + super().__init__(parent=parent, **kwargs) self.setObjectName("MultiSelectionComboBox") self.setFocusPolicy(QtCore.Qt.StrongFocus) @@ -206,21 +209,23 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): combotext = self._placeholder_text else: draw_text = False - if draw_text: - option.currentText = combotext - # Draw text as disabled -> to mimic placeholder color - option.state &= ~QtWidgets.QStyle.State_Enabled - painter.drawControl(QtWidgets.QStyle.CE_ComboBoxLabel, option) - return - font_metricts = self.fontMetrics() + lines = self._lines + if draw_text: + color = self._get_placeholder_color() + pen = painter.pen() + pen.setColor(color) + painter.setPen(pen) + lines = {0: [combotext]} + + font_metrics = self.fontMetrics() if self._item_height is None: self.updateGeometry() self.update() return - for line, items in self._lines.items(): + for line, items in lines.items(): top_y = ( option.rect.top() + (line * self._item_height) @@ -228,7 +233,7 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): ) left_x = option.rect.left() + self.left_offset for item in items: - label_rect = font_metricts.boundingRect(item) + label_rect = font_metrics.boundingRect(item) label_height = label_rect.height() label_rect.moveTop(top_y) @@ -238,22 +243,23 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): label_rect.width() + self.left_right_padding ) - bg_rect = QtCore.QRectF(label_rect) - bg_rect.setWidth( - label_rect.width() + self.left_right_padding - ) - left_x = bg_rect.right() + self.item_spacing + if not draw_text: + bg_rect = QtCore.QRectF(label_rect) + bg_rect.setWidth( + label_rect.width() + self.left_right_padding + ) + left_x = bg_rect.right() + self.item_spacing + + bg_rect.setHeight(label_height + (2 * self.top_bottom_padding)) + bg_rect.moveTop(bg_rect.top() + self.top_bottom_margins) + + path = QtGui.QPainterPath() + path.addRoundedRect(bg_rect, 5, 5) + + painter.fillPath(path, self.item_bg_color) label_rect.moveLeft(label_rect.x() + self.left_right_padding) - bg_rect.setHeight(label_height + (2 * self.top_bottom_padding)) - bg_rect.moveTop(bg_rect.top() + self.top_bottom_margins) - - path = QtGui.QPainterPath() - path.addRoundedRect(bg_rect, 5, 5) - - painter.fillPath(path, self.item_bg_color) - painter.drawText( label_rect, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, @@ -288,11 +294,11 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): line = 0 self._lines = {line: []} - font_metricts = self.fontMetrics() + font_metrics = self.fontMetrics() default_left_x = 0 + self.left_offset left_x = int(default_left_x) for item in items: - rect = font_metricts.boundingRect(item) + rect = font_metrics.boundingRect(item) width = rect.width() + (2 * self.left_right_padding) right_x = left_x + width if right_x > total_width: @@ -383,3 +389,12 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): return event.ignore() return super(MultiSelectionComboBox, self).keyPressEvent(event) + + @classmethod + def _get_placeholder_color(cls): + if cls._placeholder_color is None: + color_obj = get_objected_colors("font") + color = color_obj.get_qcolor() + color.setAlpha(67) + cls._placeholder_color = color + return cls._placeholder_color From e30b89e36d23a97a01f7fe22086c5b4f8d1291d0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 5 Feb 2025 12:09:12 +0100 Subject: [PATCH 07/17] fix formatting --- client/ayon_core/tools/utils/multiselection_combobox.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/utils/multiselection_combobox.py b/client/ayon_core/tools/utils/multiselection_combobox.py index 7a8c4c2fcc..7bd7a76abc 100644 --- a/client/ayon_core/tools/utils/multiselection_combobox.py +++ b/client/ayon_core/tools/utils/multiselection_combobox.py @@ -47,7 +47,7 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): top_bottom_padding = 2 left_right_padding = 3 left_offset = 4 - top_bottom_margins = 2 + top_bottom_margins = 1 item_spacing = 5 item_bg_color = QtGui.QColor("#31424e") @@ -250,7 +250,9 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): ) left_x = bg_rect.right() + self.item_spacing - bg_rect.setHeight(label_height + (2 * self.top_bottom_padding)) + bg_rect.setHeight( + label_height + (2 * self.top_bottom_padding) + ) bg_rect.moveTop(bg_rect.top() + self.top_bottom_margins) path = QtGui.QPainterPath() From e0507d99d2829fd6296878899468146f92b5de0d Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 6 Feb 2025 22:20:27 +0800 Subject: [PATCH 08/17] add substance designer into the template --- server/settings/tools.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/server/settings/tools.py b/server/settings/tools.py index 96851be1da..0aaa88a8b0 100644 --- a/server/settings/tools.py +++ b/server/settings/tools.py @@ -484,6 +484,17 @@ DEFAULT_TOOLS_VALUES = { "task_types": [], "tasks": [], "template": "{folder[name]}_{variant}" + }, + { + "product_types": [ + "textureSet" + ], + "hosts": [ + "substancedesigner" + ], + "task_types": [], + "tasks": [], + "template": "T_{folder[name]}{variant}" } ], "filter_creator_profiles": [] @@ -548,10 +559,13 @@ DEFAULT_TOOLS_VALUES = { }, { "product_types": [ - "simpleUnrealTexture" + "simpleUnrealTexture", + "image", + "textures" ], "hosts": [ - "standalonepublisher" + "standalonepublisher", + "substancedesigner" ], "task_types": [], "task_names": [], @@ -595,10 +609,13 @@ DEFAULT_TOOLS_VALUES = { "hero_template_name_profiles": [ { "product_types": [ - "simpleUnrealTexture" + "simpleUnrealTexture", + "image", + "textures" ], "hosts": [ - "standalonepublisher" + "standalonepublisher", + "substancedesigner" ], "task_types": [], "task_names": [], From c6f5988aae75a1fb514f20ec56bb50985b145bea Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Thu, 6 Feb 2025 17:13:29 +0100 Subject: [PATCH 09/17] use same font size for placeholder as elsewhere --- .../tools/utils/multiselection_combobox.py | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/utils/multiselection_combobox.py b/client/ayon_core/tools/utils/multiselection_combobox.py index 7bd7a76abc..a6198abb51 100644 --- a/client/ayon_core/tools/utils/multiselection_combobox.py +++ b/client/ayon_core/tools/utils/multiselection_combobox.py @@ -210,22 +210,36 @@ class MultiSelectionComboBox(QtWidgets.QComboBox): else: draw_text = False - lines = self._lines if draw_text: color = self._get_placeholder_color() pen = painter.pen() pen.setColor(color) painter.setPen(pen) - lines = {0: [combotext]} - font_metrics = self.fontMetrics() + left_x = option.rect.left() + self.left_offset + + font = self.font() + # This is hardcoded point size from styles + font.setPointSize(10) + painter.setFont(font) + + label_rect = QtCore.QRect(option.rect) + label_rect.moveLeft(left_x) + + painter.drawText( + label_rect, + QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, + combotext + ) + return if self._item_height is None: self.updateGeometry() self.update() return - for line, items in lines.items(): + font_metrics = self.fontMetrics() + for line, items in self._lines.items(): top_y = ( option.rect.top() + (line * self._item_height) From a409d55b5904525797f0520c4a872a7776ad1123 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 7 Feb 2025 16:53:57 +0800 Subject: [PATCH 10/17] big roy's comment - separate template for substancedesigner from standalonepublisher --- server/settings/tools.py | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/server/settings/tools.py b/server/settings/tools.py index 0aaa88a8b0..32c72e7a98 100644 --- a/server/settings/tools.py +++ b/server/settings/tools.py @@ -559,12 +559,21 @@ DEFAULT_TOOLS_VALUES = { }, { "product_types": [ - "simpleUnrealTexture", - "image", - "textures" + "simpleUnrealTexture" + ], + "hosts": [ + "standalonepublisher" + ], + "task_types": [], + "task_names": [], + "template_name": "simpleUnrealTexture" + }, + { + "product_types": [ + "image", + "textures", ], "hosts": [ - "standalonepublisher", "substancedesigner" ], "task_types": [], @@ -609,12 +618,21 @@ DEFAULT_TOOLS_VALUES = { "hero_template_name_profiles": [ { "product_types": [ - "simpleUnrealTexture", + "simpleUnrealTexture" + ], + "hosts": [ + "standalonepublisher" + ], + "task_types": [], + "task_names": [], + "template_name": "simpleUnrealTextureHero" + }, + { + "product_types": [ "image", "textures" ], "hosts": [ - "standalonepublisher", "substancedesigner" ], "task_types": [], From 6a57b4c4504bb19987a3e10bcf8f167e4ae121b0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Feb 2025 17:13:16 +0100 Subject: [PATCH 11/17] use odd numbers for frame size --- client/ayon_core/tools/utils/nice_checkbox.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/utils/nice_checkbox.py b/client/ayon_core/tools/utils/nice_checkbox.py index 06845c397a..b47e3e62e6 100644 --- a/client/ayon_core/tools/utils/nice_checkbox.py +++ b/client/ayon_core/tools/utils/nice_checkbox.py @@ -328,6 +328,9 @@ class NiceCheckbox(QtWidgets.QFrame): if frame_rect.width() < 0 or frame_rect.height() < 0: return + frame_rect.setLeft(frame_rect.x() + (frame_rect.width() % 2)) + frame_rect.setTop(frame_rect.y() + (frame_rect.height() % 2)) + painter = QtGui.QPainter(self) painter.setRenderHint(QtGui.QPainter.Antialiasing) @@ -364,11 +367,16 @@ class NiceCheckbox(QtWidgets.QFrame): margin_size_c = 0 checkbox_rect = QtCore.QRect( - frame_rect.x() + margin_size_c, - frame_rect.y() + margin_size_c, - frame_rect.width() - (margin_size_c * 2), - frame_rect.height() - (margin_size_c * 2) + frame_rect.x(), + frame_rect.y(), + frame_rect.width(), + frame_rect.height() ) + if margin_size_c: + checkbox_rect.adjust( + margin_size_c, margin_size_c, + -margin_size_c, -margin_size_c + ) if checkbox_rect.width() > checkbox_rect.height(): radius = floor(checkbox_rect.height() * 0.5) From 21187bb28cdc76655e562f474b65d6245de449c7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Feb 2025 17:13:25 +0100 Subject: [PATCH 12/17] use NoPen instead of transparent --- client/ayon_core/tools/utils/nice_checkbox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/utils/nice_checkbox.py b/client/ayon_core/tools/utils/nice_checkbox.py index b47e3e62e6..3d9d63b6bc 100644 --- a/client/ayon_core/tools/utils/nice_checkbox.py +++ b/client/ayon_core/tools/utils/nice_checkbox.py @@ -383,7 +383,7 @@ class NiceCheckbox(QtWidgets.QFrame): else: radius = floor(checkbox_rect.width() * 0.5) - painter.setPen(QtCore.Qt.transparent) + painter.setPen(QtCore.Qt.NoPen) painter.setBrush(bg_color) painter.drawRoundedRect(checkbox_rect, radius, radius) From cbaefffabc756a7922481263fe12a335467e5d44 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 11 Feb 2025 18:55:04 +0100 Subject: [PATCH 13/17] multiselection EnumDef allows empty items --- client/ayon_core/lib/attribute_definitions.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/lib/attribute_definitions.py b/client/ayon_core/lib/attribute_definitions.py index e8327a45b6..f3de4fc943 100644 --- a/client/ayon_core/lib/attribute_definitions.py +++ b/client/ayon_core/lib/attribute_definitions.py @@ -562,17 +562,18 @@ class EnumDef(AbstractAttrDef): multiselection: Optional[bool] = False, **kwargs ): - if not items: - raise ValueError(( - "Empty 'items' value. {} must have" + if multiselection is None: + multiselection = False + + if not items and not multiselection: + raise ValueError( + f"Empty 'items' value. {self.__class__.__name__} must have" " defined values on initialization." - ).format(self.__class__.__name__)) + ) items = self.prepare_enum_items(items) item_values = [item["value"] for item in items] item_values_set = set(item_values) - if multiselection is None: - multiselection = False if multiselection: if default is None: From 8a27bd5bc3a680d144e23c1f4a3f4a08ac3957ae Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 12 Feb 2025 10:34:36 +0100 Subject: [PATCH 14/17] implemented 'PlaceholderPlainTextEdit' --- client/ayon_core/tools/utils/__init__.py | 2 ++ client/ayon_core/tools/utils/widgets.py | 41 +++++++++++++++++++----- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/client/ayon_core/tools/utils/__init__.py b/client/ayon_core/tools/utils/__init__.py index 4714e76ea3..9206af9beb 100644 --- a/client/ayon_core/tools/utils/__init__.py +++ b/client/ayon_core/tools/utils/__init__.py @@ -5,6 +5,7 @@ from .widgets import ( ComboBox, CustomTextComboBox, PlaceholderLineEdit, + PlaceholderPlainTextEdit, ElideLabel, HintedLineEdit, ExpandingTextEdit, @@ -89,6 +90,7 @@ __all__ = ( "ComboBox", "CustomTextComboBox", "PlaceholderLineEdit", + "PlaceholderPlainTextEdit", "ElideLabel", "HintedLineEdit", "ExpandingTextEdit", diff --git a/client/ayon_core/tools/utils/widgets.py b/client/ayon_core/tools/utils/widgets.py index 059a06648b..1074b6d4fb 100644 --- a/client/ayon_core/tools/utils/widgets.py +++ b/client/ayon_core/tools/utils/widgets.py @@ -88,23 +88,48 @@ class CustomTextComboBox(ComboBox): painter.drawControl(QtWidgets.QStyle.CE_ComboBoxLabel, option) -class PlaceholderLineEdit(QtWidgets.QLineEdit): - """Set placeholder color of QLineEdit in Qt 5.12 and higher.""" - def __init__(self, *args, **kwargs): - super(PlaceholderLineEdit, self).__init__(*args, **kwargs) - # Change placeholder palette color - if hasattr(QtGui.QPalette, "PlaceholderText"): - filter_palette = self.palette() +class _Cache: + _placeholder_color = None + + @classmethod + def get_placeholder_color(cls): + if cls._placeholder_color is None: color_obj = get_objected_colors("font") color = color_obj.get_qcolor() color.setAlpha(67) + cls._placeholder_color = color + return cls._placeholder_color + + +class PlaceholderLineEdit(QtWidgets.QLineEdit): + """Set placeholder color of QLineEdit in Qt 5.12 and higher.""" + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # Change placeholder palette color + if hasattr(QtGui.QPalette, "PlaceholderText"): + filter_palette = self.palette() filter_palette.setColor( QtGui.QPalette.PlaceholderText, - color + _Cache.get_placeholder_color() ) self.setPalette(filter_palette) +class PlaceholderPlainTextEdit(QtWidgets.QPlainTextEdit): + """Set placeholder color of QPlainTextEdit in Qt 5.12 and higher.""" + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # Change placeholder palette color + if hasattr(QtGui.QPalette, "PlaceholderText"): + viewport = self.viewport() + filter_palette = viewport.palette() + filter_palette.setColor( + QtGui.QPalette.PlaceholderText, + _Cache.get_placeholder_color() + ) + viewport.setPalette(filter_palette) + + class ElideLabel(QtWidgets.QLabel): """Label which elide text. From bfbc8baa3cb5ec684099d61a2a101fedbb30d9c9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 12 Feb 2025 10:34:51 +0100 Subject: [PATCH 15/17] use placeholder widgets in attribute definitions --- client/ayon_core/tools/attribute_defs/widgets.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/attribute_defs/widgets.py b/client/ayon_core/tools/attribute_defs/widgets.py index 201fd5be48..e4f1ddec51 100644 --- a/client/ayon_core/tools/attribute_defs/widgets.py +++ b/client/ayon_core/tools/attribute_defs/widgets.py @@ -22,6 +22,8 @@ from ayon_core.tools.utils import ( FocusSpinBox, FocusDoubleSpinBox, MultiSelectionComboBox, + PlaceholderLineEdit, + PlaceholderPlainTextEdit, set_style_property, ) from ayon_core.tools.utils import NiceCheckbox @@ -502,9 +504,9 @@ class TextAttrWidget(_BaseAttrDefWidget): self.multiline = self.attr_def.multiline if self.multiline: - input_widget = QtWidgets.QPlainTextEdit(self) + input_widget = PlaceholderPlainTextEdit(self) else: - input_widget = QtWidgets.QLineEdit(self) + input_widget = PlaceholderLineEdit(self) # Override context menu event to add revert to default action input_widget.contextMenuEvent = self._input_widget_context_event From 416657eefa0aa5294ea70ebd6a20e14214f55243 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 12 Feb 2025 11:05:55 +0100 Subject: [PATCH 16/17] added empty item --- .../ayon_core/tools/attribute_defs/widgets.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/attribute_defs/widgets.py b/client/ayon_core/tools/attribute_defs/widgets.py index 93f63730f5..32671eade7 100644 --- a/client/ayon_core/tools/attribute_defs/widgets.py +++ b/client/ayon_core/tools/attribute_defs/widgets.py @@ -2,7 +2,7 @@ import copy import typing from typing import Optional -from qtpy import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore, QtGui from ayon_core.lib.attribute_definitions import ( AbstractAttrDef, @@ -655,6 +655,9 @@ class EnumAttrWidget(_BaseAttrDefWidget): for item in self.attr_def.items: input_widget.addItem(item["label"], item["value"]) + if not self.attr_def.items: + self._add_empty_item(input_widget) + idx = input_widget.findData(self.attr_def.default) if idx >= 0: input_widget.setCurrentIndex(idx) @@ -671,6 +674,20 @@ class EnumAttrWidget(_BaseAttrDefWidget): input_widget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) input_widget.customContextMenuRequested.connect(self._on_context_menu) + def _add_empty_item(self, input_widget): + model = input_widget.model() + if not isinstance(model, QtGui.QStandardItemModel): + return + + root_item = model.invisibleRootItem() + + empty_item = QtGui.QStandardItem() + empty_item.setData("< No items to select >", QtCore.Qt.DisplayRole) + empty_item.setData("", QtCore.Qt.UserRole) + empty_item.setFlags(QtCore.Qt.NoItemFlags) + + root_item.appendRow(empty_item) + def _on_context_menu(self, pos): menu = QtWidgets.QMenu(self) From f0943753c40565dcb1217b571eaba108b0c5b2b7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 12 Feb 2025 11:08:44 +0100 Subject: [PATCH 17/17] set column stretch for pre-create attributes --- client/ayon_core/tools/publisher/widgets/precreate_widget.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/ayon_core/tools/publisher/widgets/precreate_widget.py b/client/ayon_core/tools/publisher/widgets/precreate_widget.py index 5ad203d370..b786fea3b5 100644 --- a/client/ayon_core/tools/publisher/widgets/precreate_widget.py +++ b/client/ayon_core/tools/publisher/widgets/precreate_widget.py @@ -85,6 +85,8 @@ class AttributesWidget(QtWidgets.QWidget): layout.setContentsMargins(0, 0, 0, 0) layout.setHorizontalSpacing(INPUTS_LAYOUT_HSPACING) layout.setVerticalSpacing(INPUTS_LAYOUT_VSPACING) + layout.setColumnStretch(0, 0) + layout.setColumnStretch(1, 1) self._layout = layout