Document CuraContainerStack

This commit is contained in:
Arjen Hiemstra 2017-04-18 17:40:12 +02:00
parent f1b5098a0a
commit 8682eb1486

View file

@ -16,6 +16,24 @@ from UM.Settings.Interfaces import ContainerInterface
from . import Exceptions from . import Exceptions
## Base class for Cura related stacks that want to enforce certain containers are available.
#
# This class makes sure that the stack has the following containers set: user changes, quality
# changes, quality, material, variant, definition changes and finally definition. Initially,
# these will be equal to the empty instance container.
#
# The container types are determined based on the following criteria:
# - user: An InstanceContainer with the metadata entry "type" set to "user".
# - quality changes: An InstanceContainer with the metadata entry "type" set to "quality_changes".
# - quality: An InstanceContainer with the metadata entry "type" set to "quality".
# - material: An InstanceContainer with the metadata entry "type" set to "material".
# - variant: An InstanceContainer with the metadata entry "type" set to "variant".
# - definition changes: An InstanceContainer with the metadata entry "type" set to "definition_changes".
# - definition: A DefinitionContainer.
#
# Internally, this class ensures the mentioned containers are always there and kept in a specific order.
# This also means that operations on the stack that modifies the container ordering is prohibited and
# will raise an exception.
class CuraContainerStack(ContainerStack): class CuraContainerStack(ContainerStack):
def __init__(self, container_id: str, *args, **kwargs): def __init__(self, container_id: str, *args, **kwargs):
super().__init__(container_id, *args, **kwargs) super().__init__(container_id, *args, **kwargs)
@ -26,18 +44,35 @@ class CuraContainerStack(ContainerStack):
self.containersChanged.connect(self._onContainersChanged) self.containersChanged.connect(self._onContainersChanged)
# This is emitted whenever the containersChanged signal from the ContainerStack base class is emitted.
pyqtContainersChanged = pyqtSignal() pyqtContainersChanged = pyqtSignal()
## Set the user changes container.
#
# \param new_user_changes The new user changes container. It is expected to have a "type" metadata entry with the value "user".
def setUserChanges(self, new_user_changes: InstanceContainer) -> None: def setUserChanges(self, new_user_changes: InstanceContainer) -> None:
self.replaceContainer(_ContainerIndexes.UserChanges, new_user_changes) self.replaceContainer(_ContainerIndexes.UserChanges, new_user_changes)
## Get the user changes container.
#
# \return The user changes container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(InstanceContainer, fset = setUserChanges, notify = pyqtContainersChanged) @pyqtProperty(InstanceContainer, fset = setUserChanges, notify = pyqtContainersChanged)
def userChanges(self) -> InstanceContainer: def userChanges(self) -> InstanceContainer:
return self._containers[_ContainerIndexes.UserChanges] return self._containers[_ContainerIndexes.UserChanges]
## Set the quality changes container.
#
# \param new_quality_changes The new quality changes container. It is expected to have a "type" metadata entry with the value "quality_changes".
def setQualityChanges(self, new_quality_changes: InstanceContainer) -> None: def setQualityChanges(self, new_quality_changes: InstanceContainer) -> None:
self.replaceContainer(_ContainerIndexes.QualityChanges, new_quality_changes) self.replaceContainer(_ContainerIndexes.QualityChanges, new_quality_changes)
## Set the quality changes container by an ID.
#
# This will search for the specified container and set it. If no container was found, an error will be raised.
#
# \param new_quality_changes_id The ID of the new quality changes container.
#
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
def setQualityChangesById(self, new_quality_changes_id: str) -> None: def setQualityChangesById(self, new_quality_changes_id: str) -> None:
quality_changes = ContainerRegistry.getInstance().findInstanceContainers(id = new_quality_changes_id) quality_changes = ContainerRegistry.getInstance().findInstanceContainers(id = new_quality_changes_id)
if quality_changes: if quality_changes:
@ -45,13 +80,24 @@ class CuraContainerStack(ContainerStack):
else: else:
raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_quality_changes_id)) raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_quality_changes_id))
## Get the quality changes container.
#
# \return The quality changes container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(InstanceContainer, fset = setQualityChanges, notify = pyqtContainersChanged) @pyqtProperty(InstanceContainer, fset = setQualityChanges, notify = pyqtContainersChanged)
def qualityChanges(self) -> InstanceContainer: def qualityChanges(self) -> InstanceContainer:
return self._containers[_ContainerIndexes.QualityChanges] return self._containers[_ContainerIndexes.QualityChanges]
## Set the quality container.
#
# \param new_quality The new quality container. It is expected to have a "type" metadata entry with the value "quality".
def setQuality(self, new_quality: InstanceContainer) -> None: def setQuality(self, new_quality: InstanceContainer) -> None:
self.replaceContainer(_ContainerIndexes.Quality, new_quality) self.replaceContainer(_ContainerIndexes.Quality, new_quality)
## Set the quality container by an ID.
#
# \param new_quality_id The ID of the new quality container.
#
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
def setQualityById(self, new_quality_id: str) -> None: def setQualityById(self, new_quality_id: str) -> None:
quality = ContainerRegistry.getInstance().findInstanceContainers(id = new_quality_id) quality = ContainerRegistry.getInstance().findInstanceContainers(id = new_quality_id)
if quality: if quality:
@ -59,13 +105,24 @@ class CuraContainerStack(ContainerStack):
else: else:
raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_quality_id)) raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_quality_id))
## Get the quality container.
#
# \return The quality container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(InstanceContainer, fset = setQuality, notify = pyqtContainersChanged) @pyqtProperty(InstanceContainer, fset = setQuality, notify = pyqtContainersChanged)
def quality(self) -> InstanceContainer: def quality(self) -> InstanceContainer:
return self._containers[_ContainerIndexes.Quality] return self._containers[_ContainerIndexes.Quality]
## Set the material container.
#
# \param new_quality_changes The new material container. It is expected to have a "type" metadata entry with the value "quality_changes".
def setMaterial(self, new_material: InstanceContainer) -> None: def setMaterial(self, new_material: InstanceContainer) -> None:
self.replaceContainer(_ContainerIndexes.Material, new_material) self.replaceContainer(_ContainerIndexes.Material, new_material)
## Set the material container by an ID.
#
# \param new_quality_changes_id The ID of the new material container.
#
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
def setMaterialById(self, new_material_id: str) -> None: def setMaterialById(self, new_material_id: str) -> None:
material = ContainerRegistry.getInstance().findInstanceContainers(id = new_material_id) material = ContainerRegistry.getInstance().findInstanceContainers(id = new_material_id)
if material: if material:
@ -73,13 +130,24 @@ class CuraContainerStack(ContainerStack):
else: else:
raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_material_id)) raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_material_id))
## Get the material container.
#
# \return The material container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(InstanceContainer, fset = setMaterial, notify = pyqtContainersChanged) @pyqtProperty(InstanceContainer, fset = setMaterial, notify = pyqtContainersChanged)
def material(self) -> InstanceContainer: def material(self) -> InstanceContainer:
return self._containers[_ContainerIndexes.Material] return self._containers[_ContainerIndexes.Material]
## Set the variant container.
#
# \param new_quality_changes The new variant container. It is expected to have a "type" metadata entry with the value "quality_changes".
def setVariant(self, new_variant: InstanceContainer) -> None: def setVariant(self, new_variant: InstanceContainer) -> None:
self.replaceContainer(_ContainerIndexes.Variant, new_variant) self.replaceContainer(_ContainerIndexes.Variant, new_variant)
## Set the variant container by an ID.
#
# \param new_quality_changes_id The ID of the new variant container.
#
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
def setVariantById(self, new_variant_id: str) -> None: def setVariantById(self, new_variant_id: str) -> None:
variant = ContainerRegistry.getInstance().findInstanceContainers(id = new_variant_id) variant = ContainerRegistry.getInstance().findInstanceContainers(id = new_variant_id)
if variant: if variant:
@ -87,13 +155,25 @@ class CuraContainerStack(ContainerStack):
else: else:
raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_variant_id)) raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_variant_id))
## Get the variant container.
#
# \return The variant container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(InstanceContainer, fset = setVariant, notify = pyqtContainersChanged) @pyqtProperty(InstanceContainer, fset = setVariant, notify = pyqtContainersChanged)
def variant(self) -> InstanceContainer: def variant(self) -> InstanceContainer:
return self._containers[_ContainerIndexes.Variant] return self._containers[_ContainerIndexes.Variant]
## Set the definition changes container.
#
# \param new_quality_changes The new definition changes container. It is expected to have a "type" metadata entry with the value "quality_changes".
def setDefinitionChanges(self, new_definition_changes: InstanceContainer) -> None: def setDefinitionChanges(self, new_definition_changes: InstanceContainer) -> None:
self.replaceContainer(_ContainerIndexes.DefinitionChanges, new_definition_changes) self.replaceContainer(_ContainerIndexes.DefinitionChanges, new_definition_changes)
## Set the definition changes container by an ID.
#
# \param new_quality_changes_id The ID of the new definition changes container.
#
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
def setDefinitionChangesById(self, new_definition_changes_id: str) -> None: def setDefinitionChangesById(self, new_definition_changes_id: str) -> None:
new_definition_changes = ContainerRegistry.getInstance().findInstanceContainers(id = new_definition_changes_id) new_definition_changes = ContainerRegistry.getInstance().findInstanceContainers(id = new_definition_changes_id)
if new_definition_changes: if new_definition_changes:
@ -101,13 +181,24 @@ class CuraContainerStack(ContainerStack):
else: else:
raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_definition_changes_id)) raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_definition_changes_id))
## Get the definition changes container.
#
# \return The definition changes container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(InstanceContainer, fset = setDefinitionChanges, notify = pyqtContainersChanged) @pyqtProperty(InstanceContainer, fset = setDefinitionChanges, notify = pyqtContainersChanged)
def definitionChanges(self) -> InstanceContainer: def definitionChanges(self) -> InstanceContainer:
return self._containers[_ContainerIndexes.DefinitionChanges] return self._containers[_ContainerIndexes.DefinitionChanges]
## Set the definition container.
#
# \param new_quality_changes The new definition container. It is expected to have a "type" metadata entry with the value "quality_changes".
def setDefinition(self, new_definition: DefinitionContainer) -> None: def setDefinition(self, new_definition: DefinitionContainer) -> None:
self.replaceContainer(_ContainerIndexes.Definition, new_definition) self.replaceContainer(_ContainerIndexes.Definition, new_definition)
## Set the definition container by an ID.
#
# \param new_quality_changes_id The ID of the new definition container.
#
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
def setDefinitionById(self, new_definition_id: str) -> None: def setDefinitionById(self, new_definition_id: str) -> None:
new_definition = ContainerRegistry.getInstance().findDefinitionContainers(id = new_definition_id) new_definition = ContainerRegistry.getInstance().findDefinitionContainers(id = new_definition_id)
if new_definition: if new_definition:
@ -115,6 +206,9 @@ class CuraContainerStack(ContainerStack):
else: else:
raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_definition_id)) raise Exceptions.InvalidContainerError("Could not find container with id {id}".format(id = new_definition_id))
## Get the definition container.
#
# \return The definition container. Should always be a valid container, but can be equal to the empty InstanceContainer.
@pyqtProperty(DefinitionContainer, fset = setDefinition, notify = pyqtContainersChanged) @pyqtProperty(DefinitionContainer, fset = setDefinition, notify = pyqtContainersChanged)
def definition(self) -> DefinitionContainer: def definition(self) -> DefinitionContainer:
return self._containers[_ContainerIndexes.Definition] return self._containers[_ContainerIndexes.Definition]
@ -135,6 +229,16 @@ class CuraContainerStack(ContainerStack):
return False return False
## Set a property of a setting.
#
# This will set a property of a specified setting. Since the container stack does not contain
# any settings itself, it is required to specify a container to set the property on. The target
# container is matched by container type.
#
# \param key The key of the setting to set.
# \param property_name The name of the property to set.
# \param new_value The new value to set the property to.
# \param target_container The type of the container to set the property of. Defaults to "user".
def setProperty(self, key: str, property_name: str, new_value: Any, target_container: str = "user") -> None: def setProperty(self, key: str, property_name: str, new_value: Any, target_container: str = "user") -> None:
container_index = _ContainerIndexes.indexForType(target_container) container_index = _ContainerIndexes.indexForType(target_container)
if container_index != -1: if container_index != -1:
@ -144,22 +248,34 @@ class CuraContainerStack(ContainerStack):
## Overridden from ContainerStack ## Overridden from ContainerStack
# #
# Since we have a fixed order of containers in the stack, we want to enforce this. # Since we have a fixed order of containers in the stack and this method would modify the container
# ordering, we disallow this operation.
@override(ContainerStack) @override(ContainerStack)
def addContainer(self, container: ContainerInterface) -> None: def addContainer(self, container: ContainerInterface) -> None:
raise Exceptions.InvalidOperationError("Cannot add a container to Global stack") raise Exceptions.InvalidOperationError("Cannot add a container to Global stack")
## Overridden from ContainerStack ## Overridden from ContainerStack
#
# Since we have a fixed order of containers in the stack and this method would modify the container
# ordering, we disallow this operation.
@override(ContainerStack) @override(ContainerStack)
def insertContainer(self, index: int, container: ContainerInterface) -> None: def insertContainer(self, index: int, container: ContainerInterface) -> None:
raise Exceptions.InvalidOperationError("Cannot insert a container into Global stack") raise Exceptions.InvalidOperationError("Cannot insert a container into Global stack")
## Overridden from ContainerStack ## Overridden from ContainerStack
#
# Since we have a fixed order of containers in the stack and this method would modify the container
# ordering, we disallow this operation.
@override(ContainerStack) @override(ContainerStack)
def removeContainer(self, index: int) -> None: def removeContainer(self, index: int) -> None:
raise Exceptions.InvalidOperationError("Cannot remove a container from Global stack") raise Exceptions.InvalidOperationError("Cannot remove a container from Global stack")
## Overridden from ContainerStack ## Overridden from ContainerStack
#
# Replaces the container at the specified index with another container.
# This version performs checks to make sure the new container has the expected metadata and type.
#
# \throws Exception.InvalidContainerError Raised when trying to replace a container with a container that has an incorrect type.
@override(ContainerStack) @override(ContainerStack)
def replaceContainer(self, index: int, container: ContainerInterface, postpone_emit: bool = False) -> None: def replaceContainer(self, index: int, container: ContainerInterface, postpone_emit: bool = False) -> None:
expected_type = _ContainerIndexes.IndexTypeMap[index] expected_type = _ContainerIndexes.IndexTypeMap[index]
@ -172,6 +288,13 @@ class CuraContainerStack(ContainerStack):
super().replaceContainer(index, container, postpone_emit) super().replaceContainer(index, container, postpone_emit)
## Overridden from ContainerStack ## Overridden from ContainerStack
#
# This deserialize will make sure the internal list of containers matches with what we expect.
# It will first check to see if the container at a certain index already matches with what we
# expect. If it does not, it will search for a matching container with the correct type. Should
# no container with the correct type be found, it will use the empty container.
#
# \throws InvalidContainerStackError Raised when no definition can be found for the stack.
@override(ContainerStack) @override(ContainerStack)
def deserialize(self, contents: str) -> None: def deserialize(self, contents: str) -> None:
super().deserialize(contents) super().deserialize(contents)
@ -209,6 +332,8 @@ class CuraContainerStack(ContainerStack):
self.pyqtContainersChanged.emit() self.pyqtContainersChanged.emit()
## private: ## private:
# Private helper class to keep track of container positions and their types.
class _ContainerIndexes: class _ContainerIndexes:
UserChanges = 0 UserChanges = 0
QualityChanges = 1 QualityChanges = 1
@ -229,6 +354,7 @@ class _ContainerIndexes:
Definition: "definition", Definition: "definition",
} }
# Perform reverse lookup (type name -> index)
@classmethod @classmethod
def indexForType(cls, type_name: str) -> int: def indexForType(cls, type_name: str) -> int:
for key, value in cls.IndexTypeMap.items(): for key, value in cls.IndexTypeMap.items():