diff --git a/cura/QualityManager.py b/cura/QualityManager.py index 45c4eec543..8fba30838d 100644 --- a/cura/QualityManager.py +++ b/cura/QualityManager.py @@ -40,9 +40,9 @@ class QualityManager: # \param material_containers (Optional) \type{List[ContainerInstance]} If nothing is specified then # the current set of selected materials is used. # \return the matching quality changes containers \type{List[ContainerInstance]} - def findQualityChangesByName(self, quality_changes_name, machine_definition=None, material_containers=None): + def findQualityChangesByName(self, quality_changes_name, machine_definition=None): criteria = {"type": "quality_changes", "name": quality_changes_name} - return self._getFilteredContainersForStack(machine_definition, material_containers, **criteria) + return self._getFilteredContainersForStack(machine_definition, [], **criteria) ## Find a quality container by quality type. # @@ -67,8 +67,6 @@ class QualityManager: if quality_definition is not None: machine_definition = UM.Settings.ContainerRegistry.getInstance().findDefinitionContainers(id=quality_definition)[0] - machine_definition = self.getParentMachineDefinition(machine_definition) - if material_containers is None: active_stacks = cura.Settings.ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks() material_containers = [stack.findContainer(type="material") for stack in active_stacks] @@ -76,11 +74,13 @@ class QualityManager: criteria = kwargs filter_by_material = False - if machine_definition.getMetaDataEntry("has_machine_quality"): - definition_id = machine_definition.getMetaDataEntry("quality_definition", machine_definition.getId()) + machine_definition = self.getParentMachineDefinition(machine_definition) + 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 = machine_definition.getMetaDataEntry("has_materials") + filter_by_material = whole_machine_definition.getMetaDataEntry("has_materials") # Stick the material IDs in a set if material_containers is None or len(material_containers) == 0: @@ -90,6 +90,12 @@ class QualityManager: for material_instance in material_containers: material_ids.add(material_instance.getId()) + + if machine_definition.getMetaDataEntry("type") == "extruder": + extruder_id = machine_definition.getId() + else: + extruder_id = None + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**criteria) result = [] @@ -97,6 +103,8 @@ class QualityManager: # 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: continue + if extruder_id != container.getMetaDataEntry("extruder"): + continue result.append(container) return result @@ -130,3 +138,18 @@ class QualityManager: 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{DefinitionContainer} + def getWholeMachineDefinition(self, machine_definition): + machine_entry = machine_definition.getMetaDataEntry("machine") + if machine_entry is None: + # This already is a 'global' machine definition. + return machine_definition + else: + container_registry = UM.Settings.ContainerRegistry.getInstance() + whole_machine = container_registry.findDefinitionContainers(id=machine_entry)[0] + return whole_machine diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 87c2eecd19..29604abffd 100644 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -303,19 +303,25 @@ class ExtruderManager(QObject): for name in self._extruder_trains[machine_id]: yield self._extruder_trains[machine_id][name] - ## Returns a generator that will iterate over the global stack and per-extruder stacks. + ## Returns a list containing the global stack and active extruder stacks. # - # The first generated element is the global container stack. After that any extruder stacks are generated. + # The first element is the global container stack, followed by any extruder stacks. + # \return \type{List[ContainerStack]} def getActiveGlobalAndExtruderStacks(self): global_stack = UM.Application.getInstance().getGlobalContainerStack() if not global_stack: - return + return None - yield global_stack + result = [global_stack] + result.extend(self.getActiveExtruderStacks()) + return result - global_id = global_stack.getId() - for name in self._extruder_trains[global_id]: - yield self._extruder_trains[global_id][name] + ## Returns the list of active extruder stacks. + # + # \return \type{List[ContainerStack]} a list of + def getActiveExtruderStacks(self): + global_stack = UM.Application.getInstance().getGlobalContainerStack() + return list(self._extruder_trains[global_stack.getId()].values()) if global_stack else [] def __globalContainerStackChanged(self): self._addCurrentMachineExtruders() diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index a112e52dbc..ff4b6f0327 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -19,7 +19,6 @@ from . import ExtruderManager from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") -import time import os class MachineManager(QObject): @@ -602,92 +601,33 @@ class MachineManager(QObject): # \param quality_id The quality_id of either a quality or a quality_changes @pyqtSlot(str) def setActiveQuality(self, quality_id): + self.blurSettings.emit() + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = quality_id) if not containers or not self._global_container_stack: return Logger.log("d", "Attempting to change the active quality to %s", quality_id) - self.blurSettings.emit() - - quality_changes_container = self._empty_quality_changes_container - # Quality profile come in two flavours: type=quality and type=quality_changes # 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() # Get quality container and optionally the quality_changes container. if container_type == "quality": - quality_container = containers[0] - + new_quality_settings_list = self._determineQualityAndQualityChangesForQuality(quality_name) elif container_type == "quality_changes": - quality_changes_container = containers[0] - # Find a suitable quality container to match this quality changes container. - containers = QualityManager.getInstance().findQualityByQualityType(quality_changes_container.getMetaDataEntry("quality_type")) - if not containers: - Logger.log("e", "Could not find quality %s for changes %s, not changing quality", quality_changes_container.getMetaDataEntry("quality"), quality_changes_container.getId()) - return - quality_container = containers[0] - + new_quality_settings_list = self._determineQualityAndQualityChangesForQualityChanges(quality_name) else: Logger.log("e", "Tried to set quality to a container that is not of the right type") return - quality_type = quality_container.getMetaDataEntry("quality_type") - if not quality_type: - quality_type = quality_changes_container.getName() - - # Find and swap in the quality changes containers for the global stack and each extruder stack. - stacks = list(ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks()) name_changed_connect_stacks = [] # Connect these stacks to the name changed callback - for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks(): - if stack != self._global_container_stack: - # Must be an extruder stack. Use the ID of the extruders as specified by the machine definition. - extruder_id = QualityManager.getInstance().getParentMachineDefinition(stack.getBottom()).getId() - else: - extruder_id = None - criteria = { "quality_type": quality_type, "type": "quality", "extruder": extruder_id } - - # Filter candidate quality containers by the current material in the stack. - material = stack.findContainer(type = "material") - if material and material is not self._empty_material_container: - criteria["material"] = material.getId() - - # Apply a filter when machines have their own specific quality profiles. - if self._global_container_stack.getMetaDataEntry("has_machine_quality"): - criteria["definition"] = self.activeQualityDefinitionId - else: - criteria["definition"] = "fdmprinter" - - # Find the new quality container. - stack_quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**criteria) - if not stack_quality: - # Search again, except this time drop any extruder requirement. We should now get - # the same quality container as the one needed for the global stack. - criteria.pop("extruder") - stack_quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**criteria) - if not stack_quality: - stack_quality = quality_container - else: - stack_quality = stack_quality[0] - else: - stack_quality = stack_quality[0] - - # Find the new quality changes if one has been specified. - if quality_changes_container != self._empty_quality_changes_container: - changes = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers( - type="quality_changes", - quality_type=stack_quality.getMetaDataEntry("quality_type"), - name = quality_changes_container.getName(), extruder = extruder_id) - if changes: - stack_quality_changes = changes[0] - else: - Logger.log("w", "Unable to find quality changes container. Using empty container instead") - print(stack_quality.getMetaDataEntry("quality_type"), quality_changes_container.getName(), extruder_id) - stack_quality_changes = self._empty_quality_changes_container - else: - # This is case of quality container and the no-op quality changes container. - stack_quality_changes = self._empty_quality_changes_container + for setting_info in new_quality_settings_list: + stack = setting_info["stack"] + stack_quality = setting_info["quality"] + stack_quality_changes = setting_info["quality_changes"] name_changed_connect_stacks.append(stack_quality) name_changed_connect_stacks.append(stack_quality_changes) @@ -696,8 +636,9 @@ class MachineManager(QObject): # Send emits that are postponed in replaceContainer. # Here the stacks are finished replacing and every value can be resolved based on the current state. - for stack in stacks: - stack.sendPostponedEmits() + for setting_info in new_quality_settings_list: + setting_info["stack"].sendPostponedEmits() + # Connect to onQualityNameChanged for stack in name_changed_connect_stacks: stack.nameChanged.connect(self._onQualityNameChanged) @@ -707,6 +648,69 @@ class MachineManager(QObject): self.activeQualityChanged.emit() + ## Determine the quality and quality changes settings for the current machine for a quality name. + # + # \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): + 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}) + + # Find the values for each extruder. + for stack in ExtruderManager.getInstance().getActiveExtruderStacks(): + # machine_definition = QualityManager.getInstance().getParentMachineDefinition(stack.getBottom()) + 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}) + return result + + ## Determine the quality and quality changes settings for the current machine for a quality changes name. + # + # \param quality_changes_name \type{str} the name of the quality changes. + # \return \type{List[Dict]} with keys "stack", "quality" and "quality_changes". + def _determineQualityAndQualityChangesForQualityChanges(self, quality_changes_name): + result = [] + quality_manager = QualityManager.getInstance() + + global_container_stack = self._global_container_stack + global_machine_definition = quality_manager.getParentMachineDefinition(global_container_stack.getBottom()) + global_quality_changes = quality_manager.findQualityChangesByName(quality_changes_name, global_machine_definition)[0] + material = global_container_stack.findContainer(type="material") + + # 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}) + + # Find the values for each extruder. + for stack in ExtruderManager.getInstance().getActiveExtruderStacks(): + machine_definition = quality_manager.getParentMachineDefinition(stack.getBottom()) + quality_changes_profiles = quality_manager.findQualityChangesByName(quality_changes_name, machine_definition) + if quality_changes_profiles: + quality_changes = quality_changes_profiles[0] + else: + quality_changes = global_quality_changes + + material = stack.findContainer(type="material") + quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material])[0] + + result.append({"stack": stack, "quality": quality, "quality_changes": quality_changes}) + + return result + def _replaceQualityOrQualityChangesInStack(self, stack, container, postpone_emit = False): # Disconnect the signal handling from the old container. old_container = stack.findContainer(type=container.getMetaDataEntry("type"))