diff --git a/cura/Machines/ContainerNode.py b/cura/Machines/ContainerNode.py index eef1c63127..7695296cbb 100644 --- a/cura/Machines/ContainerNode.py +++ b/cura/Machines/ContainerNode.py @@ -1,64 +1,71 @@ -# Copyright (c) 2018 Ultimaker B.V. +# Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Optional, Any, Dict, Union, TYPE_CHECKING - -from collections import OrderedDict +from typing import Any, Dict, Optional from UM.ConfigurationErrorMessage import ConfigurationErrorMessage +from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Logger import Logger from UM.Settings.InstanceContainer import InstanceContainer +from UM.Decorators import deprecated - -## -# A metadata / container combination. Use getContainer() to get the container corresponding to the metadata. -# -# ContainerNode is a multi-purpose class. It has two main purposes: -# 1. It encapsulates an InstanceContainer. It contains that InstanceContainer's -# - metadata (Always) -# - container (lazy-loaded when needed) -# 2. It also serves as a node in a hierarchical InstanceContainer lookup table/tree. -# This is used in Variant, Material, and Quality Managers. +## A node in the container tree. It represents one container. # +# The container it represents is referenced by its container_id. During normal +# use of the tree, this container is not constructed. Only when parts of the +# tree need to get loaded in the container stack should it get constructed. class ContainerNode: - __slots__ = ("_metadata", "_container", "children_map") + ## Creates a new node for the container tree. + # \param container_id The ID of the container that this node should + # represent. + # \param parent The parent container node, if any. + def __init__(self, container_id: str, parent: Optional["ContainerNode"]) -> None: + self.container_id = container_id + self.parent = parent + self._container = None # type: Optional[InstanceContainer] + self.children_map = {} # type: Dict[str, ContainerNode] # Mapping from container ID to container node. - def __init__(self, metadata: Optional[Dict[str, Any]] = None) -> None: - self._metadata = metadata - self._container = None # type: Optional[InstanceContainer] - self.children_map = OrderedDict() # type: ignore # This is because it's children are supposed to override it. - - ## Get an entry value from the metadata + ## Get an entry from the metadata of the container that this node contains. + # \param entry The metadata entry key to return. + # \param default If the metadata is not present or the container is not + # found, the value of this default is returned. + # \return The value of the metadata entry, or the default if it was not + # present. + @deprecated("Get the metadata from the container with the ID of this node yourself.", "4.3") def getMetaDataEntry(self, entry: str, default: Any = None) -> Any: - if self._metadata is None: + container_metadata = ContainerRegistry.getInstance().findContainersMetadata(id = self.container_id) + if len(container_metadata) == 0: return default - return self._metadata.get(entry, default) + return container_metadata[0].get(entry, default) - def getMetadata(self) -> Dict[str, Any]: - if self._metadata is None: - return {} - return self._metadata + ## Get the child with the specified container ID. + # \param child_id The container ID to get from among the children. + # \return The child node, or ``None`` if no child is present with the + # specified ID. + @deprecated("Iterate over the children instead of requesting them one by one.", "4.3") + def getChildNode(self, child_id: str) -> Optional["ContainerNode"]: + return self.children_map.get(child_id) - def getChildNode(self, child_key: str) -> Optional["ContainerNode"]: - return self.children_map.get(child_key) + @deprecated("Use `.container` instead.", "4.3") + def getContainer(self) -> Optional[InstanceContainer]: + return self.container - def getContainer(self) -> Optional["InstanceContainer"]: - if self._metadata is None: - Logger.log("e", "Cannot get container for a ContainerNode without metadata.") - return None - - if self._container is None: - container_id = self._metadata["id"] - from UM.Settings.ContainerRegistry import ContainerRegistry - container_list = ContainerRegistry.getInstance().findInstanceContainers(id = container_id) - if not container_list: - Logger.log("e", "Failed to lazy-load container [{container_id}]. Cannot find it.".format(container_id = container_id)) + ## The container that this node's container ID refers to. + # + # This can be used to finally instantiate the container in order to put it + # in the container stack. + # \return A container. + @property + def container(self) -> Optional[InstanceContainer]: + if not self._container: + container_list = ContainerRegistry.getInstance().findInstanceContainers(id = self.container_id) + if len(container_list) == 0: + Logger.log("e", "Failed to lazy-load container [{container_id}]. Cannot find it.".format(container_id = self.container_id)) error_message = ConfigurationErrorMessage.getInstance() - error_message.addFaultyContainers(container_id) + error_message.addFaultyContainers(self.container_id) return None self._container = container_list[0] - return self._container def __str__(self) -> str: - return "%s[%s]" % (self.__class__.__name__, self.getMetaDataEntry("id")) + return "%s[%s]" % (self.__class__.__name__, self.container_id) \ No newline at end of file diff --git a/cura/Machines/ContainerTree.py b/cura/Machines/ContainerTree.py new file mode 100644 index 0000000000..30c9b8f90d --- /dev/null +++ b/cura/Machines/ContainerTree.py @@ -0,0 +1,15 @@ +# Copyright (c) 2019 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from cura.Machines.MachineNode import MachineNode + +from typing import Dict + +## This class contains a look-up tree for which containers are available at +# which stages of configuration. +# +# The tree starts at the machine definitions. For every distinct definition +# there will be one machine node here. +class ContainerTree: + def __init__(self) -> None: + self.machines = {} # type: Dict[str, MachineNode] # Mapping from definition ID to machine nodes. \ No newline at end of file diff --git a/cura/Machines/IntentNode.py b/cura/Machines/IntentNode.py new file mode 100644 index 0000000000..590cf646e4 --- /dev/null +++ b/cura/Machines/IntentNode.py @@ -0,0 +1,19 @@ +# Copyright (c) 2019 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from typing import TYPE_CHECKING + +from cura.Machines.ContainerNode import ContainerNode +from cura.Machines.MaterialNode import MaterialNode +from cura.Machines.QualityNode import QualityNode + +if TYPE_CHECKING: + from typing import Dict + +## This class represents an intent category in the container tree. +# +# This class has no more subnodes. +class IntentNode(ContainerNode): + def __init__(self, container_id: str, parent: QualityNode) -> None: + super().__init__(container_id, parent) + self.variants = {} # type: Dict[str, MaterialNode] # mapping variant IDs to their nodes. \ No newline at end of file diff --git a/cura/Machines/MachineNode.py b/cura/Machines/MachineNode.py new file mode 100644 index 0000000000..7daaf10bf5 --- /dev/null +++ b/cura/Machines/MachineNode.py @@ -0,0 +1,19 @@ +# Copyright (c) 2019 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from typing import TYPE_CHECKING + +from cura.Machines.ContainerNode import ContainerNode +from cura.Machines.ContainerTree import ContainerTree +from cura.Machines.VariantNode import VariantNode + +if TYPE_CHECKING: + from typing import Dict + +## This class represents a machine in the container tree. +# +# The subnodes of these nodes are variants. +class MachineNode(ContainerNode): + def __init__(self, container_id: str) -> None: + super().__init__(container_id, None) + self.variants = {} # type: Dict[str, VariantNode] # mapping variant IDs to their nodes. \ No newline at end of file diff --git a/cura/Machines/MaterialNode.py b/cura/Machines/MaterialNode.py index a4dcb0564f..2ac063c977 100644 --- a/cura/Machines/MaterialNode.py +++ b/cura/Machines/MaterialNode.py @@ -1,25 +1,19 @@ -# Copyright (c) 2018 Ultimaker B.V. +# Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Optional, Dict, Any -from collections import OrderedDict -from .ContainerNode import ContainerNode +from typing import TYPE_CHECKING +from cura.Machines.ContainerNode import ContainerNode +from cura.Machines.QualityNode import QualityNode +from cura.Machines.VariantNode import VariantNode + +if TYPE_CHECKING: + from typing import Dict + +## Represents a material in the container tree. # -# A MaterialNode is a node in the material lookup tree/map/table. It contains 2 (extra) fields: -# - material_map: a one-to-one map of "material_root_id" to material_node. -# - children_map: the key-value map for child nodes of this node. This is used in a lookup tree. -# -# +# Its subcontainers are quality profiles. class MaterialNode(ContainerNode): - __slots__ = ("material_map", "children_map") - - def __init__(self, metadata: Optional[Dict[str, Any]] = None) -> None: - super().__init__(metadata = metadata) - self.material_map = {} # type: Dict[str, MaterialNode] # material_root_id -> material_node - - # We overide this as we want to indicate that MaterialNodes can only contain other material nodes. - self.children_map = OrderedDict() # type: OrderedDict[str, "MaterialNode"] - - def getChildNode(self, child_key: str) -> Optional["MaterialNode"]: - return self.children_map.get(child_key) \ No newline at end of file + def __init__(self, container_id, parent: VariantNode) -> None: + super().__init__(container_id, parent) + self.qualities = {} # type: Dict[str, QualityNode] # Mapping container IDs to quality profiles. \ No newline at end of file diff --git a/cura/Machines/VariantNode.py b/cura/Machines/VariantNode.py new file mode 100644 index 0000000000..93410f2a61 --- /dev/null +++ b/cura/Machines/VariantNode.py @@ -0,0 +1,19 @@ +# Copyright (c) 2019 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from typing import TYPE_CHECKING + +from cura.Machines.ContainerNode import ContainerNode +from cura.Machines.MachineNode import MachineNode +from cura.Machines.MaterialNode import MaterialNode + +if TYPE_CHECKING: + from typing import Dict + +## This class represents an extruder variant in the container tree. +# +# The subnodes of these nodes are materials. +class VariantNode(ContainerNode): + def __init__(self, container_id: str, parent: MachineNode) -> None: + super().__init__(container_id, parent) + self.variants = {} # type: Dict[str, MaterialNode] # mapping variant IDs to their nodes. \ No newline at end of file