Merge branch 'feature_intent_container_tree' of https://github.com/Ultimaker/Cura into feature_intent_container_tree

This commit is contained in:
Remco Burema 2019-08-20 13:53:12 +02:00
commit 5039d8db05
22 changed files with 258 additions and 398 deletions

View file

@ -5,10 +5,15 @@ from UM.Logger import Logger
from UM.Settings.ContainerRegistry import ContainerRegistry # To listen to containers being added.
from UM.Settings.DefinitionContainer import DefinitionContainer
from UM.Settings.Interfaces import ContainerInterface
import cura.CuraApplication # Imported like this to prevent circular dependencies.
from cura.Machines.MachineNode import MachineNode
from typing import Dict
from typing import Dict, List, TYPE_CHECKING
import time
if TYPE_CHECKING:
from cura.Machines.QualityGroup import QualityGroup
## This class contains a look-up tree for which containers are available at
# which stages of configuration.
#
@ -29,6 +34,21 @@ class ContainerTree:
container_registry.containerAdded.connect(self._machineAdded)
self._loadAll()
## Get the quality groups available for the currently activated printer.
#
# This contains all quality groups, enabled or disabled. To check whether
# the quality group can be activated, test for the
# ``QualityGroup.is_available`` property.
# \return For every quality type, one quality group.
def getCurrentQualityGroups(self) -> Dict[str, "QualityGroup"]:
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
if global_stack is None:
return {}
variant_names = [extruder.variant.getName() for extruder in global_stack.extruders.values()]
material_bases = [extruder.material.getMetaDataEntry("base_file") for extruder in global_stack.extruders.values()]
extruder_enabled = [extruder.isEnabled for extruder in global_stack.extruders.values()]
return self.machines[global_stack.definition.getId()].getQualityGroups(variant_names, material_bases, extruder_enabled)
## Builds the initial container tree.
def _loadAll(self):
Logger.log("i", "Building container tree.")

View file

@ -1,19 +1,16 @@
# Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import TYPE_CHECKING
from typing import Dict, List
from UM.Logger import Logger
from UM.Util import parseBool
from UM.Settings.ContainerRegistry import ContainerRegistry # To find all the variants for this machine.
from UM.Settings.Interfaces import ContainerInterface
from cura.Machines.ContainerNode import ContainerNode
from cura.Machines.QualityGroup import QualityGroup # To construct groups of quality profiles that belong together.
from cura.Machines.QualityNode import QualityNode
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.
@ -36,10 +33,57 @@ class MachineNode(ContainerNode):
self.quality_definition = my_metadata.get("quality_definition", container_id)
self.exclude_materials = my_metadata.get("exclude_materials", [])
self.preferred_variant_name = my_metadata.get("preferred_variant_name", "")
self.preferred_quality_type = my_metadata.get("preferred_quality_type", "")
container_registry.containerAdded.connect(self._variantAdded)
self._loadAll()
## Get the available quality groups for this machine.
#
# This returns all quality groups, regardless of whether they are
# available to the combination of extruders or not. On the resulting
# quality groups, the is_available property is set to indicate whether the
# quality group can be selected according to the combination of extruders
# in the parameters.
# \param variant_names The names of the variants loaded in each extruder.
# \param material_bases The base file names of the materials loaded in
# each extruder.
# \param extruder_enabled Whether or not the extruders are enabled. This
# allows the function to set the is_available properly.
# \return For each available quality type, a QualityGroup instance.
def getQualityGroups(self, variant_names: List[str], material_bases: List[str], extruder_enabled: List[bool]) -> Dict[str, QualityGroup]:
if len(variant_names) != len(material_bases) or len(variant_names) != len(extruder_enabled):
Logger.log("e", "The number of extruders in the list of variants (" + str(len(variant_names)) + ") is not equal to the number of extruders in the list of materials (" + str(len(material_bases)) + ") or the list of enabled extruders (" + str(len(extruder_enabled)) + ").")
return {}
# For each extruder, find which quality profiles are available. Later we'll intersect the quality types.
qualities_per_type_per_extruder = [{} for _ in range(len(variant_names))] # type: List[Dict[str, QualityNode]]
for extruder_nr, variant_name in enumerate(variant_names):
if not extruder_enabled[extruder_nr]:
continue # No qualities are available in this extruder. It'll get skipped when calculating the available quality types.
material_base = material_bases[extruder_nr]
if variant_name not in self.variants or material_base not in self.variants[variant_name].materials:
# The printer has no variant/material-specific quality profiles. Use the global quality profiles.
qualities_per_type_per_extruder[extruder_nr] = self.global_qualities
else:
# Use the actually specialised quality profiles.
qualities_per_type_per_extruder[extruder_nr] = self.variants[variant_name].materials[material_base].qualities
# Create the quality group for each available type.
quality_groups = {}
for quality_type, global_quality_node in self.global_qualities.items():
quality_groups[quality_type] = QualityGroup(name = global_quality_node.getMetaDataEntry("name", "Unnamed profile"), quality_type = quality_type)
quality_groups[quality_type].node_for_global = global_quality_node
for extruder, qualities_per_type in qualities_per_type_per_extruder:
quality_groups[quality_type].nodes_for_extruders[extruder] = qualities_per_type[quality_type]
available_quality_types = set(quality_groups.keys())
for extruder_nr, qualities_per_type in enumerate(qualities_per_type_per_extruder):
if not extruder_enabled[extruder_nr]:
continue
available_quality_types.intersection_update(qualities_per_type.keys())
for quality_type in available_quality_types:
quality_groups[quality_type].is_available = True
return quality_groups
## (Re)loads all variants under this printer.
def _loadAll(self):
# Find all the variants for this definition ID.
@ -55,19 +99,4 @@ class MachineNode(ContainerNode):
if len(global_qualities) == 0: # This printer doesn't override the global qualities.
global_qualities = container_registry.findInstanceContainersMetadata(type = "quality", definition = "fdmprinter", global_quality = True) # Otherwise pick the global global qualities.
for global_quality in global_qualities:
self.global_qualities[global_quality["quality_type"]] = QualityNode(global_quality["id"], parent = self)
## When a variant gets added to the set of profiles, we need to update our
# tree here.
def _variantAdded(self, container: ContainerInterface):
if container.getMetaDataEntry("type") != "variant":
return # Not interested.
name = container.getMetaDataEntry("name")
if name in self.variants:
return # Already have this one.
if container.getMetaDataEntry("hardware_type") != "nozzle":
return # Only want nozzles in my tree.
if container.getMetaDataEntry("definition") != self.container_id:
return # Not a nozzle that fits in my machine.
self.variants[name] = VariantNode(container.getId(), machine = self)
self.global_qualities[global_quality["quality_type"]] = QualityNode(global_quality["id"], parent = self)

View file

@ -59,15 +59,6 @@ class MaterialManager(QObject):
# Root_material_id -> MaterialGroup
self._material_group_map = dict() # type: Dict[str, MaterialGroup]
# Approximate diameter str
self._diameter_machine_nozzle_buildplate_material_map = dict() # type: Dict[str, Dict[str, MaterialNode]]
# We're using these two maps to convert between the specific diameter material id and the generic material id
# because the generic material ids are used in qualities and definitions, while the specific diameter material is meant
# i.e. generic_pla -> generic_pla_175
# root_material_id -> approximate diameter str -> root_material_id for that diameter
self._material_diameter_map = defaultdict(dict) # type: Dict[str, Dict[str, str]]
# Material id including diameter (generic_pla_175) -> material root id (generic_pla)
self._diameter_material_map = dict() # type: Dict[str, str]
@ -75,11 +66,6 @@ class MaterialManager(QObject):
# GUID -> a list of material_groups
self._guid_material_groups_map = defaultdict(list) # type: Dict[str, List[MaterialGroup]]
# The machine definition ID for the non-machine-specific materials.
# This is used as the last fallback option if the given machine-specific material(s) cannot be found.
self._default_machine_definition_id = "fdmprinter"
self._default_approximate_diameter_for_quality_search = "3"
self._favorites = set(cura.CuraApplication.CuraApplication.getInstance().getPreferences().getValue("cura/favorite_materials").split(";"))
self.materialsUpdated.emit()
@ -87,7 +73,15 @@ class MaterialManager(QObject):
return self._material_group_map.get(root_material_id)
def getRootMaterialIDForDiameter(self, root_material_id: str, approximate_diameter: str) -> str:
return self._material_diameter_map.get(root_material_id, {}).get(approximate_diameter, root_material_id)
original_material = CuraContainerRegistry.getInstance().findInstanceContainersMetadata(id=root_material_id)[0]
if original_material["approximate_diameter"] == approximate_diameter:
return root_material_id
matching_materials = CuraContainerRegistry.getInstance().findInstanceContainersMetadata(type = "material", brand = original_material["brand"], definition = original_material["definition"], material = original_material["material"], color_name = original_material["color_name"])
for material in matching_materials:
if material["approximate_diameter"] == approximate_diameter:
return material["id"]
return root_material_id
def getRootMaterialIDWithoutDiameter(self, root_material_id: str) -> str:
return self._diameter_material_map.get(root_material_id, "")
@ -109,13 +103,16 @@ class MaterialManager(QObject):
# A convenience function to get available materials for the given machine with the extruder position.
#
def getAvailableMaterialsForMachineExtruder(self, machine: "GlobalStack",
extruder_stack: "ExtruderStack") -> Optional[Dict[str, MaterialNode]]:
extruder_stack: "ExtruderStack") -> Dict[str, MaterialNode]:
nozzle_name = None
if extruder_stack.variant.getId() != "empty_variant":
nozzle_name = extruder_stack.variant.getName()
# Fetch the available materials (ContainerNode) for the current active machine and extruder setup.
return self.getAvailableMaterials(machine.definition.getId(), nozzle_name)
materials =self.getAvailableMaterials(machine.definition.getId(), nozzle_name)
compatible_material_diameter = str(round(extruder_stack.getCompatibleMaterialDiameter()))
return {key: material for key, material in materials.items() if material.getMetaDataEntry("approximate_diameter") == compatible_material_diameter}
#
# Gets MaterialNode for the given extruder and machine with the given material name.
@ -152,27 +149,10 @@ class MaterialManager(QObject):
#
def getMaterialNodeByType(self, global_stack: "GlobalStack", position: str, nozzle_name: str,
buildplate_name: Optional[str], material_guid: str) -> Optional["MaterialNode"]:
node = None
machine_definition = global_stack.definition
extruder_definition = global_stack.extruders[position].definition
if parseBool(machine_definition.getMetaDataEntry("has_materials", False)):
material_diameter = extruder_definition.getProperty("material_diameter", "value")
if isinstance(material_diameter, SettingFunction):
material_diameter = material_diameter(global_stack)
variant_name = global_stack.extruders[position].variant.getName()
# Look at the guid to material dictionary
root_material_id = None
for material_group in self._guid_material_groups_map[material_guid]:
root_material_id = cast(str, material_group.root_material_node.getMetaDataEntry("id", ""))
break
if not root_material_id:
Logger.log("i", "Cannot find materials with guid [%s] ", material_guid)
return None
node = self.getMaterialNode(machine_definition.getId(), nozzle_name, buildplate_name,
material_diameter, root_material_id)
return node
return self.getMaterialNode(machine_definition.getId(), variant_name, buildplate_name, 3, material_guid)
# There are 2 ways to get fallback materials;
# - A fallback by type (@sa getFallbackMaterialIdByMaterialType), which adds the generic version of this material
@ -254,41 +234,21 @@ class MaterialManager(QObject):
return node
def removeMaterialByRootId(self, root_material_id: str):
material_group = self.getMaterialGroup(root_material_id)
if not material_group:
Logger.log("i", "Unable to remove the material with id %s, because it doesn't exist.", root_material_id)
return
container_registry = CuraContainerRegistry.getInstance()
nodes_to_remove = [material_group.root_material_node] + material_group.derived_material_node_list
# Sort all nodes with respect to the container ID lengths in the ascending order so the base material container
# will be the first one to be removed. We need to do this to ensure that all containers get loaded & deleted.
nodes_to_remove = sorted(nodes_to_remove, key = lambda x: len(x.getMetaDataEntry("id", "")))
# Try to load all containers first. If there is any faulty ones, they will be put into the faulty container
# list, so removeContainer() can ignore those ones.
for node in nodes_to_remove:
container_id = node.getMetaDataEntry("id", "")
results = container_registry.findContainers(id = container_id)
if not results:
container_registry.addWrongContainerId(container_id)
for node in nodes_to_remove:
container_registry.removeContainer(node.getMetaDataEntry("id", ""))
results = container_registry.findContainers(id=root_material_id)
if not results:
container_registry.addWrongContainerId(root_material_id)
for result in results:
container_registry.removeContainer(result.getMetaDataEntry("id", ""))
#
# Methods for GUI
#
@pyqtSlot("QVariant", result=bool)
def canMaterialBeRemoved(self, material_node: "MaterialNode"):
# Check if the material is active in any extruder train. In that case, the material shouldn't be removed!
# In the future we might enable this again, but right now, it's causing a ton of issues if we do (since it
# corrupts the configuration)
root_material_id = material_node.getMetaDataEntry("base_file")
material_group = self.getMaterialGroup(root_material_id)
if not material_group:
return False
nodes_to_remove = [material_group.root_material_node] + material_group.derived_material_node_list
ids_to_remove = [node.getMetaDataEntry("id", "") for node in nodes_to_remove]
ids_to_remove = [metadata.get("id", "") for metadata in CuraContainerRegistry.getInstance().findInstanceContainersMetadata(base_file=root_material_id)]
for extruder_stack in CuraContainerRegistry.getInstance().findContainerStacks(type = "extruder_train"):
if extruder_stack.material.getId() in ids_to_remove:
@ -303,38 +263,24 @@ class MaterialManager(QObject):
if CuraContainerRegistry.getInstance().isReadOnly(root_material_id):
Logger.log("w", "Cannot set name of read-only container %s.", root_material_id)
return
containers = CuraContainerRegistry.getInstance().findInstanceContainers(id = root_material_id)
containers[0].setName(name)
material_group = self.getMaterialGroup(root_material_id)
if material_group:
container = material_group.root_material_node.container
if container:
container.setName(name)
#
# Removes the given material.
#
@pyqtSlot("QVariant")
def removeMaterial(self, material_node: "MaterialNode") -> None:
root_material_id = material_node.getMetaDataEntry("base_file")
if root_material_id is not None:
self.removeMaterialByRootId(root_material_id)
#
# Creates a duplicate of a material, which has the same GUID and base_file metadata.
# Returns the root material ID of the duplicated material if successful.
#
@pyqtSlot("QVariant", result = str)
def duplicateMaterial(self, material_node: MaterialNode, new_base_id: Optional[str] = None, new_metadata: Dict[str, Any] = None) -> Optional[str]:
root_material_id = cast(str, material_node.getMetaDataEntry("base_file", ""))
def duplicateMaterialByRootId(self, root_material_id, new_base_id: Optional[str] = None, new_metadata: Dict[str, Any] = None) -> Optional[str]:
container_registry = CuraContainerRegistry.getInstance()
results = container_registry.findContainers(id=root_material_id)
material_group = self.getMaterialGroup(root_material_id)
if not material_group:
if not results:
Logger.log("i", "Unable to duplicate the material with id %s, because it doesn't exist.", root_material_id)
return None
base_container = material_group.root_material_node.container
if not base_container:
return None
base_container = results[0]
# Ensure all settings are saved.
cura.CuraApplication.CuraApplication.getInstance().saveSettings()
@ -353,11 +299,9 @@ class MaterialManager(QObject):
new_containers.append(new_base_container)
# Clone all of them.
for node in material_group.derived_material_node_list:
container_to_copy = node.container
if not container_to_copy:
continue
# Create unique IDs for every clone.
for container_to_copy in container_registry.findContainers(base_file=root_material_id):
if container_to_copy.getId() == root_material_id:
continue # We already have that one, skip it
new_id = new_base_id
if container_to_copy.getMetaDataEntry("definition") != "fdmprinter":
new_id += "_" + container_to_copy.getMetaDataEntry("definition")
@ -371,7 +315,6 @@ class MaterialManager(QObject):
if new_metadata is not None:
for key, value in new_metadata.items():
new_container.getMetaData()[key] = value
new_containers.append(new_container)
for container_to_add in new_containers:
@ -383,8 +326,15 @@ class MaterialManager(QObject):
self.addFavorite(new_base_id)
return new_base_id
#
# Creates a duplicate of a material, which has the same GUID and base_file metadata.
# Returns the root material ID of the duplicated material if successful.
#
@pyqtSlot("QVariant", result = str)
def duplicateMaterial(self, material_node: MaterialNode, new_base_id: Optional[str] = None, new_metadata: Dict[str, Any] = None) -> Optional[str]:
root_material_id = cast(str, material_node.getMetaDataEntry("base_file", ""))
return self.duplicateMaterialByRootId(root_material_id, new_base_id, new_metadata)
# Create a new material by cloning Generic PLA for the current material diameter and generate a new GUID.
# Returns the ID of the newly created material.
@pyqtSlot(result = str)
@ -403,11 +353,6 @@ class MaterialManager(QObject):
approximate_diameter = str(extruder_stack.approximateMaterialDiameter)
root_material_id = self.getRootMaterialIDForDiameter(root_material_id, approximate_diameter)
material_group = self.getMaterialGroup(root_material_id)
if not material_group: # This should never happen
Logger.log("w", "Cannot get the material group of %s.", root_material_id)
return ""
# Create a new ID & container to hold the data.
new_id = CuraContainerRegistry.getInstance().uniqueName("custom_material")
@ -416,9 +361,7 @@ class MaterialManager(QObject):
"GUID": str(uuid.uuid4()),
}
self.duplicateMaterial(material_group.root_material_node,
new_base_id = new_id,
new_metadata = new_metadata)
self.duplicateMaterialByRootId(root_material_id, new_base_id = new_id, new_metadata = new_metadata)
return new_id
@pyqtSlot(str)

View file

@ -1,7 +1,7 @@
# Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import TYPE_CHECKING
from typing import Any, TYPE_CHECKING
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.Interfaces import ContainerInterface
@ -23,8 +23,11 @@ class MaterialNode(ContainerNode):
container_registry = ContainerRegistry.getInstance()
my_metadata = container_registry.findContainersMetadata(id = container_id)[0]
self.base_file = my_metadata["base_file"]
container_registry.containerAdded.connect(self._qualityAdded)
self.material_type = my_metadata["material"]
self.guid = my_metadata["GUID"]
self._loadAll()
container_registry.containerRemoved.connect(self._onRemoved)
container_registry.containerMetaDataChanged.connect(self._onMetadataChanged)
def _loadAll(self) -> None:
container_registry = ContainerRegistry.getInstance()
@ -33,14 +36,13 @@ class MaterialNode(ContainerNode):
qualities = container_registry.findInstanceContainersMetadata(type = "quality", definition = "fdmprinter")
else:
# Need to find the qualities that specify a material profile with the same material type.
my_metadata = container_registry.findInstanceContainersMetadata(id = self.container_id)[0]
my_material_type = my_metadata.get("material")
my_material_type = self.material_type
qualities = []
qualities_any_material = container_registry.findInstanceContainersMetadata(type = "quality", definition = self.variant.machine.quality_definition, variant = self.variant.variant_name)
for material_metadata in container_registry.findInstanceContainersMetadata(type = "material", material = my_material_type):
qualities.extend((quality for quality in qualities_any_material if quality["material"] == material_metadata["id"]))
if not qualities: # No quality profiles found. Go by GUID then.
my_guid = my_metadata.get("material")
my_guid = self.guid
for material_metadata in container_registry.findInstanceContainersMetadata(type = "material", guid = my_guid):
qualities.extend((quality for quality in qualities_any_material if quality["material"] == material_metadata["id"]))
@ -49,37 +51,37 @@ class MaterialNode(ContainerNode):
if quality_id not in self.qualities:
self.qualities[quality_id] = QualityNode(quality_id, parent = self)
def _qualityAdded(self, container: ContainerInterface) -> None:
if container.getMetaDataEntry("type") != "quality":
return # Not interested.
if not self.variant.machine.has_machine_quality:
if container.getMetaDataEntry("definition") != "fdmprinter":
return # Only want global qualities.
else:
if container.getMetaDataEntry("definition") != self.variant.machine.quality_definition:
return # Doesn't match the machine.
if container.getMetaDataEntry("variant") != self.variant.variant_name:
return # Doesn't match the variant.
# Detect if we're falling back to matching via GUID.
# If so, we might need to erase the current list and put just this one in (i.e. no longer use the fallback).
container_registry = ContainerRegistry.getInstance()
my_metadata = container_registry.findInstanceContainersMetadata(id = self.container_id)[0]
my_material_type = my_metadata.get("material")
allowed_material_ids = {metadata["id"] for metadata in container_registry.findInstanceContainersMetadata(type = "material", material = my_material_type)}
# Select any quality profile; if the material is not matching by material type, we've been falling back to GUID all along.
is_fallback_guid = len(self.qualities) == 0 or next(iter(self.qualities.values())).getMetaDataEntry("material") not in allowed_material_ids
## Triggered when any container is removed, but only handles it when the
# container is removed that this node represents.
# \param container The container that was allegedly removed.
def _onRemoved(self, container: ContainerInterface) -> None:
if container.getId() == self.container_id:
# Remove myself from my parent.
if self.base_file in self.variant.materials:
del self.variant.materials[self.base_file]
if is_fallback_guid and container.getMetaDataEntry("material") in allowed_material_ids: # So far we needed the fallback, but no longer!
self.qualities.clear() # It'll get filled with the new quality profile then.
else:
if not is_fallback_guid:
if container.getMetaDataEntry("material") not in allowed_material_ids:
return # Doesn't match the material type.
else:
my_material_guid = my_metadata.get("GUID")
allowed_material_ids = {metadata["id"] for metadata in container_registry.findInstanceContainersMetadata(type = "material", guid = my_material_guid)}
if container.getMetaDataEntry("material") not in allowed_material_ids:
return # Doesn't match the material GUID.
## Triggered when any metadata changed in any container, but only handles
# it when the metadata of this node is changed.
# \param container The container whose metadata changed.
# \param kwargs Key-word arguments provided when changing the metadata.
# These are ignored. As far as I know they are never provided to this
# call.
def _onMetadataChanged(self, container: ContainerInterface, **kwargs: Any) -> None:
if container.getId() != self.container_id:
return
quality_id = container.getId()
self.qualities[quality_id] = QualityNode(quality_id, parent = self)
new_metadata = container.getMetaData()
old_base_file = self.base_file
if new_metadata["base_file"] != old_base_file:
self.base_file = new_metadata["base_file"]
if old_base_file in self.variant.materials: # Move in parent node.
del self.variant.materials[old_base_file]
self.variant.materials[self.base_file] = self
old_material_type = self.material_type
self.material_type = new_metadata["material"]
old_guid = self.guid
self.guid = new_metadata["GUID"]
if self.base_file != old_base_file or self.material_type != old_material_type or self.guid != old_guid: # List of quality profiles could've changed.
self.qualities = {}
self._loadAll() # Re-load the quality profiles for this node.

View file

@ -34,9 +34,6 @@ class BaseMaterialsModel(ListModel):
# Update this model when switching machines
self._machine_manager.activeStackChanged.connect(self._update)
# Update this model when list of materials changes
self._material_manager.materialsUpdated.connect(self._update)
self.addRoleName(Qt.UserRole + 1, "root_material_id")
self.addRoleName(Qt.UserRole + 2, "id")
@ -109,26 +106,24 @@ class BaseMaterialsModel(ListModel):
# so it's placed here for easy access.
def _canUpdate(self):
global_stack = self._machine_manager.activeMachine
if global_stack is None or not self._enabled:
return False
extruder_position = str(self._extruder_position)
if extruder_position not in global_stack.extruders:
return False
extruder_stack = global_stack.extruders[extruder_position]
self._available_materials = self._material_manager.getAvailableMaterialsForMachineExtruder(global_stack, extruder_stack)
if self._available_materials is None:
return False
self._available_materials = self._material_manager.getAvailableMaterialsForMachineExtruder(global_stack, extruder_stack)
return True
## This is another convenience function which is shared by all material
# models so it's put here to avoid having so much duplicated code.
def _createMaterialItem(self, root_material_id, container_node):
metadata = CuraContainerRegistry.getInstance().findContainersMetadata(id = container_node.container_id)[0]
metadata_list = CuraContainerRegistry.getInstance().findContainersMetadata(id = container_node.container_id)
if not metadata_list:
return None
metadata = metadata_list[0]
item = {
"root_material_id": root_material_id,
"id": metadata["id"],

View file

@ -2,11 +2,20 @@
# Cura is released under the terms of the LGPLv3 or higher.
from cura.Machines.Models.BaseMaterialsModel import BaseMaterialsModel
import cura.CuraApplication # To listen to changes to the preferences.
## Model that shows the list of favorite materials.
class FavoriteMaterialsModel(BaseMaterialsModel):
def __init__(self, parent = None):
super().__init__(parent)
cura.CuraApplication.CuraApplication.getInstance().getPreferences().preferenceChanged.connect(self._onFavoritesChanged)
self._update()
## Triggered when any preference changes, but only handles it when the list
# of favourites is changed.
def _onFavoritesChanged(self, preference_key: str) -> None:
if preference_key != "cura/favorite_materials":
return
self._update()
def _update(self):
@ -28,7 +37,8 @@ class FavoriteMaterialsModel(BaseMaterialsModel):
continue
item = self._createMaterialItem(root_material_id, container_node)
item_list.append(item)
if item:
item_list.append(item)
# Sort the item list alphabetically by name
item_list = sorted(item_list, key = lambda d: d["brand"].upper())

View file

@ -24,11 +24,12 @@ class GenericMaterialsModel(BaseMaterialsModel):
continue
# Only add results for generic materials
if container_node.getMetaDataEntry("brand").lower() != "generic":
if container_node.getMetaDataEntry("brand", "unknown").lower() != "generic":
continue
item = self._createMaterialItem(root_material_id, container_node)
item_list.append(item)
if item:
item_list.append(item)
# Sort the item list alphabetically by name
item_list = sorted(item_list, key = lambda d: d["name"].upper())

View file

@ -8,6 +8,7 @@ from PyQt5.QtCore import Qt, QObject, pyqtProperty, pyqtSignal
from UM.Qt.ListModel import ListModel
from UM.Settings.ContainerRegistry import ContainerRegistry
from cura.Machines.ContainerTree import ContainerTree
from cura.Settings.IntentManager import IntentManager
import cura.CuraApplication
@ -47,13 +48,11 @@ class IntentModel(ListModel):
def _update(self) -> None:
new_items = [] # type: List[Dict[str, Any]]
application = cura.CuraApplication.CuraApplication.getInstance()
quality_manager = application.getQualityManager()
global_stack = application.getGlobalContainerStack()
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
if not global_stack:
self.setItems(new_items)
return
quality_groups = quality_manager.getQualityGroups(global_stack)
quality_groups = ContainerTree.getInstance().getCurrentQualityGroups()
for intent_category, quality_type in IntentManager.getInstance().getCurrentAvailableIntents():
if intent_category == self._intent_category:

View file

@ -55,7 +55,8 @@ class MaterialBrandsModel(BaseMaterialsModel):
# Now handle the individual materials
item = self._createMaterialItem(root_material_id, container_node)
brand_group_dict[brand][material_type].append(item)
if item:
brand_group_dict[brand][material_type].append(item)
# Part 2: Organize the tree into models
#

View file

@ -1,10 +1,11 @@
# Copyright (c) 2018 Ultimaker B.V.
# Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import Qt, pyqtSlot
from UM.Qt.ListModel import ListModel
from UM.Logger import Logger
from UM.Qt.ListModel import ListModel
from cura.Machines.ContainerTree import ContainerTree
#
# This the QML model for the quality management page.
@ -42,7 +43,7 @@ class QualityManagementModel(ListModel):
self.setItems([])
return
quality_group_dict = self._quality_manager.getQualityGroups(global_stack)
quality_group_dict = ContainerTree.getInstance().getCurrentQualityGroups()
quality_changes_group_dict = self._quality_manager.getQualityChangesGroups(global_stack)
available_quality_types = set(quality_type for quality_type, quality_group in quality_group_dict.items()

View file

@ -105,37 +105,12 @@ class QualityManager(QObject):
# \return A dictionary with quality types as keys and the quality groups
# for those types as values.
def getQualityGroups(self, global_stack: "GlobalStack") -> Dict[str, QualityGroup]:
# Gather up the variant names and material base files for each extruder.
variant_names = [extruder.variant.getName() for extruder in global_stack.extruders.values()]
material_bases = [extruder.material.getMetaDataEntry("base_file") for extruder in global_stack.extruders.values()]
extruder_enabled = [extruder.isEnabled for extruder in global_stack.extruders.values()]
definition_id = global_stack.definition.getId()
machine_node = ContainerTree.getInstance().machines[definition_id]
# For each extruder, find which quality profiles are available. Later we'll intersect the quality types.
qualities_per_type_per_extruder = {} # type: Dict[str, Dict[str, QualityNode]]
for extruder_nr, extruder in global_stack.extruders.items():
if not extruder.isEnabled:
continue # No qualities available in this extruder. It'll get skipped when intersecting the quality types.
nozzle_name = extruder.variant.getName()
material_base = extruder.material.getMetaDataEntry("base_file")
if nozzle_name not in machine_node.variants or material_base not in machine_node.variants[nozzle_name].materials:
# The printer has no variant/material-specific quality profiles. Use the global quality profiles.
qualities_per_type_per_extruder[extruder_nr] = machine_node.global_qualities
else:
# Use the actually specialised quality profiles.
qualities_per_type_per_extruder[extruder_nr] = machine_node.variants[nozzle_name].materials[material_base].qualities
# Create the quality group for each available type.
quality_groups = {}
for quality_type, global_quality_node in machine_node.global_qualities.items():
quality_groups[quality_type] = QualityGroup(name = global_quality_node.getMetaDataEntry("name", "Unnamed profile"), quality_type = quality_type)
quality_groups[quality_type].node_for_global = global_quality_node
for extruder, qualities_per_type in qualities_per_type_per_extruder:
quality_groups[quality_type].nodes_for_extruders[extruder] = qualities_per_type[quality_type]
available_quality_types = set(quality_groups.keys())
for qualities_per_type in qualities_per_type_per_extruder.values():
available_quality_types.intersection_update(qualities_per_type.keys())
for quality_type in available_quality_types:
quality_groups[quality_type].is_available = True
return quality_groups
return ContainerTree.getInstance().machines[definition_id].getQualityGroups(variant_names, material_bases, extruder_enabled)
def getQualityGroupsForMachineDefinition(self, machine: "GlobalStack") -> Dict[str, QualityGroup]:
machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition)
@ -160,11 +135,22 @@ class QualityManager(QObject):
return quality_group_dict
## Get the quality group for the preferred quality type for a certain
# global stack.
#
# If the preferred quality type is not available, ``None`` will be
# returned.
# \param machine The global stack of the machine to get the preferred
# quality group for.
# \return The preferred quality group, or ``None`` if that is not
# available.
def getDefaultQualityType(self, machine: "GlobalStack") -> Optional[QualityGroup]:
preferred_quality_type = machine.definition.getMetaDataEntry("preferred_quality_type")
quality_group_dict = self.getQualityGroups(machine)
quality_group = quality_group_dict.get(preferred_quality_type)
return quality_group
machine_node = ContainerTree.getInstance().machines[machine.definition.getId()]
quality_groups = self.getQualityGroups(machine)
result = quality_groups.get(machine_node.preferred_quality_type)
if result is not None and result.is_available:
return result
return None # If preferred quality type is not available, leave it up for the caller.
#

View file

@ -4,7 +4,6 @@
from typing import Union, TYPE_CHECKING
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.Interfaces import ContainerInterface
from cura.Machines.ContainerNode import ContainerNode
from cura.Machines.IntentNode import IntentNode
@ -21,7 +20,6 @@ class QualityNode(ContainerNode):
super().__init__(container_id)
self.parent = parent
self.intents = {} # type: Dict[str, IntentNode]
ContainerRegistry.getInstance().containerAdded.connect(self._intentAdded)
self._loadAll()
def _loadAll(self) -> None:
@ -31,21 +29,4 @@ class QualityNode(ContainerNode):
if not isinstance(self.parent, MachineNode): # Not a global profile.
for intent in container_registry.findInstanceContainersMetadata(type = "intent", definition = self.parent.variant.machine.quality_definition, variant = self.parent.variant.variant_name, material = self.parent.base_file):
self.intents[intent["id"]] = IntentNode(intent["id"], quality = self)
# Otherwise, there are no intents for global profiles.
def _intentAdded(self, container: ContainerInterface) -> None:
from cura.Machines.MachineNode import MachineNode # Imported here to prevent circular imports.
if container.getMetaDataEntry("type") != "intent":
return # Not interested if it's not an intent.
if isinstance(self.parent, MachineNode):
return # Global profiles don't have intents.
if container.getMetaDataEntry("definition") != self.parent.variant.machine.quality_definition:
return # Incorrect printer.
if container.getMetaDataEntry("variant") != self.parent.variant.variant_name:
return # Incorrect variant.
if container.getMetaDataEntry("material") != self.parent.base_file:
return # Incorrect material.
container_id = container.getId()
if container_id in self.intents:
return # Already have this.
self.intents[container_id] = IntentNode(container_id, quality = self)
# Otherwise, there are no intents for global profiles.

View file

@ -115,7 +115,7 @@ class VariantManager:
variant_type: Optional["VariantType"] = None) -> Optional["ContainerNode"]:
if variant_type is None:
variant_node = None
variant_type_dict = self._machine_to_variant_dict_map[machine_definition_id]
variant_type_dict = self._machine_to_variant_dict_map.get("machine_definition_id", {})
for variant_dict in variant_type_dict.values():
if variant_name in variant_dict:
variant_node = variant_dict[variant_name]

View file

@ -9,7 +9,6 @@ from typing import Dict, Union, Any, TYPE_CHECKING, List
from PyQt5.QtCore import QObject, QUrl
from PyQt5.QtWidgets import QMessageBox
from UM.i18n import i18nCatalog
from UM.FlameProfiler import pyqtSlot
from UM.Logger import Logger
@ -17,18 +16,19 @@ from UM.MimeTypeDatabase import MimeTypeDatabase, MimeTypeNotFoundError
from UM.Platform import Platform
from UM.SaveFile import SaveFile
from UM.Settings.ContainerFormatError import ContainerFormatError
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.DefinitionContainer import DefinitionContainer
from UM.Settings.InstanceContainer import InstanceContainer
import cura.CuraApplication
from cura.Machines.MaterialManager import MaterialManager
if TYPE_CHECKING:
from cura.CuraApplication import CuraApplication
from cura.Machines.ContainerNode import ContainerNode
from cura.Machines.MaterialNode import MaterialNode
from cura.Machines.QualityChangesGroup import QualityChangesGroup
from cura.Machines.MaterialManager import MaterialManager
from cura.Machines.QualityManager import QualityManager
catalog = i18nCatalog("cura")
@ -323,22 +323,18 @@ class ContainerManager(QObject):
## Get a list of materials that have the same GUID as the reference material
#
# \param material_id \type{str} the id of the material for which to get the linked materials.
# \return \type{list} a list of names of materials with the same GUID
# \param material_node The node representing the material for which to get
# the same GUID.
# \param exclude_self Whether to include the name of the material you
# provided.
# \return A list of names of materials with the same GUID.
@pyqtSlot("QVariant", bool, result = "QStringList")
def getLinkedMaterials(self, material_node: "MaterialNode", exclude_self: bool = False):
guid = material_node.getMetaDataEntry("GUID", "")
self_root_material_id = material_node.getMetaDataEntry("base_file")
material_group_list = MaterialManager.getInstance().getMaterialGroupListByGUID(guid)
linked_material_names = []
if material_group_list:
for material_group in material_group_list:
if exclude_self and material_group.name == self_root_material_id:
continue
linked_material_names.append(material_group.root_material_node.getMetaDataEntry("name", ""))
return linked_material_names
def getLinkedMaterials(self, material_node: "MaterialNode", exclude_self: bool = False) -> List[str]:
same_guid = ContainerRegistry.getInstance().findInstanceContainersMetadata(guid = material_node.guid)
if exclude_self:
return [metadata["name"] for metadata in same_guid if metadata["base_file"] != material_node.base_file]
else:
return [metadata["name"] for metadata in same_guid]
## Unlink a material from all other materials by creating a new GUID
# \param material_id \type{str} the id of the material to create a new GUID for.

View file

@ -28,7 +28,6 @@ class CuraStackBuilder:
def createMachine(cls, name: str, definition_id: str) -> Optional[GlobalStack]:
from cura.CuraApplication import CuraApplication
application = CuraApplication.getInstance()
quality_manager = application.getQualityManager()
registry = application.getContainerRegistry()
definitions = registry.findDefinitionContainers(id = definition_id)
@ -64,7 +63,7 @@ class CuraStackBuilder:
registry.addContainer(new_extruder)
preferred_quality_type = machine_definition.getMetaDataEntry("preferred_quality_type")
quality_group_dict = quality_manager.getQualityGroups(new_global_stack)
quality_group_dict = ContainerTree.getInstance().getCurrentQualityGroups()
if not quality_group_dict:
# There is no available quality group, set all quality containers to empty.
new_global_stack.quality = application.empty_quality_container

View file

@ -4,6 +4,7 @@
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
from typing import Any, Dict, List, Optional, Set, Tuple, TYPE_CHECKING
import cura.CuraApplication
from cura.Machines.ContainerTree import ContainerTree
from cura.Settings.cura_empty_instance_containers import empty_intent_container
from UM.Settings.InstanceContainer import InstanceContainer
@ -75,7 +76,8 @@ class IntentManager(QObject):
# TODO: We now do this (return a default) if the global stack is missing, but not in the code below,
# even though there should always be defaults. The problem then is what to do with the quality_types.
# Currently _also_ inconsistent with 'currentAvailableIntentCategories', which _does_ return default.
quality_groups = application.getQualityManager().getQualityGroups(global_stack)
quality_groups = ContainerTree.getInstance().getCurrentQualityGroups()
# TODO: These quality nodes in that tree already contain the intent nodes. We can optimise this.
available_quality_types = {quality_group.quality_type for quality_group in quality_groups.values() if quality_group.node_for_global is not None}
final_intent_ids = set() # type: Set[str]

View file

@ -1,8 +1,9 @@
# Copyright (c) 2019 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Optional
from cura.CuraApplication import CuraApplication
from UM.Settings.ContainerRegistry import ContainerRegistry
from cura.PrinterOutput.Models.MaterialOutputModel import MaterialOutputModel
from ..BaseModel import BaseModel
@ -24,32 +25,27 @@ class ClusterPrinterConfigurationMaterial(BaseModel):
self.material = material
super().__init__(**kwargs)
## Creates a material output model based on this cloud printer material.
## Creates a material output model based on this cloud printer material.
#
# A material is chosen that matches the current GUID. If multiple such
# materials are available, read-only materials are preferred and the
# material with the earliest alphabetical name will be selected.
# \return A material output model that matches the current GUID.
def createOutputModel(self) -> MaterialOutputModel:
material_manager = CuraApplication.getInstance().getMaterialManager()
material_group_list = material_manager.getMaterialGroupListByGUID(self.guid) or []
# Sort the material groups by "is_read_only = True" first, and then the name alphabetically.
read_only_material_group_list = list(filter(lambda x: x.is_read_only, material_group_list))
non_read_only_material_group_list = list(filter(lambda x: not x.is_read_only, material_group_list))
material_group = None
if read_only_material_group_list:
read_only_material_group_list = sorted(read_only_material_group_list, key = lambda x: x.name)
material_group = read_only_material_group_list[0]
elif non_read_only_material_group_list:
non_read_only_material_group_list = sorted(non_read_only_material_group_list, key = lambda x: x.name)
material_group = non_read_only_material_group_list[0]
if material_group:
container = material_group.root_material_node.container
color = container.getMetaDataEntry("color_code")
brand = container.getMetaDataEntry("brand")
material_type = container.getMetaDataEntry("material")
name = container.getName()
container_registry = ContainerRegistry.getInstance()
same_guid = container_registry.findInstanceContainersMetadata(GUID = self.guid)
if same_guid:
read_only = sorted(filter(lambda metadata: container_registry.isReadOnly(metadata["id"]), same_guid), key = lambda metadata: metadata["name"])
if read_only:
material_metadata = read_only[0]
else:
material_metadata = min(same_guid, key = lambda metadata: metadata["name"])
else:
color = self.color
brand = self.brand
material_type = self.material
name = "Empty" if self.material == "empty" else "Unknown"
material_metadata = {
"color_code": self.color,
"brand": self.brand,
"material": self.material,
"name": "Empty" if self.material == "empty" else "Unknown"
}
return MaterialOutputModel(guid=self.guid, type=material_type, brand=brand, color=color, name=name)
return MaterialOutputModel(guid = self.guid, type = material_metadata["material"], brand = material_metadata["brand"], color = material_metadata["color_code"], name = material_metadata["name"])

View file

@ -244,7 +244,10 @@ class XmlMaterialProfile(InstanceContainer):
variant_name = container.getMetaDataEntry("variant_name")
if variant_name:
variant_dict = {"variant_node": variant_manager.getVariantNode(definition_id, variant_name),
variant_node = variant_manager.getVariantNode(definition_id, variant_name)
if variant_node is None:
continue
variant_dict = {"variant_node":variant_node ,
"material_container": container}
machine_variant_map[definition_id][variant_name] = variant_dict
continue

View file

@ -17,6 +17,8 @@ Item
property var resetEnabled: false
property var currentItem: null
property var materialManager: CuraApplication.getMaterialManager()
property var hasCurrentItem: base.currentItem != null
property var isCurrentItemActivated:
{
@ -119,7 +121,7 @@ Item
onClicked:
{
forceActiveFocus();
base.newRootMaterialIdToSwitchTo = CuraApplication.getMaterialManager().createMaterial();
base.newRootMaterialIdToSwitchTo = base.materialManager.createMaterial();
base.toActivateNewMaterial = true;
}
}
@ -134,7 +136,7 @@ Item
onClicked:
{
forceActiveFocus();
base.newRootMaterialIdToSwitchTo = CuraApplication.getMaterialManager().duplicateMaterial(base.currentItem.container_node);
base.newRootMaterialIdToSwitchTo = base.materialManager.duplicateMaterial(base.currentItem.container_node);
base.toActivateNewMaterial = true;
}
}
@ -145,7 +147,8 @@ Item
id: removeMenuButton
text: catalog.i18nc("@action:button", "Remove")
iconName: "list-remove"
enabled: base.hasCurrentItem && !base.currentItem.is_read_only && !base.isCurrentItemActivated && CuraApplication.getMaterialManager().canMaterialBeRemoved(base.currentItem.container_node)
enabled: base.hasCurrentItem && !base.currentItem.is_read_only && !base.isCurrentItemActivated && base.materialManager.canMaterialBeRemoved(base.currentItem.container_node)
onClicked:
{
forceActiveFocus();
@ -294,7 +297,7 @@ Item
{
// Set the active material as the fallback. It will be selected when the current material is deleted
base.newRootMaterialIdToSwitchTo = base.active_root_material_id
CuraApplication.getMaterialManager().removeMaterial(base.currentItem.container_node);
base.materialManager.removeMaterial(base.currentItem.container_node);
}
}

View file

@ -4,14 +4,6 @@ import pytest
from UM.Settings.Interfaces import ContainerInterface
from cura.Machines.MachineNode import MachineNode
machine_node_variant_added_test_data = [({"type": "Not a variant!"}, ["Variant One", "Variant Two"]), # Wrong type
({"type": "variant", "name": "Variant One"}, ["Variant One", "Variant Two"]), # Name already added
({"type": "variant", "name": "Variant Three", "hardware_type": "Not a nozzle"}, ["Variant One", "Variant Two"]), # Wrong hardware type
({"type": "variant", "name": "Variant Three", "hardware_type": "nozzle", "definition": "machine_3"}, ["Variant One", "Variant Two"]), # Wrong definition ID
({"type": "variant", "name": "Variant Three", "hardware_type": "nozzle", "definition": "machine_1"}, ["Variant One", "Variant Two", "Variant Three"])] # Yay! It's finally added
metadata_dict = {}
@ -44,18 +36,4 @@ def test_machineNodeInit(container_registry):
# As variants get stored by name, we want to check if those get added.
assert "Variant One" in machine_node.variants
assert "Variant Two" in machine_node.variants
assert len(machine_node.variants) == 2 # And ensure that *only* those two got added.
@pytest.mark.parametrize("metadata,variant_result_list", machine_node_variant_added_test_data)
def test_machineNodeVariantAdded(container_registry, metadata, variant_result_list):
machine_node = createMachineNode("machine_1", container_registry)
with patch("cura.Machines.MachineNode.VariantNode"): # We're not testing the variant node here, so patch it out.
with patch.dict(metadata_dict, metadata):
mocked_container = createMockedInstanceContainer()
machine_node._variantAdded(mocked_container)
assert len(variant_result_list) == len(machine_node.variants)
for name in variant_result_list:
assert name in machine_node.variants
assert len(machine_node.variants) == 2 # And ensure that *only* those two got added.

View file

@ -6,21 +6,6 @@ from cura.Machines.MaterialNode import MaterialNode
instance_container_metadata_dict = {"fdmprinter": {"no_variant": [{"id": "quality_1", "material": "material_1"}]},
"machine_1": {"variant_1": {"material_1": [{"id": "quality_2", "material": "material_1"}, {"id": "quality_3","material": "material_1"}]}}}
quality_metadata_machine_quality_test_data = [({"type": "Not a quality"}, ["quality_2", "quality_3"]), # Wrong type
({"type": "quality", "definition": "machine_2"}, ["quality_2", "quality_3"]), # Wrong defintion
({"type": "quality", "definition": "machine_1", "variant": "variant_2"}, ["quality_2", "quality_3"]), # Wrong variant
({"type": "quality", "definition": "machine_1", "variant": "variant_1", "material": "material_2"}, ["quality_2", "quality_3"]), # wrong material
]
quality_metadata_no_machine_quality =[({"type": "Not a quality"}, ["quality_1"]), # Wrong type
({"type": "quality", "definition": "machine_1"}, ["quality_1"]), # Wrong defintion (it needs fdmprinter)
({"type": "quality", "definition": "fdmprinter", "variant": "variant_2"}, ["quality_1", "quality_4"]), # Wrong variant, but should be added (as we ignore the variant)
({"type": "quality", "definition": "fdmprinter", "variant": "variant_1", "material": "material_2"}, ["quality_1", "quality_4"]), # wrong material, but should be added (as we ignore the material)
({"type": "quality", "definition": "fdmprinter", "variant": "variant_1", "material": "material_1"}, ["quality_1", "quality_4"]),
]
metadata_dict = {}
@ -86,45 +71,4 @@ def test_materialNodeInit_MachineQuality(container_registry):
assert len(node.qualities) == 2
assert "quality_2" in node.qualities
assert "quality_3" in node.qualities
@pytest.mark.parametrize("metadata,qualities_result_list", quality_metadata_machine_quality_test_data)
def test_qualityAdded_hasMachineQuality(container_registry, metadata, qualities_result_list):
variant_node = MagicMock()
variant_node.variant_name = "variant_1"
variant_node.machine.has_machine_quality = True
variant_node.machine.quality_definition = "machine_1"
container = createMockedInstanceContainer("quality_4")
with patch("cura.Machines.MaterialNode.QualityNode"):
with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)):
node = MaterialNode("material_1", variant_node)
with patch.dict(metadata_dict, metadata):
node._qualityAdded(container)
assert len(qualities_result_list) == len(node.qualities)
for name in qualities_result_list:
assert name in node.qualities
@pytest.mark.parametrize("metadata,qualities_result_list", quality_metadata_no_machine_quality)
def test_qualityAdded_noMachineQuality(container_registry, metadata, qualities_result_list):
variant_node = MagicMock()
variant_node.variant_name = "variant_1"
variant_node.machine.has_machine_quality = False
container = createMockedInstanceContainer("quality_4")
with patch("cura.Machines.MaterialNode.QualityNode"):
with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)):
node = MaterialNode("material_1", variant_node)
with patch.dict(metadata_dict, metadata):
node._qualityAdded(container)
assert len(qualities_result_list) == len(node.qualities)
for name in qualities_result_list:
assert name in node.qualities
assert "quality_3" in node.qualities

View file

@ -1,21 +1,12 @@
from unittest.mock import patch, MagicMock
import pytest
from cura.Machines.MaterialNode import MaterialNode
from cura.Machines.QualityNode import QualityNode
instance_container_metadata_dict = {"fdmprinter": {"variant_1": {"material_1": [{"id": "intent_1"}, {"id": "intent_2"}]}},
"machine_1": {"variant_2": {"material_2": [{"id": "intent_3"}, {"id": "intent_4"}]}}}
intent_metadata_intent_added_data = [({"type": "Not an intent"}, ["intent_3", "intent_4"]), # Wrong type
({"type": "intent", "definition": "machine_9000"}, ["intent_3", "intent_4"]), # wrong definition
({"type": "intent", "definition": "machine_1", "variant": "variant_299101"}, ["intent_3", "intent_4"]), # wrong variant
({"type": "intent", "definition": "machine_1", "variant": "variant_2", "material": "super cool material!"}, ["intent_3", "intent_4"]), # Wrong material
({"type": "intent", "definition": "machine_1", "variant": "variant_2", "material": "material_2"}, ["intent_3", "intent_4", "intent_9001"]), # Yay, all good.
]
metadata_dict = {}
@ -55,24 +46,4 @@ def test_qualityNode_machine_1(container_registry):
assert len(node.intents) == 2
assert "intent_3" in node.intents
assert "intent_4" in node.intents
@pytest.mark.parametrize("metadata,intent_result_list", intent_metadata_intent_added_data)
def test_intentNodeAdded(container_registry, metadata, intent_result_list):
material_node = MagicMock()
material_node.variant.machine.quality_definition = "machine_1"
material_node.variant.variant_name = "variant_2"
material_node.base_file = "material_2"
intent_container = createMockedInstanceContainer("intent_9001")
with patch("cura.Machines.QualityNode.IntentNode"):
with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)):
node = QualityNode("quality_1", material_node)
with patch.dict(metadata_dict, metadata):
node._intentAdded(intent_container)
assert len(intent_result_list) == len(node.intents)
for identifier in intent_result_list:
assert identifier in node.intents
assert "intent_4" in node.intents