diff --git a/cura/QualityManager.py b/cura/QualityManager.py index 9568f15184..22dd71f7de 100644 --- a/cura/QualityManager.py +++ b/cura/QualityManager.py @@ -44,6 +44,29 @@ class QualityManager: criteria = {"type": "quality_changes", "name": quality_changes_name} return self._getFilteredContainersForStack(machine_definition, [], **criteria) + ## 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, material_containers): + # 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 __fetchQualityTypeDictForMaterial(self, machine_definition, material): + 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. @@ -51,12 +74,36 @@ class QualityManager: # specified then the currently selected machine definition is used. # \param material_containers (Optional) \type{List[ContainerInstance]} If nothing is specified then # the current set of selected materials is used. - # \return the matching quality containers \type{List[ContainerInstance]} + # \return the matching quality container \type{ContainerInstance} def findQualityByQualityType(self, quality_type, machine_definition=None, material_containers=None): criteria = {"type": "quality"} if quality_type: criteria["quality_type"] = quality_type - return self._getFilteredContainersForStack(machine_definition, material_containers, **criteria) + result = self._getFilteredContainersForStack(machine_definition, material_containers, **criteria) + + # Fall back to using generic materials and qualities if nothing could be found. + if not result and material_containers and len(material_containers) == 1: + basic_material = self._getBasicMaterial(material_containers[0]) + result = self._getFilteredContainersForStack(machine_definition, [basic_material], **criteria) + return result[0] if result else None + + def findAllQualitiesForMachineMaterial(self, machine_definition, material_container): + criteria = {"type": "quality" } + result = self._getFilteredContainersForStack(machine_definition, [material_container], **criteria) + if not result: + basic_material = self._getBasicMaterial(material_container) + result = self._getFilteredContainersForStack(machine_definition, [basic_material], **criteria) + return result + + def _getBasicMaterial(self, material_container): + base_material = material_container.getMetaDataEntry("material") + if base_material: + # There is a basic material specified + criteria = { "type": "material", "name": base_material, "definition": "fdmprinter" } + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**criteria) + return containers[0] if containers else None + + return None def _getFilteredContainers(self, **kwargs): return self._getFilteredContainersForStack(None, None, **kwargs) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 413e7a8164..d325455d6a 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -8,7 +8,6 @@ from UM.Application import Application from UM.Preferences import Preferences from UM.Logger import Logger from UM.Message import Message -from UM.Settings.SettingRelation import RelationType import UM.Settings @@ -562,26 +561,18 @@ class MachineManager(QObject): # See if the requested quality type is available in the new situation. machine_definition = self._active_container_stack.getBottom() quality_manager = QualityManager.getInstance() - candidate_qualities = quality_manager.findQualityByQualityType(quality_type, + candidate_quality = quality_manager.findQualityByQualityType(quality_type, quality_manager.getWholeMachineDefinition(machine_definition), [material_container]) - if not candidate_qualities: - # Fall back to normal quality - quality_containers = quality_manager.findQualityByQualityType("normal", + if not candidate_quality: + # Fall back to a quality + new_quality_id = quality_manager.findQualityByQualityType(None, quality_manager.getWholeMachineDefinition(machine_definition), [material_container]) - if quality_containers: - new_quality_id = quality_containers[0].getId() - else: - # There is no normal quality for this machine/variant/material combination - quality_containers = quality_manager.findQualityByQualityType(None, - quality_manager.getWholeMachineDefinition(machine_definition), - [material_container]) - new_quality_id = quality_containers[0].getId() else: if not old_quality_changes: - new_quality_id = candidate_qualities[0].getId() + new_quality_id = candidate_quality.getId() self.setActiveQuality(new_quality_id) @@ -622,10 +613,11 @@ class MachineManager(QObject): # If we found a quality_changes profile then look up its parent quality profile. container_type = containers[0].getMetaDataEntry("type") quality_name = containers[0].getName() + quality_type = containers[0].getMetaDataEntry("quality_type") # Get quality container and optionally the quality_changes container. if container_type == "quality": - new_quality_settings_list = self._determineQualityAndQualityChangesForQuality(quality_name) + new_quality_settings_list = self._determineQualityAndQualityChangesForQualityType(quality_type) elif container_type == "quality_changes": new_quality_settings_list = self._determineQualityAndQualityChangesForQualityChanges(quality_name) else: @@ -661,26 +653,28 @@ class MachineManager(QObject): # # \param quality_name \type{str} the name of the quality. # \return \type{List[Dict]} with keys "stack", "quality" and "quality_changes". - def _determineQualityAndQualityChangesForQuality(self, quality_name): + def _determineQualityAndQualityChangesForQualityType(self, quality_type): + quality_manager = QualityManager.getInstance() result = [] empty_quality_changes = self._empty_quality_changes_container - - # Find the values for the global stack. global_container_stack = self._global_container_stack - global_machine_definition = QualityManager.getInstance().getParentMachineDefinition(global_container_stack.getBottom()) - material = global_container_stack.findContainer(type="material") - global_quality = QualityManager.getInstance().findQualityByName(quality_name, global_machine_definition, [material])[0] - result.append({"stack": global_container_stack, "quality": global_quality, "quality_changes": empty_quality_changes}) + global_machine_definition = quality_manager.getParentMachineDefinition(global_container_stack.getBottom()) - # Find the values for each extruder. - for stack in ExtruderManager.getInstance().getActiveExtruderStacks(): + extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks() + if extruder_stacks: + stacks = extruder_stacks + else: + stacks = [global_container_stack] + + for stack in stacks: material = stack.findContainer(type="material") - stack_qualities = QualityManager.getInstance().findQualityByName(quality_name, global_machine_definition, [material]) - if not stack_qualities: - # Fall back on the values used for the global stack. - result.append({"stack": stack, "quality": global_quality, "quality_changes": empty_quality_changes}) - else: - result.append({"stack": stack, "quality": stack_qualities[0], "quality_changes": empty_quality_changes}) + quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material]) + result.append({"stack": stack, "quality": quality, "quality_changes": empty_quality_changes}) + + if extruder_stacks: + # Add an extra entry for the global stack. + result.append({"stack": global_container_stack, "quality": result[0]["quality"], + "quality_changes": empty_quality_changes}) return result ## Determine the quality and quality changes settings for the current machine for a quality changes name. @@ -699,12 +693,11 @@ class MachineManager(QObject): # For the global stack, find a quality which matches the quality_type in # the quality changes profile and also satisfies any material constraints. quality_type = global_quality_changes.getMetaDataEntry("quality_type") - global_quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material])[0] - - result.append({"stack": global_container_stack, "quality": global_quality, "quality_changes": global_quality_changes}) + global_quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material]) # Find the values for each extruder. - for stack in ExtruderManager.getInstance().getActiveExtruderStacks(): + extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks() + for stack in extruder_stacks: machine_definition = quality_manager.getParentMachineDefinition(stack.getBottom()) quality_changes_profiles = quality_manager.findQualityChangesByName(quality_changes_name, machine_definition) if quality_changes_profiles: @@ -713,10 +706,17 @@ class MachineManager(QObject): quality_changes = global_quality_changes material = stack.findContainer(type="material") - quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material])[0] + quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material]) result.append({"stack": stack, "quality": quality, "quality_changes": quality_changes}) + if extruder_stacks: + # Duplicate the quality from the 1st extruder into the global stack. If anyone + # then looks in the global stack, they should get a reasonable view. + result.append({"stack": global_container_stack, "quality": result[0]["quality"], "quality_changes": global_quality_changes}) + else: + result.append({"stack": global_container_stack, "quality": global_quality, "quality_changes": global_quality_changes}) + return result def _replaceQualityOrQualityChangesInStack(self, stack, container, postpone_emit = False): diff --git a/cura/Settings/ProfilesModel.py b/cura/Settings/ProfilesModel.py index 937f151b09..3024961387 100644 --- a/cura/Settings/ProfilesModel.py +++ b/cura/Settings/ProfilesModel.py @@ -3,8 +3,8 @@ from UM.Application import Application from UM.Settings.Models.InstanceContainersModel import InstanceContainersModel -from UM.Settings.ContainerRegistry import ContainerRegistry +from cura.QualityManager import QualityManager from cura.Settings.ExtruderManager import ExtruderManager ## QML Model for listing the current list of valid quality profiles. @@ -28,36 +28,18 @@ class ProfilesModel(InstanceContainersModel): extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks() if extruder_stacks: # Multi-extruder machine detected. - - # Determine the common set of quality types which can be - # applied to all of the materials for this machine. - quality_type_dict = self.__fetchQualityTypeDictForStack(extruder_stacks[0], global_machine_definition) - common_quality_types = set(quality_type_dict.keys()) - for stack in extruder_stacks[1:]: - next_quality_type_dict = self.__fetchQualityTypeDictForStack(stack, global_machine_definition) - common_quality_types.intersection_update(set(next_quality_type_dict.keys())) - - return [quality_type_dict[quality_type] for quality_type in common_quality_types] - + materials = [stack.findContainer(type="material") for stack in extruder_stacks] else: # Machine with one extruder. - quality_type_dict = self.__fetchQualityTypeDictForStack(global_container_stack, global_machine_definition) - return list(quality_type_dict.values()) - return [] - - def __fetchQualityTypeDictForStack(self, stack, global_machine_definition): - criteria = {"type": "quality" } - if global_machine_definition.getMetaDataEntry("has_machine_quality", False): - criteria["definition"] = global_machine_definition.getId() - if global_machine_definition.getMetaDataEntry("has_materials", False): - material = stack.findContainer(type="material") - criteria["material"] = material.getId() - else: - criteria["definition"] = "fdmprinter" - - qualities = ContainerRegistry.getInstance().findInstanceContainers(**criteria) + materials = [global_container_stack.findContainer(type="material")] + quality_types = QualityManager.getInstance().findAllQualityTypesForMachineAndMaterials(global_machine_definition, + materials) + # Map the list of quality_types to InstanceContainers + qualities = QualityManager.getInstance().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 + + return [quality_type_dict[quality_type] for quality_type in quality_types]