From ca163ea5f09f8ff3fe9fd205906aded09543a838 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 15 Aug 2019 14:01:12 +0200 Subject: [PATCH 01/37] Skip inactive extruders to determine available quality types Contributes to issue CURA-6600. --- cura/Machines/QualityManager.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cura/Machines/QualityManager.py b/cura/Machines/QualityManager.py index 07ef857af4..73660ddfff 100644 --- a/cura/Machines/QualityManager.py +++ b/cura/Machines/QualityManager.py @@ -112,7 +112,7 @@ class QualityManager(QObject): 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. + 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: @@ -131,7 +131,9 @@ class QualityManager(QObject): 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(): + for extruder_nr, qualities_per_type in qualities_per_type_per_extruder.items(): + if not global_stack.extruders[extruder_nr].isEnabled: + continue available_quality_types.intersection_update(qualities_per_type.keys()) for quality_type in available_quality_types: quality_groups[quality_type].is_available = True From bee5491879430298efda8a56592aa502bbb80247 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 15 Aug 2019 14:09:18 +0200 Subject: [PATCH 02/37] Move getQualityGroups to MachineNode It's specific to that machine and the container tree structure, so this is the best place for it we could find. Contributes to issue CURA-6600. --- cura/Machines/MachineNode.py | 50 ++++++++++++++++++++++++++++++++- cura/Machines/QualityManager.py | 42 +++++++-------------------- 2 files changed, 59 insertions(+), 33 deletions(-) diff --git a/cura/Machines/MachineNode.py b/cura/Machines/MachineNode.py index b474ae94bd..4caceb3026 100644 --- a/cura/Machines/MachineNode.py +++ b/cura/Machines/MachineNode.py @@ -1,13 +1,14 @@ # 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 List, TYPE_CHECKING 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 @@ -40,6 +41,53 @@ class MachineNode(ContainerNode): 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 = [] # 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. diff --git a/cura/Machines/QualityManager.py b/cura/Machines/QualityManager.py index 73660ddfff..c71d97b76e 100644 --- a/cura/Machines/QualityManager.py +++ b/cura/Machines/QualityManager.py @@ -105,39 +105,17 @@ 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 = [] + material_bases = [] + extruder_enabled = [] + for extruder in global_stack.extruders.values(): + variant_names.append(extruder.variant.getName()) + material_bases.append(extruder.material.getMetaDataEntry("base_file")) + extruder_enabled.append(extruder.isEnabled) + 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 extruder_nr, qualities_per_type in qualities_per_type_per_extruder.items(): - if not global_stack.extruders[extruder_nr].isEnabled: - 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 + 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) From 370d7adcd5ae46d90dabe95e6f29df2a2a2b96a5 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 15 Aug 2019 14:20:58 +0200 Subject: [PATCH 03/37] Let getDefaultQualityType use the container tree structure Contributes to issue CURA-6600. --- cura/Machines/MachineNode.py | 1 + cura/Machines/QualityManager.py | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/cura/Machines/MachineNode.py b/cura/Machines/MachineNode.py index 4caceb3026..a302123044 100644 --- a/cura/Machines/MachineNode.py +++ b/cura/Machines/MachineNode.py @@ -37,6 +37,7 @@ 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() diff --git a/cura/Machines/QualityManager.py b/cura/Machines/QualityManager.py index c71d97b76e..6227653040 100644 --- a/cura/Machines/QualityManager.py +++ b/cura/Machines/QualityManager.py @@ -140,11 +140,20 @@ class QualityManager(QObject): return quality_group_dict - 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 + def getDefaultQualityType(self, machine: "GlobalStack") -> QualityGroup: + machine_node = ContainerTree.getInstance().machines[machine.definition.getId()] + variant_names = [] + material_bases = [] + extruder_enabled = [] + for extruder in machine.extruders.values(): + variant_names.append(extruder.variant.getName()) + material_bases.append(extruder.material.getMetaDataEntry("base_file")) + extruder_enabled.append(extruder.isEnabled) + quality_groups = machine_node.getQualityGroups(variant_names, material_bases, extruder_enabled) + result = quality_groups.get(machine_node.preferred_quality_type) + if result is not None: + return result + return next(iter(quality_groups.values())) # If preferred quality type is not available, pick any quality type. # From 02fbdd96a4c6e350652126a26d1f420e0d92f884 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 15 Aug 2019 14:26:28 +0200 Subject: [PATCH 04/37] Don't return quality groups that are not available And don't return an arbitrary group then. That arbitrary group could also be unavailable. Contributes to issue CURA-6600. --- cura/Machines/QualityManager.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/cura/Machines/QualityManager.py b/cura/Machines/QualityManager.py index 6227653040..664b091001 100644 --- a/cura/Machines/QualityManager.py +++ b/cura/Machines/QualityManager.py @@ -140,20 +140,22 @@ class QualityManager(QObject): return quality_group_dict - def getDefaultQualityType(self, machine: "GlobalStack") -> QualityGroup: + ## 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]: machine_node = ContainerTree.getInstance().machines[machine.definition.getId()] - variant_names = [] - material_bases = [] - extruder_enabled = [] - for extruder in machine.extruders.values(): - variant_names.append(extruder.variant.getName()) - material_bases.append(extruder.material.getMetaDataEntry("base_file")) - extruder_enabled.append(extruder.isEnabled) - quality_groups = machine_node.getQualityGroups(variant_names, material_bases, extruder_enabled) + quality_groups = self.getQualityGroups(machine) result = quality_groups.get(machine_node.preferred_quality_type) - if result is not None: + if result is not None and result.is_available: return result - return next(iter(quality_groups.values())) # If preferred quality type is not available, pick any quality type. + return None # If preferred quality type is not available, leave it up for the caller. # From 44c98ba0d1a0a63a02db0f1e71af8bb93269fe5a Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 15 Aug 2019 14:38:35 +0200 Subject: [PATCH 05/37] Fix import Should've checked first... Contributes to issue CURA-6600. --- cura/Machines/MachineNode.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cura/Machines/MachineNode.py b/cura/Machines/MachineNode.py index a302123044..d5a9739e53 100644 --- a/cura/Machines/MachineNode.py +++ b/cura/Machines/MachineNode.py @@ -1,7 +1,7 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import List, TYPE_CHECKING +from typing import Dict, List from UM.Logger import Logger from UM.Util import parseBool @@ -12,9 +12,6 @@ from cura.Machines.QualityGroup import QualityGroup # To construct groups of qu 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. From 8a515822102d68e2ce0f1e4f52d87b509c7029ef Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 15 Aug 2019 16:17:19 +0200 Subject: [PATCH 06/37] Pre-fill list of qualities_per_type_per_extruder with empty dicts Otherwise you get an IndexError later on. Contributes to issue CURA-6600. --- cura/Machines/MachineNode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Machines/MachineNode.py b/cura/Machines/MachineNode.py index d5a9739e53..990aeae935 100644 --- a/cura/Machines/MachineNode.py +++ b/cura/Machines/MachineNode.py @@ -57,7 +57,7 @@ class MachineNode(ContainerNode): 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 = [] # type: List[Dict[str, QualityNode]] + 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. From ed19e9e15c8daa24b333576a1aa1647cb455776b Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 15 Aug 2019 16:21:51 +0200 Subject: [PATCH 07/37] Use ContainerTree to find quality groups Contributes to issue CURA-6600. --- cura/Machines/Models/IntentModel.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/cura/Machines/Models/IntentModel.py b/cura/Machines/Models/IntentModel.py index 275087689b..c5d63183dd 100644 --- a/cura/Machines/Models/IntentModel.py +++ b/cura/Machines/Models/IntentModel.py @@ -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,15 @@ 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) + definition_id = global_stack.definition.getId() + 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()] + quality_groups = ContainerTree.getInstance().machines[definition_id].getQualityGroups(variant_names, material_bases, extruder_enabled) for intent_category, quality_type in IntentManager.getInstance().getCurrentAvailableIntents(): if intent_category == self._intent_category: From 3f944462edf18b9758ef29dffdcf639eb55f0f4f Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 15 Aug 2019 16:35:30 +0200 Subject: [PATCH 08/37] Use ContainerTree to get quality groups Contributes to issue CURA-6600. --- cura/Machines/Models/QualityManagementModel.py | 11 ++++++++--- cura/Machines/QualityManager.py | 11 +++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cura/Machines/Models/QualityManagementModel.py b/cura/Machines/Models/QualityManagementModel.py index 315ab010bb..850e001473 100644 --- a/cura/Machines/Models/QualityManagementModel.py +++ b/cura/Machines/Models/QualityManagementModel.py @@ -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,11 @@ class QualityManagementModel(ListModel): self.setItems([]) return - quality_group_dict = self._quality_manager.getQualityGroups(global_stack) + variant_names = [extruder.variant.getName() for extruder in global_stack.extruders] + material_bases = [extruder.material.getMetaDataEntry("base_file") for extruder in global_stack.extruders] + extruder_enabled = [extruder.isEnabled for extruder in global_stack.extruders] + definition_id = global_stack.definition.getId() + quality_group_dict = ContainerTree.getInstance().machines[definition_id].getQualityGroups(variant_names, material_bases, extruder_enabled) 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() diff --git a/cura/Machines/QualityManager.py b/cura/Machines/QualityManager.py index 664b091001..79adef2408 100644 --- a/cura/Machines/QualityManager.py +++ b/cura/Machines/QualityManager.py @@ -106,14 +106,9 @@ class QualityManager(QObject): # 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 = [] - material_bases = [] - extruder_enabled = [] - for extruder in global_stack.extruders.values(): - variant_names.append(extruder.variant.getName()) - material_bases.append(extruder.material.getMetaDataEntry("base_file")) - extruder_enabled.append(extruder.isEnabled) - + variant_names = [extruder.variant.getName() for extruder in global_stack.extruders] + material_bases = [extruder.material.getMetaDataEntry("base_file") for extruder in global_stack.extruders] + extruder_enabled = [extruder.isEnabled for extruder in global_stack.extruders] definition_id = global_stack.definition.getId() return ContainerTree.getInstance().machines[definition_id].getQualityGroups(variant_names, material_bases, extruder_enabled) From d4cd5a7ea5f74225c42b5fae8c525efc4a0cc461 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 15 Aug 2019 16:53:38 +0200 Subject: [PATCH 09/37] Add convenience function to get quality groups for current printer This is used very often all over the code. Contributes to issue CURA-6600. --- cura/Machines/ContainerTree.py | 20 ++++++++++++++++++- cura/Machines/Models/IntentModel.py | 6 +----- .../Machines/Models/QualityManagementModel.py | 6 +----- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/cura/Machines/ContainerTree.py b/cura/Machines/ContainerTree.py index 2f641973ac..e43affa714 100644 --- a/cura/Machines/ContainerTree.py +++ b/cura/Machines/ContainerTree.py @@ -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,19 @@ 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() + 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.") diff --git a/cura/Machines/Models/IntentModel.py b/cura/Machines/Models/IntentModel.py index c5d63183dd..5a44883b76 100644 --- a/cura/Machines/Models/IntentModel.py +++ b/cura/Machines/Models/IntentModel.py @@ -52,11 +52,7 @@ class IntentModel(ListModel): if not global_stack: self.setItems(new_items) return - definition_id = global_stack.definition.getId() - 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()] - quality_groups = ContainerTree.getInstance().machines[definition_id].getQualityGroups(variant_names, material_bases, extruder_enabled) + quality_groups = ContainerTree.getInstance().getCurrentQualityGroups() for intent_category, quality_type in IntentManager.getInstance().getCurrentAvailableIntents(): if intent_category == self._intent_category: diff --git a/cura/Machines/Models/QualityManagementModel.py b/cura/Machines/Models/QualityManagementModel.py index 850e001473..2a661ec49e 100644 --- a/cura/Machines/Models/QualityManagementModel.py +++ b/cura/Machines/Models/QualityManagementModel.py @@ -43,11 +43,7 @@ class QualityManagementModel(ListModel): self.setItems([]) return - variant_names = [extruder.variant.getName() for extruder in global_stack.extruders] - material_bases = [extruder.material.getMetaDataEntry("base_file") for extruder in global_stack.extruders] - extruder_enabled = [extruder.isEnabled for extruder in global_stack.extruders] - definition_id = global_stack.definition.getId() - quality_group_dict = ContainerTree.getInstance().machines[definition_id].getQualityGroups(variant_names, material_bases, extruder_enabled) + 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() From 9bcd3d8c1e7cc9493c786b99e3bf2794e6f78e85 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 15 Aug 2019 16:55:24 +0200 Subject: [PATCH 10/37] Use ContainerTree to get quality groups instead of QualityManager Contributes to issue CURA-6600. --- cura/Settings/CuraStackBuilder.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cura/Settings/CuraStackBuilder.py b/cura/Settings/CuraStackBuilder.py index e5bce516bf..d0424983cd 100644 --- a/cura/Settings/CuraStackBuilder.py +++ b/cura/Settings/CuraStackBuilder.py @@ -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 From 606bef9b5fc348fd97c0b80f3fc7ba9873fbcef0 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 15 Aug 2019 16:58:57 +0200 Subject: [PATCH 11/37] Use ContainerTree structure to get quality groups Contributes to issue CURA-6600. --- cura/Settings/IntentManager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py index 5a3c1a737f..2cfbf11ae2 100644 --- a/cura/Settings/IntentManager.py +++ b/cura/Settings/IntentManager.py @@ -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] From 46e5bc897f8a5f2e254cd02ecb52601376a96ebc Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 15 Aug 2019 17:00:43 +0200 Subject: [PATCH 12/37] Fix deprecated getQualityGroups in QualityManager It needs to get the actual extruders, not their keys. Contributes to issue CURA-6600. --- cura/Machines/QualityManager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/Machines/QualityManager.py b/cura/Machines/QualityManager.py index 79adef2408..57bcaa9c70 100644 --- a/cura/Machines/QualityManager.py +++ b/cura/Machines/QualityManager.py @@ -106,9 +106,9 @@ class QualityManager(QObject): # 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] - material_bases = [extruder.material.getMetaDataEntry("base_file") for extruder in global_stack.extruders] - extruder_enabled = [extruder.isEnabled for extruder in global_stack.extruders] + 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() return ContainerTree.getInstance().machines[definition_id].getQualityGroups(variant_names, material_bases, extruder_enabled) From 8d05ebef9dea11a4aa0e6238cd6175c260c8019b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 16 Aug 2019 10:24:50 +0200 Subject: [PATCH 13/37] Fix crash on adding a new printer CURA-6600 --- cura/Machines/ContainerTree.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cura/Machines/ContainerTree.py b/cura/Machines/ContainerTree.py index e43affa714..ca24950c78 100644 --- a/cura/Machines/ContainerTree.py +++ b/cura/Machines/ContainerTree.py @@ -42,6 +42,8 @@ class ContainerTree: # \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()] From 97e77994a1ff19462e13f596ceee8a6b0fa4a725 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 16 Aug 2019 10:55:12 +0200 Subject: [PATCH 14/37] Update the removing & duplicating of material CURA-6600 --- cura/Machines/MaterialManager.py | 43 +++++++++++--------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index bbe305a01b..d2eb829eec 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -254,25 +254,13 @@ 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 @@ -327,14 +315,14 @@ class MaterialManager(QObject): 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", "")) - material_group = self.getMaterialGroup(root_material_id) - if not material_group: + container_registry = CuraContainerRegistry.getInstance() + results = container_registry.findContainers(id=root_material_id) + + 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 +341,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 +357,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: From 80baeb98736a223775d82bf51689e25fa60324cb Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 16 Aug 2019 16:28:42 +0200 Subject: [PATCH 15/37] Remove _added functions for nodes that can't be added during runtime Among the machines, variants, materials, qualities and intents, only machines and materials can ever be added during runtime. For the rest, we don't need to listen to these signals. Contributes to issue CURA-6600. --- cura/Machines/MachineNode.py | 19 +--------- cura/Machines/MaterialNode.py | 42 ++-------------------- cura/Machines/QualityNode.py | 21 +---------- tests/Machines/TestMachineNode.py | 24 +------------ tests/Machines/TestMaterialNode.py | 58 +----------------------------- tests/Machines/TestQualityNode.py | 31 +--------------- 6 files changed, 7 insertions(+), 188 deletions(-) diff --git a/cura/Machines/MachineNode.py b/cura/Machines/MachineNode.py index 990aeae935..f0a001b2d6 100644 --- a/cura/Machines/MachineNode.py +++ b/cura/Machines/MachineNode.py @@ -6,7 +6,6 @@ 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 @@ -36,7 +35,6 @@ class MachineNode(ContainerNode): 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. @@ -101,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) \ No newline at end of file + self.global_qualities[global_quality["quality_type"]] = QualityNode(global_quality["id"], parent = self) \ No newline at end of file diff --git a/cura/Machines/MaterialNode.py b/cura/Machines/MaterialNode.py index 5fa28de77f..31a73f9e2f 100644 --- a/cura/Machines/MaterialNode.py +++ b/cura/Machines/MaterialNode.py @@ -4,7 +4,6 @@ from typing import TYPE_CHECKING from UM.Settings.ContainerRegistry import ContainerRegistry -from UM.Settings.Interfaces import ContainerInterface from cura.Machines.ContainerNode import ContainerNode from cura.Machines.QualityNode import QualityNode @@ -20,10 +19,8 @@ class MaterialNode(ContainerNode): super().__init__(container_id) self.variant = variant self.qualities = {} # type: Dict[str, QualityNode] # Mapping container IDs to quality profiles. - container_registry = ContainerRegistry.getInstance() - my_metadata = container_registry.findContainersMetadata(id = container_id)[0] + my_metadata = ContainerRegistry.getInstance().findContainersMetadata(id = container_id)[0] self.base_file = my_metadata["base_file"] - container_registry.containerAdded.connect(self._qualityAdded) self._loadAll() def _loadAll(self) -> None: @@ -47,39 +44,4 @@ class MaterialNode(ContainerNode): for quality in qualities: quality_id = quality["id"] 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 - - 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. - - quality_id = container.getId() - self.qualities[quality_id] = QualityNode(quality_id, parent = self) \ No newline at end of file + self.qualities[quality_id] = QualityNode(quality_id, parent = self) \ No newline at end of file diff --git a/cura/Machines/QualityNode.py b/cura/Machines/QualityNode.py index 8e58700589..09785056f1 100644 --- a/cura/Machines/QualityNode.py +++ b/cura/Machines/QualityNode.py @@ -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) \ No newline at end of file + # Otherwise, there are no intents for global profiles. \ No newline at end of file diff --git a/tests/Machines/TestMachineNode.py b/tests/Machines/TestMachineNode.py index db3946fc3a..fe0330609a 100644 --- a/tests/Machines/TestMachineNode.py +++ b/tests/Machines/TestMachineNode.py @@ -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 \ No newline at end of file + assert len(machine_node.variants) == 2 # And ensure that *only* those two got added. \ No newline at end of file diff --git a/tests/Machines/TestMaterialNode.py b/tests/Machines/TestMaterialNode.py index 099be2b891..f9ea0e3622 100644 --- a/tests/Machines/TestMaterialNode.py +++ b/tests/Machines/TestMaterialNode.py @@ -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 \ No newline at end of file + assert "quality_3" in node.qualities \ No newline at end of file diff --git a/tests/Machines/TestQualityNode.py b/tests/Machines/TestQualityNode.py index cad30d642c..64685689c2 100644 --- a/tests/Machines/TestQualityNode.py +++ b/tests/Machines/TestQualityNode.py @@ -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 \ No newline at end of file From 4dd5cb1a85440b54337e3abebfff877c554b7bf2 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 19 Aug 2019 09:44:39 +0200 Subject: [PATCH 16/37] Fix getMaterialByType CURA-6600 --- cura/Machines/MaterialManager.py | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index d2eb829eec..973b47fce8 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -152,27 +152,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 From 76b58134aefd411e00145b75089e7f6928fc15b2 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 19 Aug 2019 11:07:35 +0200 Subject: [PATCH 17/37] Fix getRootMaterialIdForDiameter CURA-6600 --- cura/Machines/MaterialManager.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index 973b47fce8..09416d1704 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -87,7 +87,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, "") From dc26f98a3ade105d853e51b05f99eb043553e9b7 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 19 Aug 2019 11:30:04 +0200 Subject: [PATCH 18/37] Fix import issue CURA-6600 --- cura/Settings/ContainerManager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index 1438faee96..d8f758d8a0 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -21,14 +21,14 @@ 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") From 2417705dfd6f99ebcaa4d7516ebfc4367fd155cb Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 19 Aug 2019 11:42:52 +0200 Subject: [PATCH 19/37] Prevent crashes when duplicating a material CURA-6600 --- cura/Machines/VariantManager.py | 2 +- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cura/Machines/VariantManager.py b/cura/Machines/VariantManager.py index c80545145f..9c074dcf17 100644 --- a/cura/Machines/VariantManager.py +++ b/cura/Machines/VariantManager.py @@ -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] diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 2ab9e701eb..157d871d54 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -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 From 8759aae73e0664766205c36589fba169d17072af Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 19 Aug 2019 12:47:07 +0200 Subject: [PATCH 20/37] Fix crash when materialManager could not be converted to QObject No idea why this was causing issues, but this should also be a bit faster. CURA-6600 --- resources/qml/Preferences/Materials/MaterialsPage.qml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/resources/qml/Preferences/Materials/MaterialsPage.qml b/resources/qml/Preferences/Materials/MaterialsPage.qml index a0ce3c4b49..481a256501 100644 --- a/resources/qml/Preferences/Materials/MaterialsPage.qml +++ b/resources/qml/Preferences/Materials/MaterialsPage.qml @@ -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); } } From 49e8c8d9d97df9e7a5281a736e644d2944abc43e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 19 Aug 2019 12:52:31 +0200 Subject: [PATCH 21/37] Fix the check to see if a material can be removed CURA-6600 --- cura/Machines/MaterialManager.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index 09416d1704..87245b5d53 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -262,12 +262,7 @@ class MaterialManager(QObject): # 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: From 646222f2acd596167429254eb3cb194f5284881a Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 19 Aug 2019 13:01:05 +0200 Subject: [PATCH 22/37] Fix removing of material CURA-6600 --- cura/Machines/MaterialManager.py | 6 ------ cura/Machines/Models/BaseMaterialsModel.py | 5 ++++- cura/Machines/Models/FavoriteMaterialsModel.py | 3 ++- cura/Machines/Models/GenericMaterialsModel.py | 5 +++-- cura/Machines/Models/MaterialBrandsModel.py | 3 ++- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index 87245b5d53..a360cf883a 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -253,9 +253,6 @@ class MaterialManager(QObject): 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! @@ -284,9 +281,6 @@ class MaterialManager(QObject): 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") diff --git a/cura/Machines/Models/BaseMaterialsModel.py b/cura/Machines/Models/BaseMaterialsModel.py index cd8fc70dbd..97226b6bd6 100644 --- a/cura/Machines/Models/BaseMaterialsModel.py +++ b/cura/Machines/Models/BaseMaterialsModel.py @@ -128,7 +128,10 @@ class BaseMaterialsModel(ListModel): ## 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"], diff --git a/cura/Machines/Models/FavoriteMaterialsModel.py b/cura/Machines/Models/FavoriteMaterialsModel.py index dda06a953a..5f42a17f46 100644 --- a/cura/Machines/Models/FavoriteMaterialsModel.py +++ b/cura/Machines/Models/FavoriteMaterialsModel.py @@ -28,7 +28,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()) diff --git a/cura/Machines/Models/GenericMaterialsModel.py b/cura/Machines/Models/GenericMaterialsModel.py index 8a03dcfdeb..78d3641652 100644 --- a/cura/Machines/Models/GenericMaterialsModel.py +++ b/cura/Machines/Models/GenericMaterialsModel.py @@ -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()) diff --git a/cura/Machines/Models/MaterialBrandsModel.py b/cura/Machines/Models/MaterialBrandsModel.py index c4721db5f7..85167632af 100644 --- a/cura/Machines/Models/MaterialBrandsModel.py +++ b/cura/Machines/Models/MaterialBrandsModel.py @@ -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 # From 88857a7be8150c60784399a9e8c211a9e3c66c43 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 19 Aug 2019 13:06:09 +0200 Subject: [PATCH 23/37] Fix setMaterialName CURA-6600 --- cura/Machines/MaterialManager.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index a360cf883a..4614344db6 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -274,12 +274,8 @@ 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 - - material_group = self.getMaterialGroup(root_material_id) - if material_group: - container = material_group.root_material_node.container - if container: - container.setName(name) + containers = CuraContainerRegistry.getInstance().findInstanceContainers(id = root_material_id) + containers[0].setName(name) @pyqtSlot("QVariant") def removeMaterial(self, material_node: "MaterialNode") -> None: From fcbfa88d97fdf7fa42fa7bfad6287ca88ff8abc6 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 19 Aug 2019 13:13:08 +0200 Subject: [PATCH 24/37] Fix creation of new materials CURA-6600 --- cura/Machines/MaterialManager.py | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index 4614344db6..0c90d36428 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -283,14 +283,7 @@ class MaterialManager(QObject): 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) @@ -317,7 +310,7 @@ class MaterialManager(QObject): new_containers.append(new_base_container) # Clone all of them. - for container_to_copy in container_registry.findContainers(base_file= root_material_id): + 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 @@ -344,8 +337,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) @@ -364,11 +364,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") @@ -377,9 +372,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) From d29b3078b3c46dfdbd12b2108ed18ed10ca948b0 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 19 Aug 2019 13:15:38 +0200 Subject: [PATCH 25/37] Clean up no longer used code CURA-6600 --- cura/Machines/MaterialManager.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index 0c90d36428..94e7274380 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -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() From 4ff8991196e13dde708164df86a8003fb9cea7d8 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 19 Aug 2019 14:10:36 +0200 Subject: [PATCH 26/37] Ensure that materials get filtered by diameter again CURA-6600 --- cura/Machines/MaterialManager.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index 94e7274380..51553f87c9 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -109,7 +109,10 @@ class MaterialManager(QObject): 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. From 38937d8ac44f545ec09de0708ed041b708363d6e Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 19 Aug 2019 14:47:08 +0200 Subject: [PATCH 27/37] Remove material node when material is deleted Contributes to issue CURA-6600. --- cura/Machines/MaterialNode.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/cura/Machines/MaterialNode.py b/cura/Machines/MaterialNode.py index 31a73f9e2f..13e63b70ff 100644 --- a/cura/Machines/MaterialNode.py +++ b/cura/Machines/MaterialNode.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING from UM.Settings.ContainerRegistry import ContainerRegistry +from UM.Settings.Interfaces import ContainerInterface from cura.Machines.ContainerNode import ContainerNode from cura.Machines.QualityNode import QualityNode @@ -19,9 +20,11 @@ class MaterialNode(ContainerNode): super().__init__(container_id) self.variant = variant self.qualities = {} # type: Dict[str, QualityNode] # Mapping container IDs to quality profiles. - my_metadata = ContainerRegistry.getInstance().findContainersMetadata(id = container_id)[0] + container_registry = ContainerRegistry.getInstance() + my_metadata = container_registry.findContainersMetadata(id = container_id)[0] self.base_file = my_metadata["base_file"] self._loadAll() + container_registry.containerRemoved.connect(self._onRemoved) def _loadAll(self) -> None: container_registry = ContainerRegistry.getInstance() @@ -44,4 +47,13 @@ class MaterialNode(ContainerNode): for quality in qualities: quality_id = quality["id"] if quality_id not in self.qualities: - self.qualities[quality_id] = QualityNode(quality_id, parent = self) \ No newline at end of file + self.qualities[quality_id] = QualityNode(quality_id, parent = self) + + ## 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] \ No newline at end of file From 41c573b0875cd944fffed05565b04b5839cef528 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 19 Aug 2019 15:06:02 +0200 Subject: [PATCH 28/37] Fix filtering by GUID Get the correct metadata entry, please. Contributes to issue CURA-6600. --- cura/Machines/MaterialNode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Machines/MaterialNode.py b/cura/Machines/MaterialNode.py index 13e63b70ff..cae88a6730 100644 --- a/cura/Machines/MaterialNode.py +++ b/cura/Machines/MaterialNode.py @@ -40,7 +40,7 @@ class MaterialNode(ContainerNode): 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 = my_metadata.get("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"])) From 30b2f943fdad0d4c2248a0b735815a19b334c2b7 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 19 Aug 2019 15:13:19 +0200 Subject: [PATCH 29/37] Update material node when its metadata changes This should keep the tree up to date if anything is added, removed or changed in the container registry. Contributes to issue CURA-6600. --- cura/Machines/MaterialNode.py | 38 ++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/cura/Machines/MaterialNode.py b/cura/Machines/MaterialNode.py index cae88a6730..5148007c7d 100644 --- a/cura/Machines/MaterialNode.py +++ b/cura/Machines/MaterialNode.py @@ -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"] + self.material_type = my_metadata["material"] + self.guid = my_metadata["guid"] self._loadAll() container_registry.containerRemoved.connect(self._onRemoved) + container_registry.containerMetaDataChanged(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("guid") + 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"])) @@ -56,4 +58,30 @@ class MaterialNode(ContainerNode): 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] \ No newline at end of file + del self.variant.materials[self.base_file] + + ## 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 + + 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. \ No newline at end of file From b0e6bb4bc9fcc745fd549b3062407c5105b48b91 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 19 Aug 2019 15:33:07 +0200 Subject: [PATCH 30/37] Update documentation for getLinkedMaterials Contributes to issue CURA-6600. --- cura/Settings/ContainerManager.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index d8f758d8a0..8d13dc7b48 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -323,10 +323,13 @@ 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): + def getLinkedMaterials(self, material_node: "MaterialNode", exclude_self: bool = False) -> List[str]: guid = material_node.getMetaDataEntry("GUID", "") self_root_material_id = material_node.getMetaDataEntry("base_file") From ae7c71763626a9866e9b712070c1deaa6bf6a7e2 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 19 Aug 2019 15:40:29 +0200 Subject: [PATCH 31/37] Re-implement getLinkedMaterials without material manager Contributes to issue CURA-6600. --- cura/Settings/ContainerManager.py | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index 8d13dc7b48..4edddb9d83 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -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,6 +16,7 @@ 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 @@ -327,21 +327,14 @@ class ContainerManager(QObject): # 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 + # \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) -> List[str]: - 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 + 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. From 443495973f43416fedd56ef2f7e761771f920af5 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 19 Aug 2019 16:16:28 +0200 Subject: [PATCH 32/37] Don't use deprecated material manager any more Contributes to issue CURA-6600. --- .../ClusterPrinterConfigurationMaterial.py | 50 +++++++++---------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrinterConfigurationMaterial.py b/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrinterConfigurationMaterial.py index aea244b31d..143824facd 100644 --- a/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrinterConfigurationMaterial.py +++ b/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrinterConfigurationMaterial.py @@ -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"]) From 10dbf3f7350989c259730ace2f5af29201f8da53 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 19 Aug 2019 16:24:00 +0200 Subject: [PATCH 33/37] Fix getting GUID from metadata I keep making this mistake. A donkey may not hit its leg twice on the same stone, but I ain't no ass. Contributes to issue CURA-6600. --- cura/Machines/MaterialNode.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/Machines/MaterialNode.py b/cura/Machines/MaterialNode.py index 5148007c7d..9631354243 100644 --- a/cura/Machines/MaterialNode.py +++ b/cura/Machines/MaterialNode.py @@ -24,7 +24,7 @@ class MaterialNode(ContainerNode): my_metadata = container_registry.findContainersMetadata(id = container_id)[0] self.base_file = my_metadata["base_file"] self.material_type = my_metadata["material"] - self.guid = my_metadata["guid"] + self.guid = my_metadata["GUID"] self._loadAll() container_registry.containerRemoved.connect(self._onRemoved) container_registry.containerMetaDataChanged(self._onMetadataChanged) @@ -81,7 +81,7 @@ class MaterialNode(ContainerNode): old_material_type = self.material_type self.material_type = new_metadata["material"] old_guid = self.guid - self.guid = new_metadata["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. \ No newline at end of file From 04c53c7190f24e0b570673499a569d6fece13487 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 19 Aug 2019 16:24:27 +0200 Subject: [PATCH 34/37] Fix connecting to metadata changes Otherwise the number of parameters is wrong. Contributes to issue CURA-6600. --- cura/Machines/MaterialNode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Machines/MaterialNode.py b/cura/Machines/MaterialNode.py index 9631354243..9421448287 100644 --- a/cura/Machines/MaterialNode.py +++ b/cura/Machines/MaterialNode.py @@ -27,7 +27,7 @@ class MaterialNode(ContainerNode): self.guid = my_metadata["GUID"] self._loadAll() container_registry.containerRemoved.connect(self._onRemoved) - container_registry.containerMetaDataChanged(self._onMetadataChanged) + container_registry.containerMetaDataChanged.connect(self._onMetadataChanged) def _loadAll(self) -> None: container_registry = ContainerRegistry.getInstance() From dcf7ed99695e0e395d635d0d882d17d2b0d4b643 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 19 Aug 2019 16:38:59 +0200 Subject: [PATCH 35/37] Fix finding materials by GUID See, I made the mistake again. Contributes to issue CURA-6600. --- .../src/Models/Http/ClusterPrinterConfigurationMaterial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrinterConfigurationMaterial.py b/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrinterConfigurationMaterial.py index 143824facd..8edb9fb808 100644 --- a/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrinterConfigurationMaterial.py +++ b/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrinterConfigurationMaterial.py @@ -33,7 +33,7 @@ class ClusterPrinterConfigurationMaterial(BaseModel): # \return A material output model that matches the current GUID. def createOutputModel(self) -> MaterialOutputModel: container_registry = ContainerRegistry.getInstance() - same_guid = container_registry.findInstanceContainersMetadata(guid = self.guid) + 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: From 5abb03e269bf4aedde03f8f93ce513eff5fdd710 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 19 Aug 2019 17:09:16 +0200 Subject: [PATCH 36/37] No longer update all material models when favourites change The MaterialManager.materialsUpdated signal was only called once upon init and for the rest when a favourite was added or removed. So only the FavoriteMaterialsModel would need to listen to it. Because the MaterialManager is being deprecated, the favourite materials model now just listens to the preferences changing instead, as it was supposed to be doing anyway. Contributes to issue CURA-6600. --- cura/Machines/Models/BaseMaterialsModel.py | 3 --- cura/Machines/Models/FavoriteMaterialsModel.py | 9 +++++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/cura/Machines/Models/BaseMaterialsModel.py b/cura/Machines/Models/BaseMaterialsModel.py index 97226b6bd6..ee869b17b8 100644 --- a/cura/Machines/Models/BaseMaterialsModel.py +++ b/cura/Machines/Models/BaseMaterialsModel.py @@ -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") diff --git a/cura/Machines/Models/FavoriteMaterialsModel.py b/cura/Machines/Models/FavoriteMaterialsModel.py index 5f42a17f46..bab0809ee0 100644 --- a/cura/Machines/Models/FavoriteMaterialsModel.py +++ b/cura/Machines/Models/FavoriteMaterialsModel.py @@ -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): From 46b489c3f99ccc9d0eb713831ab794653b623b7d Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 19 Aug 2019 17:32:39 +0200 Subject: [PATCH 37/37] getAvailableMaterialsForMachineExtruder can never return None So no need to check for it. Contributes to issue CURA-6600. --- cura/Machines/MaterialManager.py | 2 +- cura/Machines/Models/BaseMaterialsModel.py | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index 51553f87c9..4790314b25 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -103,7 +103,7 @@ 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() diff --git a/cura/Machines/Models/BaseMaterialsModel.py b/cura/Machines/Models/BaseMaterialsModel.py index ee869b17b8..188ce1791b 100644 --- a/cura/Machines/Models/BaseMaterialsModel.py +++ b/cura/Machines/Models/BaseMaterialsModel.py @@ -106,20 +106,15 @@ 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