diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index 50394b218d..a86e3d922c 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -351,47 +351,49 @@ class ContainerManager(QObject): return { "status": "success", "message": "Successfully imported container {0}".format(container.getName()) } + ## Update the current active quality changes container with the settings from the user container. + # + # This will go through the active global stack and all active extruder stacks and merge the changes from the user + # container into the quality_changes container. After that, the user container is cleared. + # + # \return \type{bool} True if successful, False if not. @pyqtSlot(result = bool) def updateQualityChanges(self): global_stack = UM.Application.getInstance().getGlobalContainerStack() - - containers_to_merge = [] - - global_quality_changes = global_stack.findContainer(type = "quality_changes") - if not global_quality_changes or global_quality_changes.isReadOnly(): - UM.Logger.log("e", "Could not update quality of a nonexistant or read only quality profile") + if not global_stack: return False UM.Application.getInstance().getMachineManager().blurSettings.emit() - containers_to_merge.append((global_quality_changes, global_stack.getTop())) - - for extruder in cura.Settings.ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()): - quality_changes = extruder.findContainer(type = "quality_changes") + for stack in cura.Settings.ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks(): + # Find the quality_changes container for this stack and merge the contents of the top container into it. + quality_changes = stack.findContainer(type = "quality_changes") if not quality_changes or quality_changes.isReadOnly(): - UM.Logger.log("e", "Could not update quality of a nonexistant or read only quality profile") - return False + UM.Logger.log("e", "Could not update quality of a nonexistant or read only quality profile in stack %s", stack.getId()) + continue - containers_to_merge.append((quality_changes, extruder.getTop())) - - for merge_into, merge in containers_to_merge: - self._performMerge(merge_into, merge) + self._performMerge(quality_changes, stack.getTop()) UM.Application.getInstance().getMachineManager().activeQualityChanged.emit() return True + ## Clear the top-most (user) containers of the active stacks. @pyqtSlot() def clearUserContainers(self): - global_stack = UM.Application.getInstance().getGlobalContainerStack() - UM.Application.getInstance().getMachineManager().blurSettings.emit() - for extruder in cura.Settings.ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()): - extruder.getTop().clear() - - global_stack.getTop().clear() + # Go through global and extruder stacks and clear their topmost container (the user settings). + for stack in cura.Settings.ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks(): + stack.getTop().clear() + ## Create quality changes containers from the user containers in the active stacks. + # + # This will go through the global and extruder stacks and create quality_changes containers from + # the user containers in each stack. These then replace the quality_changes containers in the + # stack and clear the user settings. + # + # \return \type{bool} True if the operation was successfully, False if not. @pyqtSlot(result = bool) def createQualityChanges(self): global_stack = UM.Application.getInstance().getGlobalContainerStack() @@ -406,49 +408,54 @@ class ContainerManager(QObject): UM.Application.getInstance().getMachineManager().blurSettings.emit() unique_name = UM.Settings.ContainerRegistry.getInstance().uniqueName(quality_container.getName()) - unique_id = unique_name.lower() - unique_id.replace(" ", "_") - stacks = [ s for s in cura.Settings.ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()) ] - stacks.insert(0, global_stack) - - for stack in stacks: + # Go through the active stacks and create quality_changes containers from the user containers. + for stack in cura.Settings.ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks(): user_container = stack.getTop() quality_container = stack.findContainer(type = "quality") quality_changes_container = stack.findContainer(type = "quality_changes") if not quality_container or not quality_changes_container: UM.Logger.log("w", "No quality or quality changes container found in stack %s, ignoring it", stack.getId()) - return False + continue - new_quality_changes = user_container.duplicate(stack.getId() + "_" + unique_id, unique_name) - new_quality_changes.setMetaDataEntry("type", "quality_changes") - new_quality_changes.addMetaDataEntry("quality", quality_container.getMetaDataEntry("quality_type")) + new_changes = self._createQualityChanges(quality_container, unique_name, stack.getId()) + self._performMerge(new_changes, user_container) - if not global_stack.getMetaDataEntry("has_machine_quality"): - new_quality_changes.setDefinition(UM.Settings.ContainerRegistry.getInstance().findContainers(id = "fdmprinter")[0]) - - if global_stack.getMetaDataEntry("has_materials"): - material = stack.findContainer(type = "material") - new_quality_changes.addMetaDataEntry("material", material.getId()) - - UM.Settings.ContainerRegistry.getInstance().addContainer(new_quality_changes) - - stack.replaceContainer(stack.getContainerIndex(quality_changes_container), new_quality_changes) - stack.getTop().clear() + UM.Settings.ContainerRegistry.getInstance().addContainer(new_changes) + stack.replaceContainer(stack.getContainerIndex(quality_changes_container), new_changes) UM.Application.getInstance().getMachineManager().activeQualityChanged.emit() return True + ## Remove all quality changes containers matching a specified name. + # + # This will search for quality_changes containers matching the supplied name and remove them. + # Note that if the machine specifies that qualities should be filtered by machine and/or material + # only the containers related to the active machine/material are removed. + # + # \param quality_name The name of the quality changes to remove. + # + # \return \type{bool} True if successful, False if not. @pyqtSlot(str, result = bool) def removeQualityChanges(self, quality_name): if not quality_name: return False - for container in self._getQualityContainers(quality_name): + for container in self._getFilteredContainers(name = quality_name, type = "quality_changes"): UM.Settings.ContainerRegistry.getInstance().removeContainer(container.getId()) return True + ## Rename a set of quality changes containers. + # + # This will search for quality_changes containers matching the supplied name and rename them. + # Note that if the machine specifies that qualities should be filtered by machine and/or material + # only the containers related to the active machine/material are renamed. + # + # \param quality_name The name of the quality changes containers to rename. + # \param new_name The new name of the quality changes. + # + # \return True if successful, False if not. @pyqtSlot(str, str, result = bool) def renameQualityChanges(self, quality_name, new_name): if not quality_name or not new_name: @@ -464,59 +471,45 @@ class ContainerManager(QObject): UM.Application.getInstance().getMachineManager().blurSettings.emit() new_name = UM.Settings.ContainerRegistry.getInstance().uniqueName(new_name) - new_id = new_name.lower() - new_id.replace(" ", "_") - for container in self._getQualityContainers(quality_name): + for container in self._getFilteredContainers(name = quality_name, type = "quality_changes"): stack_id = container.getMetaDataEntry("extruder", global_stack.getId()) - - UM.Settings.ContainerRegistry.getInstance().renameContainer(container.getId(), new_name, stack_id + "_" + new_id) + UM.Settings.ContainerRegistry.getInstance().renameContainer(container.getId(), new_name, self._createUniqueId(stack_id, new_name)) UM.Application.getInstance().getMachineManager().activeQualityChanged.emit() return True + ## Duplicate a specified set of quality or quality_changes containers. + # + # This will search for containers matching the specified name. If the container is a "quality" type container, a new + # quality_changes container will be created with the specified quality as base. If the container is a "quality_changes" + # container, it is simply duplicated and renamed. + # + # \param quality_name The name of the quality to duplicate. + # + # \return A string containing the name of the duplicated containers, or an empty string if it failed. @pyqtSlot(str, result = str) - def duplicateQuality(self, quality_name): - if not quality_name: - return "" - + def duplicateQualityOrQualityChanges(self, quality_name): global_stack = UM.Application.getInstance().getGlobalContainerStack() - if not global_stack: + if not global_stack or not quality_name: return "" - new_name = UM.Settings.ContainerRegistry.getInstance().uniqueName(quality_name) - new_id = new_name.lower() - new_id.replace(" ", "_") - containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(name = quality_name) if not containers: return "" + new_name = UM.Settings.ContainerRegistry.getInstance().uniqueName(quality_name) + container_type = containers[0].getMetaDataEntry("type") if container_type == "quality": - for container in self._getQualityContainers(quality_name, "quality"): - stack_id = container.getMetaDataEntry("extruder", global_stack.getId()) - - quality_changes = UM.Settings.InstanceContainer(stack_id + "_" + new_id) - quality_changes.setName(new_name) - quality_changes.addMetaDataEntry("type", "quality_changes") - quality_changes.addMetaDataEntry("quality", container.getMetaDataEntry("quality_type")) - - if not global_stack.getMetaDataEntry("has_machine_quality"): - quality_changes.setDefinition(UM.Settings.ContainerRegistry.getInstance().findContainers(id = "fdmprinter")[0]) - else: - quality_changes.setDefinition(global_stack.getBottom()) - - if global_stack.getMetaDataEntry("has_materials"): - material = container.getMetaDataEntry("material") - quality_changes.addMetaDataEntry("material", material) - - UM.Settings.ContainerRegistry.getInstance().addContainer(quality_changes) - + for container in self._getFilteredContainers(name = quality_name, type = "quality"): + for stack in cura.Settings.ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks(): + new_changes = self._createQualityChanges(container, new_name, stack.getId()) + UM.Settings.ContainerRegistry.getInstance().addContainer(new_changes) elif container_type == "quality_changes": - for container in self._getQualityContainers(quality_name): + for container in self._getFilteredContainers(name = quality_name, type = "quality_changes"): stack_id = container.getMetaDataEntry("extruder", global_stack.getId()) - new_container = container.duplicate(stack_id + "_" + new_id, new_name) + new_container = container.duplicate(self._createUniqueId(stack_id, new_name), new_name) UM.Settings.ContainerRegistry.getInstance().addContainer(new_container) else: return "" @@ -583,12 +576,17 @@ class ContainerManager(QObject): name_filter = "{0} ({1})".format(mime_type.comment, suffix_list) self._container_name_filters[name_filter] = entry - def _getQualityContainers(self, quality_name, container_type = "quality_changes"): + ## Return a generator that iterates over a set of containers that are filtered by machine and material when needed. + # + # \param kwargs Initial search criteria that the containers need to match. + # + # \return A generator that iterates over the list of containers matching the search criteria. + def _getFilteredContainers(self, **kwargs): global_stack = UM.Application.getInstance().getGlobalContainerStack() if not global_stack: return False - criteria = { "type": container_type, "name": quality_name } + criteria = kwargs filter_by_material = False @@ -599,18 +597,62 @@ class ContainerManager(QObject): material_ids = [] if filter_by_material: - stacks = [ s for s in cura.Settings.ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()) ] - stacks.insert(0, global_stack) - - for stack in stacks: + for stack in cura.Settings.ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks(): material_ids.append(stack.findContainer(type = "material").getId()) containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**criteria) 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: continue - if container.isReadOnly(): - continue - yield container + + ## Creates a unique ID for a container by prefixing the name with the stack ID. + # + # This method creates a unique ID for a container by prefixing it with a specified stack ID. + # This is done to ensure we have an easily identified ID for quality changes, which have the + # same name across several stacks. + # + # \param stack_id The ID of the stack to prepend. + # \param container_name The name of the container that we are creating a unique ID for. + # + # \return Container name prefixed with stack ID, in lower case with spaces replaced by underscores. + def _createUniqueId(self, stack_id, container_name): + result = stack_id + "_" + container_name + result = result.lower() + result.replace(" ", "_") + return result + + ## Create a quality changes container for a specified quality container. + # + # \param quality_container The quality container to create a changes container for. + # \param new_name The name of the new quality_changes container. + # \param stack_id The ID of the container stack the new container "belongs to". It is used primarily to ensure a unique ID. + # + # \return A new quality_changes container with the specified container as base. + def _createQualityChanges(self, quality_container, new_name, stack_id): + global_stack = UM.Application.getInstance().getGlobalContainerStack() + assert global_stack is not None + + # Create a new quality_changes container for the quality. + quality_changes = UM.Settings.InstanceContainer(self._createUniqueId(stack_id, new_name)) + quality_changes.setName(new_name) + quality_changes.addMetaDataEntry("type", "quality_changes") + quality_changes.addMetaDataEntry("quality", quality_container.getMetaDataEntry("quality_type")) + + # If we are creating a container for an extruder, ensure we add that to the container + if stack_id != global_stack.getId(): + quality_changes.addMetaDataEntry("extruder", stack_id) + + # If the machine specifies qualities should be filtered, ensure we match the current criteria. + if not global_stack.getMetaDataEntry("has_machine_quality"): + quality_changes.setDefinition(UM.Settings.ContainerRegistry.getInstance().findContainers(id = "fdmprinter")[0]) + else: + quality_changes.setDefinition(global_stack.getBottom()) + + if global_stack.getMetaDataEntry("has_materials"): + material = quality_container.getMetaDataEntry("material") + quality_changes.addMetaDataEntry("material", material) + + return quality_changes