mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-23 14:44:13 -06:00
Rework container tree structure
This sets up a few new classes, subclasses of ContainerNode. This is intended to simplify the current structure in the QualityManager. Contributes to issue CURA-6600.
This commit is contained in:
parent
037f1967c8
commit
9fda7bd0b9
6 changed files with 136 additions and 63 deletions
|
@ -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)
|
15
cura/Machines/ContainerTree.py
Normal file
15
cura/Machines/ContainerTree.py
Normal file
|
@ -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.
|
19
cura/Machines/IntentNode.py
Normal file
19
cura/Machines/IntentNode.py
Normal file
|
@ -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.
|
19
cura/Machines/MachineNode.py
Normal file
19
cura/Machines/MachineNode.py
Normal file
|
@ -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.
|
|
@ -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)
|
||||
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.
|
19
cura/Machines/VariantNode.py
Normal file
19
cura/Machines/VariantNode.py
Normal file
|
@ -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.
|
Loading…
Add table
Add a link
Reference in a new issue