Merge pull request #982 from ynput/enhancement/139-enhancement-display-overriden-vs-default-values-of-attributes-in-publisher-ui

Publisher: Display overridden vs default values of attributes
This commit is contained in:
Jakub Trllo 2024-11-05 11:18:42 +01:00 committed by GitHub
commit 9fbc08dd5e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 158 additions and 49 deletions

View file

@ -60,7 +60,11 @@
"icon-alert-tools": "#AA5050",
"icon-entity-default": "#bfccd6",
"icon-entity-disabled": "#808080",
"font-entity-deprecated": "#666666",
"font-overridden": "#91CDFC",
"overlay-messages": {
"close-btn": "#D3D8DE",
"bg-success": "#458056",

View file

@ -44,6 +44,10 @@ QLabel {
background: transparent;
}
QLabel[overriden="1"] {
color: {color:font-overridden};
}
/* Inputs */
QAbstractSpinBox, QLineEdit, QPlainTextEdit, QTextEdit {
border: 1px solid {color:border};

View file

@ -366,7 +366,7 @@ class AbstractPublisherFrontend(AbstractPublisherCommon):
@abstractmethod
def get_creator_attribute_definitions(
self, instance_ids: Iterable[str]
) -> List[Tuple[AbstractAttrDef, List[str], List[Any]]]:
) -> List[Tuple[AbstractAttrDef, Dict[str, Dict[str, Any]]]]:
pass
@abstractmethod
@ -383,7 +383,7 @@ class AbstractPublisherFrontend(AbstractPublisherCommon):
) -> List[Tuple[
str,
List[AbstractAttrDef],
Dict[str, List[Tuple[str, Any]]]
Dict[str, List[Tuple[str, Any, Any]]]
]]:
pass

View file

@ -769,7 +769,7 @@ class CreateModel:
def get_creator_attribute_definitions(
self, instance_ids: List[str]
) -> List[Tuple[AbstractAttrDef, List[str], List[Any]]]:
) -> List[Tuple[AbstractAttrDef, Dict[str, Dict[str, Any]]]]:
"""Collect creator attribute definitions for multuple instances.
Args:
@ -796,12 +796,23 @@ class CreateModel:
if found_idx is None:
idx = len(output)
output.append((attr_def, [instance_id], [value]))
output.append((
attr_def,
{
instance_id: {
"value": value,
"default": attr_def.default
}
}
))
_attr_defs[idx] = attr_def
else:
_, ids, values = output[found_idx]
ids.append(instance_id)
values.append(value)
_, info_by_id = output[found_idx]
info_by_id[instance_id] = {
"value": value,
"default": attr_def.default
}
return output
def set_instances_publish_attr_values(
@ -835,7 +846,7 @@ class CreateModel:
) -> List[Tuple[
str,
List[AbstractAttrDef],
Dict[str, List[Tuple[str, Any]]]
Dict[str, List[Tuple[str, Any, Any]]]
]]:
"""Collect publish attribute definitions for passed instances.
@ -865,21 +876,21 @@ class CreateModel:
attr_defs = attr_val.attr_defs
if not attr_defs:
continue
plugin_attr_defs = all_defs_by_plugin_name.setdefault(
plugin_name, []
)
plugin_attr_defs.append(attr_defs)
plugin_values = all_plugin_values.setdefault(plugin_name, {})
plugin_attr_defs.append(attr_defs)
for attr_def in attr_defs:
if isinstance(attr_def, UIDef):
continue
attr_values = plugin_values.setdefault(attr_def.key, [])
value = attr_val[attr_def.key]
attr_values.append((item_id, value))
attr_values.append(
(item_id, attr_val[attr_def.key], attr_def.default)
)
attr_defs_by_plugin_name = {}
for plugin_name, attr_defs in all_defs_by_plugin_name.items():
@ -893,7 +904,7 @@ class CreateModel:
output.append((
plugin_name,
attr_defs_by_plugin_name[plugin_name],
all_plugin_values
all_plugin_values[plugin_name],
))
return output

View file

@ -1,6 +1,10 @@
import typing
from typing import Dict, List, Any
from qtpy import QtWidgets, QtCore
from ayon_core.lib.attribute_definitions import UnknownDef
from ayon_core.lib.attribute_definitions import AbstractAttrDef, UnknownDef
from ayon_core.tools.utils import set_style_property
from ayon_core.tools.attribute_defs import create_widget_for_attr_def
from ayon_core.tools.publisher.abstract import AbstractPublisherFrontend
from ayon_core.tools.publisher.constants import (
@ -8,6 +12,49 @@ from ayon_core.tools.publisher.constants import (
INPUTS_LAYOUT_VSPACING,
)
if typing.TYPE_CHECKING:
from typing import Union
def _set_label_overriden(label: QtWidgets.QLabel, overriden: bool):
set_style_property(
label,
"overriden",
"1" if overriden else ""
)
class _CreateAttrDefInfo:
"""Helper class to store information about create attribute definition."""
def __init__(
self,
attr_def: AbstractAttrDef,
instance_ids: List["Union[str, None]"],
defaults: List[Any],
label_widget: "Union[None, QtWidgets.QLabel]",
):
self.attr_def: AbstractAttrDef = attr_def
self.instance_ids: List["Union[str, None]"] = instance_ids
self.defaults: List[Any] = defaults
self.label_widget: "Union[None, QtWidgets.QLabel]" = label_widget
class _PublishAttrDefInfo:
"""Helper class to store information about publish attribute definition."""
def __init__(
self,
attr_def: AbstractAttrDef,
plugin_name: str,
instance_ids: List["Union[str, None]"],
defaults: List[Any],
label_widget: "Union[None, QtWidgets.QLabel]",
):
self.attr_def: AbstractAttrDef = attr_def
self.plugin_name: str = plugin_name
self.instance_ids: List["Union[str, None]"] = instance_ids
self.defaults: List[Any] = defaults
self.label_widget: "Union[None, QtWidgets.QLabel]" = label_widget
class CreatorAttrsWidget(QtWidgets.QWidget):
"""Widget showing creator specific attributes for selected instances.
@ -51,8 +98,7 @@ class CreatorAttrsWidget(QtWidgets.QWidget):
self._controller: AbstractPublisherFrontend = controller
self._scroll_area = scroll_area
self._attr_def_id_to_instances = {}
self._attr_def_id_to_attr_def = {}
self._attr_def_info_by_id: Dict[str, _CreateAttrDefInfo] = {}
self._current_instance_ids = set()
# To store content of scroll area to prevent garbage collection
@ -81,8 +127,7 @@ class CreatorAttrsWidget(QtWidgets.QWidget):
prev_content_widget.deleteLater()
self._content_widget = None
self._attr_def_id_to_instances = {}
self._attr_def_id_to_attr_def = {}
self._attr_def_info_by_id = {}
result = self._controller.get_creator_attribute_definitions(
self._current_instance_ids
@ -97,9 +142,19 @@ class CreatorAttrsWidget(QtWidgets.QWidget):
content_layout.setVerticalSpacing(INPUTS_LAYOUT_VSPACING)
row = 0
for attr_def, instance_ids, values in result:
for attr_def, info_by_id in result:
widget = create_widget_for_attr_def(attr_def, content_widget)
default_values = []
if attr_def.is_value_def:
values = []
for item in info_by_id.values():
values.append(item["value"])
# 'set' cannot be used for default values because they can
# be unhashable types, e.g. 'list'.
default = item["default"]
if default not in default_values:
default_values.append(default)
if len(values) == 1:
value = values[0]
if value is not None:
@ -108,8 +163,10 @@ class CreatorAttrsWidget(QtWidgets.QWidget):
widget.set_value(values, True)
widget.value_changed.connect(self._input_value_changed)
self._attr_def_id_to_instances[attr_def.id] = instance_ids
self._attr_def_id_to_attr_def[attr_def.id] = attr_def
attr_def_info = _CreateAttrDefInfo(
attr_def, list(info_by_id), default_values, None
)
self._attr_def_info_by_id[attr_def.id] = attr_def_info
if not attr_def.visible:
continue
@ -121,8 +178,14 @@ class CreatorAttrsWidget(QtWidgets.QWidget):
col_num = 2 - expand_cols
label = None
is_overriden = False
if attr_def.is_value_def:
is_overriden = any(
item["value"] != item["default"]
for item in info_by_id.values()
)
label = attr_def.label or attr_def.key
if label:
label_widget = QtWidgets.QLabel(label, self)
tooltip = attr_def.tooltip
@ -138,6 +201,8 @@ class CreatorAttrsWidget(QtWidgets.QWidget):
)
if not attr_def.is_label_horizontal:
row += 1
attr_def_info.label_widget = label_widget
_set_label_overriden(label_widget, is_overriden)
content_layout.addWidget(
widget, row, col_num, 1, expand_cols
@ -165,12 +230,19 @@ class CreatorAttrsWidget(QtWidgets.QWidget):
break
def _input_value_changed(self, value, attr_id):
instance_ids = self._attr_def_id_to_instances.get(attr_id)
attr_def = self._attr_def_id_to_attr_def.get(attr_id)
if not instance_ids or not attr_def:
attr_def_info = self._attr_def_info_by_id.get(attr_id)
if attr_def_info is None:
return
if attr_def_info.label_widget is not None:
defaults = attr_def_info.defaults
is_overriden = len(defaults) != 1 or value not in defaults
_set_label_overriden(attr_def_info.label_widget, is_overriden)
self._controller.set_instances_create_attr_values(
instance_ids, attr_def.key, value
attr_def_info.instance_ids,
attr_def_info.attr_def.key,
value
)
@ -223,9 +295,7 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget):
self._controller: AbstractPublisherFrontend = controller
self._scroll_area = scroll_area
self._attr_def_id_to_instances = {}
self._attr_def_id_to_attr_def = {}
self._attr_def_id_to_plugin_name = {}
self._attr_def_info_by_id: Dict[str, _PublishAttrDefInfo] = {}
# Store content of scroll area to prevent garbage collection
self._content_widget = None
@ -254,9 +324,7 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget):
self._content_widget = None
self._attr_def_id_to_instances = {}
self._attr_def_id_to_attr_def = {}
self._attr_def_id_to_plugin_name = {}
self._attr_def_info_by_id = {}
result = self._controller.get_publish_attribute_definitions(
self._current_instance_ids, self._context_selected
@ -275,9 +343,7 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget):
content_layout.addStretch(1)
row = 0
for plugin_name, attr_defs, all_plugin_values in result:
plugin_values = all_plugin_values[plugin_name]
for plugin_name, attr_defs, plugin_values in result:
for attr_def in attr_defs:
widget = create_widget_for_attr_def(
attr_def, content_widget
@ -290,6 +356,7 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget):
widget.setVisible(False)
visible_widget = False
label_widget = None
if visible_widget:
expand_cols = 2
if attr_def.is_value_def and attr_def.is_label_horizontal:
@ -324,35 +391,58 @@ class PublishPluginAttrsWidget(QtWidgets.QWidget):
widget.value_changed.connect(self._input_value_changed)
attr_values = plugin_values[attr_def.key]
multivalue = len(attr_values) > 1
instance_ids = []
values = []
instances = []
for instance, value in attr_values:
default_values = []
is_overriden = False
for (instance_id, value, default_value) in (
plugin_values.get(attr_def.key, [])
):
instance_ids.append(instance_id)
values.append(value)
instances.append(instance)
if not is_overriden and value != default_value:
is_overriden = True
# 'set' cannot be used for default values because they can
# be unhashable types, e.g. 'list'.
if default_value not in default_values:
default_values.append(default_value)
self._attr_def_id_to_attr_def[attr_def.id] = attr_def
self._attr_def_id_to_instances[attr_def.id] = instances
self._attr_def_id_to_plugin_name[attr_def.id] = plugin_name
multivalue = len(values) > 1
self._attr_def_info_by_id[attr_def.id] = _PublishAttrDefInfo(
attr_def,
plugin_name,
instance_ids,
default_values,
label_widget,
)
if multivalue:
widget.set_value(values, multivalue)
else:
widget.set_value(values[0])
if label_widget is not None:
_set_label_overriden(label_widget, is_overriden)
self._scroll_area.setWidget(content_widget)
self._content_widget = content_widget
def _input_value_changed(self, value, attr_id):
instance_ids = self._attr_def_id_to_instances.get(attr_id)
attr_def = self._attr_def_id_to_attr_def.get(attr_id)
plugin_name = self._attr_def_id_to_plugin_name.get(attr_id)
if not instance_ids or not attr_def or not plugin_name:
attr_def_info = self._attr_def_info_by_id.get(attr_id)
if attr_def_info is None:
return
if attr_def_info.label_widget is not None:
defaults = attr_def_info.defaults
is_overriden = len(defaults) != 1 or value not in defaults
_set_label_overriden(attr_def_info.label_widget, is_overriden)
self._controller.set_instances_publish_attr_values(
instance_ids, plugin_name, attr_def.key, value
attr_def_info.instance_ids,
attr_def_info.plugin_name,
attr_def_info.attr_def.key,
value
)
def _on_instance_attr_defs_change(self, event):