diff --git a/pype/settings/entities/__init__.py b/pype/settings/entities/__init__.py index 1737a6f2ff..f4fbe4569d 100644 --- a/pype/settings/entities/__init__.py +++ b/pype/settings/entities/__init__.py @@ -7,19 +7,20 @@ from . import ( ) from .lib import NOT_SET from .base_entity import ( - SystemRootEntity, + BaseEntity, + SystemRootEntity ) from .item_entities import ( + ItemEntity, GUIEntity, - DictImmutableKeysEntity, DictMutableKeysEntity, - ListEntity, PathEntity, ListStrictEntity ) from .input_entities import ( + InputEntity, NumberEntity, BoolEntity, EnumEntity, @@ -28,6 +29,8 @@ from .input_entities import ( RawJsonEntity ) +from .list_entity import ListEntity +from .dict_immutable_keys_entity import DictImmutableKeysEntity __all__ = ( "constants", @@ -38,19 +41,24 @@ __all__ = ( "NOT_SET", + "BaseEntity", "SystemRootEntity", + "ItemEntity", "GUIEntity", - "DictImmutableKeysEntity", "DictMutableKeysEntity", - "ListEntity", "PathEntity", "ListStrictEntity", + "InputEntity", "NumberEntity", "BoolEntity", "EnumEntity", "TextEntity", "PathInput", - "RawJsonEntity" + "RawJsonEntity", + + "ListEntity", + + "DictImmutableKeysEntity" ) diff --git a/pype/settings/entities/base_entity.py b/pype/settings/entities/base_entity.py index 09db34a69e..95767f464f 100644 --- a/pype/settings/entities/base_entity.py +++ b/pype/settings/entities/base_entity.py @@ -454,38 +454,35 @@ class RootEntity(BaseEntity): def create_schema_object(self, schema_data, *args, **kwargs): if self._loaded_types is None: - from . import item_entities - from . import input_entities + from pype.settings import entities - sources = [item_entities, input_entities] known_abstract_classes = ( BaseEntity, - item_entities.ItemEntity, - input_entities.InputEntity + entities.ItemEntity, + entities.InputEntity ) self._loaded_types = {} self._gui_types = [] - for source in sources: - for attr in dir(source): - item = getattr(source, attr) - if not inspect.isclass(item): + for attr in dir(entities): + item = getattr(entities, attr) + if not inspect.isclass(item): + continue + + if not issubclass(item, BaseEntity): + continue + + if inspect.isabstract(item): + if item in known_abstract_classes: continue + item() - if not issubclass(item, BaseEntity): - continue + for schema_type in item.schema_types: + self._loaded_types[schema_type] = item - if inspect.isabstract(item): - if item in known_abstract_classes: - continue - item() - - for schema_type in item.schema_types: - self._loaded_types[schema_type] = item - - gui_type = getattr(item, "gui_type", False) - if gui_type: - self._gui_types.append(item) + gui_type = getattr(item, "gui_type", False) + if gui_type: + self._gui_types.append(item) klass = self._loaded_types.get(schema_data["type"]) if not klass: diff --git a/pype/settings/entities/dict_immutable_keys_entity.py b/pype/settings/entities/dict_immutable_keys_entity.py new file mode 100644 index 0000000000..c3d42fcc42 --- /dev/null +++ b/pype/settings/entities/dict_immutable_keys_entity.py @@ -0,0 +1,396 @@ +import copy + +from .lib import NOT_SET +from .constants import ( + OverrideState, + WRAPPER_TYPES, + METADATA_KEYS, + M_OVERRIDEN_KEY +) +from . import ( + BaseEntity, + ItemEntity, + BoolEntity, + GUIEntity +) + + +class DictImmutableKeysEntity(ItemEntity): + schema_types = ["dict"] + + def __getitem__(self, key): + return self.non_gui_children[key] + + def __setitem__(self, key, value): + child_obj = self.non_gui_children[key] + child_obj.set_value(value) + + def __iter__(self): + for key in self.keys(): + yield key + + def get(self, key, default=None): + return self.non_gui_children.get(key, default) + + def keys(self): + return self.non_gui_children.keys() + + def values(self): + return self.non_gui_children.values() + + def items(self): + return self.non_gui_children.items() + + def on_value_change(self): + raise NotImplementedError( + "{} - on_value_change".format(self.__class__.__name__) + ) + + def schema_validations(self): + if self.checkbox_key: + checkbox_child = self.non_gui_children.get(self.checkbox_key) + if not checkbox_child: + raise ValueError( + "{}: Checkbox children \"{}\" was not found.".format( + self.path, self.checkbox_key + ) + ) + if not isinstance(checkbox_child, BoolEntity): + raise TypeError(( + "{}: Checkbox children \"{}\" is not `boolean` type." + ).format(self.path, self.checkbox_key)) + + super(DictImmutableKeysEntity, self).schema_validations() + for child_obj in self.children: + child_obj.schema_validations() + + def on_change(self): + self.update_current_metadata() + for callback in self.on_change_callbacks: + callback() + self.parent.on_child_change(self) + + def on_child_change(self, _child_obj): + self.on_change() + + def _add_children(self, schema_data, first=True): + added_children = [] + for children_schema in schema_data["children"]: + if children_schema["type"] in WRAPPER_TYPES: + _children_schema = copy.deepcopy(children_schema) + wrapper_children = self._add_children( + children_schema + ) + _children_schema["children"] = wrapper_children + added_children.append(_children_schema) + continue + + child_obj = self.create_schema_object(children_schema, self) + self.children.append(child_obj) + added_children.append(child_obj) + if isinstance(child_obj, GUIEntity): + continue + + if child_obj.key in self.non_gui_children: + raise KeyError("Duplicated key \"{}\"".format(child_obj.key)) + self.non_gui_children[child_obj.key] = child_obj + + if not first: + return added_children + + for child_obj in added_children: + if isinstance(child_obj, BaseEntity): + continue + self.gui_wrappers.append(child_obj) + + def item_initalization(self): + self.default_metadata = NOT_SET + self.studio_override_metadata = NOT_SET + self.project_override_metadata = NOT_SET + + # `current_metadata` are still when schema is loaded + # - only metadata stored with dict item are gorup overrides in + # M_OVERRIDEN_KEY + self.current_metadata = {} + self.metadata_are_modified = False + + # Children are stored by key as keys are immutable and are defined by + # schema + self.valid_value_types = (dict, ) + self.children = [] + self.non_gui_children = {} + self.gui_wrappers = [] + self._add_children(self.schema_data) + + if self.is_dynamic_item: + self.require_key = False + + # GUI attributes + self.checkbox_key = self.schema_data.get("checkbox_key") + self.highlight_content = self.schema_data.get( + "highlight_content", False + ) + self.show_borders = self.schema_data.get("show_borders", True) + self.collapsible = self.schema_data.get("collapsable", True) + self.collapsed = self.schema_data.get("collapsed", True) + + # Not yet implemented + self.use_label_wrap = self.schema_data.get("use_label_wrap") or True + + def get_child_path(self, child_obj): + result_key = None + for key, _child_obj in self.non_gui_children.items(): + if _child_obj is child_obj: + result_key = key + break + + if result_key is None: + raise ValueError("Didn't found child {}".format(child_obj)) + + return "/".join([self.path, result_key]) + + def set_value(self, value): + for _key, _value in value.items(): + self.non_gui_children[_key].set_value(_value) + + def update_current_metadata(self): + # Define if current metadata are + metadata = NOT_SET + if self.override_state is OverrideState.PROJECT: + # metadata are NOT_SET if project overrides do not override this + # item + metadata = self.project_override_metadata + + if self.override_state is OverrideState.STUDIO or metadata is NOT_SET: + metadata = self.studio_override_metadata + + current_metadata = {} + for key, child_obj in self.non_gui_children.items(): + if not child_obj.is_group: + continue + + if ( + self.override_state is OverrideState.STUDIO + and not child_obj.has_studio_override + ): + continue + + if ( + self.override_state is OverrideState.PROJECT + and not child_obj.has_project_override + ): + continue + + if M_OVERRIDEN_KEY not in current_metadata: + current_metadata[M_OVERRIDEN_KEY] = [] + current_metadata[M_OVERRIDEN_KEY].append(key) + + if metadata is NOT_SET and not current_metadata: + self.metadata_are_modified = False + else: + self.metadata_are_modified = current_metadata != metadata + self.current_metadata = current_metadata + + def set_override_state(self, state): + # Change has/had override states + self.override_state = state + if state is OverrideState.NOT_DEFINED: + pass + + elif state is OverrideState.DEFAULTS: + self.has_default_value = self.default_value is not NOT_SET + + elif state is OverrideState.STUDIO: + if self.studio_override_metadata is NOT_SET: + self.had_studio_override = False + self._has_studio_override = self.had_studio_override + + elif state is OverrideState.PROJECT: + if self.project_override_metadata is NOT_SET: + self.had_project_override = False + self._has_project_override = self.had_project_override + + for child_obj in self.non_gui_children.values(): + child_obj.set_override_state(state) + + self.update_current_metadata() + + @property + def value(self): + output = {} + for key, child_obj in self.non_gui_children.items(): + output[key] = child_obj.value + return output + + @property + def has_unsaved_changes(self): + if self.metadata_are_modified: + return True + + if ( + self.override_state is OverrideState.PROJECT + and self._has_project_override != self.had_project_override + ): + return True + + elif ( + self.override_state is OverrideState.STUDIO + and self._has_studio_override != self.had_studio_override + ): + return True + + return self.child_is_modified + + @property + def child_is_modified(self): + for child_obj in self.non_gui_children.values(): + if child_obj.has_unsaved_changes: + return True + return False + + @property + def child_has_studio_override(self): + if self.override_state >= OverrideState.STUDIO: + for child_obj in self.non_gui_children.values(): + if child_obj.child_has_studio_override: + return True + return False + + @property + def child_has_project_override(self): + if self.override_state >= OverrideState.PROJECT: + for child_obj in self.non_gui_children.values(): + if child_obj.child_has_studio_override: + return True + return False + + def settings_value(self): + if self.override_state is OverrideState.NOT_DEFINED: + return NOT_SET + + if self.is_group: + if self.override_state is OverrideState.STUDIO: + if not self._has_studio_override: + return NOT_SET + elif self.override_state is OverrideState.PROJECT: + if not self._has_project_override: + return NOT_SET + + output = {} + for key, child_obj in self.non_gui_children.items(): + value = child_obj.settings_value() + if value is not NOT_SET: + output[key] = value + + if self.override_state is OverrideState.DEFAULTS: + return output + + if not output: + return NOT_SET + + output.update(self.current_metadata) + return output + + def _prepare_value(self, value): + if value is NOT_SET: + return NOT_SET, NOT_SET + + metadata = {} + for key in METADATA_KEYS: + if key in value: + metadata[key] = value.pop(key) + return value, metadata + + def update_default_value(self, value): + self.has_default_value = value is not NOT_SET + # TODO add value validation + value, metadata = self._prepare_value(value) + self.default_metadata = metadata + + if value is NOT_SET: + for child_obj in self.non_gui_children.values(): + child_obj.update_default_value(value) + return + + for _key, _value in value.items(): + child_obj = self.non_gui_children.get(_key) + if child_obj: + child_obj.update_default_value(_value) + else: + # TODO store that has unsaved changes if is group item or + # is inside group item + self.log.warning( + "Unknown key in default values \"{}\"".format(_key) + ) + + def update_studio_values(self, value): + value, metadata = self._prepare_value(value) + self.studio_override_metadata = metadata + + if value is NOT_SET: + for child_obj in self.non_gui_children.values(): + child_obj.update_studio_values(value) + return + + for _key, _value in value.items(): + child_obj = self.non_gui_children.get(_key) + if child_obj: + child_obj.update_studio_values(_value) + else: + # TODO store that has unsaved changes if is group item or + # is inside group item + self.log.warning( + "Unknown key in studio overrides \"{}\"".format(_key) + ) + + def update_project_values(self, value): + value, metadata = self._prepare_value(value) + self.project_override_metadata = metadata + + if value is NOT_SET: + for child_obj in self.non_gui_children.values(): + child_obj.update_project_values(value) + return + + for _key, _value in value.items(): + child_obj = self.non_gui_children.get(_key) + if child_obj: + child_obj.update_project_values(_value) + else: + # TODO store that has unsaved changes if is group item or + # is inside group item + self.log.warning( + "Unknown key in project overrides \"{}\"".format(_key) + ) + + def discard_changes(self): + for child_obj in self.non_gui_children.values(): + child_obj.discard_changes() + + def set_studio_default(self): + if self.override_state is not OverrideState.STUDIO: + return + + for child_obj in self.non_gui_children.values(): + child_obj.set_studio_default() + + def reset_to_pype_default(self): + if self.override_state is not OverrideState.STUDIO: + return + + for child_obj in self.non_gui_children.values(): + child_obj.reset_to_pype_default() + + def remove_overrides(self): + if self.override_state is not OverrideState.PROJECT: + return + + for child_obj in self.non_gui_children.values(): + child_obj.remove_overrides() + + def set_as_overriden(self): + if self.override_state is not OverrideState.PROJECT: + return + + for child_obj in self.non_gui_children.values(): + child_obj.set_as_overriden() diff --git a/pype/settings/entities/item_entities.py b/pype/settings/entities/item_entities.py index 0c3846fde2..696c18f2a1 100644 --- a/pype/settings/entities/item_entities.py +++ b/pype/settings/entities/item_entities.py @@ -7,9 +7,7 @@ from .lib import ( ) from .constants import ( OverrideState, - WRAPPER_TYPES, METADATA_KEYS, - M_OVERRIDEN_KEY, M_ENVIRONMENT_KEY, M_DYNAMIC_KEY_LABEL ) @@ -216,388 +214,6 @@ class GUIEntity(ItemEntity): self.require_key = False -class DictImmutableKeysEntity(ItemEntity): - schema_types = ["dict"] - - def __getitem__(self, key): - return self.non_gui_children[key] - - def __setitem__(self, key, value): - child_obj = self.non_gui_children[key] - child_obj.set_value(value) - - def __iter__(self): - for key in self.keys(): - yield key - - def get(self, key, default=None): - return self.non_gui_children.get(key, default) - - def keys(self): - return self.non_gui_children.keys() - - def values(self): - return self.non_gui_children.values() - - def items(self): - return self.non_gui_children.items() - - def on_value_change(self): - raise NotImplementedError( - "{} - on_value_change".format(self.__class__.__name__) - ) - - def schema_validations(self): - if self.checkbox_key: - checkbox_child = self.non_gui_children.get(self.checkbox_key) - if not checkbox_child: - raise ValueError( - "{}: Checkbox children \"{}\" was not found.".format( - self.path, self.checkbox_key - ) - ) - from .input_entities import BoolEntity - if not isinstance(checkbox_child, BoolEntity): - raise TypeError(( - "{}: Checkbox children \"{}\" is not `boolean` type." - ).format(self.path, self.checkbox_key)) - - super(DictImmutableKeysEntity, self).schema_validations() - for child_obj in self.children: - child_obj.schema_validations() - - def on_change(self): - self.update_current_metadata() - for callback in self.on_change_callbacks: - callback() - self.parent.on_child_change(self) - - def on_child_change(self, _child_obj): - self.on_change() - - def _add_children(self, schema_data, first=True): - added_children = [] - for children_schema in schema_data["children"]: - if children_schema["type"] in WRAPPER_TYPES: - _children_schema = copy.deepcopy(children_schema) - wrapper_children = self._add_children( - children_schema - ) - _children_schema["children"] = wrapper_children - added_children.append(_children_schema) - continue - - child_obj = self.create_schema_object(children_schema, self) - self.children.append(child_obj) - added_children.append(child_obj) - if isinstance(child_obj, GUIEntity): - continue - - if child_obj.key in self.non_gui_children: - raise KeyError("Duplicated key \"{}\"".format(child_obj.key)) - self.non_gui_children[child_obj.key] = child_obj - - if not first: - return added_children - - for child_obj in added_children: - if isinstance(child_obj, BaseEntity): - continue - self.gui_wrappers.append(child_obj) - - def item_initalization(self): - self.default_metadata = NOT_SET - self.studio_override_metadata = NOT_SET - self.project_override_metadata = NOT_SET - - # `current_metadata` are still when schema is loaded - # - only metadata stored with dict item are gorup overrides in - # M_OVERRIDEN_KEY - self.current_metadata = {} - self.metadata_are_modified = False - - # Children are stored by key as keys are immutable and are defined by - # schema - self.valid_value_types = (dict, ) - self.children = [] - self.non_gui_children = {} - self.gui_wrappers = [] - self._add_children(self.schema_data) - - if self.is_dynamic_item: - self.require_key = False - - # GUI attributes - self.checkbox_key = self.schema_data.get("checkbox_key") - self.highlight_content = self.schema_data.get( - "highlight_content", False - ) - self.show_borders = self.schema_data.get("show_borders", True) - self.collapsible = self.schema_data.get("collapsable", True) - self.collapsed = self.schema_data.get("collapsed", True) - - # Not yet implemented - self.use_label_wrap = self.schema_data.get("use_label_wrap") or True - - def get_child_path(self, child_obj): - result_key = None - for key, _child_obj in self.non_gui_children.items(): - if _child_obj is child_obj: - result_key = key - break - - if result_key is None: - raise ValueError("Didn't found child {}".format(child_obj)) - - return "/".join([self.path, result_key]) - - def set_value(self, value): - for _key, _value in value.items(): - self.non_gui_children[_key].set_value(_value) - - def update_current_metadata(self): - # Define if current metadata are - metadata = NOT_SET - if self.override_state is OverrideState.PROJECT: - # metadata are NOT_SET if project overrides do not override this - # item - metadata = self.project_override_metadata - - if self.override_state is OverrideState.STUDIO or metadata is NOT_SET: - metadata = self.studio_override_metadata - - current_metadata = {} - for key, child_obj in self.non_gui_children.items(): - if not child_obj.is_group: - continue - - if ( - self.override_state is OverrideState.STUDIO - and not child_obj.has_studio_override - ): - continue - - if ( - self.override_state is OverrideState.PROJECT - and not child_obj.has_project_override - ): - continue - - if M_OVERRIDEN_KEY not in current_metadata: - current_metadata[M_OVERRIDEN_KEY] = [] - current_metadata[M_OVERRIDEN_KEY].append(key) - - if metadata is NOT_SET and not current_metadata: - self.metadata_are_modified = False - else: - self.metadata_are_modified = current_metadata != metadata - self.current_metadata = current_metadata - - def set_override_state(self, state): - # Change has/had override states - self.override_state = state - if state is OverrideState.NOT_DEFINED: - pass - - elif state is OverrideState.DEFAULTS: - self.has_default_value = self.default_value is not NOT_SET - - elif state is OverrideState.STUDIO: - if self.studio_override_metadata is NOT_SET: - self.had_studio_override = False - self._has_studio_override = self.had_studio_override - - elif state is OverrideState.PROJECT: - if self.project_override_metadata is NOT_SET: - self.had_project_override = False - self._has_project_override = self.had_project_override - - for child_obj in self.non_gui_children.values(): - child_obj.set_override_state(state) - - self.update_current_metadata() - - @property - def value(self): - output = {} - for key, child_obj in self.non_gui_children.items(): - output[key] = child_obj.value - return output - - @property - def has_unsaved_changes(self): - if self.metadata_are_modified: - return True - - if ( - self.override_state is OverrideState.PROJECT - and self._has_project_override != self.had_project_override - ): - return True - - elif ( - self.override_state is OverrideState.STUDIO - and self._has_studio_override != self.had_studio_override - ): - return True - - return self.child_is_modified - - @property - def child_is_modified(self): - for child_obj in self.non_gui_children.values(): - if child_obj.has_unsaved_changes: - return True - return False - - @property - def child_has_studio_override(self): - if self.override_state >= OverrideState.STUDIO: - for child_obj in self.non_gui_children.values(): - if child_obj.child_has_studio_override: - return True - return False - - @property - def child_has_project_override(self): - if self.override_state >= OverrideState.PROJECT: - for child_obj in self.non_gui_children.values(): - if child_obj.child_has_studio_override: - return True - return False - - def settings_value(self): - if self.override_state is OverrideState.NOT_DEFINED: - return NOT_SET - - if self.is_group: - if self.override_state is OverrideState.STUDIO: - if not self._has_studio_override: - return NOT_SET - elif self.override_state is OverrideState.PROJECT: - if not self._has_project_override: - return NOT_SET - - output = {} - for key, child_obj in self.non_gui_children.items(): - value = child_obj.settings_value() - if value is not NOT_SET: - output[key] = value - - if self.override_state is OverrideState.DEFAULTS: - return output - - if not output: - return NOT_SET - - output.update(self.current_metadata) - return output - - def _prepare_value(self, value): - if value is NOT_SET: - return NOT_SET, NOT_SET - - metadata = {} - for key in METADATA_KEYS: - if key in value: - metadata[key] = value.pop(key) - return value, metadata - - def update_default_value(self, value): - self.has_default_value = value is not NOT_SET - # TODO add value validation - value, metadata = self._prepare_value(value) - self.default_metadata = metadata - - if value is NOT_SET: - for child_obj in self.non_gui_children.values(): - child_obj.update_default_value(value) - return - - for _key, _value in value.items(): - child_obj = self.non_gui_children.get(_key) - if child_obj: - child_obj.update_default_value(_value) - else: - # TODO store that has unsaved changes if is group item or - # is inside group item - self.log.warning( - "Unknown key in default values \"{}\"".format(_key) - ) - - def update_studio_values(self, value): - value, metadata = self._prepare_value(value) - self.studio_override_metadata = metadata - - if value is NOT_SET: - for child_obj in self.non_gui_children.values(): - child_obj.update_studio_values(value) - return - - for _key, _value in value.items(): - child_obj = self.non_gui_children.get(_key) - if child_obj: - child_obj.update_studio_values(_value) - else: - # TODO store that has unsaved changes if is group item or - # is inside group item - self.log.warning( - "Unknown key in studio overrides \"{}\"".format(_key) - ) - - def update_project_values(self, value): - value, metadata = self._prepare_value(value) - self.project_override_metadata = metadata - - if value is NOT_SET: - for child_obj in self.non_gui_children.values(): - child_obj.update_project_values(value) - return - - for _key, _value in value.items(): - child_obj = self.non_gui_children.get(_key) - if child_obj: - child_obj.update_project_values(_value) - else: - # TODO store that has unsaved changes if is group item or - # is inside group item - self.log.warning( - "Unknown key in project overrides \"{}\"".format(_key) - ) - - def discard_changes(self): - for child_obj in self.non_gui_children.values(): - child_obj.discard_changes() - - def set_studio_default(self): - if self.override_state is not OverrideState.STUDIO: - return - - for child_obj in self.non_gui_children.values(): - child_obj.set_studio_default() - - def reset_to_pype_default(self): - if self.override_state is not OverrideState.STUDIO: - return - - for child_obj in self.non_gui_children.values(): - child_obj.reset_to_pype_default() - - def remove_overrides(self): - if self.override_state is not OverrideState.PROJECT: - return - - for child_obj in self.non_gui_children.values(): - child_obj.remove_overrides() - - def set_as_overriden(self): - if self.override_state is not OverrideState.PROJECT: - return - - for child_obj in self.non_gui_children.values(): - child_obj.set_as_overriden() - - class DictMutableKeysEntity(ItemEntity): schema_types = ["dict-modifiable"] _miss_arg = object() @@ -900,9 +516,6 @@ class DictMutableKeysEntity(ItemEntity): pass return False - def discard_changes(self): - pass - def settings_value(self): if self.override_state is OverrideState.NOT_DEFINED: return NOT_SET @@ -920,6 +533,9 @@ class DictMutableKeysEntity(ItemEntity): output.update(copy.deepcopy(self.current_metadata)) return output + def discard_changes(self): + pass + def remove_overrides(self): pass @@ -957,233 +573,6 @@ class DictMutableKeysEntity(ItemEntity): self.project_override_metadata = metadata -class ListEntity(ItemEntity): - schema_types = ["list"] - - def __iter__(self): - for item in self.children: - yield item - - def append(self, item): - child_obj = self.add_new_item() - child_obj.set_value(item) - self.on_change() - - def extend(self, items): - for item in items: - self.append(item) - - def clear(self): - self.children.clear() - self.on_change() - - def pop(self, idx): - self.children.pop(idx) - self.on_change() - - def remove(self, item): - for idx, child_obj in enumerate(self.children): - if child_obj.value == item: - self.pop(idx) - return - raise ValueError("ListEntity.remove(x): x not in ListEntity") - - def insert(self, idx, item): - child_obj = self.add_new_item(idx) - child_obj.set_value(item) - self.on_change() - - def add_new_item(self, idx=None): - child_obj = self.create_schema_object(self.item_schema, self, True) - child_obj.set_override_state(self.override_state) - if idx is None: - self.children.append(child_obj) - else: - self.children.insert(idx, child_obj) - return child_obj - - def item_initalization(self): - self.valid_value_types = (list, ) - self.children = [] - - item_schema = self.schema_data["object_type"] - if not isinstance(item_schema, dict): - item_schema = {"type": item_schema} - self.item_schema = item_schema - - if not self.group_item: - self.is_group = True - - # GUI attributes - self.use_label_wrap = self.schema_data.get("use_label_wrap") or False - # Used only if `use_label_wrap` is set to True - self.collapsible = self.schema_data.get("collapsible") or True - self.collapsed = self.schema_data.get("collapsed") or False - - def schema_validations(self): - super(ListEntity, self).schema_validations() - - if self.is_dynamic_item and self.use_label_wrap: - raise ValueError( - "`ListWidget` can't have set `use_label_wrap` to True and" - " be used as widget at the same time." - ) - - if self.use_label_wrap and not self.label: - raise ValueError( - "`ListWidget` can't have set `use_label_wrap` to True and" - " not have set \"label\" key at the same time." - ) - - for child_obj in self.children: - child_obj.schema_validations() - - def get_child_path(self, child_obj): - result_idx = None - for idx, _child_obj in enumerate(self.children): - if _child_obj is child_obj: - result_idx = idx - break - - if result_idx is None: - raise ValueError("Didn't found child {}".format(child_obj)) - - return "/".join([self.path, str(result_idx)]) - - def set_value(self, value): - pass - - def on_change(self): - pass - - def on_child_change(self, child_obj): - print("{} - on_child_change".format(self.__class__.__name__)) - - def on_value_change(self): - raise NotImplementedError(self.__class__.__name__) - - def set_override_state(self, state): - self.override_state = state - if ( - not self.has_default_value - and state in (OverrideState.STUDIO, OverrideState.PROJECT) - ): - raise DefaultsNotDefined(self) - - self._set_value() - - def _set_value(self, value=NOT_SET): - while self.children: - self.children.pop(0) - - if self.override_state is OverrideState.NOT_DEFINED: - return - - if value is NOT_SET: - if self.override_state is OverrideState.PROJECT: - if self.had_project_override: - value = self.project_override_value - elif self.had_studio_override: - value = self.studio_override_value - else: - value = self.default_value - - elif self.override_state is OverrideState.STUDIO: - if self.had_studio_override: - value = self.studio_override_value - else: - value = self.default_value - - elif self.override_state is OverrideState.DEFAULTS: - value = self.default_value - - if value is NOT_SET: - value = self.value_on_not_set - - for item in value: - child_obj = self.create_schema_object(self.item_schema, self, True) - self.children.append(child_obj) - child_obj.update_default_value(item) - if self.override_state is OverrideState.STUDIO: - if self.had_studio_override: - child_obj.update_studio_values(item) - - elif self.override_state is OverrideState.PROJECT: - if self.had_project_override: - child_obj.update_project_values(item) - - for child_obj in self.children: - child_obj.set_override_state(self.override_state) - - @property - def value(self): - output = [] - for child_obj in self.children: - output.append(child_obj.value) - return output - - @property - def child_has_studio_override(self): - pass - - @property - def has_unsaved_changes(self): - pass - - @property - def child_is_modified(self): - pass - - @property - def child_has_project_override(self): - if self.override_state is OverrideState.PROJECT: - # TODO implement - pass - return False - - def discard_changes(self): - pass - - def settings_value(self): - if self.override_state is OverrideState.NOT_DEFINED: - return NOT_SET - - if self.is_group: - if self.override_state is OverrideState.STUDIO: - if not self._has_studio_override: - return NOT_SET - elif self.override_state is OverrideState.PROJECT: - if not self._has_project_override: - return NOT_SET - - output = [] - for child_obj in self.children: - output.append(child_obj.settings_value()) - return output - - def remove_overrides(self): - pass - - def reset_to_pype_default(self): - pass - - def set_as_overriden(self): - pass - - def set_studio_default(self): - pass - - def update_default_value(self, value): - self.has_default_value = value is not NOT_SET - self.default_value = value - - def update_studio_values(self, value): - self.studio_override_value = value - - def update_project_values(self, value): - self.project_override_value = value - - class PathEntity(ItemEntity): schema_types = ["path-widget"] platforms = ("windows", "darwin", "linux") diff --git a/pype/settings/entities/list_entity.py b/pype/settings/entities/list_entity.py new file mode 100644 index 0000000000..c6541a6389 --- /dev/null +++ b/pype/settings/entities/list_entity.py @@ -0,0 +1,264 @@ +from .item_entities import ItemEntity +from .constants import OverrideState +from .lib import ( + NOT_SET, + DefaultsNotDefined +) + + +class ListEntity(ItemEntity): + schema_types = ["list"] + + def __iter__(self): + for item in self.children: + yield item + + def append(self, item): + child_obj = self.add_new_item() + child_obj.set_value(item) + self.on_change() + + def extend(self, items): + for item in items: + self.append(item) + + def clear(self): + self.children.clear() + self.on_change() + + def pop(self, idx): + item = self.children.pop(idx) + self.on_change() + return item + + def remove(self, item): + for idx, child_obj in enumerate(self.children): + if child_obj.value == item: + self.pop(idx) + return + raise ValueError("ListEntity.remove(x): x not in ListEntity") + + def insert(self, idx, item): + child_obj = self.add_new_item(idx) + child_obj.set_value(item) + self.on_change() + + def add_new_item(self, idx=None): + child_obj = self.create_schema_object(self.item_schema, self, True) + child_obj.set_override_state(self.override_state) + if idx is None: + self.children.append(child_obj) + else: + self.children.insert(idx, child_obj) + return child_obj + + def item_initalization(self): + self.valid_value_types = (list, ) + self.children = [] + + item_schema = self.schema_data["object_type"] + if not isinstance(item_schema, dict): + item_schema = {"type": item_schema} + self.item_schema = item_schema + + if not self.group_item: + self.is_group = True + + # GUI attributes + self.use_label_wrap = self.schema_data.get("use_label_wrap") or False + # Used only if `use_label_wrap` is set to True + self.collapsible = self.schema_data.get("collapsible") or True + self.collapsed = self.schema_data.get("collapsed") or False + + def schema_validations(self): + super(ListEntity, self).schema_validations() + + if self.is_dynamic_item and self.use_label_wrap: + raise ValueError( + "`ListWidget` can't have set `use_label_wrap` to True and" + " be used as widget at the same time." + ) + + if self.use_label_wrap and not self.label: + raise ValueError( + "`ListWidget` can't have set `use_label_wrap` to True and" + " not have set \"label\" key at the same time." + ) + + for child_obj in self.children: + child_obj.schema_validations() + + def get_child_path(self, child_obj): + result_idx = None + for idx, _child_obj in enumerate(self.children): + if _child_obj is child_obj: + result_idx = idx + break + + if result_idx is None: + raise ValueError("Didn't found child {}".format(child_obj)) + + return "/".join([self.path, str(result_idx)]) + + def set_value(self, value): + pass + + def on_change(self): + value_is_modified = None + if self.override_state is OverrideState.PROJECT: + # Only value change + if ( + self._has_project_override + and self.project_override_value is not NOT_SET + ): + value_is_modified = ( + self._current_value != self.project_override_value + ) + + if ( + self.override_state is OverrideState.STUDIO + or value_is_modified is None + ): + if ( + self._has_studio_override + and self.studio_override_value is not NOT_SET + ): + value_is_modified = ( + self._current_value != self.studio_override_value + ) + + if value_is_modified is None: + value_is_modified = self._current_value != self.default_value + + self.value_is_modified = value_is_modified + + for callback in self.on_change_callbacks: + callback() + self.parent.on_child_change(self) + + def on_child_change(self, child_obj): + print("{} - on_child_change".format(self.__class__.__name__)) + + def on_value_change(self): + raise NotImplementedError(self.__class__.__name__) + + def set_override_state(self, state): + self.override_state = state + if ( + not self.has_default_value + and state in (OverrideState.STUDIO, OverrideState.PROJECT) + ): + raise DefaultsNotDefined(self) + + self._set_value() + + def _set_value(self, value=NOT_SET): + while self.children: + self.children.pop(0) + + if self.override_state is OverrideState.NOT_DEFINED: + return + + if value is NOT_SET: + if self.override_state is OverrideState.PROJECT: + if self.had_project_override: + value = self.project_override_value + elif self.had_studio_override: + value = self.studio_override_value + else: + value = self.default_value + + elif self.override_state is OverrideState.STUDIO: + if self.had_studio_override: + value = self.studio_override_value + else: + value = self.default_value + + elif self.override_state is OverrideState.DEFAULTS: + value = self.default_value + + if value is NOT_SET: + value = self.value_on_not_set + + for item in value: + child_obj = self.create_schema_object(self.item_schema, self, True) + self.children.append(child_obj) + child_obj.update_default_value(item) + if self.override_state is OverrideState.STUDIO: + if self.had_studio_override: + child_obj.update_studio_values(item) + + elif self.override_state is OverrideState.PROJECT: + if self.had_project_override: + child_obj.update_project_values(item) + + for child_obj in self.children: + child_obj.set_override_state(self.override_state) + + @property + def value(self): + output = [] + for child_obj in self.children: + output.append(child_obj.value) + return output + + @property + def child_has_studio_override(self): + pass + + @property + def has_unsaved_changes(self): + pass + + @property + def child_is_modified(self): + pass + + @property + def child_has_project_override(self): + if self.override_state is OverrideState.PROJECT: + # TODO implement + pass + return False + + def discard_changes(self): + pass + + def settings_value(self): + if self.override_state is OverrideState.NOT_DEFINED: + return NOT_SET + + if self.is_group: + if self.override_state is OverrideState.STUDIO: + if not self._has_studio_override: + return NOT_SET + elif self.override_state is OverrideState.PROJECT: + if not self._has_project_override: + return NOT_SET + + output = [] + for child_obj in self.children: + output.append(child_obj.settings_value()) + return output + + def remove_overrides(self): + pass + + def reset_to_pype_default(self): + pass + + def set_as_overriden(self): + pass + + def set_studio_default(self): + pass + + def update_default_value(self, value): + self.has_default_value = value is not NOT_SET + self.default_value = value + + def update_studio_values(self, value): + self.studio_override_value = value + + def update_project_values(self, value): + self.project_override_value = value