Merge pull request #2184 from pypeclub/feature/OP-1938_Project-roots-entity-for-settings

Settings: Dictionary based on project roots
This commit is contained in:
Jakub Trllo 2021-11-01 11:42:13 +01:00 committed by GitHub
commit c6b2788274
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 226 additions and 26 deletions

View file

@ -110,7 +110,10 @@ from .enum_entity import (
)
from .list_entity import ListEntity
from .dict_immutable_keys_entity import DictImmutableKeysEntity
from .dict_immutable_keys_entity import (
DictImmutableKeysEntity,
RootsDictEntity
)
from .dict_mutable_keys_entity import DictMutableKeysEntity
from .dict_conditional import (
DictConditionalEntity,
@ -169,6 +172,7 @@ __all__ = (
"ListEntity",
"DictImmutableKeysEntity",
"RootsDictEntity",
"DictMutableKeysEntity",

View file

@ -510,7 +510,7 @@ class BaseItemEntity(BaseEntity):
pass
@abstractmethod
def _item_initalization(self):
def _item_initialization(self):
"""Entity specific initialization process."""
pass
@ -920,7 +920,7 @@ class ItemEntity(BaseItemEntity):
_default_label_wrap["collapsed"]
)
self._item_initalization()
self._item_initialization()
def save(self):
"""Call save on root item."""

View file

@ -9,7 +9,7 @@ from .exceptions import (
class ColorEntity(InputEntity):
schema_types = ["color"]
def _item_initalization(self):
def _item_initialization(self):
self.valid_value_types = (list, )
self.value_on_not_set = [0, 0, 0, 255]
self.use_alpha = self.schema_data.get("use_alpha", True)

View file

@ -107,7 +107,7 @@ class DictConditionalEntity(ItemEntity):
for _key, _value in new_value.items():
self.non_gui_children[self.current_enum][_key].set(_value)
def _item_initalization(self):
def _item_initialization(self):
self._default_metadata = NOT_SET
self._studio_override_metadata = NOT_SET
self._project_override_metadata = NOT_SET

View file

@ -4,7 +4,8 @@ import collections
from .lib import (
WRAPPER_TYPES,
OverrideState,
NOT_SET
NOT_SET,
STRING_TYPE
)
from openpype.settings.constants import (
METADATA_KEYS,
@ -18,6 +19,7 @@ from . import (
GUIEntity
)
from .exceptions import (
DefaultsNotDefined,
SchemaDuplicatedKeys,
EntitySchemaError,
InvalidKeySymbols
@ -172,7 +174,7 @@ class DictImmutableKeysEntity(ItemEntity):
for child_obj in added_children:
self.gui_layout.append(child_obj)
def _item_initalization(self):
def _item_initialization(self):
self._default_metadata = NOT_SET
self._studio_override_metadata = NOT_SET
self._project_override_metadata = NOT_SET
@ -547,3 +549,178 @@ class DictImmutableKeysEntity(ItemEntity):
super(DictImmutableKeysEntity, self).reset_callbacks()
for child_entity in self.children:
child_entity.reset_callbacks()
class RootsDictEntity(DictImmutableKeysEntity):
"""Entity that adds ability to fill value for roots of current project.
Value schema is defined by `object_type`.
It is not possible to change override state (Studio values will always
contain studio overrides and same for project). That is because roots can
be totally different for each project.
"""
_origin_schema_data = None
schema_types = ["dict-roots"]
def _item_initialization(self):
origin_schema_data = self.schema_data
self.separate_items = origin_schema_data.get("separate_items", True)
object_type = origin_schema_data.get("object_type")
if isinstance(object_type, STRING_TYPE):
object_type = {"type": object_type}
self.object_type = object_type
if not self.is_group:
self.is_group = True
schema_data = copy.deepcopy(self.schema_data)
schema_data["children"] = []
self.schema_data = schema_data
self._origin_schema_data = origin_schema_data
self._default_value = NOT_SET
self._studio_value = NOT_SET
self._project_value = NOT_SET
super(RootsDictEntity, self)._item_initialization()
def schema_validations(self):
if self.object_type is None:
reason = (
"Missing children definitions for root values"
" ('object_type' not filled)."
)
raise EntitySchemaError(self, reason)
if not isinstance(self.object_type, dict):
reason = (
"Children definitions for root values must be dictionary"
" ('object_type' is \"{}\")."
).format(str(type(self.object_type)))
raise EntitySchemaError(self, reason)
super(RootsDictEntity, self).schema_validations()
def set_override_state(self, state, ignore_missing_defaults):
self.children = []
self.non_gui_children = {}
self.gui_layout = []
roots_entity = self.get_entity_from_path(
"project_anatomy/roots"
)
children = []
first = True
for key in roots_entity.keys():
if first:
first = False
elif self.separate_items:
children.append({"type": "separator"})
child = copy.deepcopy(self.object_type)
child["key"] = key
child["label"] = key
children.append(child)
schema_data = copy.deepcopy(self.schema_data)
schema_data["children"] = children
self._add_children(schema_data)
self._set_children_values(state)
super(RootsDictEntity, self).set_override_state(
state, True
)
if state == OverrideState.STUDIO:
self.add_to_studio_default()
elif state == OverrideState.PROJECT:
self.add_to_project_override()
def on_child_change(self, child_obj):
if self._override_state is OverrideState.STUDIO:
if not child_obj.has_studio_override:
self.add_to_studio_default()
elif self._override_state is OverrideState.PROJECT:
if not child_obj.has_project_override:
self.add_to_project_override()
return super(RootsDictEntity, self).on_child_change(child_obj)
def _set_children_values(self, state):
if state >= OverrideState.DEFAULTS:
default_value = self._default_value
if default_value is NOT_SET:
if state > OverrideState.DEFAULTS:
raise DefaultsNotDefined(self)
else:
default_value = {}
for key, child_obj in self.non_gui_children.items():
child_value = default_value.get(key, NOT_SET)
child_obj.update_default_value(child_value)
if state >= OverrideState.STUDIO:
value = self._studio_value
if value is NOT_SET:
value = {}
for key, child_obj in self.non_gui_children.items():
child_value = value.get(key, NOT_SET)
child_obj.update_studio_value(child_value)
if state >= OverrideState.PROJECT:
value = self._project_value
if value is NOT_SET:
value = {}
for key, child_obj in self.non_gui_children.items():
child_value = value.get(key, NOT_SET)
child_obj.update_project_value(child_value)
def _update_current_metadata(self):
"""Override this method as this entity should not have metadata."""
self._metadata_are_modified = False
self._current_metadata = {}
def update_default_value(self, value):
"""Update default values.
Not an api method, should be called by parent.
"""
value = self._check_update_value(value, "default")
value, _ = self._prepare_value(value)
self._default_value = value
self._default_metadata = {}
self.has_default_value = value is not NOT_SET
def update_studio_value(self, value):
"""Update studio override values.
Not an api method, should be called by parent.
"""
value = self._check_update_value(value, "studio override")
value, _ = self._prepare_value(value)
self._studio_value = value
self._studio_override_metadata = {}
self.had_studio_override = value is not NOT_SET
def update_project_value(self, value):
"""Update project override values.
Not an api method, should be called by parent.
"""
value = self._check_update_value(value, "project override")
value, _metadata = self._prepare_value(value)
self._project_value = value
self._project_override_metadata = {}
self.had_project_override = value is not NOT_SET

View file

@ -191,7 +191,7 @@ class DictMutableKeysEntity(EndpointEntity):
child_entity = self.children_by_key[key]
self.set_child_label(child_entity, label)
def _item_initalization(self):
def _item_initialization(self):
self._default_metadata = {}
self._studio_override_metadata = {}
self._project_override_metadata = {}

View file

@ -8,7 +8,7 @@ from .lib import (
class BaseEnumEntity(InputEntity):
def _item_initalization(self):
def _item_initialization(self):
self.multiselection = True
self.value_on_not_set = None
self.enum_items = None
@ -70,7 +70,7 @@ class BaseEnumEntity(InputEntity):
class EnumEntity(BaseEnumEntity):
schema_types = ["enum"]
def _item_initalization(self):
def _item_initialization(self):
self.multiselection = self.schema_data.get("multiselection", False)
self.enum_items = self.schema_data.get("enum_items")
# Default is optional and non breaking attribute
@ -157,7 +157,7 @@ class HostsEnumEntity(BaseEnumEntity):
"standalonepublisher"
]
def _item_initalization(self):
def _item_initialization(self):
self.multiselection = self.schema_data.get("multiselection", True)
use_empty_value = False
if not self.multiselection:
@ -250,7 +250,7 @@ class HostsEnumEntity(BaseEnumEntity):
class AppsEnumEntity(BaseEnumEntity):
schema_types = ["apps-enum"]
def _item_initalization(self):
def _item_initialization(self):
self.multiselection = True
self.value_on_not_set = []
self.enum_items = []
@ -317,7 +317,7 @@ class AppsEnumEntity(BaseEnumEntity):
class ToolsEnumEntity(BaseEnumEntity):
schema_types = ["tools-enum"]
def _item_initalization(self):
def _item_initialization(self):
self.multiselection = True
self.value_on_not_set = []
self.enum_items = []
@ -376,7 +376,7 @@ class ToolsEnumEntity(BaseEnumEntity):
class TaskTypeEnumEntity(BaseEnumEntity):
schema_types = ["task-types-enum"]
def _item_initalization(self):
def _item_initialization(self):
self.multiselection = self.schema_data.get("multiselection", True)
if self.multiselection:
self.valid_value_types = (list, )
@ -452,7 +452,7 @@ class TaskTypeEnumEntity(BaseEnumEntity):
class DeadlineUrlEnumEntity(BaseEnumEntity):
schema_types = ["deadline_url-enum"]
def _item_initalization(self):
def _item_initialization(self):
self.multiselection = self.schema_data.get("multiselection", True)
self.enum_items = []
@ -503,7 +503,7 @@ class DeadlineUrlEnumEntity(BaseEnumEntity):
class AnatomyTemplatesEnumEntity(BaseEnumEntity):
schema_types = ["anatomy-templates-enum"]
def _item_initalization(self):
def _item_initialization(self):
self.multiselection = False
self.enum_items = []

View file

@ -362,7 +362,7 @@ class NumberEntity(InputEntity):
float_number_regex = re.compile(r"^\d+\.\d+$")
int_number_regex = re.compile(r"^\d+$")
def _item_initalization(self):
def _item_initialization(self):
self.minimum = self.schema_data.get("minimum", -99999)
self.maximum = self.schema_data.get("maximum", 99999)
self.decimal = self.schema_data.get("decimal", 0)
@ -420,7 +420,7 @@ class NumberEntity(InputEntity):
class BoolEntity(InputEntity):
schema_types = ["boolean"]
def _item_initalization(self):
def _item_initialization(self):
self.valid_value_types = (bool, )
value_on_not_set = self.convert_to_valid_type(
self.schema_data.get("default", True)
@ -431,7 +431,7 @@ class BoolEntity(InputEntity):
class TextEntity(InputEntity):
schema_types = ["text"]
def _item_initalization(self):
def _item_initialization(self):
self.valid_value_types = (STRING_TYPE, )
self.value_on_not_set = ""
@ -449,7 +449,7 @@ class TextEntity(InputEntity):
class PathInput(InputEntity):
schema_types = ["path-input"]
def _item_initalization(self):
def _item_initialization(self):
self.valid_value_types = (STRING_TYPE, )
self.value_on_not_set = ""
@ -460,7 +460,7 @@ class PathInput(InputEntity):
class RawJsonEntity(InputEntity):
schema_types = ["raw-json"]
def _item_initalization(self):
def _item_initialization(self):
# Schema must define if valid value is dict or list
store_as_string = self.schema_data.get("store_as_string", False)
is_list = self.schema_data.get("is_list", False)

View file

@ -48,7 +48,7 @@ class PathEntity(ItemEntity):
raise AttributeError(self.attribute_error_msg.format("items"))
return self.child_obj.items()
def _item_initalization(self):
def _item_initialization(self):
if self.group_item is None and not self.is_group:
self.is_group = True
@ -216,7 +216,7 @@ class ListStrictEntity(ItemEntity):
return self.children[idx]
return default
def _item_initalization(self):
def _item_initialization(self):
self.valid_value_types = (list, )
self.require_key = True

View file

@ -149,7 +149,7 @@ class ListEntity(EndpointEntity):
return list(value)
return NOT_SET
def _item_initalization(self):
def _item_initialization(self):
self.valid_value_types = (list, )
self.children = []
self.value_on_not_set = []

View file

@ -65,7 +65,7 @@ class RootEntity(BaseItemEntity):
super(RootEntity, self).__init__(schema_data)
self._require_restart_callbacks = []
self._item_ids_require_restart = set()
self._item_initalization()
self._item_initialization()
if reset:
self.reset()
@ -176,7 +176,7 @@ class RootEntity(BaseItemEntity):
for child_obj in added_children:
self.gui_layout.append(child_obj)
def _item_initalization(self):
def _item_initialization(self):
# Store `self` to `root_item` for children entities
self.root_item = self

View file

@ -208,6 +208,25 @@
}
```
## dict-roots
- entity can be used only in Project settings
- keys of dictionary are based on current project roots
- they are not updated "live" it is required to save root changes and then
modify values on this entity
# TODO do live updates
```
{
"type": "dict-roots",
"key": "roots",
"label": "Roots",
"object_type": {
"type": "path",
"multiplatform": true,
"multipath": false
}
}
```
## dict-conditional
- is similar to `dict` but has always available one enum entity
- the enum entity has single selection and it's value define other children entities