diff --git a/cura/Machines/QualityManager.py b/cura/Machines/QualityManager.py index a5457344f1..f7ca84e83d 100644 --- a/cura/Machines/QualityManager.py +++ b/cura/Machines/QualityManager.py @@ -360,3 +360,27 @@ class QualityManager(QObject): self._updateQualityGroupsAvailability(machine, quality_group_dict.values()) return quality_group_dict + + def getQualityGroupsForMachineDefinition(self, machine: str) -> dict: + # Get machine definition ID for quality search + machine_definition_id = getMachineDefinitionIDForQualitySearch(machine) + + # To find the quality container for the GlobalStack, check in the following fall-back manner: + # (1) the machine-specific node + # (2) the generic node + machine_node = self._machine_variant_material_quality_type_to_quality_dict.get(machine_definition_id) + default_machine_node = self._machine_variant_material_quality_type_to_quality_dict.get( + self._default_machine_definition_id) + nodes_to_check = [machine_node, default_machine_node] + + # Iterate over all quality_types in the machine node + quality_group_dict = dict() + for node in nodes_to_check: + if node and node.quality_type_map: + for quality_type, quality_node in node.quality_type_map.items(): + quality_group = QualityGroup(quality_node.metadata["name"], quality_type) + quality_group.node_for_global = quality_node + quality_group_dict[quality_type] = quality_group + break + + return quality_group_dict diff --git a/cura/QualityManager.py b/cura/QualityManager.py deleted file mode 100644 index 358bf4e422..0000000000 --- a/cura/QualityManager.py +++ /dev/null @@ -1,296 +0,0 @@ -# Copyright (c) 2017 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. - -# This collects a lot of quality and quality changes related code which was split between ContainerManager -# and the MachineManager and really needs to usable from both. -from typing import Any, Dict, List, Optional, TYPE_CHECKING - -from UM.Application import Application -from UM.Settings.ContainerRegistry import ContainerRegistry -from UM.Settings.InstanceContainer import InstanceContainer -from cura.Settings.ExtruderManager import ExtruderManager - -if TYPE_CHECKING: - from cura.Settings.GlobalStack import GlobalStack - from cura.Settings.ExtruderStack import ExtruderStack - from UM.Settings.DefinitionContainer import DefinitionContainerInterface - -class QualityManager: - - ## Get the singleton instance for this class. - @classmethod - def getInstance(cls) -> "QualityManager": - # Note: Explicit use of class name to prevent issues with inheritance. - if not QualityManager.__instance: - QualityManager.__instance = cls() - return QualityManager.__instance - - __instance = None # type: "QualityManager" - - ## Fetch the list of available quality types for this combination of machine definition and materials. - # - # \param machine_definition \type{DefinitionContainer} - # \param material_containers \type{List[InstanceContainer]} - # \return \type{List[str]} - def findAllQualityTypesForMachineAndMaterials(self, machine_definition: "DefinitionContainerInterface", material_containers: List[InstanceContainer]) -> List[str]: - # Determine the common set of quality types which can be - # applied to all of the materials for this machine. - quality_type_dict = self.__fetchQualityTypeDictForMaterial(machine_definition, material_containers[0]) - common_quality_types = set(quality_type_dict.keys()) - for material_container in material_containers[1:]: - next_quality_type_dict = self.__fetchQualityTypeDictForMaterial(machine_definition, material_container) - common_quality_types.intersection_update(set(next_quality_type_dict.keys())) - - return list(common_quality_types) - - def findAllQualitiesForMachineAndMaterials(self, machine_definition: "DefinitionContainerInterface", material_containers: List[InstanceContainer]) -> List[InstanceContainer]: - # Determine the common set of quality types which can be - # applied to all of the materials for this machine. - quality_type_dict = self.__fetchQualityTypeDictForMaterial(machine_definition, material_containers[0]) - qualities = set(quality_type_dict.values()) - for material_container in material_containers[1:]: - next_quality_type_dict = self.__fetchQualityTypeDictForMaterial(machine_definition, material_container) - qualities.intersection_update(set(next_quality_type_dict.values())) - - return list(qualities) - - ## Fetches a dict of quality types names to quality profiles for a combination of machine and material. - # - # \param machine_definition \type{DefinitionContainer} the machine definition. - # \param material \type{InstanceContainer} the material. - # \return \type{Dict[str, InstanceContainer]} the dict of suitable quality type names mapping to qualities. - def __fetchQualityTypeDictForMaterial(self, machine_definition: "DefinitionContainerInterface", material: InstanceContainer) -> Dict[str, InstanceContainer]: - qualities = self.findAllQualitiesForMachineMaterial(machine_definition, material) - quality_type_dict = {} - for quality in qualities: - quality_type_dict[quality.getMetaDataEntry("quality_type")] = quality - return quality_type_dict - - ## Find a quality container by quality type. - # - # \param quality_type \type{str} the name of the quality type to search for. - # \param machine_definition (Optional) \type{InstanceContainer} If nothing is - # specified then the currently selected machine definition is used. - # \param material_containers_metadata If nothing is specified then the - # current set of selected materials is used. - # \return the matching quality container \type{InstanceContainer} - def findQualityByQualityType(self, quality_type: str, machine_definition: Optional["DefinitionContainerInterface"] = None, material_containers_metadata: Optional[List[Dict[str, Any]]] = None, **kwargs) -> InstanceContainer: - criteria = kwargs - criteria["type"] = "quality" - if quality_type: - criteria["quality_type"] = quality_type - result = self._getFilteredContainersForStack(machine_definition, material_containers_metadata, **criteria) - # Fall back to using generic materials and qualities if nothing could be found. - if not result and material_containers_metadata and len(material_containers_metadata) == 1: - basic_materials = self._getBasicMaterialMetadatas(material_containers_metadata[0]) - if basic_materials: - result = self._getFilteredContainersForStack(machine_definition, basic_materials, **criteria) - return result[0] if result else None - - ## Find all suitable qualities for a combination of machine and material. - # - # \param machine_definition \type{DefinitionContainer} the machine definition. - # \param material_container \type{InstanceContainer} the material. - # \return \type{List[InstanceContainer]} the list of suitable qualities. - def findAllQualitiesForMachineMaterial(self, machine_definition: "DefinitionContainerInterface", material_container: InstanceContainer) -> List[InstanceContainer]: - criteria = {"type": "quality"} - result = self._getFilteredContainersForStack(machine_definition, [material_container.getMetaData()], **criteria) - if not result: - basic_materials = self._getBasicMaterialMetadatas(material_container.getMetaData()) - if basic_materials: - result = self._getFilteredContainersForStack(machine_definition, basic_materials, **criteria) - - return result - - ## Find all quality changes for a machine. - # - # \param machine_definition \type{DefinitionContainer} the machine definition. - # \return \type{List[InstanceContainer]} the list of quality changes - def findAllQualityChangesForMachine(self, machine_definition: "DefinitionContainerInterface") -> List[InstanceContainer]: - if machine_definition.getMetaDataEntry("has_machine_quality"): - definition_id = machine_definition.getId() - else: - definition_id = "fdmprinter" - - filter_dict = { "type": "quality_changes", "definition": definition_id } - quality_changes_list = ContainerRegistry.getInstance().findInstanceContainers(**filter_dict) - return quality_changes_list - - def findAllExtruderDefinitionsForMachine(self, machine_definition: "DefinitionContainerInterface") -> List["DefinitionContainerInterface"]: - filter_dict = { "machine": machine_definition.getId() } - return ContainerRegistry.getInstance().findDefinitionContainers(**filter_dict) - - ## Find all quality changes for a given extruder. - # - # \param extruder_definition The extruder to find the quality changes for. - # \return The list of quality changes for the given extruder. - def findAllQualityChangesForExtruder(self, extruder_definition: "DefinitionContainerInterface") -> List[InstanceContainer]: - filter_dict = {"type": "quality_changes", "extruder": extruder_definition.getId()} - return ContainerRegistry.getInstance().findInstanceContainers(**filter_dict) - - ## Find all usable qualities for a machine and extruders. - # - # Finds all of the qualities for this combination of machine and extruders. - # Only one quality per quality type is returned. i.e. if there are 2 qualities with quality_type=normal - # then only one of then is returned (at random). - # - # \param global_container_stack \type{GlobalStack} the global machine definition - # \param extruder_stacks \type{List[ExtruderStack]} the list of extruder stacks - # \return \type{List[InstanceContainer]} the list of the matching qualities. The quality profiles - # return come from the first extruder in the given list of extruders. - def findAllUsableQualitiesForMachineAndExtruders(self, global_container_stack: "GlobalStack", extruder_stacks: List["ExtruderStack"]) -> List[InstanceContainer]: - global_machine_definition = global_container_stack.getBottom() - - machine_manager = Application.getInstance().getMachineManager() - active_stack_id = machine_manager.activeStackId - - materials = [] - - for stack in extruder_stacks: - if stack.getId() == active_stack_id and machine_manager.newMaterial: - materials.append(machine_manager.newMaterial) - else: - materials.append(stack.material) - - quality_types = self.findAllQualityTypesForMachineAndMaterials(global_machine_definition, materials) - - # Map the list of quality_types to InstanceContainers - qualities = self.findAllQualitiesForMachineMaterial(global_machine_definition, materials[0]) - quality_type_dict = {} - for quality in qualities: - quality_type_dict[quality.getMetaDataEntry("quality_type")] = quality - - return [quality_type_dict[quality_type] for quality_type in quality_types] - - ## Fetch more basic versions of a material. - # - # This tries to find a generic or basic version of the given material. - # \param material_container \type{Dict[str, Any]} The metadata of a - # material to find the basic versions of. - # \return \type{List[Dict[str, Any]]} A list of the metadata of basic - # materials, or an empty list if none could be found. - def _getBasicMaterialMetadatas(self, material_container: Dict[str, Any]) -> List[Dict[str, Any]]: - if "definition" not in material_container: - definition_id = "fdmprinter" - else: - material_container_definition = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = material_container["definition"]) - if not material_container_definition: - definition_id = "fdmprinter" - else: - material_container_definition = material_container_definition[0] - if "has_machine_quality" not in material_container_definition: - definition_id = "fdmprinter" - else: - definition_id = material_container_definition.get("quality_definition", material_container_definition["id"]) - - base_material = material_container.get("material") - if base_material: - # There is a basic material specified - criteria = { - "type": "material", - "name": base_material, - "definition": definition_id, - "variant": material_container.get("variant") - } - containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(**criteria) - return containers - - return [] - - def _getFilteredContainers(self, **kwargs): - return self._getFilteredContainersForStack(None, None, **kwargs) - - def _getFilteredContainersForStack(self, machine_definition: "DefinitionContainerInterface" = None, material_metadata: Optional[List[Dict[str, Any]]] = None, **kwargs): - # Fill in any default values. - if machine_definition is None: - machine_definition = Application.getInstance().getGlobalContainerStack().getBottom() - quality_definition_id = machine_definition.getMetaDataEntry("quality_definition") - if quality_definition_id is not None: - machine_definition = ContainerRegistry.getInstance().findDefinitionContainers(id = quality_definition_id)[0] - - if not material_metadata: - active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks() - if active_stacks: - material_metadata = [stack.material.getMetaData() for stack in active_stacks] - - criteria = kwargs - filter_by_material = False - - machine_definition = self.getParentMachineDefinition(machine_definition) - criteria["definition"] = machine_definition.getId() - found_containers_with_machine_definition = ContainerRegistry.getInstance().findInstanceContainersMetadata(**criteria) - whole_machine_definition = self.getWholeMachineDefinition(machine_definition) - if whole_machine_definition.getMetaDataEntry("has_machine_quality"): - definition_id = machine_definition.getMetaDataEntry("quality_definition", whole_machine_definition.getId()) - criteria["definition"] = definition_id - - filter_by_material = whole_machine_definition.getMetaDataEntry("has_materials") - # only fall back to "fdmprinter" when there is no container for this machine - elif not found_containers_with_machine_definition: - criteria["definition"] = "fdmprinter" - - # Stick the material IDs in a set - material_ids = set() - - for material_instance in material_metadata: - if material_instance is not None: - # Add the parent material too. - for basic_material in self._getBasicMaterialMetadatas(material_instance): - material_ids.add(basic_material["id"]) - material_ids.add(material_instance["id"]) - containers = ContainerRegistry.getInstance().findInstanceContainers(**criteria) - - result = [] - for container in containers: - # If the machine specifies we should filter by material, exclude containers that do not match any active material. - if filter_by_material and container.getMetaDataEntry("material") not in material_ids and "global_quality" not in kwargs: - continue - result.append(container) - - return result - - ## Get the parent machine definition of a machine definition. - # - # \param machine_definition \type{DefinitionContainer} This may be a normal machine definition or - # an extruder definition. - # \return \type{DefinitionContainer} the parent machine definition. If the given machine - # definition doesn't have a parent then it is simply returned. - def getParentMachineDefinition(self, machine_definition: "DefinitionContainerInterface") -> "DefinitionContainerInterface": - container_registry = ContainerRegistry.getInstance() - - machine_entry = machine_definition.getMetaDataEntry("machine") - if machine_entry is None: - # We have a normal (whole) machine defintion - quality_definition = machine_definition.getMetaDataEntry("quality_definition") - if quality_definition is not None: - parent_machine_definition = container_registry.findDefinitionContainers(id = quality_definition)[0] - return self.getParentMachineDefinition(parent_machine_definition) - else: - return machine_definition - else: - # This looks like an extruder. Find the rest of the machine. - whole_machine = container_registry.findDefinitionContainers(id = machine_entry)[0] - parent_machine = self.getParentMachineDefinition(whole_machine) - if whole_machine is parent_machine: - # This extruder already belongs to a 'parent' machine def. - return machine_definition - else: - # Look up the corresponding extruder definition in the parent machine definition. - extruder_position = machine_definition.getMetaDataEntry("position") - parent_extruder_id = parent_machine.getMetaDataEntry("machine_extruder_trains")[extruder_position] - return container_registry.findDefinitionContainers(id = parent_extruder_id)[0] - - ## Get the whole/global machine definition from an extruder definition. - # - # \param machine_definition \type{DefinitionContainer} This may be a normal machine definition or - # an extruder definition. - # \return \type{DefinitionContainerInterface} - def getWholeMachineDefinition(self, machine_definition: "DefinitionContainerInterface") -> "DefinitionContainerInterface": - machine_entry = machine_definition.getMetaDataEntry("machine") - if machine_entry is None: - # This already is a 'global' machine definition. - return machine_definition - else: - container_registry = ContainerRegistry.getInstance() - whole_machine = container_registry.findDefinitionContainers(id = machine_entry)[0] - return whole_machine diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index 7103167b9c..329fbe77b7 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -335,41 +335,16 @@ class CuraContainerRegistry(ContainerRegistry): return catalog.i18nc("@info:status", "Profile is missing a quality type.") quality_type_criteria = {"quality_type": quality_type} - if self._machineHasOwnQualities(): - global_container_stack = Application.getInstance().getGlobalContainerStack() - definition_id = getMachineDefinitionIDForQualitySearch(global_container_stack) - profile.setDefinition(definition_id) - if self._machineHasOwnMaterials(): - active_material_id = self._activeMaterialId() - if active_material_id and active_material_id != "empty": # only update if there is an active material - profile.addMetaDataEntry("material", active_material_id) - quality_type_criteria["material"] = active_material_id - - quality_type_criteria["definition"] = profile.getDefinition().getId() - - else: - profile.setDefinition("fdmprinter") - quality_type_criteria["definition"] = "fdmprinter" - - machine_definition = Application.getInstance().getGlobalContainerStack().getBottom() - del quality_type_criteria["definition"] - - # materials = None - - if "material" in quality_type_criteria: - # materials = ContainerRegistry.getInstance().findInstanceContainers(id = quality_type_criteria["material"]) - del quality_type_criteria["material"] - - # Do not filter quality containers here with materials because we are trying to import a profile, so it should - # NOT be restricted by the active materials on the current machine. - materials = None + global_stack = Application.getInstance().getGlobalContainerStack() + definition_id = getMachineDefinitionIDForQualitySearch(global_stack) + profile.setDefinition(definition_id) # Check to make sure the imported profile actually makes sense in context of the current configuration. # This prevents issues where importing a "draft" profile for a machine without "draft" qualities would report as # successfully imported but then fail to show up. - from cura.QualityManager import QualityManager - qualities = QualityManager.getInstance()._getFilteredContainersForStack(machine_definition, materials, **quality_type_criteria) - if not qualities: + quality_manager = CuraApplication.getInstance()._quality_manager + quality_group_dict = quality_manager.getQualityGroupsForMachineDefinition(global_stack) + if quality_type not in quality_group_dict: return catalog.i18nc("@info:status", "Could not find a quality type {0} for the current configuration.", quality_type) ContainerRegistry.getInstance().addContainer(profile) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 3ff5cb1011..7be3f0798e 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -26,7 +26,6 @@ from UM.Signal import postponeSignals, CompressTechnique from cura.Machines.MachineTools import getMachineDefinitionIDForQualitySearch -from cura.QualityManager import QualityManager from cura.PrinterOutputDevice import PrinterOutputDevice from cura.Settings.ExtruderManager import ExtruderManager @@ -36,7 +35,6 @@ from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") if TYPE_CHECKING: - from UM.Settings.DefinitionContainer import DefinitionContainer from cura.Settings.CuraContainerStack import CuraContainerStack from cura.Settings.GlobalStack import GlobalStack diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 8291ca3b52..d584a0efd6 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -23,7 +23,6 @@ from cura.Settings.ExtruderManager import ExtruderManager from cura.Settings.ExtruderStack import ExtruderStack from cura.Settings.GlobalStack import GlobalStack from cura.Settings.CuraContainerStack import _ContainerIndexes -from cura.QualityManager import QualityManager from cura.CuraApplication import CuraApplication from configparser import ConfigParser @@ -858,10 +857,11 @@ class ThreeMFWorkspaceReader(WorkspaceReader): if machine_extruder_count is not None: extruder_stacks_in_use = extruder_stacks[:machine_extruder_count] - available_quality = QualityManager.getInstance().findAllUsableQualitiesForMachineAndExtruders(global_stack, - extruder_stacks_in_use) + quality_manager = CuraApplication.getInstance()._quality_manager + all_quality_groups = quality_manager.getQualityGroups(global_stack) + available_quality_types = [qt for qt, qg in all_quality_groups.items() if qg.is_available] if not has_not_supported: - has_not_supported = not available_quality + has_not_supported = not available_quality_types quality_has_been_changed = False @@ -875,8 +875,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader): # The machine in the project has non-empty quality and there are usable qualities for this machine. # We need to check if the current quality_type is still usable for this machine, if not, then the quality # will be reset to the "preferred quality" if present, otherwise "normal". - available_quality_types = [q.getMetaDataEntry("quality_type") for q in available_quality] - if global_stack.quality.getMetaDataEntry("quality_type") not in available_quality_types: # We are here because the quality_type specified in the project is not supported any more, # so we need to switch it to the "preferred quality" if present, otherwise "normal".