def diff(self, collection: T.bpy_prop_collection, key: str, collection_property: T.Property, context: Context) -> Optional[DeltaUpdate]: """ Computes the difference between the state of an item tracked by this proxy and its Blender state. As this proxy tracks a collection, the result will be a DeltaUpdate that contains a DatablockCollectionProxy with an Delta item per added, deleted or update item Args: collection: the collection diff against this proxy collection_property: the property of collection in its enclosing object """ # This method is called from the depsgraph handler. The proxy holds a representation of the Blender state # before the modification being processed. So the changeset is (Blender state - proxy state) # TODO how can this replace BpyBlendDiff ? diff = self.__class__() item_property = collection_property.fixed_type # keys are uuids # BpyDataCollectionDiff.diff() for why proxies without datablocks are ignores proxy_keys = {k for k, v in self._data.items() if v.target(context)} blender_items = { datablock.mixer_uuid: datablock for datablock in collection.values() } blender_keys = blender_items.keys() added_keys = blender_keys - proxy_keys deleted_keys = proxy_keys - blender_keys maybe_updated_keys = proxy_keys & blender_keys for k in added_keys: value = read_attribute(blender_items[k], k, item_property, collection, context) assert isinstance(value, (DatablockProxy, DatablockRefProxy)) diff._data[k] = DeltaAddition(value) for k in deleted_keys: diff._data[k] = DeltaDeletion(self._data[k]) for k in maybe_updated_keys: delta = diff_attribute(blender_items[k], k, item_property, self.data(k), context) if delta is not None: assert isinstance(delta, DeltaUpdate) diff._data[k] = delta if len(diff._data): return DeltaUpdate(diff) return None
def load( self, bl_collection: T.bpy_prop_collection, context: Context, ): self._sequence.clear() for i, v in enumerate(bl_collection.values()): context.visit_state.push(v, i) try: self._sequence.append(_proxy_factory(v).load(v, context)) except Exception as e: logger.error(f"Exception during load at {context.visit_state.display_path()} ...") logger.error(f"... {e!r}") finally: context.visit_state.pop() return self
def load( self, bl_collection: T.bpy_prop_collection, key: Union[int, str], bl_collection_property: T.Property, context: Context, ): context.visit_state.path.append(key) try: self._sequence = [ _proxy_factory(v).load(v, i, context) for i, v in enumerate(bl_collection.values()) ] finally: context.visit_state.path.pop() return self
def load( self, bl_collection: T.bpy_prop_collection, key: Union[int, str], bl_collection_property: T.Property, context: Context, ): if len(bl_collection) == 0: self._data.clear() return self try: context.visit_state.path.append(key) # no keys means it is a sequence. However bl_collection.items() returns [(index, item)...] is_sequence = not bl_collection.keys() if is_sequence: # easier for the encoder to always have a dict self._data = { MIXER_SEQUENCE: [ StructProxy.make(v).load(v, i, context) for i, v in enumerate(bl_collection.values()) ] } else: self._data = {k: StructProxy().load(v, k, context) for k, v in bl_collection.items()} finally: context.visit_state.path.pop() return self