From 774511370466667553ad06514935d25d3bc0a113 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 19 Oct 2017 15:32:21 +0200 Subject: [PATCH 01/73] Re-use stack variable We're creating the variable but then requesting the 0th element from the list each time... That can be more logical and more efficient. Contributes to issue CURA-4243. --- plugins/3MFReader/ThreeMFWorkspaceReader.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 5b1e084262..b4fc562378 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -651,13 +651,13 @@ class ThreeMFWorkspaceReader(WorkspaceReader): # HACK # There is a machine, check if it has authentication data. If so, keep that data. - network_authentication_id = container_stacks[0].getMetaDataEntry("network_authentication_id") - network_authentication_key = container_stacks[0].getMetaDataEntry("network_authentication_key") - container_stacks[0].deserialize(archive.open(global_stack_file).read().decode("utf-8")) + network_authentication_id = stack.getMetaDataEntry("network_authentication_id") + network_authentication_key = stack.getMetaDataEntry("network_authentication_key") + stack.deserialize(archive.open(global_stack_file).read().decode("utf-8")) if network_authentication_id: - container_stacks[0].addMetaDataEntry("network_authentication_id", network_authentication_id) + stack.addMetaDataEntry("network_authentication_id", network_authentication_id) if network_authentication_key: - container_stacks[0].addMetaDataEntry("network_authentication_key", network_authentication_key) + stack.addMetaDataEntry("network_authentication_key", network_authentication_key) elif self._resolve_strategies["machine"] == "new": # create a new global stack From 2fc198a7ad61f4ddb14a031b3e92e10aec2ae52a Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 19 Oct 2017 15:48:19 +0200 Subject: [PATCH 02/73] Move manual criterion into base criteria filter This code was filtering on certain criteria, and then filtering the results based on an additional metadata field. Let's just move that last field in the original criteria too... Contributes to issue CURA-4243. --- cura/QualityManager.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cura/QualityManager.py b/cura/QualityManager.py index b6d47d919b..90ca285736 100644 --- a/cura/QualityManager.py +++ b/cura/QualityManager.py @@ -209,11 +209,13 @@ class QualityManager: definition_id = "fdmprinter" if base_material: # There is a basic material specified - criteria = { "type": "material", "name": base_material, "definition": definition_id } + criteria = { + "type": "material", + "name": base_material, + "definition": definition_id, + "variant": material_container.getMetadataEntry("variant") + } containers = ContainerRegistry.getInstance().findInstanceContainers(**criteria) - containers = [basic_material for basic_material in containers if - basic_material.getMetaDataEntry("variant") == material_container.getMetaDataEntry( - "variant")] return containers return [] From f7e048aee539431c8c09e0abace22965efcf9518 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 19 Oct 2017 16:37:03 +0200 Subject: [PATCH 03/73] Remove unused variable Contributes to issue CURA-4243. --- cura/Settings/QualitySettingsModel.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cura/Settings/QualitySettingsModel.py b/cura/Settings/QualitySettingsModel.py index 2ab4e2a9b5..4dcaec245a 100644 --- a/cura/Settings/QualitySettingsModel.py +++ b/cura/Settings/QualitySettingsModel.py @@ -92,7 +92,6 @@ class QualitySettingsModel(UM.Qt.ListModel.ListModel): items = [] - settings = collections.OrderedDict() definition_container = Application.getInstance().getGlobalContainerStack().getBottom() containers = self._container_registry.findInstanceContainers(id = self._quality_id) From 6c4c7fff4d69844a14b7ba38217d564f54316c34 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 20 Oct 2017 15:09:45 +0200 Subject: [PATCH 04/73] Use findContainersMetadata whenever possible This ensures that we only load those containers that we actually need the data of. Contributes to issue CURA-4243. --- cura/PrintInformation.py | 2 +- cura/PrinterOutputDevice.py | 8 +- cura/QualityManager.py | 14 +-- cura/Settings/ContainerManager.py | 91 +++++++++---------- cura/Settings/CuraContainerRegistry.py | 18 ++-- cura/Settings/CuraContainerStack.py | 8 +- cura/Settings/CuraStackBuilder.py | 8 +- cura/Settings/ExtruderManager.py | 12 +-- cura/Settings/MachineManager.py | 36 ++++---- cura/Settings/ProfilesModel.py | 10 +- plugins/3MFReader/ThreeMFWorkspaceReader.py | 19 ++-- .../NetworkPrinterOutputDevice.py | 4 +- .../XmlMaterialProfile/XmlMaterialProfile.py | 30 ++---- tests/Settings/TestCuraContainerRegistry.py | 4 +- 14 files changed, 126 insertions(+), 138 deletions(-) diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index 86bcc2719e..9a500bec96 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -227,7 +227,7 @@ class PrintInformation(QObject): pass active_material_id = Application.getInstance().getMachineManager().activeMaterialId - active_material_containers = ContainerRegistry.getInstance().findInstanceContainers(id=active_material_id) + active_material_containers = ContainerRegistry.getInstance().findInstanceContainers(id = active_material_id) if active_material_containers: self._active_material_container = active_material_containers[0] diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index c6e98257ba..59a5e3ff2a 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -463,9 +463,9 @@ class PrinterOutputDevice(QObject, OutputDevice): result.append(i18n_catalog.i18nc("@item:material", "No material loaded")) continue - containers = self._container_registry.findInstanceContainers(type = "material", GUID = material_id) + containers = self._container_registry.findInstanceContainersMetadata(type = "material", GUID = material_id) if containers: - result.append(containers[0].getName()) + result.append(containers[0]["name"]) else: result.append(i18n_catalog.i18nc("@item:material", "Unknown material")) return result @@ -485,9 +485,9 @@ class PrinterOutputDevice(QObject, OutputDevice): result.append("#00000000") #No material. continue - containers = self._container_registry.findInstanceContainers(type = "material", GUID = material_id) + containers = self._container_registry.findInstanceContainersMetadata(type = "material", GUID = material_id) if containers: - result.append(containers[0].getMetaDataEntry("color_code")) + result.append(containers[0]["color_code"]) else: result.append("#00000000") #Unknown material. return result diff --git a/cura/QualityManager.py b/cura/QualityManager.py index 90ca285736..5c0a16da96 100644 --- a/cura/QualityManager.py +++ b/cura/QualityManager.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016 Ultimaker B.V. +# 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 @@ -229,7 +229,7 @@ class QualityManager: 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] + machine_definition = ContainerRegistry.getInstance().findDefinitionContainers(id = quality_definition_id)[0] # for convenience if material_containers is None: @@ -245,7 +245,7 @@ class QualityManager: machine_definition = self.getParentMachineDefinition(machine_definition) criteria["definition"] = machine_definition.getId() - found_containers_with_machine_definition = ContainerRegistry.getInstance().findInstanceContainers(**criteria) + 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()) @@ -290,13 +290,13 @@ class QualityManager: # 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] + 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] + 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. @@ -305,7 +305,7 @@ class QualityManager: # 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] + return container_registry.findDefinitionContainers(id = parent_extruder_id)[0] ## Get the whole/global machine definition from an extruder definition. # @@ -319,5 +319,5 @@ class QualityManager: return machine_definition else: container_registry = ContainerRegistry.getInstance() - whole_machine = container_registry.findDefinitionContainers(id=machine_entry)[0] + whole_machine = container_registry.findDefinitionContainers(id = machine_entry)[0] return whole_machine diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index 85aed93cf6..52b97cd60d 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -55,14 +55,14 @@ class ContainerManager(QObject): # \return The ID of the new container, or an empty string if duplication failed. @pyqtSlot(str, result = str) def duplicateContainer(self, container_id): - containers = self._container_registry.findContainers(None, id = container_id) + #TODO: It should be able to duplicate a container of which only the metadata is known. + containers = self._container_registry.findContainers(id = container_id) if not containers: Logger.log("w", "Could duplicate container %s because it was not found.", container_id) return "" container = containers[0] - new_container = None new_name = self._container_registry.uniqueName(container.getName()) # Only InstanceContainer has a duplicate method at the moment. # So fall back to serialize/deserialize when no duplicate method exists. @@ -87,14 +87,14 @@ class ContainerManager(QObject): # \return True if successful, False if not. @pyqtSlot(str, str, str, result = bool) def renameContainer(self, container_id, new_id, new_name): - containers = self._container_registry.findContainers(None, id = container_id) + containers = self._container_registry.findContainers(id = container_id) if not containers: Logger.log("w", "Could rename container %s because it was not found.", container_id) return False container = containers[0] # First, remove the container from the registry. This will clean up any files related to the container. - self._container_registry.removeContainer(container) + self._container_registry.removeContainer(container_id) # Ensure we have a unique name for the container new_name = self._container_registry.uniqueName(new_name) @@ -115,7 +115,7 @@ class ContainerManager(QObject): # \return True if the container was successfully removed, False if not. @pyqtSlot(str, result = bool) def removeContainer(self, container_id): - containers = self._container_registry.findContainers(None, id = container_id) + containers = self._container_registry.findContainers(id = container_id) if not containers: Logger.log("w", "Could remove container %s because it was not found.", container_id) return False @@ -135,14 +135,14 @@ class ContainerManager(QObject): # \return True if successfully merged, False if not. @pyqtSlot(str, result = bool) def mergeContainers(self, merge_into_id, merge_id): - containers = self._container_registry.findContainers(None, id = merge_into_id) + containers = self._container_registry.findContainers(id = merge_into_id) if not containers: Logger.log("w", "Could merge into container %s because it was not found.", merge_into_id) return False merge_into = containers[0] - containers = self._container_registry.findContainers(None, id = merge_id) + containers = self._container_registry.findContainers(id = merge_id) if not containers: Logger.log("w", "Could not merge container %s because it was not found", merge_id) return False @@ -164,7 +164,7 @@ class ContainerManager(QObject): # \return True if successful, False if not. @pyqtSlot(str, result = bool) def clearContainer(self, container_id): - containers = self._container_registry.findContainers(None, id = container_id) + containers = self._container_registry.findContainers(id = container_id) if not containers: Logger.log("w", "Could clear container %s because it was not found.", container_id) return False @@ -179,16 +179,12 @@ class ContainerManager(QObject): @pyqtSlot(str, str, result=str) def getContainerMetaDataEntry(self, container_id, entry_name): - containers = self._container_registry.findContainers(None, id=container_id) - if not containers: + metadatas = self._container_registry.findContainersMetadata(id = container_id) + if not metadatas: Logger.log("w", "Could not get metadata of container %s because it was not found.", container_id) return "" - result = containers[0].getMetaDataEntry(entry_name) - if result is not None: - return str(result) - else: - return "" + return str(metadatas[0].get(entry_name, "")) ## Set a metadata entry of the specified container. # @@ -204,7 +200,7 @@ class ContainerManager(QObject): # \return True if successful, False if not. @pyqtSlot(str, str, str, result = bool) def setContainerMetaDataEntry(self, container_id, entry_name, entry_value): - containers = self._container_registry.findContainers(None, id = container_id) + containers = self._container_registry.findContainers(id = container_id) #We need the complete container, since we need to know whether the container is read-only or not. if not containers: Logger.log("w", "Could not set metadata of container %s because it was not found.", container_id) return False @@ -254,7 +250,7 @@ class ContainerManager(QObject): # \return True if successful, False if not. @pyqtSlot(str, str, str, str, result = bool) def setContainerProperty(self, container_id, setting_key, property_name, property_value): - containers = self._container_registry.findContainers(None, id = container_id) + containers = self._container_registry.findContainers(id = container_id) if not containers: Logger.log("w", "Could not set properties of container %s because it was not found.", container_id) return False @@ -300,7 +296,7 @@ class ContainerManager(QObject): ## Set the name of the specified container. @pyqtSlot(str, str, result = bool) def setContainerName(self, container_id, new_name): - containers = self._container_registry.findContainers(None, id = container_id) + containers = self._container_registry.findContainers(id = container_id) #We need to get the full container, not just metadata, since we need to know whether it's read-only. if not containers: Logger.log("w", "Could not set name of container %s because it was not found.", container_id) return False @@ -317,18 +313,15 @@ class ContainerManager(QObject): ## Find instance containers matching certain criteria. # - # This effectively forwards to ContainerRegistry::findInstanceContainers. + # This effectively forwards to + # ContainerRegistry::findInstanceContainersMetadata. # # \param criteria A dict of key - value pairs to search for. # # \return A list of container IDs that match the given criteria. @pyqtSlot("QVariantMap", result = "QVariantList") def findInstanceContainers(self, criteria): - result = [] - for entry in self._container_registry.findInstanceContainers(**criteria): - result.append(entry.getId()) - - return result + return [entry["id"] for entry in self._container_registry.findInstanceContainersMetadata(**criteria)] @pyqtSlot(str, result = bool) def isContainerUsed(self, container_id): @@ -336,15 +329,17 @@ class ContainerManager(QObject): # check if this is a material container. If so, check if any material with the same base is being used by any # stacks. container_ids_to_check = [container_id] - container_results = self._container_registry.findInstanceContainers(id = container_id, type = "material") + container_results = self._container_registry.findInstanceContainersMetadata(id = container_id, type = "material") if container_results: this_container = container_results[0] - material_base_file = this_container.getMetaDataEntry("base_file", this_container.getId()) + material_base_file = this_container["id"] + if "base_file" in this_container: + material_base_file = this_container["base_file"] # check all material container IDs with the same base - material_containers = self._container_registry.findInstanceContainers(base_file = material_base_file, + material_containers = self._container_registry.findInstanceContainersMetadata(base_file = material_base_file, type = "material") if material_containers: - container_ids_to_check = [container.getId() for container in material_containers] + container_ids_to_check = [container["id"] for container in material_containers] all_stacks = self._container_registry.findContainerStacks() for stack in all_stacks: @@ -412,7 +407,7 @@ class ContainerManager(QObject): else: mime_type = self._container_name_filters[file_type]["mime"] - containers = self._container_registry.findContainers(None, id = container_id) + containers = self._container_registry.findContainers(id = container_id) if not containers: return { "status": "error", "message": "Container not found"} container = containers[0] @@ -616,9 +611,9 @@ class ContainerManager(QObject): elif activate_quality: definition_id = "fdmprinter" if not self._machine_manager.filterQualityByMachine else self._machine_manager.activeDefinitionId - containers = self._container_registry.findInstanceContainers(type = "quality", definition = definition_id, quality_type = activate_quality_type) + containers = self._container_registry.findInstanceContainersMetadata(type = "quality", definition = definition_id, quality_type = activate_quality_type) if containers: - self._machine_manager.setActiveQuality(containers[0].getId()) + self._machine_manager.setActiveQuality(containers[0]["id"]) self._machine_manager.activeQualityChanged.emit() return containers_found @@ -653,11 +648,13 @@ class ContainerManager(QObject): container_registry = self._container_registry - containers_to_rename = self._container_registry.findInstanceContainers(type = "quality_changes", name = quality_name) + containers_to_rename = self._container_registry.findInstanceContainersMetadata(type = "quality_changes", name = quality_name) for container in containers_to_rename: - stack_id = container.getMetaDataEntry("extruder", global_stack.getId()) - container_registry.renameContainer(container.getId(), new_name, self._createUniqueId(stack_id, new_name)) + stack_id = global_stack.getId() + if "extruder" in container: + stack_id = container["extruder"] + container_registry.renameContainer(container["id"], new_name, self._createUniqueId(stack_id, new_name)) if not containers_to_rename: Logger.log("e", "Unable to rename %s, because we could not find the profile", quality_name) @@ -754,7 +751,7 @@ class ContainerManager(QObject): # \return \type{str} the id of the newly created container. @pyqtSlot(str, result = str) def duplicateMaterial(self, material_id: str) -> str: - containers = self._container_registry.findInstanceContainers(id=material_id) + containers = self._container_registry.findInstanceContainers(id = material_id) if not containers: Logger.log("d", "Unable to duplicate the material with id %s, because it doesn't exist.", material_id) return "" @@ -789,12 +786,12 @@ class ContainerManager(QObject): return "" approximate_diameter = str(round(global_stack.getProperty("material_diameter", "value"))) - containers = self._container_registry.findInstanceContainers(id = "generic_pla*", approximate_diameter = approximate_diameter) + containers = self._container_registry.findInstanceContainersMetadata(id = "generic_pla*", approximate_diameter = approximate_diameter) if not containers: Logger.log("d", "Unable to create a new material by cloning Generic PLA, because it cannot be found for the material diameter for this machine.") return "" - base_file = containers[0].getMetaDataEntry("base_file") + base_file = containers[0].get("base_file") containers = self._container_registry.findInstanceContainers(id = base_file) if not containers: Logger.log("d", "Unable to create a new material by cloning Generic PLA, because the base file for Generic PLA for this machine can not be found.") @@ -835,12 +832,12 @@ class ContainerManager(QObject): has_variants = parseBool(global_stack.getMetaDataEntry("has_variants", default = False)) if has_machine_materials or has_variant_materials: if has_variants: - materials = self._container_registry.findInstanceContainers(type = "material", base_file = base_file, definition = global_stack.getBottom().getId(), variant = self._machine_manager.activeVariantId) + materials = self._container_registry.findInstanceContainersMetadata(type = "material", base_file = base_file, definition = global_stack.getBottom().getId(), variant = self._machine_manager.activeVariantId) else: - materials = self._container_registry.findInstanceContainers(type = "material", base_file = base_file, definition = global_stack.getBottom().getId()) + materials = self._container_registry.findInstanceContainersMetadata(type = "material", base_file = base_file, definition = global_stack.getBottom().getId()) if materials: - return materials[0].getId() + return materials[0]["id"] Logger.log("w", "Unable to find a suitable container based on %s for the current machine .", base_file) return "" # do not activate a new material if a container can not be found @@ -853,25 +850,25 @@ class ContainerManager(QObject): # \return \type{list} a list of names of materials with the same GUID @pyqtSlot(str, result = "QStringList") def getLinkedMaterials(self, material_id: str): - containers = self._container_registry.findInstanceContainers(id=material_id) + containers = self._container_registry.findInstanceContainersMetadata(id = material_id) if not containers: Logger.log("d", "Unable to find materials linked to material with id %s, because it doesn't exist.", material_id) return [] material_container = containers[0] - material_base_file = material_container.getMetaDataEntry("base_file", "") - material_guid = material_container.getMetaDataEntry("GUID", "") + material_base_file = material_container.get("base_file", "") + material_guid = material_container.get("GUID", "") if not material_guid: Logger.log("d", "Unable to find materials linked to material with id %s, because it doesn't have a GUID.", material_id) return [] - containers = self._container_registry.findInstanceContainers(type = "material", GUID = material_guid) + containers = self._container_registry.findInstanceContainersMetadata(type = "material", GUID = material_guid) linked_material_names = [] for container in containers: - if container.getId() in [material_id, material_base_file] or container.getMetaDataEntry("base_file") != container.getId(): + if container["id"] in [material_id, material_base_file] or container.get("base_file") != container["id"]: continue - linked_material_names.append(container.getName()) + linked_material_names.append(container["name"]) return linked_material_names ## Unlink a material from all other materials by creating a new GUID @@ -1004,7 +1001,7 @@ class ContainerManager(QObject): # If the machine specifies qualities should be filtered, ensure we match the current criteria. if not machine_definition.getMetaDataEntry("has_machine_quality"): - quality_changes.setDefinition(self._container_registry.findContainers(id = "fdmprinter")[0]) + quality_changes.setDefinition(self._container_registry.findDefinitionContainers(id = "fdmprinter")[0]) else: quality_changes.setDefinition(QualityManager.getInstance().getParentMachineDefinition(machine_definition)) diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index 12f27e8156..8903e195dc 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -86,8 +86,8 @@ class CuraContainerRegistry(ContainerRegistry): def _containerExists(self, container_type, container_name): container_class = ContainerStack if container_type == "machine" else InstanceContainer - return self.findContainers(container_class, id = container_name, type = container_type, ignore_case = True) or \ - self.findContainers(container_class, name = container_name, type = container_type) + return self.findContainersMetadata(id = container_name, type = container_type, ignore_case = True) or \ + self.findContainersMetadata(container_type = container_class, name = container_name, type = container_type) ## Exports an profile to a file # @@ -116,7 +116,7 @@ class CuraContainerRegistry(ContainerRegistry): found_containers = [] extruder_positions = [] for instance_id in instance_ids: - containers = ContainerRegistry.getInstance().findInstanceContainers(id=instance_id) + containers = ContainerRegistry.getInstance().findInstanceContainers(id = instance_id) if containers: found_containers.append(containers[0]) @@ -126,9 +126,9 @@ class CuraContainerRegistry(ContainerRegistry): # Global stack extruder_positions.append(-1) else: - extruder_containers = ContainerRegistry.getInstance().findDefinitionContainers(id=extruder_id) + extruder_containers = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = extruder_id) if extruder_containers: - extruder_positions.append(int(extruder_containers[0].getMetaDataEntry("position", 0))) + extruder_positions.append(int(extruder_containers[0].get("position", 0))) else: extruder_positions.append(0) # Ensure the profiles are always exported in order (global, extruder 0, extruder 1, ...) @@ -294,7 +294,7 @@ class CuraContainerRegistry(ContainerRegistry): quality_type_criteria["definition"] = profile.getDefinition().getId() else: - profile.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0]) + profile.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")[0]) quality_type_criteria["definition"] = "fdmprinter" machine_definition = Application.getInstance().getGlobalContainerStack().getBottom() @@ -335,7 +335,7 @@ class CuraContainerRegistry(ContainerRegistry): global_container_stack = Application.getInstance().getGlobalContainerStack() if global_container_stack: definition_id = Application.getInstance().getMachineManager().getQualityDefinitionId(global_container_stack.getBottom()) - definition = self.findDefinitionContainers(id=definition_id)[0] + definition = self.findDefinitionContainers(id = definition_id)[0] if definition: return definition @@ -397,13 +397,13 @@ class CuraContainerRegistry(ContainerRegistry): # set after upgrading, because the proper global stack was not yet loaded. This method # makes sure those extruders also get the right stack set. def _fixupExtruders(self): - extruder_stacks = self.findContainers(ExtruderStack.ExtruderStack) + extruder_stacks = self.findContainers(container_type = ExtruderStack.ExtruderStack) for extruder_stack in extruder_stacks: if extruder_stack.getNextStack(): # Has the right next stack, so ignore it. continue - machines = ContainerRegistry.getInstance().findContainerStacks(id=extruder_stack.getMetaDataEntry("machine", "")) + machines = ContainerRegistry.getInstance().findContainerStacks(id = extruder_stack.getMetaDataEntry("machine", "")) if machines: extruder_stack.setNextStack(machines[0]) else: diff --git a/cura/Settings/CuraContainerStack.py b/cura/Settings/CuraContainerStack.py index 2d3bf683f6..7f34cc12e7 100755 --- a/cura/Settings/CuraContainerStack.py +++ b/cura/Settings/CuraContainerStack.py @@ -14,7 +14,7 @@ from UM.Settings.ContainerStack import ContainerStack, InvalidContainerStackErro from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.DefinitionContainer import DefinitionContainer from UM.Settings.ContainerRegistry import ContainerRegistry -from UM.Settings.Interfaces import ContainerInterface +from UM.Settings.Interfaces import ContainerInterface, DefinitionContainerInterface from . import Exceptions @@ -235,7 +235,7 @@ class CuraContainerStack(ContainerStack): ## Set the definition container. # # \param new_quality_changes The new definition container. It is expected to have a "type" metadata entry with the value "quality_changes". - def setDefinition(self, new_definition: DefinitionContainer) -> None: + def setDefinition(self, new_definition: DefinitionContainerInterface) -> None: self.replaceContainer(_ContainerIndexes.Definition, new_definition) ## Set the definition container by an ID. @@ -544,10 +544,10 @@ class CuraContainerStack(ContainerStack): material_search_criteria["variant"] = self.variant.id else: material_search_criteria["definition"] = "fdmprinter" - material_containers = registry.findInstanceContainers(**material_search_criteria) + material_containers = registry.findInstanceContainersMetadata(**material_search_criteria) # Try all materials to see if there is a quality profile available. for material_container in material_containers: - search_criteria["material"] = material_container.getId() + search_criteria["material"] = material_container["id"] containers = registry.findInstanceContainers(**search_criteria) if containers: diff --git a/cura/Settings/CuraStackBuilder.py b/cura/Settings/CuraStackBuilder.py index 09815da319..f7a1592155 100644 --- a/cura/Settings/CuraStackBuilder.py +++ b/cura/Settings/CuraStackBuilder.py @@ -3,7 +3,7 @@ from UM.Logger import Logger -from UM.Settings.DefinitionContainer import DefinitionContainer +from UM.Settings.Interfaces import DefinitionContainerInterface from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.ContainerRegistry import ContainerRegistry @@ -34,7 +34,7 @@ class CuraStackBuilder: # Make sure the new name does not collide with any definition or (quality) profile # createUniqueName() only looks at other stacks, but not at definitions or quality profiles # Note that we don't go for uniqueName() immediately because that function matches with ignore_case set to true - if registry.findContainers(id = generated_name): + if registry.findContainersMetadata(id = generated_name): generated_name = registry.uniqueName(generated_name) new_global_stack = cls.createGlobalStack( @@ -74,7 +74,7 @@ class CuraStackBuilder: # # \return A new Global stack instance with the specified parameters. @classmethod - def createExtruderStack(cls, new_stack_id: str, definition: DefinitionContainer, machine_definition: DefinitionContainer, **kwargs) -> ExtruderStack: + def createExtruderStack(cls, new_stack_id: str, definition: DefinitionContainerInterface, machine_definition: DefinitionContainerInterface, **kwargs) -> ExtruderStack: stack = ExtruderStack(new_stack_id) stack.setName(definition.getName()) stack.setDefinition(definition) @@ -127,7 +127,7 @@ class CuraStackBuilder: # # \return A new Global stack instance with the specified parameters. @classmethod - def createGlobalStack(cls, new_stack_id: str, definition: DefinitionContainer, **kwargs) -> GlobalStack: + def createGlobalStack(cls, new_stack_id: str, definition: DefinitionContainerInterface, **kwargs) -> GlobalStack: stack = GlobalStack(new_stack_id) stack.setDefinition(definition) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index c8daca7f92..8585c3cf7d 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -222,7 +222,7 @@ class ExtruderManager(QObject): position = extruder_definition.getMetaDataEntry("position", None) if not position: Logger.log("w", "Extruder definition %s specifies no position metadata entry.", extruder_definition.getId()) - if not container_registry.findContainerStacks(machine = machine_id, position = position): # Doesn't exist yet. + if not container_registry.findContainerStacksMetadata(machine = machine_id, position = position): # Doesn't exist yet. self.createExtruderTrain(extruder_definition, machine_definition, position, machine_id) changed = True @@ -357,13 +357,13 @@ class ExtruderManager(QObject): if preferred_quality: search_criteria["id"] = preferred_quality - containers = ContainerRegistry.getInstance().findInstanceContainers(**search_criteria) - if not containers and preferred_quality: + quality_containers = ContainerRegistry.getInstance().findInstanceContainers(**search_criteria) + if not quality_containers and preferred_quality: Logger.log("w", "The preferred quality \"%s\" of machine %s doesn't exist or is not a quality profile.", preferred_quality, machine_id) search_criteria.pop("id", None) - containers = ContainerRegistry.getInstance().findInstanceContainers(**search_criteria) - if containers: - quality = containers[0] + quality_containers = ContainerRegistry.getInstance().findInstanceContainers(**search_criteria) + if quality_containers: + quality = quality_containers[0] container_stack.addContainer(quality) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 563965915a..c5dec6b6cc 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -95,7 +95,7 @@ class MachineManager(QObject): # There might already be some output devices by the time the signal is connected self._onOutputDevicesChanged() - if active_machine_id != "" and ContainerRegistry.getInstance().findContainerStacks(id = active_machine_id): + if active_machine_id != "" and ContainerRegistry.getInstance().findContainerStacksMetadata(id = active_machine_id): # An active machine was saved, so restore it. self.setActiveMachine(active_machine_id) if self._global_container_stack and self._global_container_stack.getProperty("machine_extruder_count", "value") > 1: @@ -145,13 +145,13 @@ class MachineManager(QObject): @pyqtProperty(int, constant=True) def totalNumberOfSettings(self) -> int: - return len(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0].getAllKeys()) + return len(ContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")[0].getAllKeys()) def _onHotendIdChanged(self, index: Union[str, int], hotend_id: str) -> None: if not self._global_container_stack: return - containers = ContainerRegistry.getInstance().findInstanceContainers(type="variant", definition=self._global_container_stack.getBottom().getId(), name=hotend_id) + containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(type="variant", definition=self._global_container_stack.getBottom().getId(), name=hotend_id) if containers: # New material ID is known extruder_manager = ExtruderManager.getInstance() machine_id = self.activeMachineId @@ -163,7 +163,7 @@ class MachineManager(QObject): break if matching_extruder and matching_extruder.variant.getName() != hotend_id: # Save the material that needs to be changed. Multiple changes will be handled by the callback. - self._auto_hotends_changed[str(index)] = containers[0].getId() + self._auto_hotends_changed[str(index)] = containers[0]["id"] self._printer_output_devices[0].materialHotendChangedMessage(self._materialHotendChangedCallback) else: Logger.log("w", "No variant found for printer definition %s with id %s" % (self._global_container_stack.getBottom().getId(), hotend_id)) @@ -176,7 +176,7 @@ class MachineManager(QObject): if self._global_container_stack.getMetaDataEntry("has_machine_materials", False): definition_id = self.activeQualityDefinitionId extruder_manager = ExtruderManager.getInstance() - containers = ContainerRegistry.getInstance().findInstanceContainers(type = "material", definition = definition_id, GUID = material_id) + containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(type = "material", definition = definition_id, GUID = material_id) if containers: # New material ID is known extruders = list(extruder_manager.getMachineExtruders(self.activeMachineId)) matching_extruder = None @@ -190,12 +190,12 @@ class MachineManager(QObject): if self._global_container_stack.getBottom().getMetaDataEntry("has_variants") and matching_extruder.variant: variant_id = self.getQualityVariantId(self._global_container_stack.getBottom(), matching_extruder.variant) for container in containers: - if container.getMetaDataEntry("variant") == variant_id: - self._auto_materials_changed[str(index)] = container.getId() + if container.get("variant") == variant_id: + self._auto_materials_changed[str(index)] = container["id"] break else: # Just use the first result we found. - self._auto_materials_changed[str(index)] = containers[0].getId() + self._auto_materials_changed[str(index)] = containers[0]["id"] self._printer_output_devices[0].materialHotendChangedMessage(self._materialHotendChangedCallback) else: Logger.log("w", "No material definition found for printer definition %s and GUID %s" % (definition_id, material_id)) @@ -818,7 +818,7 @@ class MachineManager(QObject): with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue): self.blurSettings.emit() - containers = ContainerRegistry.getInstance().findInstanceContainers(id = quality_id) + containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = quality_id) if not containers or not self._global_container_stack: return @@ -826,9 +826,9 @@ class MachineManager(QObject): # 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() - quality_type = containers[0].getMetaDataEntry("quality_type") + container_type = containers[0].get("type") + quality_name = containers[0]["name"] + quality_type = containers[0].get("quality_type") # Get quality container and optionally the quality_changes container. if container_type == "quality": @@ -1091,15 +1091,15 @@ class MachineManager(QObject): # activate a new machine before removing a machine because this is safer if activate_new_machine: - machine_stacks = ContainerRegistry.getInstance().findContainerStacks(type = "machine") - other_machine_stacks = [s for s in machine_stacks if s.getId() != machine_id] + machine_stacks = ContainerRegistry.getInstance().findContainerStacksMetadata(type = "machine") + other_machine_stacks = [s for s in machine_stacks if s["id"] != machine_id] if other_machine_stacks: - self.setActiveMachine(other_machine_stacks[0].getId()) + self.setActiveMachine(other_machine_stacks[0]["id"]) ExtruderManager.getInstance().removeMachineExtruders(machine_id) - containers = ContainerRegistry.getInstance().findInstanceContainers(type = "user", machine = machine_id) + containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(type = "user", machine = machine_id) for container in containers: - ContainerRegistry.getInstance().removeContainer(container.getId()) + ContainerRegistry.getInstance().removeContainer(container["id"]) ContainerRegistry.getInstance().removeContainer(machine_id) @pyqtProperty(bool, notify = globalContainerChanged) @@ -1138,7 +1138,7 @@ class MachineManager(QObject): # \returns DefinitionID (string) if found, None otherwise @pyqtSlot(str, result = str) def getDefinitionByMachineId(self, machine_id: str) -> str: - containers = ContainerRegistry.getInstance().findContainerStacks(id=machine_id) + containers = ContainerRegistry.getInstance().findContainerStacks(id = machine_id) if containers: return containers[0].getBottom().getId() diff --git a/cura/Settings/ProfilesModel.py b/cura/Settings/ProfilesModel.py index bf1993b184..6ba38dcf48 100644 --- a/cura/Settings/ProfilesModel.py +++ b/cura/Settings/ProfilesModel.py @@ -125,8 +125,12 @@ class ProfilesModel(InstanceContainersModel): # active machine and material, and later yield the right ones. tmp_all_quality_items = OrderedDict() for item in super()._recomputeItems(): - profile = container_registry.findContainers(id=item["id"]) - quality_type = profile[0].getMetaDataEntry("quality_type") if profile else "" + + profiles = container_registry.findContainersMetadata(id = item["id"]) + if not profiles or "quality_type" not in profiles[0]: + quality_type = "" + else: + quality_type = profiles[0]["quality_type"] if quality_type not in tmp_all_quality_items: tmp_all_quality_items[quality_type] = {"suitable_container": None, "all_containers": []} @@ -155,7 +159,7 @@ class ProfilesModel(InstanceContainersModel): # Now all the containers are set for item in containers: - profile = container_registry.findContainers(id=item["id"]) + profile = container_registry.findContainers(id = item["id"]) if not profile: self._setItemLayerHeight(item, "", unit) item["available"] = False diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index b4fc562378..f12c02fe8d 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -130,7 +130,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader): # A few lists of containers in this project files. # When loading the global stack file, it may be associated with those containers, which may or may not be # in Cura already, so we need to provide them as alternative search lists. - definition_container_list = [] instance_container_list = [] material_container_list = [] @@ -146,20 +145,20 @@ class ThreeMFWorkspaceReader(WorkspaceReader): definition_container_files = [name for name in cura_file_names if name.endswith(self._definition_container_suffix)] for each_definition_container_file in definition_container_files: container_id = self._stripFileToId(each_definition_container_file) - definitions = self._container_registry.findDefinitionContainers(id=container_id) + definitions = self._container_registry.findDefinitionContainersMetadata(id = container_id) if not definitions: definition_container = DefinitionContainer(container_id) definition_container.deserialize(archive.open(each_definition_container_file).read().decode("utf-8")) + definition_container = definition_container.getMetaData() else: definition_container = definitions[0] - definition_container_list.append(definition_container) - definition_container_type = definition_container.getMetaDataEntry("type") + definition_container_type = definition_container.get("type") if definition_container_type == "machine": - machine_type = definition_container.getName() - variant_type_name = definition_container.getMetaDataEntry("variants_name", variant_type_name) + machine_type = definition_container["name"] + variant_type_name = definition_container.get("variants_name", variant_type_name) machine_definition_container_count += 1 elif definition_container_type == "extruder": @@ -456,7 +455,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): extruder_stack_id_map = {} # new and old ExtruderStack IDs map if self._resolve_strategies["machine"] == "new": # We need a new id if the id already exists - if self._container_registry.findContainerStacks(id = global_stack_id_original): + if self._container_registry.findContainerStacksMetadata(id = global_stack_id_original): global_stack_id_new = self.getNewId(global_stack_id_original) global_stack_need_rename = True @@ -465,7 +464,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): for each_extruder_stack_file in extruder_stack_files: old_container_id = self._stripFileToId(each_extruder_stack_file) new_container_id = old_container_id - if self._container_registry.findContainerStacks(id = old_container_id): + if self._container_registry.findContainerStacksMetadata(id = old_container_id): # get a new name for this extruder new_container_id = self.getNewId(old_container_id) @@ -479,7 +478,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): definition_container_files = [name for name in cura_file_names if name.endswith(self._definition_container_suffix)] for definition_container_file in definition_container_files: container_id = self._stripFileToId(definition_container_file) - definitions = self._container_registry.findDefinitionContainers(id = container_id) + definitions = self._container_registry.findDefinitionContainersMetadata(id = container_id) if not definitions: definition_container = DefinitionContainer(container_id) definition_container.deserialize(archive.open(definition_container_file).read().decode("utf-8")) @@ -626,7 +625,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): pass quality_and_definition_changes_instance_containers.append(instance_container) else: - existing_container = self._container_registry.findInstanceContainers(id = container_id) + existing_container = self._container_registry.findInstanceContainersMetadata(id = container_id) if not existing_container: containers_to_add.append(instance_container) if global_stack_need_rename: diff --git a/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py b/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py index 9dedc87df4..07a8df985c 100755 --- a/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py +++ b/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py @@ -724,10 +724,10 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): remote_material_guid, material.getMetaDataEntry("GUID")) - remote_materials = UM.Settings.ContainerRegistry.ContainerRegistry.getInstance().findInstanceContainers(type = "material", GUID = remote_material_guid, read_only = True) + remote_materials = UM.Settings.ContainerRegistry.ContainerRegistry.getInstance().findInstanceContainersMetadata(type = "material", GUID = remote_material_guid, read_only = True) remote_material_name = "Unknown" if remote_materials: - remote_material_name = remote_materials[0].getName() + remote_material_name = remote_materials[0]["name"] warnings.append(i18n_catalog.i18nc("@label", "Different material (Cura: {0}, Printer: {1}) selected for extruder {2}").format(material.getName(), remote_material_name, index + 1)) try: diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index de3b3fb71f..4dc955db42 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -94,18 +94,6 @@ class XmlMaterialProfile(InstanceContainer): if not base_container.isReadOnly(): base_container.setDirty(dirty) - ## Overridden from InstanceContainer - # def setProperty(self, key, property_name, property_value, container = None): - # if self.isReadOnly(): - # return - # - # super().setProperty(key, property_name, property_value) - # - # basefile = self.getMetaDataEntry("base_file", self._id) #if basefile is self.id, this is a basefile. - # for container in UM.Settings.ContainerRegistry.ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile): - # if not container.isReadOnly(): - # container.setDirty(True) - ## Overridden from InstanceContainer # base file: common settings + supported machines # machine / variant combination: only changes for itself. @@ -222,8 +210,8 @@ class XmlMaterialProfile(InstanceContainer): # Map machine human-readable names to IDs product_id_map = {} - for container in registry.findDefinitionContainers(type = "machine"): - product_id_map[container.getName()] = container.getId() + for container in registry.findDefinitionContainersMetadata(type = "machine"): + product_id_map[container["name"]] = container["id"] for definition_id, container in machine_container_map.items(): definition = container.getDefinition() @@ -249,11 +237,11 @@ class XmlMaterialProfile(InstanceContainer): # Find all hotend sub-profiles corresponding to this material and machine and add them to this profile. for hotend_id, hotend in machine_nozzle_map[definition_id].items(): - variant_containers = registry.findInstanceContainers(id = hotend.getMetaDataEntry("variant")) + variant_containers = registry.findInstanceContainersMetadata(id = hotend.getMetaDataEntry("variant")) if not variant_containers: continue - builder.start("hotend", {"id": variant_containers[0].getName()}) + builder.start("hotend", {"id": variant_containers[0]["id"]}) # Compatible is a special case, as it's added as a meta data entry (instead of an instance). compatible = hotend.getMetaDataEntry("compatible") @@ -519,8 +507,8 @@ class XmlMaterialProfile(InstanceContainer): # Map machine human-readable names to IDs product_id_map = {} - for container in ContainerRegistry.getInstance().findDefinitionContainers(type = "machine"): - product_id_map[container.getName()] = container.getId() + for container in ContainerRegistry.getInstance().findDefinitionContainersMetadata(type = "machine"): + product_id_map[container["name"]] = container["id"] machines = data.iterfind("./um:settings/um:machine", self.__namespaces) for machine in machines: @@ -592,10 +580,10 @@ class XmlMaterialProfile(InstanceContainer): if hotend_id is None: continue - variant_containers = ContainerRegistry.getInstance().findInstanceContainers(id = hotend_id) + variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = hotend_id) if not variant_containers: # It is not really properly defined what "ID" is so also search for variants by name. - variant_containers = ContainerRegistry.getInstance().findInstanceContainers(definition = definition.id, name = hotend_id) + variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = definition.id, name = hotend_id) if not variant_containers: #Logger.log("d", "No variants found with ID or name %s for machine %s", hotend_id, definition.id) @@ -630,7 +618,7 @@ class XmlMaterialProfile(InstanceContainer): new_hotend_material._name = self.getName() new_hotend_material.setMetaData(copy.deepcopy(self.getMetaData())) new_hotend_material.setDefinition(definition) - new_hotend_material.addMetaDataEntry("variant", variant_containers[0].id) + new_hotend_material.addMetaDataEntry("variant", variant_containers[0]["id"]) # Don't use setMetadata, as that overrides it for all materials with same base file new_hotend_material.getMetaData()["compatible"] = hotend_compatibility new_hotend_material.getMetaData()["machine_manufacturer"] = machine_manufacturer diff --git a/tests/Settings/TestCuraContainerRegistry.py b/tests/Settings/TestCuraContainerRegistry.py index a6d33afc6d..d502d31cc3 100644 --- a/tests/Settings/TestCuraContainerRegistry.py +++ b/tests/Settings/TestCuraContainerRegistry.py @@ -140,8 +140,8 @@ def test_loadTypes(filename, output_class, container_registry): #Check whether the resulting type was correct. stack_id = filename.split(".")[0] - for container in container_registry._containers: #Stupid ContainerRegistry class doesn't expose any way of getting at this except by prodding the privates. - if container.getId() == stack_id: #This is the one we're testing. + for container_id, container in container_registry._containers.items(): #Stupid ContainerRegistry class doesn't expose any way of getting at this except by prodding the privates. + if container_id == stack_id: #This is the one we're testing. assert type(container) == output_class break else: From 7ac3c1446b416315f6e87dbf5bacecd5cf97db99 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 27 Oct 2017 16:25:52 +0200 Subject: [PATCH 05/73] Make ignored metadata keys a set It needs to be a set now for Uranium. Contributes to issue CURA-4243. --- plugins/3MFWriter/ThreeMFWorkspaceWriter.py | 2 +- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py index 9c143f0057..0fffe693cc 100644 --- a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py +++ b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py @@ -95,7 +95,7 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter): file_in_archive.compress_type = zipfile.ZIP_DEFLATED # Do not include the network authentication keys - ignore_keys = ["network_authentication_id", "network_authentication_key"] + ignore_keys = {"network_authentication_id", "network_authentication_key"} serialized_data = container.serialize(ignored_metadata_keys = ignore_keys) archive.writestr(file_in_archive, serialized_data) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 4dc955db42..dd07ec73e8 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -97,7 +97,7 @@ class XmlMaterialProfile(InstanceContainer): ## Overridden from InstanceContainer # base file: common settings + supported machines # machine / variant combination: only changes for itself. - def serialize(self, ignored_metadata_keys: Optional[List] = None): + def serialize(self, ignored_metadata_keys: Optional[set] = None): registry = ContainerRegistry.getInstance() base_file = self.getMetaDataEntry("base_file", "") @@ -119,8 +119,8 @@ class XmlMaterialProfile(InstanceContainer): metadata = copy.deepcopy(self.getMetaData()) # setting_version is derived from the "version" tag in the schema, so don't serialize it into a file if ignored_metadata_keys is None: - ignored_metadata_keys = [] - ignored_metadata_keys = ignored_metadata_keys + ["setting_version"] + ignored_metadata_keys = set() + ignored_metadata_keys |= {"setting_version"} # remove the keys that we want to ignore in the metadata for key in ignored_metadata_keys: if key in metadata: From a31c39b2253630d9ce695ecb9ce7c34fa5f64b4f Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 27 Oct 2017 16:35:58 +0200 Subject: [PATCH 06/73] Use setters and getters for name and ID Because their implementation changed, these would have been implemented in some other way. Instead I use the getters and setters which are inherited from the parent class and therefore are already updated. Contributes to issue CURA-4243. --- .../XmlMaterialProfile/XmlMaterialProfile.py | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index dd07ec73e8..bce9a4139a 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -45,7 +45,7 @@ class XmlMaterialProfile(InstanceContainer): def setReadOnly(self, read_only): super().setReadOnly(read_only) - basefile = self.getMetaDataEntry("base_file", self._id) # if basefile is self.id, this is a basefile. + basefile = self.getMetaDataEntry("base_file", self.getId()) # if basefile is self.getId, this is a basefile. for container in ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile): container._read_only = read_only # prevent loop instead of calling setReadOnly @@ -57,7 +57,7 @@ class XmlMaterialProfile(InstanceContainer): super().setMetaDataEntry(key, value) - basefile = self.getMetaDataEntry("base_file", self._id) #if basefile is self.id, this is a basefile. + basefile = self.getMetaDataEntry("base_file", self.getId()) #if basefile is self.getId, this is a basefile. # Update all containers that share basefile for container in ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile): if container.getMetaDataEntry(key, None) != value: # Prevent recursion @@ -76,7 +76,7 @@ class XmlMaterialProfile(InstanceContainer): super().setName(new_name) - basefile = self.getMetaDataEntry("base_file", self._id) # if basefile is self.id, this is a basefile. + basefile = self.getMetaDataEntry("base_file", self.getId()) # if basefile is self.getId, this is a basefile. # Update the basefile as well, this is actually what we're trying to do # Update all containers that share GUID and basefile containers = ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile) @@ -87,7 +87,7 @@ class XmlMaterialProfile(InstanceContainer): def setDirty(self, dirty): super().setDirty(dirty) base_file = self.getMetaDataEntry("base_file", None) - if base_file is not None and base_file != self._id: + if base_file is not None and base_file != self.getId(): containers = ContainerRegistry.getInstance().findContainers(id=base_file) if containers: base_container = containers[0] @@ -101,7 +101,7 @@ class XmlMaterialProfile(InstanceContainer): registry = ContainerRegistry.getInstance() base_file = self.getMetaDataEntry("base_file", "") - if base_file and self.id != base_file: + if base_file and self.getId() != base_file: # Since we create an instance of XmlMaterialProfile for each machine and nozzle in the profile, # we should only serialize the "base" material definition, since that can then take care of # serializing the machine/nozzle specific profiles. @@ -150,7 +150,7 @@ class XmlMaterialProfile(InstanceContainer): builder.end("color") builder.start("label") - builder.data(self._name) + builder.data(self.getName()) builder.end("label") builder.end("name") @@ -182,16 +182,16 @@ class XmlMaterialProfile(InstanceContainer): ## Begin Settings Block builder.start("settings") - if self.getDefinition().id == "fdmprinter": + if self.getDefinition().getId() == "fdmprinter": for instance in self.findInstances(): self._addSettingElement(builder, instance) machine_container_map = {} machine_nozzle_map = {} - all_containers = registry.findInstanceContainers(GUID = self.getMetaDataEntry("GUID"), base_file = self._id) + all_containers = registry.findInstanceContainers(GUID = self.getMetaDataEntry("GUID"), base_file = self.getId()) for container in all_containers: - definition_id = container.getDefinition().id + definition_id = container.getDefinition().getId() if definition_id == "fdmprinter": continue @@ -229,7 +229,7 @@ class XmlMaterialProfile(InstanceContainer): builder.end("machine_identifier") for instance in container.findInstances(): - if self.getDefinition().id == "fdmprinter" and self.getInstance(instance.definition.key) and self.getProperty(instance.definition.key, "value") == instance.value: + if self.getDefinition().getId() == "fdmprinter" and self.getInstance(instance.definition.key) and self.getProperty(instance.definition.key, "value") == instance.value: # If the settings match that of the base profile, just skip since we inherit the base profile. continue @@ -385,7 +385,7 @@ class XmlMaterialProfile(InstanceContainer): def clearData(self): self._metadata = {} - self._name = "" + self.setName("") self._definition = None self._instances = {} self._read_only = False @@ -424,7 +424,7 @@ class XmlMaterialProfile(InstanceContainer): self.clearData() # Ensure any previous data is gone. meta_data = {} meta_data["type"] = "material" - meta_data["base_file"] = self.id + meta_data["base_file"] = self.getId() meta_data["status"] = "unknown" # TODO: Add material verification common_setting_values = {} @@ -451,9 +451,9 @@ class XmlMaterialProfile(InstanceContainer): label = entry.find("./um:label", self.__namespaces) if label is not None: - self._name = label.text + self.setName(label.text) else: - self._name = self._profile_name(material.text, color.text) + self.setName(self._profile_name(material.text, color.text)) meta_data["brand"] = brand.text meta_data["material"] = material.text meta_data["color_name"] = color.text @@ -545,7 +545,7 @@ class XmlMaterialProfile(InstanceContainer): machine_manufacturer = identifier.get("manufacturer", definition.getMetaDataEntry("manufacturer", "Unknown")) #If the XML material doesn't specify a manufacturer, use the one in the actual printer definition. if machine_compatibility: - new_material_id = self.id + "_" + machine_id + new_material_id = self.getId() + "_" + machine_id # The child or derived material container may already exist. This can happen when a material in a # project file and the a material in Cura have the same ID. @@ -560,7 +560,7 @@ class XmlMaterialProfile(InstanceContainer): is_new_material = True # Update the private directly, as we want to prevent the lookup that is done when using setName - new_material._name = self.getName() + new_material.setName(self.getName()) new_material.setMetaData(copy.deepcopy(self.getMetaData())) new_material.setDefinition(definition) # Don't use setMetadata, as that overrides it for all materials with same base file @@ -583,10 +583,9 @@ class XmlMaterialProfile(InstanceContainer): variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = hotend_id) if not variant_containers: # It is not really properly defined what "ID" is so also search for variants by name. - variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = definition.id, name = hotend_id) + variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = definition.getId(), name = hotend_id) if not variant_containers: - #Logger.log("d", "No variants found with ID or name %s for machine %s", hotend_id, definition.id) continue hotend_compatibility = machine_compatibility @@ -602,7 +601,7 @@ class XmlMaterialProfile(InstanceContainer): else: Logger.log("d", "Unsupported material setting %s", key) - new_hotend_id = self.id + "_" + machine_id + "_" + hotend_id.replace(" ", "_") + new_hotend_id = self.getId() + "_" + machine_id + "_" + hotend_id.replace(" ", "_") # Same as machine compatibility, keep the derived material containers consistent with the parent # material @@ -615,7 +614,7 @@ class XmlMaterialProfile(InstanceContainer): is_new_material = True # Update the private directly, as we want to prevent the lookup that is done when using setName - new_hotend_material._name = self.getName() + new_hotend_material.setName(self.getName()) new_hotend_material.setMetaData(copy.deepcopy(self.getMetaData())) new_hotend_material.setDefinition(definition) new_hotend_material.addMetaDataEntry("variant", variant_containers[0]["id"]) From 8bbb6c1af53f96f71ee8cd6ab623712924cd5ba4 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 27 Oct 2017 16:50:13 +0200 Subject: [PATCH 07/73] Load and serialize container ID and name properly: not in metadata We shouldn't write the name or ID, who are now in the metadata. Also we should load the name and ID properly from the file. Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index bce9a4139a..4d828a4314 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -133,6 +133,8 @@ class XmlMaterialProfile(InstanceContainer): metadata.pop("type", "") metadata.pop("base_file", "") metadata.pop("approximate_diameter", "") + metadata.pop("id", "") + metadata.pop("container_type", "") ## Begin Name Block builder.start("name") @@ -417,15 +419,18 @@ class XmlMaterialProfile(InstanceContainer): try: data = ET.fromstring(serialized) except: - Logger.logException("e", "An exception occured while parsing the material profile") + Logger.logException("e", "An exception occurred while parsing the material profile") return # Reset previous metadata + old_id = self.getId() self.clearData() # Ensure any previous data is gone. meta_data = {} meta_data["type"] = "material" meta_data["base_file"] = self.getId() meta_data["status"] = "unknown" # TODO: Add material verification + meta_data["id"] = old_id + meta_data["container_type"] = XmlMaterialProfile common_setting_values = {} From 839c8ccc52563811234bbd754dd2cbc4dd76a734 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 30 Oct 2017 11:15:41 +0100 Subject: [PATCH 08/73] Fix 'Could not get metadata of container ' Turns out that the side bar header was requesting the metadata before the active material was set. Now I'm just saying that if the active material was not set, it should not be compatible. Contributes to issue CURA-4243, side-ways. --- cura/Settings/MachineManager.py | 2 +- resources/qml/Preferences/MaterialView.qml | 2 +- resources/qml/SidebarHeader.qml | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index c5dec6b6cc..2a77032a33 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -151,7 +151,7 @@ class MachineManager(QObject): if not self._global_container_stack: return - containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(type="variant", definition=self._global_container_stack.getBottom().getId(), name=hotend_id) + containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(type = "variant", definition = self._global_container_stack.getBottom().getId(), name = hotend_id) if containers: # New material ID is known extruder_manager = ExtruderManager.getInstance() machine_id = self.activeMachineId diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index 143f29c86e..311150c6b9 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -41,7 +41,7 @@ TabView Tab { - title: catalog.i18nc("@title","Information") + title: catalog.i18nc("@title", "Information") anchors.margins: UM.Theme.getSize("default_margin").width diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index f3887e2885..551d85ac13 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -252,6 +252,10 @@ Column visible: Cura.MachineManager.hasMaterials property var valueError: { + if(Cura.MachineManager.activeMaterialId === "") + { + return false + } var data = Cura.ContainerManager.getContainerMetaDataEntry(Cura.MachineManager.activeMaterialId, "compatible") if(data == "False") { From 3a9c8d16a21b2ffa25ff902429f1a6c647398d4d Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 30 Oct 2017 15:07:46 +0100 Subject: [PATCH 09/73] Fix remaining references to instance_container._id These must've slipped through the cracks. Contributes to issue CURA-4243. --- cura/Settings/GlobalStack.py | 2 +- plugins/3MFReader/ThreeMFWorkspaceReader.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py index 88218c2f1e..5e1be36d67 100755 --- a/cura/Settings/GlobalStack.py +++ b/cura/Settings/GlobalStack.py @@ -74,7 +74,7 @@ class GlobalStack(CuraContainerStack): return if any(item.getId() == extruder.id for item in self._extruders.values()): - Logger.log("w", "Extruder [%s] has already been added to this stack [%s]", extruder.id, self._id) + Logger.log("w", "Extruder [%s] has already been added to this stack [%s]", extruder.id, self.getId()) return self._extruders[position] = extruder diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index f12c02fe8d..15ee48164d 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -567,7 +567,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): if old_extruder_id: new_extruder_id = extruder_stack_id_map[old_extruder_id] new_id = new_extruder_id + "_current_settings" - instance_container._id = new_id + instance_container.setMetaDataEntry("id", new_id) instance_container.setName(new_id) instance_container.setMetaDataEntry("extruder", new_extruder_id) containers_to_add.append(instance_container) @@ -576,7 +576,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): if machine_id: new_machine_id = self.getNewId(machine_id) new_id = new_machine_id + "_current_settings" - instance_container._id = new_id + instance_container.setMetadataEntry("id", new_id) instance_container.setName(new_id) instance_container.setMetaDataEntry("machine", new_machine_id) containers_to_add.append(instance_container) From 8eaec149fad8c2d592c0f133d798a5da7b92ac14 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 30 Oct 2017 15:54:26 +0100 Subject: [PATCH 10/73] Make sure the ID stays in the metadata when clearing it Previously the ID was also exempted from the clear. Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 4d828a4314..651f2906ff 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -386,7 +386,7 @@ class XmlMaterialProfile(InstanceContainer): first.append(element) def clearData(self): - self._metadata = {} + self._metadata = {"id": self.getId()} self.setName("") self._definition = None self._instances = {} From d24fa3bc3bc7448c56076933e7643ad040e4f12f Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 31 Oct 2017 08:23:57 +0100 Subject: [PATCH 11/73] Set name directly in metadata when clearing Because the setName function checks if the name is equal to the previous name, but at that point the previous name doesn't exist. Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 651f2906ff..74d7cc93ee 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -386,8 +386,10 @@ class XmlMaterialProfile(InstanceContainer): first.append(element) def clearData(self): - self._metadata = {"id": self.getId()} - self.setName("") + self._metadata = { + "id": self.getId(), + "name": "" + } self._definition = None self._instances = {} self._read_only = False From a08875c5ebbc98ce23556ae231b9ea20e0957ccf Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 31 Oct 2017 11:11:56 +0100 Subject: [PATCH 12/73] Remove redundant setting of name It's just copied along with the rest of the metadata now. Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 74d7cc93ee..df148c19f1 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -566,8 +566,6 @@ class XmlMaterialProfile(InstanceContainer): new_material = XmlMaterialProfile(new_material_id) is_new_material = True - # Update the private directly, as we want to prevent the lookup that is done when using setName - new_material.setName(self.getName()) new_material.setMetaData(copy.deepcopy(self.getMetaData())) new_material.setDefinition(definition) # Don't use setMetadata, as that overrides it for all materials with same base file From 74bd527b03662b67e5f2d6a564aa07aada1b6e21 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 1 Nov 2017 14:52:03 +0100 Subject: [PATCH 13/73] Load product_id_map from file For now this file is hard-coded. We should eventually try to generate this in the build system. Contributes to issue CURA-4243. --- .../XmlMaterialProfile/XmlMaterialProfile.py | 24 +++++++++++++------ plugins/XmlMaterialProfile/product_to_id.json | 12 ++++++++++ 2 files changed, 29 insertions(+), 7 deletions(-) create mode 100644 plugins/XmlMaterialProfile/product_to_id.json diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index df148c19f1..dec988ad15 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -3,7 +3,10 @@ import copy import io -from typing import List, Optional +import json #To parse the product-to-id mapping file. +import os.path #To find the product-to-id mapping. +import sys +from typing import Dict, Optional import xml.etree.ElementTree as ET from UM.Resources import Resources @@ -11,6 +14,7 @@ from UM.Logger import Logger from cura.CuraApplication import CuraApplication import UM.Dictionary +from UM.PluginRegistry import PluginRegistry from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.ContainerRegistry import ContainerRegistry @@ -211,9 +215,7 @@ class XmlMaterialProfile(InstanceContainer): machine_container_map[definition_id] = container # Map machine human-readable names to IDs - product_id_map = {} - for container in registry.findDefinitionContainersMetadata(type = "machine"): - product_id_map[container["name"]] = container["id"] + product_id_map = self.getProductIdMap() for definition_id, container in machine_container_map.items(): definition = container.getDefinition() @@ -513,9 +515,7 @@ class XmlMaterialProfile(InstanceContainer): self._dirty = False # Map machine human-readable names to IDs - product_id_map = {} - for container in ContainerRegistry.getInstance().findDefinitionContainersMetadata(type = "machine"): - product_id_map[container["name"]] = container["id"] + product_id_map = self.getProductIdMap() machines = data.iterfind("./um:settings/um:machine", self.__namespaces) for machine in machines: @@ -656,6 +656,16 @@ class XmlMaterialProfile(InstanceContainer): else: return material_name + ## Gets a mapping from product names in the XML files to their definition + # IDs. + # + # This loads the mapping from a file. + @classmethod + def getProductIdMap(cls) -> Dict[str, str]: + product_to_id_file = os.path.join(os.path.dirname(sys.modules[cls.__module__].__file__), "product_to_id.json") + with open(product_to_id_file) as f: + return json.load(f) + ## Parse the value of the "material compatible" property. def _parseCompatibleValue(self, value: str): return value in {"yes", "unknown"} diff --git a/plugins/XmlMaterialProfile/product_to_id.json b/plugins/XmlMaterialProfile/product_to_id.json new file mode 100644 index 0000000000..d6b8f3bade --- /dev/null +++ b/plugins/XmlMaterialProfile/product_to_id.json @@ -0,0 +1,12 @@ +{ + "Ultimaker 2": "ultimaker2", + "Ultimaker 2 Extended": "ultimaker2_extended", + "Ultimaker 2 Extended+": "ultimaker2_extended_plus", + "Ultimaker 2 Go": "ultimaker2_go", + "Ultimaker 2+": "ultimaker2_plus", + "Ultimaker 3": "ultimaker3", + "Ultimaker 3 Extended": "ultimaker3_extended", + "Ultimaker Original": "ultimaker_original", + "Ultimaker Original+": "ultimaker_original_plus", + "IMADE3D JellyBOX": "imade3d_jellybox" +} \ No newline at end of file From ddf5ab04944019e4f89ee2a5b334fa5f05cc9f72 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 1 Nov 2017 14:52:57 +0100 Subject: [PATCH 14/73] Make _parseCompatibleValue a class method It might as well be static, since it doesn't need an instance. Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index dec988ad15..1583ba9212 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -667,7 +667,8 @@ class XmlMaterialProfile(InstanceContainer): return json.load(f) ## Parse the value of the "material compatible" property. - def _parseCompatibleValue(self, value: str): + @classmethod + def _parseCompatibleValue(cls, value: str): return value in {"yes", "unknown"} # Map XML file setting names to internal names From 07947d5d2cbcfdf7daf8a8749eca87900f8271f0 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 1 Nov 2017 14:53:52 +0100 Subject: [PATCH 15/73] Override getIdsFromFile to load multiple IDs This should result in all IDs in the XML file, since there are multiple per file here. Contributes to issue CURA-4243. --- .../XmlMaterialProfile/XmlMaterialProfile.py | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 1583ba9212..03120b4c3a 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -640,6 +640,51 @@ class XmlMaterialProfile(InstanceContainer): for container_to_add in containers_to_add: ContainerRegistry.getInstance().addContainer(container_to_add) + ## Override of getIdsFromFile because the XML files contain multiple IDs. + @classmethod + def getIdsFromFile(cls, file_name): + result_ids = super().getIdsFromFile(file_name) #The base file has the default ID, taken from the file name without extension. + base_id = result_ids[0] + + try: + data = ET.parse(file_name) + except: #IOError, PermissionError, or anything from the ElementTree library. + Logger.logException("e", "An exception occurred while parsing the material profile") + return + + common_compatibility = True + compatible_entries = data.iterfind("./um:settings/um:setting[@key='hardware compatible']", cls.__namespaces) + try: + common_compatibility = cls._parseCompatibleValue(next(compatible_entries).text) + except StopIteration: #No 'hardware compatible' setting. + pass + + #Get a mapping from the product names to the definition IDs. + product_id_map = cls.getProductIdMap() + + for machine in data.iterfind("./um:settings/um:machine", cls.__namespaces): + machine_compatibility = common_compatibility + compatible_entries = data.iterfind("./um:setting[@key='hardware compatible']", cls.__namespaces) + try: + machine_compatibility = cls._parseCompatibleValue(next(compatible_entries).text) + except StopIteration: #No 'hardware compatible' setting. + pass + + for identifier in machine.iterfind("./um:machine_identifier", cls.__namespaces): #For all machines. + machine_id = product_id_map.get(identifier.get("product"), None) + if machine_id is None: + #Let's try again with some naive heuristics. + machine_id = identifier.get("product").replace(" ", "").lower() + if machine_compatibility: + result_ids.append(base_id + "_" + machine_id) + + for hotend in machine.iterfind("./um:hotend", cls.__namespaces): #For all hotends. + hotend_id = hotend.get("id") + if hotend_id is None: + continue + result_ids.append(base_id + "_" + machine_id + "_" + hotend_id) + return result_ids + def _addSettingElement(self, builder, instance): try: key = UM.Dictionary.findKey(self.__material_settings_setting_map, instance.definition.key) From e48b151d40d3a57ab3bfc5f4ed6b0b4f60fe0f91 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 1 Nov 2017 16:17:26 +0100 Subject: [PATCH 16/73] Express getConfigurationTypeFromSerialized as function of its parent This prevents code duplication a bit and allows us to make it a classmethod. Contributes to issue CURA-4243. --- cura/Settings/GlobalStack.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py index 5e1be36d67..863e6b16f2 100755 --- a/cura/Settings/GlobalStack.py +++ b/cura/Settings/GlobalStack.py @@ -43,15 +43,11 @@ class GlobalStack(CuraContainerStack): def getLoadingPriority(cls) -> int: return 2 - def getConfigurationTypeFromSerialized(self, serialized: str) -> Optional[str]: - configuration_type = None - try: - parser = self._readAndValidateSerialized(serialized) - configuration_type = parser["metadata"].get("type") - if configuration_type == "machine": - configuration_type = "machine_stack" - except Exception as e: - Logger.log("e", "Could not get configuration type: %s", e) + @classmethod + def getConfigurationTypeFromSerialized(cls, serialized: str) -> Optional[str]: + configuration_type = super().getConfigurationTypeFromSerialized(serialized) + if configuration_type == "machine": + return "machine_stack" return configuration_type ## Add an extruder to the list of extruders of this stack. From d369f92020c6906034b3f17ebee382ffe52806ef Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 1 Nov 2017 16:18:08 +0100 Subject: [PATCH 17/73] Make get[ConfigurationType|Version]FromSerialized class methods This way we can call them before an instance has been created, in order to upgrade the serialized forms. Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 03120b4c3a..506670d5ee 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -14,7 +14,6 @@ from UM.Logger import Logger from cura.CuraApplication import CuraApplication import UM.Dictionary -from UM.PluginRegistry import PluginRegistry from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.ContainerRegistry import ContainerRegistry @@ -37,7 +36,8 @@ class XmlMaterialProfile(InstanceContainer): # # \param xml_version: The version number found in an XML file. # \return The corresponding setting_version. - def xmlVersionToSettingVersion(self, xml_version: str) -> int: + @classmethod + def xmlVersionToSettingVersion(cls, xml_version: str) -> int: if xml_version == "1.3": return 3 return 0 #Older than 1.3. @@ -398,18 +398,20 @@ class XmlMaterialProfile(InstanceContainer): self._dirty = False self._path = "" - def getConfigurationTypeFromSerialized(self, serialized: str) -> Optional[str]: + @classmethod + def getConfigurationTypeFromSerialized(cls, serialized: str) -> Optional[str]: return "materials" - def getVersionFromSerialized(self, serialized: str) -> Optional[int]: + @classmethod + def getVersionFromSerialized(cls, serialized: str) -> Optional[int]: data = ET.fromstring(serialized) version = 1 # get setting version if "version" in data.attrib: - setting_version = self.xmlVersionToSettingVersion(data.attrib["version"]) + setting_version = cls.xmlVersionToSettingVersion(data.attrib["version"]) else: - setting_version = self.xmlVersionToSettingVersion("1.2") + setting_version = cls.xmlVersionToSettingVersion("1.2") return version * 1000000 + setting_version From cf7bad1bbb4c5984ff0a404dc9e525ea70f1e492 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 2 Nov 2017 10:18:52 +0100 Subject: [PATCH 18/73] Make _profile_name a class method This way we can execute it statically while getting the metadata. Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 506670d5ee..cb93d51b63 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -697,7 +697,8 @@ class XmlMaterialProfile(InstanceContainer): builder.data(str(instance.value)) builder.end("setting") - def _profile_name(self, material_name, color_name): + @classmethod + def _profile_name(cls, material_name, color_name): if color_name != "Generic": return "%s %s" % (color_name, material_name) else: From 202f7f87f883cb3b57f33ba4b4a3c76ac16b951e Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 2 Nov 2017 12:53:06 +0100 Subject: [PATCH 19/73] Remove ID from definition files Sometimes it's out of sync. Sometimes even missing. Let's just not use that any more. Contributes to issue CURA-4243. --- resources/definitions/101Hero.def.json | 1 - resources/definitions/3dator.def.json | 1 - resources/definitions/abax_pri3.def.json | 1 - resources/definitions/abax_pri5.def.json | 1 - resources/definitions/abax_titan.def.json | 1 - resources/definitions/alya3dp.def.json | 1 - resources/definitions/bfb.def.json | 1 - resources/definitions/bq_hephestos.def.json | 1 - resources/definitions/bq_hephestos_2.def.json | 1 - resources/definitions/bq_hephestos_xl.def.json | 1 - resources/definitions/bq_witbox.def.json | 1 - resources/definitions/bq_witbox_2.def.json | 1 - resources/definitions/cartesio.def.json | 1 - resources/definitions/creality_cr10.def.json | 1 - resources/definitions/creality_cr10s4.def.json | 1 - resources/definitions/creality_cr10s5.def.json | 1 - resources/definitions/custom.def.json | 1 - resources/definitions/dagoma_discoeasy200.def.json | 1 - resources/definitions/delta_go.def.json | 1 - resources/definitions/deltabot.def.json | 1 - resources/definitions/easyarts_ares.def.json | 1 - resources/definitions/fabtotum.def.json | 1 - resources/definitions/fdmextruder.def.json | 1 - resources/definitions/fdmprinter.def.json | 1 - resources/definitions/grr_neo.def.json | 1 - resources/definitions/helloBEEprusa.def.json | 1 - resources/definitions/imade3d_jellybox.def.json | 1 - resources/definitions/innovo_inventor.def.json | 1 - resources/definitions/julia.def.json | 1 - resources/definitions/kemiq_q2_beta.def.json | 1 - resources/definitions/kemiq_q2_gama.def.json | 1 - resources/definitions/kossel_mini.def.json | 1 - resources/definitions/kossel_pro.def.json | 1 - resources/definitions/kupido.def.json | 1 - resources/definitions/m180.def.json | 1 - resources/definitions/makeR_pegasus.def.json | 1 - resources/definitions/makeR_prusa_tairona_i3.def.json | 1 - resources/definitions/makeit_pro_l.def.json | 1 - resources/definitions/makeit_pro_m.def.json | 1 - resources/definitions/maker_starter.def.json | 1 - resources/definitions/makerbotreplicator.def.json | 1 - resources/definitions/mankati_fullscale_xt_plus.def.json | 1 - resources/definitions/mendel90.def.json | 1 - resources/definitions/ord.def.json | 1 - resources/definitions/peopoly_moai.def.json | 1 - resources/definitions/printrbot_play.def.json | 1 - resources/definitions/printrbot_play_heated.def.json | 1 - resources/definitions/printrbot_simple.def.json | 1 - resources/definitions/printrbot_simple_extended.def.json | 1 - resources/definitions/prusa_i3.def.json | 1 - resources/definitions/prusa_i3_mk2.def.json | 1 - resources/definitions/prusa_i3_xl.def.json | 1 - resources/definitions/punchtec_connect_xl.def.json | 1 - resources/definitions/raise3D_N2_dual.def.json | 6 +----- resources/definitions/raise3D_N2_plus_dual.def.json | 4 ---- resources/definitions/raise3D_N2_plus_single.def.json | 4 ---- resources/definitions/raise3D_N2_single.def.json | 4 ---- resources/definitions/renkforce_rf100.def.json | 1 - resources/definitions/rigid3d.def.json | 1 - resources/definitions/rigid3d_3rdgen.def.json | 1 - resources/definitions/rigid3d_hobby.def.json | 1 - resources/definitions/rigid3d_zero.def.json | 1 - resources/definitions/rigid3d_zero2.def.json | 1 - resources/definitions/rigidbot.def.json | 1 - resources/definitions/rigidbot_big.def.json | 1 - resources/definitions/robo_3d_r1.def.json | 1 - resources/definitions/tam.def.json | 1 - resources/definitions/tevo_tarantula.def.json | 1 - resources/definitions/ultimaker.def.json | 1 - resources/definitions/ultimaker2.def.json | 1 - resources/definitions/ultimaker2_extended.def.json | 1 - resources/definitions/ultimaker2_extended_plus.def.json | 1 - resources/definitions/ultimaker2_go.def.json | 1 - resources/definitions/ultimaker2_plus.def.json | 1 - resources/definitions/ultimaker3.def.json | 1 - resources/definitions/ultimaker3_extended.def.json | 1 - resources/definitions/ultimaker_original.def.json | 1 - resources/definitions/ultimaker_original_dual.def.json | 1 - resources/definitions/ultimaker_original_plus.def.json | 1 - resources/definitions/uniqbot_one.def.json | 1 - resources/definitions/vertex_delta_k8800.def.json | 1 - resources/definitions/vertex_k8400.def.json | 1 - resources/definitions/vertex_k8400_dual.def.json | 1 - resources/definitions/zone3d_printer.def.json | 1 - 84 files changed, 1 insertion(+), 97 deletions(-) diff --git a/resources/definitions/101Hero.def.json b/resources/definitions/101Hero.def.json index 3d19aef626..824c9da32b 100644 --- a/resources/definitions/101Hero.def.json +++ b/resources/definitions/101Hero.def.json @@ -1,5 +1,4 @@ { - "id": "101Hero", "version": 2, "name": "101Hero", "inherits": "fdmprinter", diff --git a/resources/definitions/3dator.def.json b/resources/definitions/3dator.def.json index b72a49a35b..069d2d9d95 100644 --- a/resources/definitions/3dator.def.json +++ b/resources/definitions/3dator.def.json @@ -1,5 +1,4 @@ { - "id": "3Dator", "version": 2, "name": "3Dator", "inherits": "fdmprinter", diff --git a/resources/definitions/abax_pri3.def.json b/resources/definitions/abax_pri3.def.json index fa826e6f94..2fa648096f 100644 --- a/resources/definitions/abax_pri3.def.json +++ b/resources/definitions/abax_pri3.def.json @@ -1,5 +1,4 @@ { - "id": "PRi3", "name": "ABAX PRi3", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/abax_pri5.def.json b/resources/definitions/abax_pri5.def.json index b5588e5c96..cbebb576b0 100644 --- a/resources/definitions/abax_pri5.def.json +++ b/resources/definitions/abax_pri5.def.json @@ -1,5 +1,4 @@ { - "id": "PRi5", "name": "ABAX PRi5", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/abax_titan.def.json b/resources/definitions/abax_titan.def.json index 53b768c93f..5f7a99d64d 100644 --- a/resources/definitions/abax_titan.def.json +++ b/resources/definitions/abax_titan.def.json @@ -1,5 +1,4 @@ { - "id": "Titan", "name": "ABAX Titan", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/alya3dp.def.json b/resources/definitions/alya3dp.def.json index 5fa6630f51..2fda102249 100644 --- a/resources/definitions/alya3dp.def.json +++ b/resources/definitions/alya3dp.def.json @@ -1,5 +1,4 @@ { - "id": "alya3dp", "name": "ALYA", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/bfb.def.json b/resources/definitions/bfb.def.json index fddec169c5..7a76e231a8 100644 --- a/resources/definitions/bfb.def.json +++ b/resources/definitions/bfb.def.json @@ -1,5 +1,4 @@ { - "id": "bfb", "name": "BFB", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/bq_hephestos.def.json b/resources/definitions/bq_hephestos.def.json index 91eeecb500..0fb40a4741 100644 --- a/resources/definitions/bq_hephestos.def.json +++ b/resources/definitions/bq_hephestos.def.json @@ -1,5 +1,4 @@ { - "id": "bq_hephestos", "name": "BQ Prusa i3 Hephestos", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/bq_hephestos_2.def.json b/resources/definitions/bq_hephestos_2.def.json index 272e547af0..a3493334b6 100644 --- a/resources/definitions/bq_hephestos_2.def.json +++ b/resources/definitions/bq_hephestos_2.def.json @@ -1,5 +1,4 @@ { - "id": "bq_hephestos_2", "version": 2, "name": "BQ Hephestos 2", "inherits": "fdmprinter", diff --git a/resources/definitions/bq_hephestos_xl.def.json b/resources/definitions/bq_hephestos_xl.def.json index a5a183b1e9..75b756c71e 100644 --- a/resources/definitions/bq_hephestos_xl.def.json +++ b/resources/definitions/bq_hephestos_xl.def.json @@ -1,5 +1,4 @@ { - "id": "bq_hephestos_xl", "version": 2, "name": "BQ Prusa i3 Hephestos XL", "inherits": "fdmprinter", diff --git a/resources/definitions/bq_witbox.def.json b/resources/definitions/bq_witbox.def.json index ca14151e7a..ef4e12b704 100644 --- a/resources/definitions/bq_witbox.def.json +++ b/resources/definitions/bq_witbox.def.json @@ -1,5 +1,4 @@ { - "id": "bq_witbox", "version": 2, "name": "BQ Witbox", "inherits": "fdmprinter", diff --git a/resources/definitions/bq_witbox_2.def.json b/resources/definitions/bq_witbox_2.def.json index f634d9c9c8..ab3786ad1f 100644 --- a/resources/definitions/bq_witbox_2.def.json +++ b/resources/definitions/bq_witbox_2.def.json @@ -1,5 +1,4 @@ { - "id": "bq_witbox_2", "version": 2, "name": "BQ Witbox 2", "inherits": "fdmprinter", diff --git a/resources/definitions/cartesio.def.json b/resources/definitions/cartesio.def.json index 45b0111209..f544d7644b 100644 --- a/resources/definitions/cartesio.def.json +++ b/resources/definitions/cartesio.def.json @@ -1,5 +1,4 @@ { - "id": "cartesio", "name": "Cartesio", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/creality_cr10.def.json b/resources/definitions/creality_cr10.def.json index ced6f32b7b..bc9c16bbf9 100644 --- a/resources/definitions/creality_cr10.def.json +++ b/resources/definitions/creality_cr10.def.json @@ -1,5 +1,4 @@ { - "id": "creality_cr10", "name": "Creality CR-10", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/creality_cr10s4.def.json b/resources/definitions/creality_cr10s4.def.json index 13c2e53391..cebe255705 100644 --- a/resources/definitions/creality_cr10s4.def.json +++ b/resources/definitions/creality_cr10s4.def.json @@ -1,5 +1,4 @@ { - "id": "creality_cr10s4", "name": "Creality CR-10 S4", "version": 2, "inherits": "creality_cr10", diff --git a/resources/definitions/creality_cr10s5.def.json b/resources/definitions/creality_cr10s5.def.json index 15cf0b93d9..e6aaa88b04 100644 --- a/resources/definitions/creality_cr10s5.def.json +++ b/resources/definitions/creality_cr10s5.def.json @@ -1,5 +1,4 @@ { - "id": "creality_cr10s5", "name": "Creality CR-10 S5", "version": 2, "inherits": "creality_cr10", diff --git a/resources/definitions/custom.def.json b/resources/definitions/custom.def.json index 8f15f00a0f..e973a75bbf 100644 --- a/resources/definitions/custom.def.json +++ b/resources/definitions/custom.def.json @@ -1,5 +1,4 @@ { - "id": "custom", "version": 2, "name": "Custom FDM printer", "inherits": "fdmprinter", diff --git a/resources/definitions/dagoma_discoeasy200.def.json b/resources/definitions/dagoma_discoeasy200.def.json index 8f1a792bc0..32f4a8b297 100644 --- a/resources/definitions/dagoma_discoeasy200.def.json +++ b/resources/definitions/dagoma_discoeasy200.def.json @@ -1,5 +1,4 @@ { - "id": "Dagoma_discoeasy200", "name": "Dagoma DiscoEasy200", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/delta_go.def.json b/resources/definitions/delta_go.def.json index 36e0787039..a6d75b2983 100644 --- a/resources/definitions/delta_go.def.json +++ b/resources/definitions/delta_go.def.json @@ -1,5 +1,4 @@ { -"id": "Delta_Go", "name": "Delta Go", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/deltabot.def.json b/resources/definitions/deltabot.def.json index cee5ab1be1..e3b676d0ec 100644 --- a/resources/definitions/deltabot.def.json +++ b/resources/definitions/deltabot.def.json @@ -1,5 +1,4 @@ { - "id": "deltabot", "name": "DeltaBot", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/easyarts_ares.def.json b/resources/definitions/easyarts_ares.def.json index 8d3e5338b6..982496de4c 100644 --- a/resources/definitions/easyarts_ares.def.json +++ b/resources/definitions/easyarts_ares.def.json @@ -1,5 +1,4 @@ { - "id": "easyarts_ares", "name": "EasyArts Ares", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/fabtotum.def.json b/resources/definitions/fabtotum.def.json index eb33280401..87ce11a35c 100644 --- a/resources/definitions/fabtotum.def.json +++ b/resources/definitions/fabtotum.def.json @@ -1,5 +1,4 @@ { - "id": "fabtotum", "version": 2, "name": "FABtotum Personal Fabricator", "inherits": "fdmprinter", diff --git a/resources/definitions/fdmextruder.def.json b/resources/definitions/fdmextruder.def.json index 8ed194fc2d..13f3cbd7af 100644 --- a/resources/definitions/fdmextruder.def.json +++ b/resources/definitions/fdmextruder.def.json @@ -1,5 +1,4 @@ { - "id": "fdmextruder", "name": "Extruder", "version": 2, "metadata": diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index e2176a8009..6a71effd09 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1,5 +1,4 @@ { - "id": "fdmprinter", "name": "FDM Printer Base Description", "version": 2, "metadata": diff --git a/resources/definitions/grr_neo.def.json b/resources/definitions/grr_neo.def.json index 42c2e75319..847f638fbd 100644 --- a/resources/definitions/grr_neo.def.json +++ b/resources/definitions/grr_neo.def.json @@ -1,5 +1,4 @@ { - "id": "grr_neo", "version": 2, "name": "German RepRap Neo", "inherits": "fdmprinter", diff --git a/resources/definitions/helloBEEprusa.def.json b/resources/definitions/helloBEEprusa.def.json index 660e182187..2699a6c7d7 100644 --- a/resources/definitions/helloBEEprusa.def.json +++ b/resources/definitions/helloBEEprusa.def.json @@ -1,5 +1,4 @@ { - "id": "BEEVERYCREATIVE-helloBEEprusa", "version": 2, "name": "Hello BEE Prusa", "inherits": "fdmprinter", diff --git a/resources/definitions/imade3d_jellybox.def.json b/resources/definitions/imade3d_jellybox.def.json index 0c0f29c070..11df730408 100644 --- a/resources/definitions/imade3d_jellybox.def.json +++ b/resources/definitions/imade3d_jellybox.def.json @@ -1,5 +1,4 @@ { - "id": "imade3d_jellybox", "version": 2, "name": "IMADE3D JellyBOX", "inherits": "fdmprinter", diff --git a/resources/definitions/innovo_inventor.def.json b/resources/definitions/innovo_inventor.def.json index 5fc6c83ca2..84b6697d34 100644 --- a/resources/definitions/innovo_inventor.def.json +++ b/resources/definitions/innovo_inventor.def.json @@ -1,5 +1,4 @@ { - "id": "innovo-inventor", "version": 2, "name": "Innovo INVENTOR", "inherits": "fdmprinter", diff --git a/resources/definitions/julia.def.json b/resources/definitions/julia.def.json index fe10473596..7fdee30272 100644 --- a/resources/definitions/julia.def.json +++ b/resources/definitions/julia.def.json @@ -1,5 +1,4 @@ { - "id": "julia", "name": "Julia", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/kemiq_q2_beta.def.json b/resources/definitions/kemiq_q2_beta.def.json index d5bb8a895e..7946fbfad2 100644 --- a/resources/definitions/kemiq_q2_beta.def.json +++ b/resources/definitions/kemiq_q2_beta.def.json @@ -1,5 +1,4 @@ { - "id": "kemiq_q2_beta", "version": 2, "name": "Kemiq Q2 Beta", "inherits": "fdmprinter", diff --git a/resources/definitions/kemiq_q2_gama.def.json b/resources/definitions/kemiq_q2_gama.def.json index 1cb1b45c21..64051606f2 100644 --- a/resources/definitions/kemiq_q2_gama.def.json +++ b/resources/definitions/kemiq_q2_gama.def.json @@ -1,5 +1,4 @@ { - "id": "kemiq_q2_gama", "version": 2, "name": "Kemiq Q2 Gama", "inherits": "fdmprinter", diff --git a/resources/definitions/kossel_mini.def.json b/resources/definitions/kossel_mini.def.json index d915c148a3..df9c29b548 100644 --- a/resources/definitions/kossel_mini.def.json +++ b/resources/definitions/kossel_mini.def.json @@ -1,5 +1,4 @@ { - "id": "kossel_mini", "version": 2, "name": "Kossel Mini", "inherits": "fdmprinter", diff --git a/resources/definitions/kossel_pro.def.json b/resources/definitions/kossel_pro.def.json index 58f1c7f94e..fa0cb571fa 100644 --- a/resources/definitions/kossel_pro.def.json +++ b/resources/definitions/kossel_pro.def.json @@ -1,5 +1,4 @@ { - "id": "kossel_pro", "version": 2, "name": "Kossel Pro", "inherits": "fdmprinter", diff --git a/resources/definitions/kupido.def.json b/resources/definitions/kupido.def.json index 8dec63f6ec..b16ba62ccc 100644 --- a/resources/definitions/kupido.def.json +++ b/resources/definitions/kupido.def.json @@ -1,5 +1,4 @@ { - "id": "kupido", "name": "Kupido", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/m180.def.json b/resources/definitions/m180.def.json index 1e8ac1767b..71aa729b7e 100644 --- a/resources/definitions/m180.def.json +++ b/resources/definitions/m180.def.json @@ -1,5 +1,4 @@ { - "id": "m180", "version": 2, "name": "Malyan M180", "inherits": "fdmprinter", diff --git a/resources/definitions/makeR_pegasus.def.json b/resources/definitions/makeR_pegasus.def.json index b164b2983f..fb5a7dce75 100644 --- a/resources/definitions/makeR_pegasus.def.json +++ b/resources/definitions/makeR_pegasus.def.json @@ -1,5 +1,4 @@ { - "id": "makeR_pegasus", "version": 2, "name": "makeR Pegasus", "inherits": "fdmprinter", diff --git a/resources/definitions/makeR_prusa_tairona_i3.def.json b/resources/definitions/makeR_prusa_tairona_i3.def.json index caccb2ebe6..a82fffd254 100644 --- a/resources/definitions/makeR_prusa_tairona_i3.def.json +++ b/resources/definitions/makeR_prusa_tairona_i3.def.json @@ -1,5 +1,4 @@ { - "id": "makeR_prusa_tairona_i3", "version": 2, "name": "makeR Prusa Tairona i3", "inherits": "fdmprinter", diff --git a/resources/definitions/makeit_pro_l.def.json b/resources/definitions/makeit_pro_l.def.json index 36f7354b64..2a64a6729c 100644 --- a/resources/definitions/makeit_pro_l.def.json +++ b/resources/definitions/makeit_pro_l.def.json @@ -1,5 +1,4 @@ { - "id": "makeit_pro_l", "version": 2, "name": "MAKEiT Pro-L", "inherits": "fdmprinter", diff --git a/resources/definitions/makeit_pro_m.def.json b/resources/definitions/makeit_pro_m.def.json index f45e0b6635..43a5440165 100644 --- a/resources/definitions/makeit_pro_m.def.json +++ b/resources/definitions/makeit_pro_m.def.json @@ -1,5 +1,4 @@ { - "id": "makeit_pro_m", "version": 2, "name": "MAKEiT Pro-M", "inherits": "fdmprinter", diff --git a/resources/definitions/maker_starter.def.json b/resources/definitions/maker_starter.def.json index 8358ba0064..e7e6cb5dcd 100644 --- a/resources/definitions/maker_starter.def.json +++ b/resources/definitions/maker_starter.def.json @@ -1,5 +1,4 @@ { - "id": "maker_starter", "version": 2, "name": "3DMaker Starter", "inherits": "fdmprinter", diff --git a/resources/definitions/makerbotreplicator.def.json b/resources/definitions/makerbotreplicator.def.json index 7844976912..3d690990ce 100644 --- a/resources/definitions/makerbotreplicator.def.json +++ b/resources/definitions/makerbotreplicator.def.json @@ -1,5 +1,4 @@ { - "id": "makerbotreplicator", "name": "MakerBotReplicator", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/mankati_fullscale_xt_plus.def.json b/resources/definitions/mankati_fullscale_xt_plus.def.json index 6c3115b1dc..15ba889efc 100644 --- a/resources/definitions/mankati_fullscale_xt_plus.def.json +++ b/resources/definitions/mankati_fullscale_xt_plus.def.json @@ -1,5 +1,4 @@ { - "id": "mankati_fullscale_xt_plus", "version": 2, "name": "Mankati Fullscale XT Plus", "inherits": "fdmprinter", diff --git a/resources/definitions/mendel90.def.json b/resources/definitions/mendel90.def.json index 60f3307758..95f47d7aac 100644 --- a/resources/definitions/mendel90.def.json +++ b/resources/definitions/mendel90.def.json @@ -1,5 +1,4 @@ { - "id": "mendel90", "name": "Mendel90", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/ord.def.json b/resources/definitions/ord.def.json index d9909c4f1f..3238583ceb 100644 --- a/resources/definitions/ord.def.json +++ b/resources/definitions/ord.def.json @@ -1,5 +1,4 @@ { - "id": "ord", "name": "RoVa3D", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/peopoly_moai.def.json b/resources/definitions/peopoly_moai.def.json index 6b0e0ae547..78aca31dae 100644 --- a/resources/definitions/peopoly_moai.def.json +++ b/resources/definitions/peopoly_moai.def.json @@ -1,5 +1,4 @@ { - "id": "peopoly_moai", "version": 2, "name": "Peopoly Moai", "inherits": "fdmprinter", diff --git a/resources/definitions/printrbot_play.def.json b/resources/definitions/printrbot_play.def.json index 452b5e131f..36ef93e60c 100644 --- a/resources/definitions/printrbot_play.def.json +++ b/resources/definitions/printrbot_play.def.json @@ -1,5 +1,4 @@ { - "id": "printrbot_play", "version": 2, "name": "Printrbot Play", "inherits": "fdmprinter", diff --git a/resources/definitions/printrbot_play_heated.def.json b/resources/definitions/printrbot_play_heated.def.json index 02157a0913..9ca365a5c0 100644 --- a/resources/definitions/printrbot_play_heated.def.json +++ b/resources/definitions/printrbot_play_heated.def.json @@ -1,5 +1,4 @@ { - "id": "printrbot_play_heated", "version": 2, "name": "Printrbot Play (Heated Bed)", "inherits": "printrbot_play", diff --git a/resources/definitions/printrbot_simple.def.json b/resources/definitions/printrbot_simple.def.json index eba47c88ae..7c86617ce5 100644 --- a/resources/definitions/printrbot_simple.def.json +++ b/resources/definitions/printrbot_simple.def.json @@ -1,5 +1,4 @@ { - "id": "printrbot_simple", "version": 2, "name": "Printrbot Simple", "inherits": "fdmprinter", diff --git a/resources/definitions/printrbot_simple_extended.def.json b/resources/definitions/printrbot_simple_extended.def.json index b08e0c7d5d..95395efb23 100644 --- a/resources/definitions/printrbot_simple_extended.def.json +++ b/resources/definitions/printrbot_simple_extended.def.json @@ -1,5 +1,4 @@ { - "id": "printrbot_simple_extended", "version": 2, "name": "Printrbot Simple Metal Extended", "inherits": "fdmprinter", diff --git a/resources/definitions/prusa_i3.def.json b/resources/definitions/prusa_i3.def.json index 4f0f5b13d7..4599763397 100644 --- a/resources/definitions/prusa_i3.def.json +++ b/resources/definitions/prusa_i3.def.json @@ -1,5 +1,4 @@ { - "id": "prusa_i3", "version": 2, "name": "Prusa i3", "inherits": "fdmprinter", diff --git a/resources/definitions/prusa_i3_mk2.def.json b/resources/definitions/prusa_i3_mk2.def.json index ef3ef8159e..b72cc70d54 100644 --- a/resources/definitions/prusa_i3_mk2.def.json +++ b/resources/definitions/prusa_i3_mk2.def.json @@ -1,5 +1,4 @@ { - "id": "prusa_i3_mk2", "version": 2, "name": "Prusa i3 Mk2", "inherits": "fdmprinter", diff --git a/resources/definitions/prusa_i3_xl.def.json b/resources/definitions/prusa_i3_xl.def.json index e49838c95f..b27c460554 100644 --- a/resources/definitions/prusa_i3_xl.def.json +++ b/resources/definitions/prusa_i3_xl.def.json @@ -1,5 +1,4 @@ { - "id": "prusa_i3_xl", "version": 2, "name": "Prusa i3 xl", "inherits": "fdmprinter", diff --git a/resources/definitions/punchtec_connect_xl.def.json b/resources/definitions/punchtec_connect_xl.def.json index 16ba59dcb7..02d3562b41 100644 --- a/resources/definitions/punchtec_connect_xl.def.json +++ b/resources/definitions/punchtec_connect_xl.def.json @@ -1,5 +1,4 @@ { - "id": "punchtec_connect_xl", "name": "Punchtec Connect XL", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/raise3D_N2_dual.def.json b/resources/definitions/raise3D_N2_dual.def.json index d3d8f0e651..fe0a2bf3b8 100644 --- a/resources/definitions/raise3D_N2_dual.def.json +++ b/resources/definitions/raise3D_N2_dual.def.json @@ -1,5 +1,4 @@ { - "id": "raise3D_N2_dual", "version": 2, "name": "Raise3D N2 Dual", "inherits": "fdmprinter", @@ -33,7 +32,7 @@ }, "machine_heated_bed": { "default_value": true - }, + }, "machine_nozzle_size": { "default_value": 0.4 }, @@ -55,9 +54,6 @@ "machine_min_cool_heat_time_window": { "default_value": 3600 }, - "machine_nozzle_size": { - "default_value": 0.4 - }, "material_diameter": { "default_value": 1.75 }, diff --git a/resources/definitions/raise3D_N2_plus_dual.def.json b/resources/definitions/raise3D_N2_plus_dual.def.json index f49af40355..bddb2587e2 100644 --- a/resources/definitions/raise3D_N2_plus_dual.def.json +++ b/resources/definitions/raise3D_N2_plus_dual.def.json @@ -1,5 +1,4 @@ { - "id": "raise3D_N2_plus_dual", "version": 2, "name": "Raise3D N2 Plus Dual", "inherits": "fdmprinter", @@ -55,9 +54,6 @@ "machine_min_cool_heat_time_window": { "default_value": 3600 }, - "machine_nozzle_size": { - "default_value": 0.4 - }, "material_diameter": { "default_value": 1.75 }, diff --git a/resources/definitions/raise3D_N2_plus_single.def.json b/resources/definitions/raise3D_N2_plus_single.def.json index 5f10666528..ffcb723a27 100644 --- a/resources/definitions/raise3D_N2_plus_single.def.json +++ b/resources/definitions/raise3D_N2_plus_single.def.json @@ -1,5 +1,4 @@ { - "id": "raise3D_N2_plus_single", "version": 2, "name": "Raise3D N2 Plus Single", "inherits": "fdmprinter", @@ -50,9 +49,6 @@ "machine_min_cool_heat_time_window": { "default_value": 3600 }, - "machine_nozzle_size": { - "default_value": 0.4 - }, "material_diameter": { "default_value": 1.75 }, diff --git a/resources/definitions/raise3D_N2_single.def.json b/resources/definitions/raise3D_N2_single.def.json index 225794130c..1d01e479b1 100644 --- a/resources/definitions/raise3D_N2_single.def.json +++ b/resources/definitions/raise3D_N2_single.def.json @@ -1,5 +1,4 @@ { - "id": "raise3D_N2_single", "version": 2, "name": "Raise3D N2 Single", "inherits": "fdmprinter", @@ -50,9 +49,6 @@ "machine_min_cool_heat_time_window": { "default_value": 3600 }, - "machine_nozzle_size": { - "default_value": 0.4 - }, "material_diameter": { "default_value": 1.75 }, diff --git a/resources/definitions/renkforce_rf100.def.json b/resources/definitions/renkforce_rf100.def.json index 55e764800a..6f6de81643 100644 --- a/resources/definitions/renkforce_rf100.def.json +++ b/resources/definitions/renkforce_rf100.def.json @@ -1,5 +1,4 @@ { - "id": "RF100", "version": 2, "name": "Renkforce RF100", "inherits": "fdmprinter", diff --git a/resources/definitions/rigid3d.def.json b/resources/definitions/rigid3d.def.json index 75e435f880..97b0ebd276 100644 --- a/resources/definitions/rigid3d.def.json +++ b/resources/definitions/rigid3d.def.json @@ -1,5 +1,4 @@ { - "id": "rigid3d", "name": "Rigid3D", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/rigid3d_3rdgen.def.json b/resources/definitions/rigid3d_3rdgen.def.json index 3191817ecd..46c22bfa57 100644 --- a/resources/definitions/rigid3d_3rdgen.def.json +++ b/resources/definitions/rigid3d_3rdgen.def.json @@ -1,5 +1,4 @@ { - "id": "rigid3d_3rdgen", "name": "Rigid3D 3rdGen", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/rigid3d_hobby.def.json b/resources/definitions/rigid3d_hobby.def.json index 02e3cc514c..872cc3e6f4 100644 --- a/resources/definitions/rigid3d_hobby.def.json +++ b/resources/definitions/rigid3d_hobby.def.json @@ -1,5 +1,4 @@ { - "id": "rigid3d_hobby", "name": "Rigid3D Hobby", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/rigid3d_zero.def.json b/resources/definitions/rigid3d_zero.def.json index 7e99112621..56fb8284c0 100644 --- a/resources/definitions/rigid3d_zero.def.json +++ b/resources/definitions/rigid3d_zero.def.json @@ -1,5 +1,4 @@ { - "id": "rigid3d_zero", "name": "Rigid3D Zero", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/rigid3d_zero2.def.json b/resources/definitions/rigid3d_zero2.def.json index e2a77db895..27ceb87c29 100644 --- a/resources/definitions/rigid3d_zero2.def.json +++ b/resources/definitions/rigid3d_zero2.def.json @@ -1,5 +1,4 @@ { - "id": "rigid3d_zero2", "name": "Rigid3D Zero2", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/rigidbot.def.json b/resources/definitions/rigidbot.def.json index d183554947..bed10025d7 100644 --- a/resources/definitions/rigidbot.def.json +++ b/resources/definitions/rigidbot.def.json @@ -1,5 +1,4 @@ { - "id": "rigidbot", "version": 2, "name": "RigidBot", "inherits": "fdmprinter", diff --git a/resources/definitions/rigidbot_big.def.json b/resources/definitions/rigidbot_big.def.json index 33c4fd3d27..08cf3b2ab8 100644 --- a/resources/definitions/rigidbot_big.def.json +++ b/resources/definitions/rigidbot_big.def.json @@ -1,5 +1,4 @@ { - "id": "rigidbotbig", "version": 2, "name": "RigidBotBig", "inherits": "fdmprinter", diff --git a/resources/definitions/robo_3d_r1.def.json b/resources/definitions/robo_3d_r1.def.json index 88b3fba01f..b179779c59 100644 --- a/resources/definitions/robo_3d_r1.def.json +++ b/resources/definitions/robo_3d_r1.def.json @@ -1,5 +1,4 @@ { - "id": "robo_3d_r1", "name": "Robo 3D R1", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/tam.def.json b/resources/definitions/tam.def.json index 93ebe43ea6..20bc96358d 100644 --- a/resources/definitions/tam.def.json +++ b/resources/definitions/tam.def.json @@ -1,5 +1,4 @@ { - "id": "typeamachines", "version": 2, "name": "Type A Machines Series 1 2014", "inherits": "fdmprinter", diff --git a/resources/definitions/tevo_tarantula.def.json b/resources/definitions/tevo_tarantula.def.json index 29bfae77a4..097281aaeb 100644 --- a/resources/definitions/tevo_tarantula.def.json +++ b/resources/definitions/tevo_tarantula.def.json @@ -1,5 +1,4 @@ { - "id": "tevo_tarantula", "version": 2, "name": "Tevo Tarantula", "inherits": "fdmprinter", diff --git a/resources/definitions/ultimaker.def.json b/resources/definitions/ultimaker.def.json index 797a05e324..1ca7444d04 100644 --- a/resources/definitions/ultimaker.def.json +++ b/resources/definitions/ultimaker.def.json @@ -1,5 +1,4 @@ { - "id": "ultimaker_base", "version": 2, "name": "Ultimaker", "inherits": "fdmprinter", diff --git a/resources/definitions/ultimaker2.def.json b/resources/definitions/ultimaker2.def.json index 6974e61938..038071574f 100644 --- a/resources/definitions/ultimaker2.def.json +++ b/resources/definitions/ultimaker2.def.json @@ -1,5 +1,4 @@ { - "id": "ultimaker2", "version": 2, "name": "Ultimaker 2", "inherits": "ultimaker", diff --git a/resources/definitions/ultimaker2_extended.def.json b/resources/definitions/ultimaker2_extended.def.json index 16f9e6f25e..94667b154d 100644 --- a/resources/definitions/ultimaker2_extended.def.json +++ b/resources/definitions/ultimaker2_extended.def.json @@ -1,5 +1,4 @@ { - "id": "ultimaker2_extended", "version": 2, "name": "Ultimaker 2 Extended", "inherits": "ultimaker2", diff --git a/resources/definitions/ultimaker2_extended_plus.def.json b/resources/definitions/ultimaker2_extended_plus.def.json index 525aaf5b72..adde89d858 100644 --- a/resources/definitions/ultimaker2_extended_plus.def.json +++ b/resources/definitions/ultimaker2_extended_plus.def.json @@ -1,5 +1,4 @@ { - "id": "ultimaker2_extended_plus", "version": 2, "name": "Ultimaker 2 Extended+", "inherits": "ultimaker2_plus", diff --git a/resources/definitions/ultimaker2_go.def.json b/resources/definitions/ultimaker2_go.def.json index 9fb94022ce..e98381a7d7 100644 --- a/resources/definitions/ultimaker2_go.def.json +++ b/resources/definitions/ultimaker2_go.def.json @@ -1,5 +1,4 @@ { - "id": "ultimaker2_go", "version": 2, "name": "Ultimaker 2 Go", "inherits": "ultimaker2", diff --git a/resources/definitions/ultimaker2_plus.def.json b/resources/definitions/ultimaker2_plus.def.json index 7214e6b7fe..58833904d2 100644 --- a/resources/definitions/ultimaker2_plus.def.json +++ b/resources/definitions/ultimaker2_plus.def.json @@ -1,5 +1,4 @@ { - "id": "ultimaker2_plus", "version": 2, "name": "Ultimaker 2+", "inherits": "ultimaker2", diff --git a/resources/definitions/ultimaker3.def.json b/resources/definitions/ultimaker3.def.json index 21f80e18fd..b15358df32 100644 --- a/resources/definitions/ultimaker3.def.json +++ b/resources/definitions/ultimaker3.def.json @@ -1,5 +1,4 @@ { - "id": "ultimaker3", "version": 2, "name": "Ultimaker 3", "inherits": "ultimaker", diff --git a/resources/definitions/ultimaker3_extended.def.json b/resources/definitions/ultimaker3_extended.def.json index e47ccf4ba3..385199f4f1 100644 --- a/resources/definitions/ultimaker3_extended.def.json +++ b/resources/definitions/ultimaker3_extended.def.json @@ -1,5 +1,4 @@ { - "id": "ultimaker3_extended", "version": 2, "name": "Ultimaker 3 Extended", "inherits": "ultimaker3", diff --git a/resources/definitions/ultimaker_original.def.json b/resources/definitions/ultimaker_original.def.json index ef2f5fae5c..a0a486429b 100644 --- a/resources/definitions/ultimaker_original.def.json +++ b/resources/definitions/ultimaker_original.def.json @@ -1,5 +1,4 @@ { - "id": "ultimaker_original", "version": 2, "name": "Ultimaker Original", "inherits": "ultimaker", diff --git a/resources/definitions/ultimaker_original_dual.def.json b/resources/definitions/ultimaker_original_dual.def.json index 8745434360..a1dbea6b6d 100644 --- a/resources/definitions/ultimaker_original_dual.def.json +++ b/resources/definitions/ultimaker_original_dual.def.json @@ -1,5 +1,4 @@ { - "id": "ultimaker_original_dual", "version": 2, "name": "Ultimaker Original Dual Extrusion", "inherits": "ultimaker", diff --git a/resources/definitions/ultimaker_original_plus.def.json b/resources/definitions/ultimaker_original_plus.def.json index 8e401d550c..115e1e2752 100644 --- a/resources/definitions/ultimaker_original_plus.def.json +++ b/resources/definitions/ultimaker_original_plus.def.json @@ -1,5 +1,4 @@ { - "id": "ultimaker_original_plus", "version": 2, "name": "Ultimaker Original+", "inherits": "ultimaker_original", diff --git a/resources/definitions/uniqbot_one.def.json b/resources/definitions/uniqbot_one.def.json index 410f7e57a6..ad14728269 100644 --- a/resources/definitions/uniqbot_one.def.json +++ b/resources/definitions/uniqbot_one.def.json @@ -1,5 +1,4 @@ { - "id": "uniqbot_one", "version": 2, "name": "Uniqbot", "inherits": "fdmprinter", diff --git a/resources/definitions/vertex_delta_k8800.def.json b/resources/definitions/vertex_delta_k8800.def.json index b832620942..9cdc06865e 100644 --- a/resources/definitions/vertex_delta_k8800.def.json +++ b/resources/definitions/vertex_delta_k8800.def.json @@ -1,5 +1,4 @@ { - "id": "K8800", "name": "Vertex Delta K8800", "version": 2, "inherits": "fdmprinter", diff --git a/resources/definitions/vertex_k8400.def.json b/resources/definitions/vertex_k8400.def.json index a23e1fc893..8abf9acd7f 100644 --- a/resources/definitions/vertex_k8400.def.json +++ b/resources/definitions/vertex_k8400.def.json @@ -1,5 +1,4 @@ { - "id": "vertex_k8400", "version": 2, "name": "Vertex K8400", "inherits": "fdmprinter", diff --git a/resources/definitions/vertex_k8400_dual.def.json b/resources/definitions/vertex_k8400_dual.def.json index 9e24bab5d3..fc006a1d5f 100644 --- a/resources/definitions/vertex_k8400_dual.def.json +++ b/resources/definitions/vertex_k8400_dual.def.json @@ -1,5 +1,4 @@ { - "id": "vertex_k8400_dual", "version": 2, "name": "Vertex K8400 Dual", "inherits": "fdmprinter", diff --git a/resources/definitions/zone3d_printer.def.json b/resources/definitions/zone3d_printer.def.json index a1ed56c7ef..bac8968951 100644 --- a/resources/definitions/zone3d_printer.def.json +++ b/resources/definitions/zone3d_printer.def.json @@ -1,5 +1,4 @@ { - "id": "zone3d_printer", "name": "Zone3d Printer", "version": 2, "inherits": "fdmprinter", From c4debbea8b5c517df8458ef0233cbd6266726c5c Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 21 Nov 2017 10:59:01 +0100 Subject: [PATCH 20/73] Implement deserializeMetadata Let's hope this works... Contributes to issue CURA-4243. --- .../XmlMaterialProfile/XmlMaterialProfile.py | 157 +++++++++++++++++- 1 file changed, 156 insertions(+), 1 deletion(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index cb93d51b63..7feea6db3b 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -6,7 +6,7 @@ import io import json #To parse the product-to-id mapping file. import os.path #To find the product-to-id mapping. import sys -from typing import Dict, Optional +from typing import Any, Dict, List, Optional import xml.etree.ElementTree as ET from UM.Resources import Resources @@ -642,6 +642,161 @@ class XmlMaterialProfile(InstanceContainer): for container_to_add in containers_to_add: ContainerRegistry.getInstance().addContainer(container_to_add) + @classmethod + def deserializeMetadata(cls, serialized: str, container_id: str) -> List[Dict[str, Any]]: + result_metadata = [] #All the metadata that we found except the base (because the base is returned). + + #Update the serialized data to the latest version. + serialized = cls._updateSerialized(serialized) + + base_metadata = { + "type": "material", + "status": "unknown", #TODO: Add material verification. + "container_type": XmlMaterialProfile + } + + try: + data = ET.fromstring(serialized) + except: + Logger.logException("e", "An exception occurred while parsing the material profile") + return [] + + #TODO: Implement the tag. It's unused at the moment though. + + if "version" in data.attrib: + base_metadata["setting_version"] = cls.xmlVersionToSettingVersion(data.attrib["version"]) + else: + base_metadata["setting_version"] = cls.xmlVersionToSettingVersion("1.2") #1.2 and lower didn't have that version number there yet. + + for entry in data.iterfind("./um:metadata/*", cls.__namespaces): + tag_name = _tag_without_namespace(entry) + + if tag_name == "name": + brand = entry.find("./um:brand", cls.__namespaces) + material = entry.find("./um:material", cls.__namespaces) + color = entry.find("./um:color", cls.__namespaces) + label = entry.find("./um:label", cls.__namespaces) + + if label is not None: + base_metadata["name"] = label.text + else: + base_metadata["name"] = cls._profile_name(material.text, color.text) + base_metadata["brand"] = brand.text + base_metadata["material"] = material.text + base_metadata["color_name"] = color.text + continue + + #Setting_version is derived from the "version" tag in the schema earlier, so don't set it here. + if tag_name == "setting_version": + continue + + base_metadata[tag_name] = entry.text + + if "description" not in base_metadata: + base_metadata["description"] = "" + if "adhesion_info" not in base_metadata: + base_metadata["adhesion_info"] = "" + + property_values = {} + properties = data.iterfind("./um:properties/*", cls.__namespaces) + for entry in properties: + tag_name = _tag_without_namespace(entry) + property_values[tag_name] = entry.text + + base_metadata["approximate_diameter"] = str(round(float(property_values.get("diameter", 2.85)))) # In mm + base_metadata["properties"] = property_values + + compatible_entries = data.iterfind("./um:settings/um:setting[@key='hardware compatible']", cls.__namespaces) + try: + common_compatibility = cls._parseCompatibleValue(next(compatible_entries).text) + except StopIteration: #No 'hardware compatible' setting. + common_compatibility = True + base_metadata["compatible"] = common_compatibility + result_metadata.append(base_metadata) + + # Map machine human-readable names to IDs + product_id_map = cls.getProductIdMap() + + for machine in data.iterfind("./um:settings/um:machine", cls.__namespaces): + machine_compatibility = common_compatibility + for entry in machine.iterfind("./um:setting", cls.__namespaces): + key = entry.get("key") + if key == "hardware compatible": + machine_compatibility = cls._parseCompatibleValue(entry.text) + + for identifier in machine.iterfind("./um:machine_identifier", cls.__namespaces): + machine_id = product_id_map.get(identifier.get("product"), None) + if machine_id is None: + # Lets try again with some naive heuristics. + machine_id = identifier.get("product").replace(" ", "").lower() + definition_metadata = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = machine_id) + if not definition_metadata: + Logger.log("w", "No definition found for machine ID %s", machine_id) + continue + definition_metadata = definition_metadata[0] + + machine_manufacturer = identifier.get("manufacturer", definition_metadata.get("manufacturer", "Unknown")) #If the XML material doesn't specify a manufacturer, use the one in the actual printer definition. + + if machine_compatibility: + new_material_id = container_id + "_" + machine_id + + # The child or derived material container may already exist. This can happen when a material in a + # project file and the a material in Cura have the same ID. + # In the case if a derived material already exists, override that material container because if + # the data in the parent material has been changed, the derived ones should be updated too. + found_materials = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = new_material_id) + if found_materials: + new_material_metadata = found_materials[0] + else: + new_material_metadata = {} + + new_material_metadata.update(base_metadata) + new_material_metadata["compatible"] = machine_compatibility + new_material_metadata["machine_manufacturer"] = machine_manufacturer + + if len(found_materials) == 0: #This is a new material. + result_metadata.append(new_material_metadata) + + for hotend in machine.iterfind("./um:hotend", cls.__namespaces): + hotend_id = hotend.get("id") + if hotend_id is None: + continue + + variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = hotend_id) + if not variant_containers: + # It is not really properly defined what "ID" is so also search for variants by name. + variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = machine_id, name = hotend_id) + if not variant_containers: + continue + + hotend_compatibility = machine_compatibility + for entry in hotend.iterfind("./um:setting", cls.__namespaces): + key = entry.get("key") + if key == "hardware compatible": + hotend_compatibility = cls._parseCompatibleValue(entry.text) + + new_hotend_id = container_id + "_" + machine_id + "_" + hotend_id.replace(" ", "_") + + # Same as machine compatibility, keep the derived material containers consistent with the parent + # material + found_materials = ContainerRegistry.getInstance().findInstanceContainers(id = new_hotend_id) + if found_materials: + new_hotend_material_metadata = found_materials[0] + else: + new_hotend_material_metadata = {} + + new_hotend_material_metadata["name"] = base_metadata["name"] + new_hotend_material_metadata.update(base_metadata) + new_hotend_material_metadata["variant"] = variant_containers[0]["id"] + new_hotend_material_metadata["compatible"] = hotend_compatibility + new_hotend_material_metadata["machine_manufacturer"] = machine_manufacturer + + if len(found_materials) == 0: + result_metadata.append(new_hotend_material_metadata) + + for metadata in result_metadata: + #ContainerRegistry.getInstance().metadata[metadata["id"]] = metadata + ## Override of getIdsFromFile because the XML files contain multiple IDs. @classmethod def getIdsFromFile(cls, file_name): From 0831451371846236cbe8140bc08eb23cc2aedafb Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 21 Nov 2017 11:05:11 +0100 Subject: [PATCH 21/73] Fix returning resulting metadata Forgot to change that back. Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 7feea6db3b..583febea99 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -795,7 +795,8 @@ class XmlMaterialProfile(InstanceContainer): result_metadata.append(new_hotend_material_metadata) for metadata in result_metadata: - #ContainerRegistry.getInstance().metadata[metadata["id"]] = metadata + result_metadata.append(metadata) + return result_metadata ## Override of getIdsFromFile because the XML files contain multiple IDs. @classmethod From 4fa38c26e8b4e586733529e905bd7cce49365eb8 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 21 Nov 2017 11:46:14 +0100 Subject: [PATCH 22/73] Don't add any extra metadata to the end of the list you're iterating over Stupid mistake. This causes infinite loops. Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 583febea99..7a2ad3fe51 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -794,8 +794,6 @@ class XmlMaterialProfile(InstanceContainer): if len(found_materials) == 0: result_metadata.append(new_hotend_material_metadata) - for metadata in result_metadata: - result_metadata.append(metadata) return result_metadata ## Override of getIdsFromFile because the XML files contain multiple IDs. From def7b6994daa4e25a4016fbde433cc3c16229b55 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 21 Nov 2017 12:50:10 +0100 Subject: [PATCH 23/73] Store correct ID in metadata after deserialising Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 7a2ad3fe51..5dfa130145 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -652,7 +652,8 @@ class XmlMaterialProfile(InstanceContainer): base_metadata = { "type": "material", "status": "unknown", #TODO: Add material verification. - "container_type": XmlMaterialProfile + "container_type": XmlMaterialProfile, + "id": container_id } try: @@ -751,6 +752,7 @@ class XmlMaterialProfile(InstanceContainer): new_material_metadata = {} new_material_metadata.update(base_metadata) + new_material_metadata["id"] = new_material_id new_material_metadata["compatible"] = machine_compatibility new_material_metadata["machine_manufacturer"] = machine_manufacturer @@ -790,6 +792,7 @@ class XmlMaterialProfile(InstanceContainer): new_hotend_material_metadata["variant"] = variant_containers[0]["id"] new_hotend_material_metadata["compatible"] = hotend_compatibility new_hotend_material_metadata["machine_manufacturer"] = machine_manufacturer + new_hotend_material_metadata["id"] = new_hotend_id if len(found_materials) == 0: result_metadata.append(new_hotend_material_metadata) From 3ee2e0488f95e3311d9c6354c29dff954bb401bc Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 23 Nov 2017 15:31:22 +0100 Subject: [PATCH 24/73] Only load metadata when finding hotends belonging to material We only need to have the hotend metadata. Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 5dfa130145..a6825f33a6 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -781,7 +781,7 @@ class XmlMaterialProfile(InstanceContainer): # Same as machine compatibility, keep the derived material containers consistent with the parent # material - found_materials = ContainerRegistry.getInstance().findInstanceContainers(id = new_hotend_id) + found_materials = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = new_hotend_id) if found_materials: new_hotend_material_metadata = found_materials[0] else: From 1e07325d8f4543b198ae894aac244f8a824f920b Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 23 Nov 2017 16:11:02 +0100 Subject: [PATCH 25/73] Remove unused getIdsFromFile Since we now return all metadata at once from every file, we don't need this any more. We need to side-load the metadata anyway. Contributes to issue CURA-4243. --- .../XmlMaterialProfile/XmlMaterialProfile.py | 45 ------------------- 1 file changed, 45 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index a6825f33a6..3d341778f3 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -799,51 +799,6 @@ class XmlMaterialProfile(InstanceContainer): return result_metadata - ## Override of getIdsFromFile because the XML files contain multiple IDs. - @classmethod - def getIdsFromFile(cls, file_name): - result_ids = super().getIdsFromFile(file_name) #The base file has the default ID, taken from the file name without extension. - base_id = result_ids[0] - - try: - data = ET.parse(file_name) - except: #IOError, PermissionError, or anything from the ElementTree library. - Logger.logException("e", "An exception occurred while parsing the material profile") - return - - common_compatibility = True - compatible_entries = data.iterfind("./um:settings/um:setting[@key='hardware compatible']", cls.__namespaces) - try: - common_compatibility = cls._parseCompatibleValue(next(compatible_entries).text) - except StopIteration: #No 'hardware compatible' setting. - pass - - #Get a mapping from the product names to the definition IDs. - product_id_map = cls.getProductIdMap() - - for machine in data.iterfind("./um:settings/um:machine", cls.__namespaces): - machine_compatibility = common_compatibility - compatible_entries = data.iterfind("./um:setting[@key='hardware compatible']", cls.__namespaces) - try: - machine_compatibility = cls._parseCompatibleValue(next(compatible_entries).text) - except StopIteration: #No 'hardware compatible' setting. - pass - - for identifier in machine.iterfind("./um:machine_identifier", cls.__namespaces): #For all machines. - machine_id = product_id_map.get(identifier.get("product"), None) - if machine_id is None: - #Let's try again with some naive heuristics. - machine_id = identifier.get("product").replace(" ", "").lower() - if machine_compatibility: - result_ids.append(base_id + "_" + machine_id) - - for hotend in machine.iterfind("./um:hotend", cls.__namespaces): #For all hotends. - hotend_id = hotend.get("id") - if hotend_id is None: - continue - result_ids.append(base_id + "_" + machine_id + "_" + hotend_id) - return result_ids - def _addSettingElement(self, builder, instance): try: key = UM.Dictionary.findKey(self.__material_settings_setting_map, instance.definition.key) From 37935a8a90e75973a29e1fdb28c7876ba0cb7371 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 23 Nov 2017 16:47:55 +0100 Subject: [PATCH 26/73] Load only metadata on start-up Contributes to issue CURA-4243. --- cura/CuraApplication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 27451c745c..b8c816615e 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -279,7 +279,7 @@ class CuraApplication(QtApplication): ContainerRegistry.getInstance().addContainer(empty_quality_changes_container) with ContainerRegistry.getInstance().lockFile(): - ContainerRegistry.getInstance().load() + ContainerRegistry.getInstance().loadAllMetadata() # set the setting version for Preferences preferences = Preferences.getInstance() From ebe766a7c8cbf74ab459c0efd8cbd47204c2b960 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 24 Nov 2017 16:45:09 +0100 Subject: [PATCH 27/73] Set definition by their ID The new function for setting the definition just adds the ID to the metadata. Contributes to issue CURA-4243. --- cura/Settings/ContainerManager.py | 4 ++-- cura/Settings/CuraContainerRegistry.py | 4 ++-- cura/Settings/CuraStackBuilder.py | 14 +++++++------- cura/Settings/ExtruderManager.py | 2 +- plugins/3MFReader/ThreeMFReader.py | 2 +- plugins/GCodeWriter/GCodeWriter.py | 4 ++-- plugins/LegacyProfileReader/LegacyProfileReader.py | 2 +- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 8 +++++--- tests/Settings/TestCuraContainerRegistry.py | 6 +++--- 9 files changed, 24 insertions(+), 22 deletions(-) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index 52b97cd60d..a93dd3c1b6 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -1001,9 +1001,9 @@ class ContainerManager(QObject): # If the machine specifies qualities should be filtered, ensure we match the current criteria. if not machine_definition.getMetaDataEntry("has_machine_quality"): - quality_changes.setDefinition(self._container_registry.findDefinitionContainers(id = "fdmprinter")[0]) + quality_changes.setDefinition("fdmprinter") else: - quality_changes.setDefinition(QualityManager.getInstance().getParentMachineDefinition(machine_definition)) + quality_changes.setDefinition(QualityManager.getInstance().getParentMachineDefinition(machine_definition).getId()) from cura.CuraApplication import CuraApplication quality_changes.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index 8903e195dc..aa1f7617cb 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -284,7 +284,7 @@ class CuraContainerRegistry(ContainerRegistry): quality_type_criteria = {"quality_type": quality_type} if self._machineHasOwnQualities(): - profile.setDefinition(self._activeQualityDefinition()) + profile.setDefinition(self._activeQualityDefinition().getId()) 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 @@ -294,7 +294,7 @@ class CuraContainerRegistry(ContainerRegistry): quality_type_criteria["definition"] = profile.getDefinition().getId() else: - profile.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")[0]) + profile.setDefinition(fdmprinter) quality_type_criteria["definition"] = "fdmprinter" machine_definition = Application.getInstance().getGlobalContainerStack().getBottom() diff --git a/cura/Settings/CuraStackBuilder.py b/cura/Settings/CuraStackBuilder.py index f7a1592155..4dabbf317f 100644 --- a/cura/Settings/CuraStackBuilder.py +++ b/cura/Settings/CuraStackBuilder.py @@ -56,7 +56,7 @@ class CuraStackBuilder: new_extruder = cls.createExtruderStack( new_extruder_id, definition = extruder_definition, - machine_definition = machine_definition, + machine_definition_id = machine_definition.getId(), quality = "default", material = "default", variant = "default", @@ -69,12 +69,13 @@ class CuraStackBuilder: # # \param new_stack_id The ID of the new stack. # \param definition The definition to base the new stack on. - # \param machine_definition The machine definition to use for the user container. + # \param machine_definition_id The ID of the machine definition to use for + # the user container. # \param kwargs You can add keyword arguments to specify IDs of containers to use for a specific type, for example "variant": "0.4mm" # # \return A new Global stack instance with the specified parameters. @classmethod - def createExtruderStack(cls, new_stack_id: str, definition: DefinitionContainerInterface, machine_definition: DefinitionContainerInterface, **kwargs) -> ExtruderStack: + def createExtruderStack(cls, new_stack_id: str, definition: DefinitionContainerInterface, machine_definition_id: str, **kwargs) -> ExtruderStack: stack = ExtruderStack(new_stack_id) stack.setName(definition.getName()) stack.setDefinition(definition) @@ -87,7 +88,7 @@ class CuraStackBuilder: user_container.addMetaDataEntry("extruder", new_stack_id) from cura.CuraApplication import CuraApplication user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) - user_container.setDefinition(machine_definition) + user_container.setDefinition(machine_definition_id) stack.setUserChanges(user_container) @@ -136,7 +137,7 @@ class CuraStackBuilder: user_container.addMetaDataEntry("machine", new_stack_id) from cura.CuraApplication import CuraApplication user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) - user_container.setDefinition(definition) + user_container.setDefinition(definition.getId()) stack.setUserChanges(user_container) @@ -172,8 +173,7 @@ class CuraStackBuilder: unique_container_name = ContainerRegistry.getInstance().uniqueName(container_name) definition_changes_container = InstanceContainer(unique_container_name) - definition = container_stack.getBottom() - definition_changes_container.setDefinition(definition) + definition_changes_container.setDefinition(container_stack.getBottom().getId()) definition_changes_container.addMetaDataEntry("type", "definition_changes") definition_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 8585c3cf7d..e01561c5f4 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -379,7 +379,7 @@ class ExtruderManager(QObject): user_profile.addMetaDataEntry("extruder", extruder_stack_id) from cura.CuraApplication import CuraApplication user_profile.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) - user_profile.setDefinition(machine_definition) + user_profile.setDefinition(machine_definition.getId()) container_registry.addContainer(user_profile) container_stack.addContainer(user_profile) diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index a34bf771d7..f4c675fdf9 100755 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -124,7 +124,7 @@ class ThreeMFReader(MeshReader): # Get the definition & set it definition = QualityManager.getInstance().getParentMachineDefinition(global_container_stack.getBottom()) - um_node.callDecoration("getStack").getTop().setDefinition(definition) + um_node.callDecoration("getStack").getTop().setDefinition(definition.getId()) setting_container = um_node.callDecoration("getStack").getTop() diff --git a/plugins/GCodeWriter/GCodeWriter.py b/plugins/GCodeWriter/GCodeWriter.py index 3860590ef7..1e4fb666b7 100644 --- a/plugins/GCodeWriter/GCodeWriter.py +++ b/plugins/GCodeWriter/GCodeWriter.py @@ -75,9 +75,9 @@ class GCodeWriter(MeshWriter): def _createFlattenedContainerInstance(self, instance_container1, instance_container2): flat_container = InstanceContainer(instance_container2.getName()) if instance_container1.getDefinition(): - flat_container.setDefinition(instance_container1.getDefinition()) + flat_container.setDefinition(instance_container1.getDefinition().getId()) else: - flat_container.setDefinition(instance_container2.getDefinition()) + flat_container.setDefinition(instance_container2.getDefinition().getId()) flat_container.setMetaData(copy.deepcopy(instance_container2.getMetaData())) for key in instance_container2.getAllKeys(): diff --git a/plugins/LegacyProfileReader/LegacyProfileReader.py b/plugins/LegacyProfileReader/LegacyProfileReader.py index 3d680f2b97..69e79bb974 100644 --- a/plugins/LegacyProfileReader/LegacyProfileReader.py +++ b/plugins/LegacyProfileReader/LegacyProfileReader.py @@ -121,7 +121,7 @@ class LegacyProfileReader(ProfileReader): Logger.log("e", "Dictionary of Doom has no translation. Is it the correct JSON file?") return None current_printer_definition = global_container_stack.getBottom() - profile.setDefinition(current_printer_definition) + profile.setDefinition(current_printer_definition.getId()) for new_setting in dict_of_doom["translation"]: # Evaluate all new settings that would get a value from the translations. old_setting_expression = dict_of_doom["translation"][new_setting] compiled = compile(old_setting_expression, new_setting, "eval") diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 3d341778f3..7ab1e0f7ad 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -496,8 +496,9 @@ class XmlMaterialProfile(InstanceContainer): meta_data["approximate_diameter"] = str(round(float(property_values.get("diameter", 2.85)))) # In mm meta_data["properties"] = property_values + meta_data["definition"] = "fdmprinter" - self.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")[0]) + self.setDefinition("fdmprinter") common_compatibility = True settings = data.iterfind("./um:settings/um:setting", self.__namespaces) @@ -569,7 +570,7 @@ class XmlMaterialProfile(InstanceContainer): is_new_material = True new_material.setMetaData(copy.deepcopy(self.getMetaData())) - new_material.setDefinition(definition) + new_material.setDefinition(machine_id) # Don't use setMetadata, as that overrides it for all materials with same base file new_material.getMetaData()["compatible"] = machine_compatibility new_material.getMetaData()["machine_manufacturer"] = machine_manufacturer @@ -623,7 +624,7 @@ class XmlMaterialProfile(InstanceContainer): # Update the private directly, as we want to prevent the lookup that is done when using setName new_hotend_material.setName(self.getName()) new_hotend_material.setMetaData(copy.deepcopy(self.getMetaData())) - new_hotend_material.setDefinition(definition) + new_hotend_material.setDefinition(machine_id) new_hotend_material.addMetaDataEntry("variant", variant_containers[0]["id"]) # Don't use setMetadata, as that overrides it for all materials with same base file new_hotend_material.getMetaData()["compatible"] = hotend_compatibility @@ -706,6 +707,7 @@ class XmlMaterialProfile(InstanceContainer): base_metadata["approximate_diameter"] = str(round(float(property_values.get("diameter", 2.85)))) # In mm base_metadata["properties"] = property_values + base_metadata["definition"] = "fdmprinter" compatible_entries = data.iterfind("./um:settings/um:setting[@key='hardware compatible']", cls.__namespaces) try: diff --git a/tests/Settings/TestCuraContainerRegistry.py b/tests/Settings/TestCuraContainerRegistry.py index d502d31cc3..1abd098338 100644 --- a/tests/Settings/TestCuraContainerRegistry.py +++ b/tests/Settings/TestCuraContainerRegistry.py @@ -74,7 +74,7 @@ def test_addContainerGoodSettingVersion(container_registry, definition_container instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance") instance.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) - instance.setDefinition(definition_container) + instance.setDefinition(definition_container.getId()) mock_super_add_container = unittest.mock.MagicMock() #Take the role of the Uranium-ContainerRegistry where the resulting containers get registered. with unittest.mock.patch("UM.Settings.ContainerRegistry.ContainerRegistry.addContainer", mock_super_add_container): @@ -89,7 +89,7 @@ def test_addContainerNoSettingVersion(container_registry, definition_container): instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance") #Don't add setting_version metadata. - instance.setDefinition(definition_container) + instance.setDefinition(definition_container.getId()) mock_super_add_container = unittest.mock.MagicMock() #Take the role of the Uranium-ContainerRegistry where the resulting container should not get registered. with unittest.mock.patch("UM.Settings.ContainerRegistry.ContainerRegistry.addContainer", mock_super_add_container): @@ -104,7 +104,7 @@ def test_addContainerBadSettingVersion(container_registry, definition_container) instance = UM.Settings.InstanceContainer.InstanceContainer(container_id = "Test Instance") instance.addMetaDataEntry("setting_version", 9001) #Wrong version! - instance.setDefinition(definition_container) + instance.setDefinition(definition_container.getId()) mock_super_add_container = unittest.mock.MagicMock() #Take the role of the Uranium-ContainerRegistry where the resulting container should not get registered. with unittest.mock.patch("UM.Settings.ContainerRegistry.ContainerRegistry.addContainer", mock_super_add_container): From aa52b9682ea4168ea001e9927fab12b11e9c5967 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 27 Nov 2017 09:10:55 +0100 Subject: [PATCH 28/73] Fix adding name when loading complete material file Also, don't use setName since that only operates on the base file and sends out unnecessary signals. Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 7ab1e0f7ad..554afa2dc8 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -451,8 +451,8 @@ class XmlMaterialProfile(InstanceContainer): else: meta_data["setting_version"] = self.xmlVersionToSettingVersion("1.2") #1.2 and lower didn't have that version number there yet. - metadata = data.iterfind("./um:metadata/*", self.__namespaces) - for entry in metadata: + meta_data["name"] = "Unknown Material" #In case the name tag is missing. + for entry in data.iterfind("./um:metadata/*", self.__namespaces): tag_name = _tag_without_namespace(entry) if tag_name == "name": @@ -462,9 +462,9 @@ class XmlMaterialProfile(InstanceContainer): label = entry.find("./um:label", self.__namespaces) if label is not None: - self.setName(label.text) + meta_data["name"] = label.text else: - self.setName(self._profile_name(material.text, color.text)) + meta_data["name"] = self._profile_name(material.text, color.text) meta_data["brand"] = brand.text meta_data["material"] = material.text meta_data["color_name"] = color.text @@ -570,6 +570,7 @@ class XmlMaterialProfile(InstanceContainer): is_new_material = True new_material.setMetaData(copy.deepcopy(self.getMetaData())) + new_material.getMetaData()["name"] = self.getName() new_material.setDefinition(machine_id) # Don't use setMetadata, as that overrides it for all materials with same base file new_material.getMetaData()["compatible"] = machine_compatibility @@ -621,11 +622,10 @@ class XmlMaterialProfile(InstanceContainer): new_hotend_material = XmlMaterialProfile(new_hotend_id) is_new_material = True - # Update the private directly, as we want to prevent the lookup that is done when using setName - new_hotend_material.setName(self.getName()) new_hotend_material.setMetaData(copy.deepcopy(self.getMetaData())) + new_hotend_material.getMetaData()["name"] = self.getName() new_hotend_material.setDefinition(machine_id) - new_hotend_material.addMetaDataEntry("variant", variant_containers[0]["id"]) + new_hotend_material.getMetaData()["variant"] = variant_containers[0]["id"] # Don't use setMetadata, as that overrides it for all materials with same base file new_hotend_material.getMetaData()["compatible"] = hotend_compatibility new_hotend_material.getMetaData()["machine_manufacturer"] = machine_manufacturer @@ -789,7 +789,6 @@ class XmlMaterialProfile(InstanceContainer): else: new_hotend_material_metadata = {} - new_hotend_material_metadata["name"] = base_metadata["name"] new_hotend_material_metadata.update(base_metadata) new_hotend_material_metadata["variant"] = variant_containers[0]["id"] new_hotend_material_metadata["compatible"] = hotend_compatibility From 9938678347c986400faf53fa44585a1853b6570d Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 27 Nov 2017 09:16:27 +0100 Subject: [PATCH 29/73] Fix setting IDs of empty containers The _id hasn't been used for a very long time. Contributes to issue CURA-4243. --- cura/CuraApplication.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index b8c816615e..d21428e76e 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -260,21 +260,21 @@ class CuraApplication(QtApplication): # We need them to simplify the switching between materials. empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer() empty_variant_container = copy.deepcopy(empty_container) - empty_variant_container._id = "empty_variant" + empty_variant_container.setMetaDataEntry("id", "empty_variant") empty_variant_container.addMetaDataEntry("type", "variant") ContainerRegistry.getInstance().addContainer(empty_variant_container) empty_material_container = copy.deepcopy(empty_container) - empty_material_container._id = "empty_material" + empty_material_container.setMetaDataEntry("id", "empty_material") empty_material_container.addMetaDataEntry("type", "material") ContainerRegistry.getInstance().addContainer(empty_material_container) empty_quality_container = copy.deepcopy(empty_container) - empty_quality_container._id = "empty_quality" + empty_quality_container.setMetaDataEntry("id", "empty_quality") empty_quality_container.setName("Not Supported") empty_quality_container.addMetaDataEntry("quality_type", "normal") empty_quality_container.addMetaDataEntry("type", "quality") ContainerRegistry.getInstance().addContainer(empty_quality_container) empty_quality_changes_container = copy.deepcopy(empty_container) - empty_quality_changes_container._id = "empty_quality_changes" + empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes") empty_quality_changes_container.addMetaDataEntry("type", "quality_changes") ContainerRegistry.getInstance().addContainer(empty_quality_changes_container) From e0f0e098ffa08ee412149d866970885541b23b13 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 27 Nov 2017 09:54:12 +0100 Subject: [PATCH 30/73] Use MaterialsModel for materialsModel This specialized InstanceContainersModel has an override that makes the model a bit more efficient when non-materials get their metadata updated. Contributes to issue CURA-4243. --- resources/qml/Menus/MaterialMenu.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/MaterialMenu.qml b/resources/qml/Menus/MaterialMenu.qml index 1688bc228a..3fb694af90 100644 --- a/resources/qml/Menus/MaterialMenu.qml +++ b/resources/qml/Menus/MaterialMenu.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Ultimaker B.V. +// Copyright (c) 2017 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 @@ -143,7 +143,7 @@ Menu } //: Model used to populate the brandModel - UM.InstanceContainersModel + Cura.MaterialsModel { id: materialsModel filter: materialFilter() From 9571181178c534d66c00cb034bc4d0ae39245ed9 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 27 Nov 2017 10:37:54 +0100 Subject: [PATCH 31/73] Fix ID of derived materials Because the ID is now in the metadata it would get overwritten by this deep copy. We need to set it again. Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 554afa2dc8..36e4c4017b 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -570,6 +570,7 @@ class XmlMaterialProfile(InstanceContainer): is_new_material = True new_material.setMetaData(copy.deepcopy(self.getMetaData())) + new_material.getMetaData()["id"] = new_material_id new_material.getMetaData()["name"] = self.getName() new_material.setDefinition(machine_id) # Don't use setMetadata, as that overrides it for all materials with same base file @@ -623,6 +624,7 @@ class XmlMaterialProfile(InstanceContainer): is_new_material = True new_hotend_material.setMetaData(copy.deepcopy(self.getMetaData())) + new_hotend_material.getMetaData()["id"] = new_hotend_id new_hotend_material.getMetaData()["name"] = self.getName() new_hotend_material.setDefinition(machine_id) new_hotend_material.getMetaData()["variant"] = variant_containers[0]["id"] From a2b0c4535c63dc0a330e80248852a35fa63c1889 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 27 Nov 2017 10:55:51 +0100 Subject: [PATCH 32/73] Only load metadata of machines for derived profiles We only need the metadata of these machines (now that the ID is in the metadata). We won't need all of those machines because some of them are not added. Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 36e4c4017b..ead0864d6f 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -545,14 +545,14 @@ class XmlMaterialProfile(InstanceContainer): # Lets try again with some naive heuristics. machine_id = identifier.get("product").replace(" ", "").lower() - definitions = ContainerRegistry.getInstance().findDefinitionContainers(id = machine_id) + definitions = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = machine_id) if not definitions: Logger.log("w", "No definition found for machine ID %s", machine_id) continue definition = definitions[0] - machine_manufacturer = identifier.get("manufacturer", definition.getMetaDataEntry("manufacturer", "Unknown")) #If the XML material doesn't specify a manufacturer, use the one in the actual printer definition. + machine_manufacturer = identifier.get("manufacturer", definition.get("manufacturer", "Unknown")) #If the XML material doesn't specify a manufacturer, use the one in the actual printer definition. if machine_compatibility: new_material_id = self.getId() + "_" + machine_id @@ -593,7 +593,7 @@ class XmlMaterialProfile(InstanceContainer): variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = hotend_id) if not variant_containers: # It is not really properly defined what "ID" is so also search for variants by name. - variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = definition.getId(), name = hotend_id) + variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = definition["id"], name = hotend_id) if not variant_containers: continue From fab85616c74f089185f6a18a382c7cc0dcc2c0fe Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 27 Nov 2017 11:23:24 +0100 Subject: [PATCH 33/73] Remove test for upgrading from legacy stacks to modern stacks This is because ContainerRegistry.saveAll has been removed. That function was only used by this test. I don't think it's worth keeping this unit test for the effort. It's only for code that's passed through in the version upgrade from 2.4 to 2.5 anyway. Contributes to issue CURA-4243. --- tests/Settings/TestCuraContainerRegistry.py | 38 +-------------------- 1 file changed, 1 insertion(+), 37 deletions(-) diff --git a/tests/Settings/TestCuraContainerRegistry.py b/tests/Settings/TestCuraContainerRegistry.py index 1abd098338..e6dc6b99d8 100644 --- a/tests/Settings/TestCuraContainerRegistry.py +++ b/tests/Settings/TestCuraContainerRegistry.py @@ -145,40 +145,4 @@ def test_loadTypes(filename, output_class, container_registry): assert type(container) == output_class break else: - assert False #Container stack with specified ID was not loaded. - -## Tests whether loading a legacy file moves the upgraded file properly. -def test_loadLegacyFileRenamed(container_registry): - #Create a temporary file for the registry to load. - stacks_folder = os.path.join(os.path.dirname(os.path.abspath(__file__)), "stacks") - temp_file = os.path.join(stacks_folder, "temporary.stack.cfg") - temp_file_source = os.path.join(stacks_folder, "MachineLegacy.stack.cfg") - shutil.copyfile(temp_file_source, temp_file) - - #Mock some dependencies. - UM.Settings.ContainerStack.setContainerRegistry(container_registry) - Resources.getAllResourcesOfType = unittest.mock.MagicMock(return_value = [temp_file]) #Return a temporary file that we'll make for this test. - - def findContainers(container_type = 0, id = None): - if id == "MachineLegacy": - return None - - container = UM.Settings.ContainerRegistry._EmptyInstanceContainer(id) - container.getNextStack = unittest.mock.MagicMock() - return [container] - - old_find_containers = container_registry.findContainers - container_registry.findContainers = findContainers - - with unittest.mock.patch("cura.Settings.GlobalStack.GlobalStack.findContainer"): - container_registry.load() - - container_registry.findContainers = old_find_containers - - container_registry.saveAll() - print("all containers in registry", container_registry._containers) - assert not os.path.isfile(temp_file) - mime_type = container_registry.getMimeTypeForContainer(GlobalStack) - file_name = urllib.parse.quote_plus("MachineLegacy") + "." + mime_type.preferredSuffix - path = Resources.getStoragePath(Resources.ContainerStacks, file_name) - assert os.path.isfile(path) + assert False #Container stack with specified ID was not loaded. \ No newline at end of file From 4189f8dfa379ff2b56e0759443376d530eae9398 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 27 Nov 2017 11:59:37 +0100 Subject: [PATCH 34/73] Use new findDirtyContainers function This prevents all instance containers from being loaded every time we auto-save. Contributes to issue CURA-4243. --- cura/CuraApplication.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index d21428e76e..696b16ac37 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -433,10 +433,7 @@ class CuraApplication(QtApplication): # Lock file for "more" atomically loading and saving to/from config dir. with ContainerRegistry.getInstance().lockFile(): - for instance in ContainerRegistry.getInstance().findInstanceContainers(): - if not instance.isDirty(): - continue - + for instance in ContainerRegistry.getInstance().findDirtyContainers(container_type = InstanceContainer): try: data = instance.serialize() except NotImplementedError: From 99780c3b4417916722ca315b12d4c53be8d2460c Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 27 Nov 2017 13:31:29 +0100 Subject: [PATCH 35/73] Fix casing of getMetadataEntry This is done a bit inconsistently because it's unclear whether metadata should be considered as one word or two. I'd say it is one word, not 'meta data', but people seem to disagree. Contributes to issue CURA-4243. --- cura/QualityManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/QualityManager.py b/cura/QualityManager.py index 5c0a16da96..17d3fb652c 100644 --- a/cura/QualityManager.py +++ b/cura/QualityManager.py @@ -213,7 +213,7 @@ class QualityManager: "type": "material", "name": base_material, "definition": definition_id, - "variant": material_container.getMetadataEntry("variant") + "variant": material_container.getMetaDataEntry("variant") } containers = ContainerRegistry.getInstance().findInstanceContainers(**criteria) return containers From 54a12d14c48ffd847b855981d20cee0e5132ce46 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 27 Nov 2017 14:19:30 +0100 Subject: [PATCH 36/73] Add string representation for debugging My IDE shows the str(inst) representation of an object when indicating what value is in there. This makes it easier to find stuff. It'll also make debug prints more clear than the default stuff of Python. Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index ead0864d6f..23d4ed3860 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -834,6 +834,10 @@ class XmlMaterialProfile(InstanceContainer): def _parseCompatibleValue(cls, value: str): return value in {"yes", "unknown"} + ## Small string representation for debugging. + def __str__(self): + return "".format(my_id = self.getId(), name = self.getName(), base_file = self.getMetaDataEntry("base_file")) + # Map XML file setting names to internal names __material_settings_setting_map = { "print temperature": "default_material_print_temperature", From 952e3974ebca0a98e7c58fa486676f38b6b349b6 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 27 Nov 2017 14:22:09 +0100 Subject: [PATCH 37/73] Remove convenience handling of 'None' Because this parameter has type checking, we'll get warned if this variable is None. Contributes to issue CURA-4243. --- cura/QualityManager.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cura/QualityManager.py b/cura/QualityManager.py index 17d3fb652c..1f9c1b8b44 100644 --- a/cura/QualityManager.py +++ b/cura/QualityManager.py @@ -231,10 +231,6 @@ class QualityManager: if quality_definition_id is not None: machine_definition = ContainerRegistry.getInstance().findDefinitionContainers(id = quality_definition_id)[0] - # for convenience - if material_containers is None: - material_containers = [] - if not material_containers: active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks() if active_stacks: From e5427eded6adc2a10a64bbddb2ff6f5ae3ff9472 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 27 Nov 2017 15:33:14 +0100 Subject: [PATCH 38/73] Refactor _getBasicMaterials to only handle metadata Otherwise the full profile of all materials and all their definitions gets loaded. Contributes to issue CURA-4243. --- cura/QualityManager.py | 49 +++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/cura/QualityManager.py b/cura/QualityManager.py index 1f9c1b8b44..f776ed56e8 100644 --- a/cura/QualityManager.py +++ b/cura/QualityManager.py @@ -3,11 +3,10 @@ # 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 List, Optional, Dict, TYPE_CHECKING +from typing import Any, Dict, List, Optional, TYPE_CHECKING from UM.Application import Application from UM.Settings.ContainerRegistry import ContainerRegistry -from UM.Settings.DefinitionContainer import DefinitionContainer from UM.Settings.InstanceContainer import InstanceContainer from cura.Settings.ExtruderManager import ExtruderManager @@ -42,7 +41,7 @@ class QualityManager: # 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_materials = self._getBasicMaterials(material_containers[0]) + basic_materials = self._getBasicMaterialMetadatas(material_containers[0].getMetaData()) result = self._getFilteredContainersForStack(machine_definition, basic_materials, **criteria) return result[0] if result else None @@ -119,7 +118,7 @@ class QualityManager: 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_materials = self._getBasicMaterials(material_containers[0]) + basic_materials = self._getBasicMaterialMetadatas(material_containers[0].getMetaData()) if basic_materials: result = self._getFilteredContainersForStack(machine_definition, basic_materials, **criteria) return result[0] if result else None @@ -131,9 +130,9 @@ class QualityManager: # \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], **criteria) + result = self._getFilteredContainersForStack(machine_definition, [material_container.getMetaData()], **criteria) if not result: - basic_materials = self._getBasicMaterials(material_container) + basic_materials = self._getBasicMaterialMetadatas(material_container.getMetaData()) if basic_materials: result = self._getFilteredContainersForStack(machine_definition, basic_materials, **criteria) @@ -198,13 +197,19 @@ class QualityManager: ## Fetch more basic versions of a material. # # This tries to find a generic or basic version of the given material. - # \param material_container \type{InstanceContainer} the material - # \return \type{List[InstanceContainer]} a list of the basic materials or an empty list if one could not be found. - def _getBasicMaterials(self, material_container: InstanceContainer): - base_material = material_container.getMetaDataEntry("material") - material_container_definition = material_container.getDefinition() - if material_container_definition and material_container_definition.getMetaDataEntry("has_machine_quality"): - definition_id = material_container.getDefinition().getMetaDataEntry("quality_definition", material_container.getDefinition().getId()) + # \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]]: + base_material = material_container.get("material") + material_container_definition = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = material_container["definition"]) + if material_container_definition: + material_container_definition = material_container_definition[0] + if "has_machine_quality" in material_container_definition: + definition_id = material_container_definition.get("quality_definition", material_container_definition["id"]) + else: + definition_id = "fdmprinter" else: definition_id = "fdmprinter" if base_material: @@ -213,9 +218,9 @@ class QualityManager: "type": "material", "name": base_material, "definition": definition_id, - "variant": material_container.getMetaDataEntry("variant") + "variant": material_container.get("variant") } - containers = ContainerRegistry.getInstance().findInstanceContainers(**criteria) + containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(**criteria) return containers return [] @@ -223,7 +228,7 @@ class QualityManager: def _getFilteredContainers(self, **kwargs): return self._getFilteredContainersForStack(None, None, **kwargs) - def _getFilteredContainersForStack(self, machine_definition: "DefinitionContainerInterface" = None, material_containers: List[InstanceContainer] = None, **kwargs): + def _getFilteredContainersForStack(self, machine_definition: "DefinitionContainerInterface" = None, material_metadata: List[Dict[str, Any]] = None, **kwargs): # Fill in any default values. if machine_definition is None: machine_definition = Application.getInstance().getGlobalContainerStack().getBottom() @@ -231,10 +236,10 @@ class QualityManager: if quality_definition_id is not None: machine_definition = ContainerRegistry.getInstance().findDefinitionContainers(id = quality_definition_id)[0] - if not material_containers: + if not material_metadata: active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks() if active_stacks: - material_containers = [stack.material for stack in active_stacks] + material_metadata = [stack.material.getMetaData() for stack in active_stacks] criteria = kwargs filter_by_material = False @@ -255,12 +260,12 @@ class QualityManager: # Stick the material IDs in a set material_ids = set() - for material_instance in material_containers: + for material_instance in material_metadata: if material_instance is not None: # Add the parent material too. - for basic_material in self._getBasicMaterials(material_instance): - material_ids.add(basic_material.getId()) - material_ids.add(material_instance.getId()) + 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 = [] From eb3981b4e0734fbe5f5ae093e7db90cfdae9835d Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 28 Nov 2017 14:26:14 +0100 Subject: [PATCH 39/73] Let _fetchInstanceContainers return containers split by loaded or not It must now return two dictionaries: One for the profiles that have been completely loaded and one for the profiles that are only metadata. We could probably improve on these a little bit, since all of these (except the material model) will now load all available quality profiles. I'll see if it is necessary to optimise that. Contributes to issue CURA-4243. --- cura/Settings/ProfilesModel.py | 4 ++-- cura/Settings/QualityAndUserProfilesModel.py | 14 ++++++++------ cura/Settings/UserProfilesModel.py | 14 +++++++------- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/cura/Settings/ProfilesModel.py b/cura/Settings/ProfilesModel.py index 6ba38dcf48..65ac556e78 100644 --- a/cura/Settings/ProfilesModel.py +++ b/cura/Settings/ProfilesModel.py @@ -53,7 +53,7 @@ class ProfilesModel(InstanceContainersModel): def _fetchInstanceContainers(self): global_container_stack = Application.getInstance().getGlobalContainerStack() if global_container_stack is None: - return [] + return {}, {} global_stack_definition = global_container_stack.getBottom() # Get the list of extruders and place the selected extruder at the front of the list. @@ -83,7 +83,7 @@ class ProfilesModel(InstanceContainersModel): if quality.getMetaDataEntry("quality_type") not in quality_type_set: result.append(quality) - return result + return {item.getId():item for item in result}, {} ## Re-computes the items in this model, and adds the layer height role. def _recomputeItems(self): diff --git a/cura/Settings/QualityAndUserProfilesModel.py b/cura/Settings/QualityAndUserProfilesModel.py index 9d7d913d5e..06b9733249 100644 --- a/cura/Settings/QualityAndUserProfilesModel.py +++ b/cura/Settings/QualityAndUserProfilesModel.py @@ -18,7 +18,7 @@ class QualityAndUserProfilesModel(ProfilesModel): def _fetchInstanceContainers(self): global_container_stack = Application.getInstance().getGlobalContainerStack() if not global_container_stack: - return [] + return {}, {} # Fetch the list of quality changes. quality_manager = QualityManager.getInstance() @@ -50,19 +50,21 @@ class QualityAndUserProfilesModel(ProfilesModel): # The actual list of quality profiles come from the first extruder in the extruder list. quality_list = quality_manager.findAllUsableQualitiesForMachineAndExtruders(global_container_stack, extruder_stacks) + result = {quality.getId():quality for quality in quality_list} # Filter the quality_change by the list of available quality_types quality_type_set = set([x.getMetaDataEntry("quality_type") for x in quality_list]) if multiple_extrusion: # If the printer has multiple extruders then quality changes related to the current extruder are kept - filtered_quality_changes = [qc for qc in quality_changes_list if qc.getMetaDataEntry("quality_type") in quality_type_set and + filtered_quality_changes = {qc.getId():qc for qc in quality_changes_list if qc.getMetaDataEntry("quality_type") in quality_type_set and qc.getMetaDataEntry("extruder") is not None and (qc.getMetaDataEntry("extruder") == active_extruder.definition.getMetaDataEntry("quality_definition") or - qc.getMetaDataEntry("extruder") == active_extruder.definition.getId())] + qc.getMetaDataEntry("extruder") == active_extruder.definition.getId())} else: # If not, the quality changes of the global stack are selected - filtered_quality_changes = [qc for qc in quality_changes_list if qc.getMetaDataEntry("quality_type") in quality_type_set and - qc.getMetaDataEntry("extruder") is None] + filtered_quality_changes = {qc.getId():qc for qc in quality_changes_list if qc.getMetaDataEntry("quality_type") in quality_type_set and + qc.getMetaDataEntry("extruder") is None} + result.update(filtered_quality_changes) - return quality_list + filtered_quality_changes + return result, {} diff --git a/cura/Settings/UserProfilesModel.py b/cura/Settings/UserProfilesModel.py index aa815ef4aa..a5fc1bc7c2 100644 --- a/cura/Settings/UserProfilesModel.py +++ b/cura/Settings/UserProfilesModel.py @@ -18,7 +18,7 @@ class UserProfilesModel(ProfilesModel): def _fetchInstanceContainers(self): global_container_stack = Application.getInstance().getGlobalContainerStack() if not global_container_stack: - return [] + return {}, {} # Fetch the list of quality changes. quality_manager = QualityManager.getInstance() @@ -27,7 +27,7 @@ class UserProfilesModel(ProfilesModel): # Detecting if the machine has multiple extrusion multiple_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1 - # Get the list of extruders and place the selected extruder at the front of the list. + # Get the list of extruders and place the selected extruder at the front of the list. extruder_manager = ExtruderManager.getInstance() active_extruder = extruder_manager.getActiveExtruderStack() extruder_stacks = extruder_manager.getActiveExtruderStacks() @@ -56,13 +56,13 @@ class UserProfilesModel(ProfilesModel): if multiple_extrusion: # If the printer has multiple extruders then quality changes related to the current extruder are kept - filtered_quality_changes = [qc for qc in quality_changes_list if qc.getMetaDataEntry("quality_type") in quality_type_set and + filtered_quality_changes = {qc.getId():qc for qc in quality_changes_list if qc.getMetaDataEntry("quality_type") in quality_type_set and qc.getMetaDataEntry("extruder") is not None and (qc.getMetaDataEntry("extruder") == active_extruder.definition.getMetaDataEntry("quality_definition") or - qc.getMetaDataEntry("extruder") == active_extruder.definition.getId())] + qc.getMetaDataEntry("extruder") == active_extruder.definition.getId())} else: # If not, the quality changes of the global stack are selected - filtered_quality_changes = [qc for qc in quality_changes_list if qc.getMetaDataEntry("quality_type") in quality_type_set and - qc.getMetaDataEntry("extruder") is None] + filtered_quality_changes = {qc.getId():qc for qc in quality_changes_list if qc.getMetaDataEntry("quality_type") in quality_type_set and + qc.getMetaDataEntry("extruder") is None} - return filtered_quality_changes + return filtered_quality_changes, {} From 56d6664c8c404f1d043f30b5bbb226c56e30b36a Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 28 Nov 2017 15:33:21 +0100 Subject: [PATCH 40/73] Guard against materials not having a definition The empty container has no definition. Contributes to issue CURA-4243. --- cura/QualityManager.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/cura/QualityManager.py b/cura/QualityManager.py index f776ed56e8..4ed2242ec1 100644 --- a/cura/QualityManager.py +++ b/cura/QualityManager.py @@ -202,16 +202,20 @@ class QualityManager: # \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]]: - base_material = material_container.get("material") - material_container_definition = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = material_container["definition"]) - if material_container_definition: - material_container_definition = material_container_definition[0] - if "has_machine_quality" in material_container_definition: - definition_id = material_container_definition.get("quality_definition", material_container_definition["id"]) - else: - definition_id = "fdmprinter" - else: + 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 = { From 381d9bf3f1b53f9bc17301b7b9dcbe841d54052e Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 28 Nov 2017 16:15:20 +0100 Subject: [PATCH 41/73] Log which file caused it to go wrong Contributes to issue CURA-4243. --- cura/Settings/MachineManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 2a77032a33..285a329cae 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -836,7 +836,7 @@ class MachineManager(QObject): elif container_type == "quality_changes": 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") + Logger.log("e", "Tried to set quality to a container that is not of the right type: {container_id}".format(container_id = containers[0]["id"])) return # Check if it was at all possible to find new settings From c63ef6fed60ac49d78123a543338f5121d17b571 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 28 Nov 2017 16:58:04 +0100 Subject: [PATCH 42/73] Remove unused variables The machine_name variable is later redefined. Contributes to issue CURA-4243. --- plugins/3MFReader/ThreeMFWorkspaceReader.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 15ee48164d..011bac022c 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -119,7 +119,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader): Logger.log("w", "Could not find reader that was able to read the scene data for 3MF workspace") return WorkspaceReader.PreReadResult.failed - machine_name = "" machine_type = "" variant_type_name = i18n_catalog.i18nc("@label", "Nozzle") @@ -131,7 +130,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader): # When loading the global stack file, it may be associated with those containers, which may or may not be # in Cura already, so we need to provide them as alternative search lists. instance_container_list = [] - material_container_list = [] resolve_strategy_keys = ["machine", "material", "quality_changes"] self._resolve_strategies = {k: None for k in resolve_strategy_keys} From 8707396ad7698c5a02a75b383df54ca295731831 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 28 Nov 2017 17:30:00 +0100 Subject: [PATCH 43/73] Remove isReadOnly functionality Everyone should now ask it from the container registry. Contributes to issue CURA-4243. --- cura/Settings/ContainerManager.py | 36 +++++++++---------- cura/Settings/CuraContainerRegistry.py | 1 - cura/Settings/ExtruderManager.py | 2 +- cura/Settings/MachineManager.py | 5 +-- plugins/3MFReader/ThreeMFWorkspaceReader.py | 7 ++-- .../NetworkPrinterOutputDevice.py | 5 +-- .../XmlMaterialProfile/XmlMaterialProfile.py | 27 ++++++-------- 7 files changed, 35 insertions(+), 48 deletions(-) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index a93dd3c1b6..be302d2a6f 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -164,15 +164,15 @@ class ContainerManager(QObject): # \return True if successful, False if not. @pyqtSlot(str, result = bool) def clearContainer(self, container_id): + if self._container_registry.isReadOnly(container_id): + Logger.log("w", "Cannot clear read-only container %s", container_id) + return False + containers = self._container_registry.findContainers(id = container_id) if not containers: Logger.log("w", "Could clear container %s because it was not found.", container_id) return False - if containers[0].isReadOnly(): - Logger.log("w", "Cannot clear read-only container %s", container_id) - return False - containers[0].clear() return True @@ -200,6 +200,10 @@ class ContainerManager(QObject): # \return True if successful, False if not. @pyqtSlot(str, str, str, result = bool) def setContainerMetaDataEntry(self, container_id, entry_name, entry_value): + if self._container_registry.isReadOnly(container_id): + Logger.log("w", "Cannot set metadata of read-only container %s.", container_id) + return False + containers = self._container_registry.findContainers(id = container_id) #We need the complete container, since we need to know whether the container is read-only or not. if not containers: Logger.log("w", "Could not set metadata of container %s because it was not found.", container_id) @@ -207,10 +211,6 @@ class ContainerManager(QObject): container = containers[0] - if container.isReadOnly(): - Logger.log("w", "Cannot set metadata of read-only container %s.", container_id) - return False - entries = entry_name.split("/") entry_name = entries.pop() @@ -250,6 +250,10 @@ class ContainerManager(QObject): # \return True if successful, False if not. @pyqtSlot(str, str, str, str, result = bool) def setContainerProperty(self, container_id, setting_key, property_name, property_value): + if self._container_registry.isReadOnly(container_id): + Logger.log("w", "Cannot set properties of read-only container %s.", container_id) + return False + containers = self._container_registry.findContainers(id = container_id) if not containers: Logger.log("w", "Could not set properties of container %s because it was not found.", container_id) @@ -257,10 +261,6 @@ class ContainerManager(QObject): container = containers[0] - if container.isReadOnly(): - Logger.log("w", "Cannot set properties of read-only container %s.", container_id) - return False - container.setProperty(setting_key, property_name, property_value) basefile = container.getMetaDataEntry("base_file", container_id) @@ -296,18 +296,16 @@ class ContainerManager(QObject): ## Set the name of the specified container. @pyqtSlot(str, str, result = bool) def setContainerName(self, container_id, new_name): + if self._container_registry.isReadOnly(container_id): + Logger.log("w", "Cannot set name of read-only container %s.", container_id) + return False + containers = self._container_registry.findContainers(id = container_id) #We need to get the full container, not just metadata, since we need to know whether it's read-only. if not containers: Logger.log("w", "Could not set name of container %s because it was not found.", container_id) return False - container = containers[0] - - if container.isReadOnly(): - Logger.log("w", "Cannot set name of read-only container %s.", container_id) - return False - - container.setName(new_name) + containers[0].setName(new_name) return True diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index aa1f7617cb..b19bdca2cf 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -266,7 +266,6 @@ class CuraContainerRegistry(ContainerRegistry): # # \return None if configuring was successful or an error message if an error occurred. def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str) -> Optional[str]: - profile.setReadOnly(False) profile.setDirty(True) # Ensure the profiles are correctly saved new_id = self.createUniqueName("quality_changes", "", id_seed, catalog.i18nc("@label", "Custom profile")) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index e01561c5f4..03681d9e82 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -332,7 +332,7 @@ class ExtruderManager(QObject): preferred_materials = container_registry.findInstanceContainers(**search_criteria) if len(preferred_materials) >= 1: # In some cases we get multiple materials. In that case, prefer materials that are marked as read only. - read_only_preferred_materials = [preferred_material for preferred_material in preferred_materials if preferred_material.isReadOnly()] + read_only_preferred_materials = [preferred_material for preferred_material in preferred_materials if container_registry.isReadOnly(preferred_material.getId())] if len(read_only_preferred_materials) >= 1: material = read_only_preferred_materials[0] else: diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 285a329cae..e117efe289 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -704,10 +704,7 @@ class MachineManager(QObject): ## Check if a container is read_only @pyqtSlot(str, result = bool) def isReadOnly(self, container_id: str) -> bool: - containers = ContainerRegistry.getInstance().findInstanceContainers(id = container_id) - if not containers or not self._active_container_stack: - return True - return containers[0].isReadOnly() + return ContainerRegistry.getInstance().isReadOnly(container_id) ## Copy the value of the setting of the current extruder to all other extruders as well as the global container. @pyqtSlot(str) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 011bac022c..bfd31f2ebe 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -180,11 +180,10 @@ class ThreeMFWorkspaceReader(WorkspaceReader): material_container_files = [name for name in cura_file_names if name.endswith(self._material_container_suffix)] for material_container_file in material_container_files: container_id = self._stripFileToId(material_container_file) - materials = self._container_registry.findInstanceContainers(id=container_id) material_labels.append(self._getMaterialLabelFromSerialized(archive.open(material_container_file).read().decode("utf-8"))) - if materials: + if self._container_registry.findContainersMetadata(id = container_id): #This material already exists. containers_found_dict["material"] = True - if not materials[0].isReadOnly(): # Only non readonly materials can be in conflict + if not self._container_registry.isReadOnly(container_id): # Only non readonly materials can be in conflict material_conflict = True Job.yieldThread() @@ -501,7 +500,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): containers_to_add.append(material_container) else: material_container = materials[0] - if not material_container.isReadOnly(): # Only create new materials if they are not read only. + if not self._container_registry.isReadOnly(container_id): # Only create new materials if they are not read only. if self._resolve_strategies["material"] == "override": material_container.deserialize(archive.open(material_container_file).read().decode("utf-8")) elif self._resolve_strategies["material"] == "new": diff --git a/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py b/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py index 07a8df985c..0193434b18 100755 --- a/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py +++ b/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py @@ -969,7 +969,8 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): ## Send all material profiles to the printer. def sendMaterialProfiles(self): - for container in UM.Settings.ContainerRegistry.ContainerRegistry.getInstance().findInstanceContainers(type = "material"): + registry = UM.Settings.ContainerRegistry.ContainerRegistry.getInstance() + for container in registry.findInstanceContainers(type = "material"): try: xml_data = container.serialize() if xml_data == "" or xml_data is None: @@ -978,7 +979,7 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): names = ContainerManager.getInstance().getLinkedMaterials(container.getId()) if names: # There are other materials that share this GUID. - if not container.isReadOnly(): + if not registry.isReadOnly(container.getId()): continue # If it's not readonly, it's created by user, so skip it. material_multi_part = QHttpMultiPart(QHttpMultiPart.FormDataType) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 23d4ed3860..47b0b4f2e9 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -45,25 +45,18 @@ class XmlMaterialProfile(InstanceContainer): def getInheritedFiles(self): return self._inherited_files - ## Overridden from InstanceContainer - def setReadOnly(self, read_only): - super().setReadOnly(read_only) - - basefile = self.getMetaDataEntry("base_file", self.getId()) # if basefile is self.getId, this is a basefile. - for container in ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile): - container._read_only = read_only # prevent loop instead of calling setReadOnly - ## Overridden from InstanceContainer # set the meta data for all machine / variant combinations def setMetaDataEntry(self, key, value): - if self.isReadOnly(): + registry = ContainerRegistry.getInstance() + if registry.isReadOnly(self.getId()): return super().setMetaDataEntry(key, value) basefile = self.getMetaDataEntry("base_file", self.getId()) #if basefile is self.getId, this is a basefile. # Update all containers that share basefile - for container in ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile): + for container in registry.findInstanceContainers(base_file = basefile): if container.getMetaDataEntry(key, None) != value: # Prevent recursion container.setMetaDataEntry(key, value) @@ -71,7 +64,8 @@ class XmlMaterialProfile(InstanceContainer): # without this function the setName would only set the name of the specific nozzle / material / machine combination container # The function is a bit tricky. It will not set the name of all containers if it has the correct name itself. def setName(self, new_name): - if self.isReadOnly(): + registry = ContainerRegistry.getInstance() + if registry.isReadOnly(self.getId()): return # Not only is this faster, it also prevents a major loop that causes a stack overflow. @@ -83,7 +77,7 @@ class XmlMaterialProfile(InstanceContainer): basefile = self.getMetaDataEntry("base_file", self.getId()) # if basefile is self.getId, this is a basefile. # Update the basefile as well, this is actually what we're trying to do # Update all containers that share GUID and basefile - containers = ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile) + containers = registry.findInstanceContainers(base_file = basefile) for container in containers: container.setName(new_name) @@ -91,12 +85,11 @@ class XmlMaterialProfile(InstanceContainer): def setDirty(self, dirty): super().setDirty(dirty) base_file = self.getMetaDataEntry("base_file", None) - if base_file is not None and base_file != self.getId(): - containers = ContainerRegistry.getInstance().findContainers(id=base_file) + registry = ContainerRegistry.getInstance() + if base_file is not None and base_file != self.getId() and not registry.isReadOnly(base_file): + containers = registry.findContainers(id = base_file) if containers: - base_container = containers[0] - if not base_container.isReadOnly(): - base_container.setDirty(dirty) + containers[0].setDirty(dirty) ## Overridden from InstanceContainer # base file: common settings + supported machines From 99cd139ba40e3e9846d5f9f0e99f36d5f1d32e49 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 29 Nov 2017 13:39:24 +0100 Subject: [PATCH 44/73] Remove setting definition double Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 7265f93090..dfcd41abd2 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -491,8 +491,6 @@ class XmlMaterialProfile(InstanceContainer): meta_data["properties"] = property_values meta_data["definition"] = "fdmprinter" - self.setDefinition("fdmprinter") - common_compatibility = True settings = data.iterfind("./um:settings/um:setting", self.__namespaces) for entry in settings: From 838e49f3daf4198bc8d5d14a176020be4b469af7 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 29 Nov 2017 14:33:04 +0100 Subject: [PATCH 45/73] Pass just metadata through quality manager instead of full containers Where we only need metadata, we should pass metadata instead of full containers. Also add some type hinting. Contributes to issue CURA-4243. --- cura/QualityManager.py | 26 +++++++++++++------------- cura/Settings/ContainerManager.py | 26 ++++++++++++++------------ cura/Settings/MachineManager.py | 22 ++++++++-------------- 3 files changed, 35 insertions(+), 39 deletions(-) diff --git a/cura/QualityManager.py b/cura/QualityManager.py index 42ef297559..76a0c86a5f 100644 --- a/cura/QualityManager.py +++ b/cura/QualityManager.py @@ -32,16 +32,16 @@ class QualityManager: # \param quality_name # \param machine_definition (Optional) \type{DefinitionContainerInterface} If nothing is # specified then the currently selected machine definition is used. - # \param material_containers (Optional) \type{List[InstanceContainer]} If nothing is specified then - # the current set of selected materials 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 findQualityByName(self, quality_name: str, machine_definition: Optional["DefinitionContainerInterface"] = None, material_containers: List[InstanceContainer] = None) -> Optional[InstanceContainer]: + def findQualityByName(self, quality_name: str, machine_definition: Optional["DefinitionContainerInterface"] = None, material_containers_metadata: Optional[List[Dict[str, Any]]] = None) -> Optional[InstanceContainer]: criteria = {"type": "quality", "name": quality_name} - result = self._getFilteredContainersForStack(machine_definition, material_containers, **criteria) + 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 and len(material_containers) == 1: - basic_materials = self._getBasicMaterialMetadatas(material_containers[0].getMetaData()) + if not result and material_containers_metadata and len(material_containers_metadata) == 1: + basic_materials = self._getBasicMaterialMetadatas(material_containers_metadata[0]) result = self._getFilteredContainersForStack(machine_definition, basic_materials, **criteria) return result[0] if result else None @@ -107,18 +107,18 @@ class QualityManager: # \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 (Optional) \type{List[InstanceContainer]} If nothing is specified then - # the current set of selected materials 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: List[InstanceContainer] = None, **kwargs) -> 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, **criteria) + 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 and len(material_containers) == 1: - basic_materials = self._getBasicMaterialMetadatas(material_containers[0].getMetaData()) + 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 @@ -236,7 +236,7 @@ class QualityManager: def _getFilteredContainers(self, **kwargs): return self._getFilteredContainersForStack(None, None, **kwargs) - def _getFilteredContainersForStack(self, machine_definition: "DefinitionContainerInterface" = None, material_metadata: List[Dict[str, Any]] = 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() diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index be302d2a6f..8abe627f3d 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -4,7 +4,7 @@ import os.path import urllib import uuid -from typing import Dict, Union +from typing import Any, Dict, List, Union from PyQt5.QtCore import QObject, QUrl, QVariant from UM.FlameProfiler import pyqtSlot @@ -677,27 +677,29 @@ class ContainerManager(QObject): machine_definition = global_stack.getBottom() active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks() - material_containers = [stack.material for stack in active_stacks] + if active_stacks is None: + return "" + material_metadatas = [stack.material.getMetaData() for stack in active_stacks] result = self._duplicateQualityOrQualityChangesForMachineType(quality_name, base_name, QualityManager.getInstance().getParentMachineDefinition(machine_definition), - material_containers) + material_metadatas) return result[0].getName() if result else "" ## Duplicate a quality or quality changes profile specific to a machine type # - # \param quality_name \type{str} the name of the quality or quality changes container to duplicate. - # \param base_name \type{str} the desired name for the new container. - # \param machine_definition \type{DefinitionContainer} - # \param material_instances \type{List[InstanceContainer]} - # \return \type{str} the name of the newly created container. - def _duplicateQualityOrQualityChangesForMachineType(self, quality_name, base_name, machine_definition, material_instances): + # \param quality_name The name of the quality or quality changes container to duplicate. + # \param base_name The desired name for the new container. + # \param machine_definition The machine with the specific machine type. + # \param material_metadatas Metadata of materials + # \return List of duplicated quality profiles. + def _duplicateQualityOrQualityChangesForMachineType(self, quality_name: str, base_name: str, machine_definition: DefinitionContainer, material_metadatas: List[Dict[str, Any]]) -> List[InstanceContainer]: Logger.log("d", "Attempting to duplicate the quality %s", quality_name) if base_name is None: base_name = quality_name # Try to find a Quality with the name. - container = QualityManager.getInstance().findQualityByName(quality_name, machine_definition, material_instances) + container = QualityManager.getInstance().findQualityByName(quality_name, machine_definition, material_metadatas) if container: Logger.log("d", "We found a quality to duplicate.") return self._duplicateQualityForMachineType(container, base_name, machine_definition) @@ -706,7 +708,7 @@ class ContainerManager(QObject): return self._duplicateQualityChangesForMachineType(quality_name, base_name, machine_definition) # Duplicate a quality profile - def _duplicateQualityForMachineType(self, quality_container, base_name, machine_definition): + def _duplicateQualityForMachineType(self, quality_container, base_name, machine_definition) -> List[InstanceContainer]: if base_name is None: base_name = quality_container.getName() new_name = self._container_registry.uniqueName(base_name) @@ -730,7 +732,7 @@ class ContainerManager(QObject): return new_change_instances # Duplicate a quality changes container - def _duplicateQualityChangesForMachineType(self, quality_changes_name, base_name, machine_definition): + def _duplicateQualityChangesForMachineType(self, quality_changes_name, base_name, machine_definition) -> List[InstanceContainer]: new_change_instances = [] for container in QualityManager.getInstance().findQualityChangesByName(quality_changes_name, machine_definition): diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index e4e48ae6fd..9a3f529355 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -768,7 +768,7 @@ class MachineManager(QObject): if quality_type: candidate_quality = quality_manager.findQualityByQualityType(quality_type, quality_manager.getWholeMachineDefinition(global_stack.definition), - [material_container]) + [material_container.getMetaData()]) if not candidate_quality or isinstance(candidate_quality, type(self._empty_quality_changes_container)): Logger.log("d", "Attempting to find fallback quality") @@ -923,13 +923,13 @@ class MachineManager(QObject): # find qualities for extruders for extruder_stack in extruder_stacks: - material = extruder_stack.material + material_metadata = extruder_stack.material.getMetaData() # TODO: fix this if self._new_material_container and extruder_stack.getId() == self._active_container_stack.getId(): - material = self._new_material_container + material_metadata = self._new_material_container.getMetaData() - quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material]) + quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material_metadata]) if not quality: # No quality profile is found for this quality type. @@ -979,12 +979,6 @@ class MachineManager(QObject): Logger.log("e", "Could not find the global quality changes container with name %s", quality_changes_name) return None - material = global_container_stack.material - - # find a quality type that matches both machine and materials - if self._new_material_container and self._active_container_stack.getId() == global_container_stack.getId(): - material = self._new_material_container - # 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") @@ -1004,12 +998,12 @@ class MachineManager(QObject): if not quality_changes: quality_changes = self._empty_quality_changes_container - material = extruder_stack.material + material_metadata = extruder_stack.material.getMetaData() if self._new_material_container and self._active_container_stack.getId() == extruder_stack.getId(): - material = self._new_material_container + material_metadata = self._new_material_container.getMetaData() - quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material]) + quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material_metadata]) if not quality: # No quality profile found for this quality type. @@ -1022,7 +1016,7 @@ class MachineManager(QObject): }) # append the global quality changes - global_quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material], global_quality = "True") + global_quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, global_quality = "True") # if there is not global quality but we're using a single extrusion machine, copy the quality of the first extruder - CURA-4482 if not global_quality and len(extruder_stacks) == 1: From cacc4b858654917ad1c4a4a0c2e44f8517430d55 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 29 Nov 2017 16:39:56 +0100 Subject: [PATCH 46/73] Properly set the definition to the currently processed machine Otherwise all of them stay at fdmprinter. Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index dfcd41abd2..e3f9413003 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -565,6 +565,7 @@ class XmlMaterialProfile(InstanceContainer): # Don't use setMetadata, as that overrides it for all materials with same base file new_material.getMetaData()["compatible"] = machine_compatibility new_material.getMetaData()["machine_manufacturer"] = machine_manufacturer + new_material.getMetaData()["definition"] = machine_id new_material.setCachedValues(cached_machine_setting_properties) @@ -615,11 +616,11 @@ class XmlMaterialProfile(InstanceContainer): new_hotend_material.setMetaData(copy.deepcopy(self.getMetaData())) new_hotend_material.getMetaData()["id"] = new_hotend_id new_hotend_material.getMetaData()["name"] = self.getName() - new_hotend_material.setDefinition(machine_id) new_hotend_material.getMetaData()["variant"] = variant_containers[0]["id"] # Don't use setMetadata, as that overrides it for all materials with same base file new_hotend_material.getMetaData()["compatible"] = hotend_compatibility new_hotend_material.getMetaData()["machine_manufacturer"] = machine_manufacturer + new_hotend_material.getMetaData()["definition"] = machine_id cached_hotend_setting_properties = cached_machine_setting_properties.copy() cached_hotend_setting_properties.update(hotend_setting_values) @@ -748,6 +749,7 @@ class XmlMaterialProfile(InstanceContainer): new_material_metadata["id"] = new_material_id new_material_metadata["compatible"] = machine_compatibility new_material_metadata["machine_manufacturer"] = machine_manufacturer + new_material_metadata["definition"] = machine_id if len(found_materials) == 0: #This is a new material. result_metadata.append(new_material_metadata) @@ -785,6 +787,7 @@ class XmlMaterialProfile(InstanceContainer): new_hotend_material_metadata["compatible"] = hotend_compatibility new_hotend_material_metadata["machine_manufacturer"] = machine_manufacturer new_hotend_material_metadata["id"] = new_hotend_id + new_hotend_material_metadata["definition"] = machine_id if len(found_materials) == 0: result_metadata.append(new_hotend_material_metadata) From c34de83e3f6849d8af81d30980fafe2ac8f6d2fe Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 29 Nov 2017 18:04:07 +0100 Subject: [PATCH 47/73] Use isLoaded to check if a material is already loaded This prevents us from having to make an expensive findContainers call if it's not necessary, and also prevents us from unnecessarily loading the subcontainers in a loop during loading. Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index e3f9413003..b203f36684 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -550,10 +550,9 @@ class XmlMaterialProfile(InstanceContainer): # project file and the a material in Cura have the same ID. # In the case if a derived material already exists, override that material container because if # the data in the parent material has been changed, the derived ones should be updated too. - found_materials = ContainerRegistry.getInstance().findInstanceContainers(id = new_material_id) - is_new_material = False - if found_materials: - new_material = found_materials[0] + if ContainerRegistry.getInstance().isLoaded(new_material_id): + new_material = ContainerRegistry.getInstance().findContainers(id = new_material_id)[0] + is_new_material = False else: new_material = XmlMaterialProfile(new_material_id) is_new_material = True @@ -605,10 +604,9 @@ class XmlMaterialProfile(InstanceContainer): # Same as machine compatibility, keep the derived material containers consistent with the parent # material - found_materials = ContainerRegistry.getInstance().findInstanceContainers(id = new_hotend_id) - is_new_material = False - if found_materials: - new_hotend_material = found_materials[0] + if ContainerRegistry.getInstance().isLoaded(new_hotend_id): + new_hotend_material = ContainerRegistry.getInstance().findContainers(id = new_hotend_id)[0] + is_new_material = False else: new_hotend_material = XmlMaterialProfile(new_hotend_id) is_new_material = True From c1ba64fd9e93d8e364804b6f8ff2fc7d133de5f8 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 30 Nov 2017 00:10:02 +0100 Subject: [PATCH 48/73] Correct variants after all metadata is loaded As explained in the block of documentation, we can't find the variants because there is no guarantee that the variants have been loaded by the time that the metadata of materials is deserialised and we don't know their IDs either (so no lazy loading). This registers a function to be called upon completely loading the metadata, which runs a query on the metadata to find the variant by its definition and name. A bit hacky but I see no better solution. Contributes to issue CURA-4243. --- .../XmlMaterialProfile/XmlMaterialProfile.py | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index b203f36684..414886e1da 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -17,7 +17,6 @@ import UM.Dictionary from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.ContainerRegistry import ContainerRegistry - ## Handles serializing and deserializing material containers from an XML file class XmlMaterialProfile(InstanceContainer): CurrentFdmMaterialVersion = "1.3" @@ -761,8 +760,6 @@ class XmlMaterialProfile(InstanceContainer): if not variant_containers: # It is not really properly defined what "ID" is so also search for variants by name. variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = machine_id, name = hotend_id) - if not variant_containers: - continue hotend_compatibility = machine_compatibility for entry in hotend.iterfind("./um:setting", cls.__namespaces): @@ -781,7 +778,11 @@ class XmlMaterialProfile(InstanceContainer): new_hotend_material_metadata = {} new_hotend_material_metadata.update(base_metadata) - new_hotend_material_metadata["variant"] = variant_containers[0]["id"] + if variant_containers: + new_hotend_material_metadata["variant"] = variant_containers[0]["id"] + else: + new_hotend_material_metadata["variant"] = hotend_id + _with_missing_variants.append(new_hotend_material_metadata) new_hotend_material_metadata["compatible"] = hotend_compatibility new_hotend_material_metadata["machine_manufacturer"] = machine_manufacturer new_hotend_material_metadata["id"] = new_hotend_id @@ -876,4 +877,22 @@ def _indent(elem, level = 0): # We are only interested in the actual tag name, so discard everything # before the last } def _tag_without_namespace(element): - return element.tag[element.tag.rfind("}") + 1:] \ No newline at end of file + return element.tag[element.tag.rfind("}") + 1:] + +#While loading XML profiles, some of these profiles don't know what variant +#they belong to. We'd like to search by the machine ID and the variant's +#name, but we don't know the variant's ID. Not all variants have been loaded +#yet so we can't run a filter on the name and machine. The ID is unknown +#so we can't lazily load the variant either. So we have to wait until all +#the rest is loaded properly and then assign the correct variant to the +#material files that were missing it. +_with_missing_variants = [] +def _fillMissingVariants(): + registry = ContainerRegistry.getInstance() + for variant_metadata in _with_missing_variants: + variants = registry.findContainersMetadata(definition = variant_metadata["definition"], name = variant_metadata["variant"]) + if not variants: + Logger.log("w", "Could not find variant for variant-specific material {material_id}.".format(material_id = variant_metadata["id"])) + continue + variant_metadata["variant"] = variants[0]["id"] +ContainerRegistry.allMetadataLoaded.connect(_fillMissingVariants) \ No newline at end of file From f9c59c7e1ebad7177984471c63a3c7c0ff7f6ecc Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 30 Nov 2017 00:33:10 +0100 Subject: [PATCH 49/73] Don't update model if extruders haven't all been loaded yet This model is created while the extruder stacks have not yet been filled in. You can't connect yet. When the extruder is ready, _extrudersChanged will be called again and then the new extruder will also get connected. Contributes to issue CURA-4243. --- cura/Settings/ExtrudersModel.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cura/Settings/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py index 40d13461cc..48517c4a7b 100644 --- a/cura/Settings/ExtrudersModel.py +++ b/cura/Settings/ExtrudersModel.py @@ -142,6 +142,8 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): self._active_machine_extruders = [] extruder_manager = ExtruderManager.getInstance() for extruder in extruder_manager.getExtruderStacks(): + if extruder is None: #This extruder wasn't loaded yet. This happens asynchronously while this model is constructed from QML. + continue extruder.containersChanged.connect(self._onExtruderStackContainersChanged) self._active_machine_extruders.append(extruder) From d29d73413f3138b8d90c286ba7e1d0e3ff2236c9 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Thu, 30 Nov 2017 01:11:10 +0100 Subject: [PATCH 50/73] Remove unused function Contributes to issue CURA-4243. --- cura/Settings/ContainerManager.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index 8abe627f3d..daf30d0792 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -954,14 +954,6 @@ class ContainerManager(QObject): name_filter = "{0} ({1})".format(mime_type.comment, suffix_list) self._container_name_filters[name_filter] = entry - ## Get containers filtered by machine type and material if required. - # - # \param kwargs Initial search criteria that the containers need to match. - # - # \return A list of containers matching the search criteria. - def _getFilteredContainers(self, **kwargs): - return QualityManager.getInstance()._getFilteredContainers(**kwargs) - ## 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. From 17ede12573b9b25c399638931853aa5b980a9fc9 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 1 Dec 2017 16:07:30 +0100 Subject: [PATCH 51/73] Also set base_file metadata when deserialising just metadata Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 414886e1da..7a76c1522e 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -643,7 +643,8 @@ class XmlMaterialProfile(InstanceContainer): "type": "material", "status": "unknown", #TODO: Add material verification. "container_type": XmlMaterialProfile, - "id": container_id + "id": container_id, + "base_file": container_id } try: From 083a4a802477100e3acda5bb005f841d44f31c2d Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 1 Dec 2017 16:19:58 +0100 Subject: [PATCH 52/73] Rewrite cloning of materials The original was based on reading the original file back, but that won't work if the files are provided by an arbitrary container provider. Instead we'll actually make a copy of all the profiles that need to be copied. It's much faster as well. Contributes to issue CURA-4243. --- cura/Settings/ContainerManager.py | 54 +++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index daf30d0792..94a79c9911 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -1,6 +1,7 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import copy import os.path import urllib import uuid @@ -751,27 +752,52 @@ class ContainerManager(QObject): # \return \type{str} the id of the newly created container. @pyqtSlot(str, result = str) def duplicateMaterial(self, material_id: str) -> str: - containers = self._container_registry.findInstanceContainers(id = material_id) - if not containers: + original = self._container_registry.findContainersMetadata(id = material_id) + if not original: Logger.log("d", "Unable to duplicate the material with id %s, because it doesn't exist.", material_id) return "" + original = original[0] + + base_container_id = original.get("base_file") + base_container = self._container_registry.findContainers(id = base_container_id) + if not base_container: + Logger.log("d", "Unable to duplicate the material with id {material_id}, because base_file {base_container_id} doesn't exist.".format(material_id = material_id, base_container_id = base_container_id)) + return "" + base_container = base_container[0] + + #We'll copy all containers with the same base. + #This way the correct variant and machine still gets assigned when loading the copy of the material. + containers_to_copy = self._container_registry.findInstanceContainers(base_file = base_container_id) # Ensure all settings are saved. Application.getInstance().saveSettings() # Create a new ID & container to hold the data. - new_id = self._container_registry.uniqueName(material_id) - container_type = type(containers[0]) # Could be either a XMLMaterialProfile or a InstanceContainer - duplicated_container = container_type(new_id) + new_containers = [] + new_base_id = self._container_registry.uniqueName(base_container.getId()) + new_base_container = copy.deepcopy(base_container) + new_base_container.getMetaData()["id"] = new_base_id + new_base_container.getMetaData()["base_file"] = new_base_id + new_containers.append(new_base_container) - # Instead of duplicating we load the data from the basefile again. - # This ensures that the inheritance goes well and all "cut up" subclasses of the xmlMaterial profile - # are also correctly created. - with open(containers[0].getPath(), encoding="utf-8") as f: - duplicated_container.deserialize(f.read()) - duplicated_container.setDirty(True) - self._container_registry.addContainer(duplicated_container) - return self._getMaterialContainerIdForActiveMachine(new_id) + #Clone all of them. + clone_of_original = None #Keeping track of which one is the clone of the original material, since we need to return that. + for container_to_copy in containers_to_copy: + #Create unique IDs for every clone. + current_id = container_to_copy.getId() + new_id = self._container_registry.uniqueName(current_id) + if current_id == material_id: + clone_of_original = new_id + + new_container = copy.deepcopy(container_to_copy) + new_container.getMetaData()["id"] = new_id + new_container.getMetaData()["base_file"] = new_base_id + new_containers.append(new_container) + + for container_to_add in new_containers: + container_to_add.setDirty(True) + ContainerRegistry.getInstance().addContainer(container_to_add) + return self._getMaterialContainerIdForActiveMachine(clone_of_original) ## Create a new material by cloning Generic PLA for the current material diameter and setting the GUID to something unqiue # @@ -839,7 +865,7 @@ class ContainerManager(QObject): if materials: return materials[0]["id"] - Logger.log("w", "Unable to find a suitable container based on %s for the current machine .", base_file) + Logger.log("w", "Unable to find a suitable container based on %s for the current machine.", base_file) return "" # do not activate a new material if a container can not be found return base_file From bc28189ff5867dd81cf58f25fec40139d3831f84 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Dec 2017 10:55:30 +0100 Subject: [PATCH 53/73] Don't serialize the name of the metadata It's being serialized in a different place. Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 7a76c1522e..940f88773f 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -131,6 +131,7 @@ class XmlMaterialProfile(InstanceContainer): metadata.pop("approximate_diameter", "") metadata.pop("id", "") metadata.pop("container_type", "") + metadata.pop("name", "") ## Begin Name Block builder.start("name") From 72c7d2bd76c0ea0d6acfd50b519757ee6940f307 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Dec 2017 11:04:28 +0100 Subject: [PATCH 54/73] Reconstruct ID for submaterials from unique base name When you make the ID of the subprofile unique it doesn't get linked to the base profile any more since the '#2' gets put at the end. Contributes to issue CURA-4243. --- cura/Settings/ContainerManager.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index 94a79c9911..c552a6fe8c 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -785,7 +785,11 @@ class ContainerManager(QObject): for container_to_copy in containers_to_copy: #Create unique IDs for every clone. current_id = container_to_copy.getId() - new_id = self._container_registry.uniqueName(current_id) + new_id = new_base_id + if container_to_copy.getMetaDataEntry("definition") != "fdmprinter": + new_id += "_" + container_to_copy.getMetaDataEntry("definition") + if container_to_copy.getMetaDataEntry("variant"): + new_id += "_" + container_to_copy.getMetaDataEntry("variant") if current_id == material_id: clone_of_original = new_id From 44537c4ef46d9ad2730faca8eb19cfbf2782b170 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Mon, 4 Dec 2017 11:11:57 +0100 Subject: [PATCH 55/73] Added sorting by brand to MaterialsModel. Contributes to issue CURA-4243. --- cura/Settings/MaterialsModel.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cura/Settings/MaterialsModel.py b/cura/Settings/MaterialsModel.py index bab8929765..e3d56c2f86 100644 --- a/cura/Settings/MaterialsModel.py +++ b/cura/Settings/MaterialsModel.py @@ -1,6 +1,7 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Any, List from UM.Settings.ContainerRegistry import ContainerRegistry #To listen for changes to the materials. from UM.Settings.Models.InstanceContainersModel import InstanceContainersModel #We're extending this class. @@ -23,3 +24,10 @@ class MaterialsModel(InstanceContainersModel): def _onContainerChanged(self, container): if container.getMetaDataEntry("type", "") == "material": super()._onContainerChanged(container) + + ## Group brand together + def _sortKey(self, item) -> List[Any]: + result = [] + result.append(item["metadata"]["brand"]) + result.extend(super()._sortKey(item)) + return result From ac97d0d83f41717088b565c2b9f52bc9f323e4fb Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Mon, 4 Dec 2017 13:23:30 +0100 Subject: [PATCH 56/73] Sort materials by brand, material, name, color_name. Contributes to issue CURA-4243. --- cura/Settings/MaterialsModel.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cura/Settings/MaterialsModel.py b/cura/Settings/MaterialsModel.py index e3d56c2f86..36f7805281 100644 --- a/cura/Settings/MaterialsModel.py +++ b/cura/Settings/MaterialsModel.py @@ -29,5 +29,8 @@ class MaterialsModel(InstanceContainersModel): def _sortKey(self, item) -> List[Any]: result = [] result.append(item["metadata"]["brand"]) + result.append(item["metadata"]["material"]) + result.append(item["metadata"]["name"]) + result.append(item["metadata"]["color_name"]) result.extend(super()._sortKey(item)) return result From c52451217b72e5aa93deebeefcea9740897aeeaf Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Mon, 4 Dec 2017 16:29:21 +0100 Subject: [PATCH 57/73] Use timer instead of calling _update directly. Speeds up when the signal is called very often in a small period. Contributes to issue CURA-4243. --- cura/Settings/MaterialsModel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Settings/MaterialsModel.py b/cura/Settings/MaterialsModel.py index 36f7805281..1b9b015108 100644 --- a/cura/Settings/MaterialsModel.py +++ b/cura/Settings/MaterialsModel.py @@ -19,7 +19,7 @@ class MaterialsModel(InstanceContainersModel): # \param container The container whose metadata was changed. def _onContainerMetaDataChanged(self, container): if container.getMetaDataEntry("type") == "material": #Only need to update if a material was changed. - self._update() + self._container_change_timer.start() def _onContainerChanged(self, container): if container.getMetaDataEntry("type", "") == "material": From 90e8256f466ee2875bb28b7fe39a8796bb618f31 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Mon, 4 Dec 2017 16:30:21 +0100 Subject: [PATCH 58/73] Fix that renaming a custom material also updates the selected material. The namechanged signal was not connected at startup, so the update would only work if you change material first before renaming. Contributes to CURA-4243. --- cura/CuraApplication.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index a395329552..c051e429e2 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -728,6 +728,8 @@ class CuraApplication(QtApplication): def getMachineManager(self, *args): if self._machine_manager is None: self._machine_manager = MachineManager.createMachineManager() + # explicitly sets current material and set internal state: also fixes problem with material namechange signal + self._machine_manager.setActiveMaterial(self._machine_manager.activeMaterialId) return self._machine_manager def getExtruderManager(self, *args): From 3b251919497e77e5dc4dfd5956603973d30bc1cc Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Mon, 4 Dec 2017 17:00:43 +0100 Subject: [PATCH 59/73] Undo a fix that causes Cura to crash when starting up clean. Contributes to issue CURA-4243. --- cura/CuraApplication.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index c051e429e2..a395329552 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -728,8 +728,6 @@ class CuraApplication(QtApplication): def getMachineManager(self, *args): if self._machine_manager is None: self._machine_manager = MachineManager.createMachineManager() - # explicitly sets current material and set internal state: also fixes problem with material namechange signal - self._machine_manager.setActiveMaterial(self._machine_manager.activeMaterialId) return self._machine_manager def getExtruderManager(self, *args): From 41cde7c7f4520d2840f5c64e6868fba03324e04a Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Mon, 4 Dec 2017 17:07:37 +0100 Subject: [PATCH 60/73] Fix connecting material name change signal on startup. Contributes to CURA-4243. --- cura/Settings/MachineManager.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 9a3f529355..1d73e5e261 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -117,6 +117,10 @@ class MachineManager(QObject): "The selected material is incompatible with the selected machine or configuration."), title = catalog.i18nc("@info:title", "Incompatible Material")) + containers = ContainerRegistry.getInstance().findInstanceContainers(id = self.activeMaterialId) + if containers: + containers[0].nameChanged.connect(self._onMaterialNameChanged) + globalContainerChanged = pyqtSignal() # Emitted whenever the global stack is changed (ie: when changing between printers, changing a global profile, but not when changing a value) activeMaterialChanged = pyqtSignal() activeVariantChanged = pyqtSignal() From ce78bddfbb4e0130df01124253d5e9ff3e013955 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Tue, 5 Dec 2017 09:51:57 +0100 Subject: [PATCH 61/73] Prefer a read-only material in findDefaultMaterial. Contributes to CURA-4243. --- cura/Settings/CuraContainerStack.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/cura/Settings/CuraContainerStack.py b/cura/Settings/CuraContainerStack.py index bf001c7f08..9e30e3dd66 100755 --- a/cura/Settings/CuraContainerStack.py +++ b/cura/Settings/CuraContainerStack.py @@ -487,11 +487,17 @@ class CuraContainerStack(ContainerStack): search_criteria.pop("name", None) materials = ContainerRegistry.getInstance().findInstanceContainers(**search_criteria) - if materials: - return materials[0] + if not materials: + Logger.log("w", "Could not find a valid material for stack {stack}", stack = self.id) + return None + + for material in materials: + # Prefer a read-only material + if ContainerRegistry.getInstance().isReadOnly(material.getId()): + return material + + return materials[0] - Logger.log("w", "Could not find a valid material for stack {stack}", stack = self.id) - return None ## Find the quality that should be used as "default" quality. # From 7d24933835b073b63ed3d50431f153c3a81ce9f5 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Tue, 5 Dec 2017 11:55:54 +0100 Subject: [PATCH 62/73] Fix save to file. Changed order of setting metadata for correct overwriting. Contributes to issue CURA-4243. --- plugins/GCodeWriter/GCodeWriter.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/GCodeWriter/GCodeWriter.py b/plugins/GCodeWriter/GCodeWriter.py index 1e4fb666b7..c992a548de 100644 --- a/plugins/GCodeWriter/GCodeWriter.py +++ b/plugins/GCodeWriter/GCodeWriter.py @@ -74,11 +74,14 @@ class GCodeWriter(MeshWriter): ## Create a new container with container 2 as base and container 1 written over it. def _createFlattenedContainerInstance(self, instance_container1, instance_container2): flat_container = InstanceContainer(instance_container2.getName()) + + # The metadata includes id, name and definition + flat_container.setMetaData(copy.deepcopy(instance_container2.getMetaData())) + if instance_container1.getDefinition(): flat_container.setDefinition(instance_container1.getDefinition().getId()) else: flat_container.setDefinition(instance_container2.getDefinition().getId()) - flat_container.setMetaData(copy.deepcopy(instance_container2.getMetaData())) for key in instance_container2.getAllKeys(): flat_container.setProperty(key, "value", instance_container2.getProperty(key, "value")) From 3e2e1336823f85a3f6cb3b23c10b898153cafb88 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 6 Dec 2017 10:37:06 +0100 Subject: [PATCH 63/73] Add type hinting for getMachineManager I like it when my IDE can give me hints, so return types are important. Contributes to issue CURA-4243. --- cura/CuraApplication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index a395329552..9293a7e07b 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -725,7 +725,7 @@ class CuraApplication(QtApplication): self.exec_() - def getMachineManager(self, *args): + def getMachineManager(self, *args) -> MachineManager: if self._machine_manager is None: self._machine_manager = MachineManager.createMachineManager() return self._machine_manager From 80f4c9181d87c39cb7417ff32d05bf4cbbcf4701 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 6 Dec 2017 10:40:58 +0100 Subject: [PATCH 64/73] Correct variant name when duplicating materials It needs to be the variant name (swapping spaces for underscores) to be consistent with the deserialize functions. Contributes to issue CURA-4243. --- cura/Settings/ContainerManager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index c552a6fe8c..e3b14bf210 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -789,7 +789,8 @@ class ContainerManager(QObject): if container_to_copy.getMetaDataEntry("definition") != "fdmprinter": new_id += "_" + container_to_copy.getMetaDataEntry("definition") if container_to_copy.getMetaDataEntry("variant"): - new_id += "_" + container_to_copy.getMetaDataEntry("variant") + variant = self._container_registry.findContainers(id = container_to_copy.getMetaDataEntry("variant"))[0] + new_id += "_" + variant.getName().replace(" ", "_") if current_id == material_id: clone_of_original = new_id From ec36424a4d2a3ea6721e741f33404d5a824a28b4 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 6 Dec 2017 10:42:02 +0100 Subject: [PATCH 65/73] Update UserProfilesModel when metadata of materials changes The metadata may influence which profiles are being shown. Contributes to issue CURA-4243. --- cura/Settings/UserProfilesModel.py | 37 +++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/cura/Settings/UserProfilesModel.py b/cura/Settings/UserProfilesModel.py index 2db07942a7..f69c0ff7d1 100644 --- a/cura/Settings/UserProfilesModel.py +++ b/cura/Settings/UserProfilesModel.py @@ -1,7 +1,7 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from UM.Application import Application +from UM.Application import Application from cura.QualityManager import QualityManager from cura.Settings.ProfilesModel import ProfilesModel from cura.Settings.ExtruderManager import ExtruderManager @@ -12,6 +12,16 @@ class UserProfilesModel(ProfilesModel): def __init__(self, parent = None): super().__init__(parent) + #Need to connect to the metaDataChanged signal of the active materials. + self.__current_extruders = [] + self.__current_materials = [] + + Application.getInstance().getExtruderManager().extrudersChanged.connect(self.__onExtrudersChanged) + self.__onExtrudersChanged() + self.__current_materials = [extruder.material for extruder in self.__current_extruders] + for material in self.__current_materials: + material.metaDataChanged.connect(self._onContainerChanged) + ## Fetch the list of containers to display. # # See UM.Settings.Models.InstanceContainersModel._fetchInstanceContainers(). @@ -43,3 +53,28 @@ class UserProfilesModel(ProfilesModel): qc.getMetaDataEntry("extruder") == active_extruder.definition.getId())} return filtered_quality_changes, {} + + ## Called when a container changed on an extruder stack. + # + # If it's the material we need to connect to the metaDataChanged signal of + # that. + def __onContainerChanged(self, new_container): + #Careful not to update when a quality or quality changes profile changed! + #If you then update you're going to have an infinite recursion because the update may change the container. + if new_container.getMetaDataEntry("type") == "material": + for material in self.__current_materials: + material.metaDataChanged.disconnect(self._onContainerChanged) + self.__current_materials = [extruder.material for extruder in self.__current_extruders] + for material in self.__current_materials: + material.metaDataChanged.connect(self._onContainerChanged) + + ## Called when the current set of extruders change. + # + # This makes sure that we are listening to the signal for when the + # materials change. + def __onExtrudersChanged(self): + for extruder in self.__current_extruders: + extruder.containersChanged.disconnect(self.__onContainerChanged) + self.__current_extruders = Application.getInstance().getExtruderManager().getExtruderStacks() + for extruder in self.__current_extruders: + extruder.containersChanged.connect(self.__onContainerChanged) \ No newline at end of file From eb171231c21426d2f6e927fe6e780c934e1f6e46 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 6 Dec 2017 11:02:50 +0100 Subject: [PATCH 66/73] Fix removeMaterial. The base_file must be removed first, or it gets loaded halfway during remove containers. Contributes to issue CURA-4243. --- cura/Settings/ContainerManager.py | 2 +- cura/Settings/MaterialsModel.py | 1 + resources/qml/Preferences/MaterialsPage.qml | 9 ++++++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index c552a6fe8c..2c98030cfd 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -118,7 +118,7 @@ class ContainerManager(QObject): def removeContainer(self, container_id): containers = self._container_registry.findContainers(id = container_id) if not containers: - Logger.log("w", "Could remove container %s because it was not found.", container_id) + Logger.log("w", "Could not remove container %s because it was not found.", container_id) return False self._container_registry.removeContainer(containers[0].getId()) diff --git a/cura/Settings/MaterialsModel.py b/cura/Settings/MaterialsModel.py index 1b9b015108..c4b0329336 100644 --- a/cura/Settings/MaterialsModel.py +++ b/cura/Settings/MaterialsModel.py @@ -32,5 +32,6 @@ class MaterialsModel(InstanceContainersModel): result.append(item["metadata"]["material"]) result.append(item["metadata"]["name"]) result.append(item["metadata"]["color_name"]) + result.append(item["metadata"]["id"]) result.extend(super()._sortKey(item)) return result diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index cd04b79b20..c33cdbfc89 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -182,6 +182,7 @@ UM.ManagementPage { Cura.MachineManager.setActiveMaterial(material_id) } + // TODO: this doesn't work because the source is a bit delayed base.objectList.currentIndex = base.getIndexById(material_id); } }, @@ -292,10 +293,16 @@ UM.ManagementPage base_file = base.currentItem.id } var guid = Cura.ContainerManager.getContainerMetaDataEntry(base.currentItem.id, "GUID") + // remove base container first, it otherwise triggers loading the base file while removing other containers + var base_containers = Cura.ContainerManager.findInstanceContainers({"GUID": guid, "id": base_file, "base_file": base_file, "type": "material"}) + for(var i in base_containers) + { + Cura.ContainerManager.removeContainer(base_containers[i]); + } var containers = Cura.ContainerManager.findInstanceContainers({"GUID": guid, "base_file": base_file, "type": "material"}) for(var i in containers) { - Cura.ContainerManager.removeContainer(containers[i]) + Cura.ContainerManager.removeContainer(containers[i]); } if(base.objectList.currentIndex > 0) { From f0c3aaf532ccd228ba28304f9e5f88f0c7d2389d Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 6 Dec 2017 14:05:09 +0100 Subject: [PATCH 67/73] Add built-in profiles to profiles screen. Contributes to issue CURA-4243. --- cura/Settings/QualityAndUserProfilesModel.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cura/Settings/QualityAndUserProfilesModel.py b/cura/Settings/QualityAndUserProfilesModel.py index 52333c3b6e..8d47f1d4ab 100644 --- a/cura/Settings/QualityAndUserProfilesModel.py +++ b/cura/Settings/QualityAndUserProfilesModel.py @@ -40,4 +40,7 @@ class QualityAndUserProfilesModel(ProfilesModel): qc.getMetaDataEntry("extruder") is not None and (qc.getMetaDataEntry("extruder") == active_extruder.definition.getMetaDataEntry("quality_definition") or qc.getMetaDataEntry("extruder") == active_extruder.definition.getId())} - return filtered_quality_changes, {} + + result = filtered_quality_changes + result.update({q.getId():q for q in quality_list}) + return result, {} From 4245847dabe03c1a8824db8f5acc9f0081498a87 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 6 Dec 2017 16:39:59 +0100 Subject: [PATCH 68/73] Moved saveSettings and saveStack to Uraniums ContainerRegistry. Contributes to issue CURA-4342. --- cura/CuraApplication.py | 74 +++++------------------------------------ 1 file changed, 9 insertions(+), 65 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 9293a7e07b..b02903dc98 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -171,13 +171,13 @@ class CuraApplication(QtApplication): Resources.addStorageType(self.ResourceTypes.MachineStack, "machine_instances") Resources.addStorageType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes") - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer) - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer) - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer) - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer) - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.ExtruderStack) - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MachineStack) - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.DefinitionChangesContainer) + ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality") + ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer, "variant") + ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer, "material") + ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer, "user") + ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.ExtruderStack, "extruder_train") + ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MachineStack, "machine") + ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes") ## Initialise the version upgrade manager with Cura's storage paths. import UM.VersionUpgradeManager #Needs to be here to prevent circular dependencies. @@ -465,66 +465,10 @@ class CuraApplication(QtApplication): if not self._started: # Do not do saving during application start return - # Lock file for "more" atomically loading and saving to/from config dir. - with ContainerRegistry.getInstance().lockFile(): - for instance in ContainerRegistry.getInstance().findDirtyContainers(container_type = InstanceContainer): - try: - data = instance.serialize() - except NotImplementedError: - continue - except Exception: - Logger.logException("e", "An exception occurred when serializing container %s", instance.getId()) - continue - - mime_type = ContainerRegistry.getMimeTypeForContainer(type(instance)) - file_name = urllib.parse.quote_plus(instance.getId()) + "." + mime_type.preferredSuffix - instance_type = instance.getMetaDataEntry("type") - path = None - if instance_type == "material": - path = Resources.getStoragePath(self.ResourceTypes.MaterialInstanceContainer, file_name) - elif instance_type == "quality" or instance_type == "quality_changes": - path = Resources.getStoragePath(self.ResourceTypes.QualityInstanceContainer, file_name) - elif instance_type == "user": - path = Resources.getStoragePath(self.ResourceTypes.UserInstanceContainer, file_name) - elif instance_type == "variant": - path = Resources.getStoragePath(self.ResourceTypes.VariantInstanceContainer, file_name) - elif instance_type == "definition_changes": - path = Resources.getStoragePath(self.ResourceTypes.DefinitionChangesContainer, file_name) - - if path: - instance.setPath(path) - with SaveFile(path, "wt") as f: - f.write(data) - - for stack in ContainerRegistry.getInstance().findContainerStacks(): - self.saveStack(stack) + ContainerRegistry.getInstance().saveDirtyContainers() def saveStack(self, stack): - if not stack.isDirty(): - return - try: - data = stack.serialize() - except NotImplementedError: - return - except Exception: - Logger.logException("e", "An exception occurred when serializing container %s", stack.getId()) - return - - mime_type = ContainerRegistry.getMimeTypeForContainer(type(stack)) - file_name = urllib.parse.quote_plus(stack.getId()) + "." + mime_type.preferredSuffix - - path = None - if isinstance(stack, GlobalStack): - path = Resources.getStoragePath(self.ResourceTypes.MachineStack, file_name) - elif isinstance(stack, ExtruderStack): - path = Resources.getStoragePath(self.ResourceTypes.ExtruderStack, file_name) - else: - path = Resources.getStoragePath(Resources.ContainerStacks, file_name) - - stack.setPath(path) - with SaveFile(path, "wt") as f: - f.write(data) - + ContainerRegistry.getInstance().saveStack(stack) @pyqtSlot(str, result = QUrl) def getDefaultPath(self, key): From a387c10686f347e4431e99977a887864550fa182 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Thu, 7 Dec 2017 09:59:00 +0100 Subject: [PATCH 69/73] Fix serialize hotend id in xml material. Contributes to issue CURA-4243. --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 940f88773f..c4912826ee 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -238,7 +238,8 @@ class XmlMaterialProfile(InstanceContainer): if not variant_containers: continue - builder.start("hotend", {"id": variant_containers[0]["id"]}) + # The hotend identifier is not the containers name, but its "name". + builder.start("hotend", {"id": variant_containers[0]["name"]}) # Compatible is a special case, as it's added as a meta data entry (instead of an instance). compatible = hotend.getMetaDataEntry("compatible") @@ -897,4 +898,4 @@ def _fillMissingVariants(): Logger.log("w", "Could not find variant for variant-specific material {material_id}.".format(material_id = variant_metadata["id"])) continue variant_metadata["variant"] = variants[0]["id"] -ContainerRegistry.allMetadataLoaded.connect(_fillMissingVariants) \ No newline at end of file +ContainerRegistry.allMetadataLoaded.connect(_fillMissingVariants) From 79d5282b36da1c5ae8ebf2b8d92d5e4cd2789f10 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Thu, 7 Dec 2017 10:39:13 +0100 Subject: [PATCH 70/73] Added quality_changes to ContainerRegistry, rename saveStack to saveContainer. Contributes to issue CURA-4243. --- cura/CuraApplication.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index b02903dc98..692fb14af8 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -172,6 +172,7 @@ class CuraApplication(QtApplication): Resources.addStorageType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes") ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality") + ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality_changes") ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer, "variant") ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer, "material") ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer, "user") @@ -468,7 +469,7 @@ class CuraApplication(QtApplication): ContainerRegistry.getInstance().saveDirtyContainers() def saveStack(self, stack): - ContainerRegistry.getInstance().saveStack(stack) + ContainerRegistry.getInstance().saveContainer(stack) @pyqtSlot(str, result = QUrl) def getDefaultPath(self, key): From e621ace3e49b721cea9870fae67e5ff618779092 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 8 Dec 2017 12:42:19 +0100 Subject: [PATCH 71/73] Remove unnecessarily setting definition This definition is included in the metadata of container 2. Contributes to issue CURA-4243. --- plugins/GCodeWriter/GCodeWriter.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/GCodeWriter/GCodeWriter.py b/plugins/GCodeWriter/GCodeWriter.py index c992a548de..f30e2dd43a 100644 --- a/plugins/GCodeWriter/GCodeWriter.py +++ b/plugins/GCodeWriter/GCodeWriter.py @@ -80,8 +80,6 @@ class GCodeWriter(MeshWriter): if instance_container1.getDefinition(): flat_container.setDefinition(instance_container1.getDefinition().getId()) - else: - flat_container.setDefinition(instance_container2.getDefinition().getId()) for key in instance_container2.getAllKeys(): flat_container.setProperty(key, "value", instance_container2.getProperty(key, "value")) From 430f3dad27b2ad6e5d1eeddd258c045ef585fa08 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 8 Dec 2017 16:57:48 +0100 Subject: [PATCH 72/73] Use GlobalContainerStack.definition instead of .getBottom If we know it's a CuraContainerStack we can use .definition safely. Contributes to issue CURA-4243. --- cura/Settings/ContainerManager.py | 4 ++-- cura/Settings/MachineManager.py | 30 +++++++++++++----------------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index 9108f563ab..bbabdadbde 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -324,8 +324,8 @@ class ContainerManager(QObject): ## Find instance containers matching certain criteria. # - # This effectively forwards to - # ContainerRegistry::findInstanceContainersMetadata. + # This effectively forwards to + # ContainerRegistry::findInstanceContainersMetadata. # # \param criteria A dict of key - value pairs to search for. # diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 2e6fc8d07a..16d42ce653 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -171,7 +171,7 @@ class MachineManager(QObject): if not self._global_container_stack: return - containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(type = "variant", definition = self._global_container_stack.getBottom().getId(), name = hotend_id) + containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(type = "variant", definition = self._global_container_stack.definition.getId(), name = hotend_id) if containers: # New material ID is known extruder_manager = ExtruderManager.getInstance() machine_id = self.activeMachineId @@ -186,7 +186,7 @@ class MachineManager(QObject): self._auto_hotends_changed[str(index)] = containers[0]["id"] self._printer_output_devices[0].materialHotendChangedMessage(self._materialHotendChangedCallback) else: - Logger.log("w", "No variant found for printer definition %s with id %s" % (self._global_container_stack.getBottom().getId(), hotend_id)) + Logger.log("w", "No variant found for printer definition %s with id %s" % (self._global_container_stack.definition.getId(), hotend_id)) def _onMaterialIdChanged(self, index: Union[str, int], material_id: str): if not self._global_container_stack: @@ -207,8 +207,8 @@ class MachineManager(QObject): if matching_extruder and matching_extruder.material.getMetaDataEntry("GUID") != material_id: # Save the material that needs to be changed. Multiple changes will be handled by the callback. - if self._global_container_stack.getBottom().getMetaDataEntry("has_variants") and matching_extruder.variant: - variant_id = self.getQualityVariantId(self._global_container_stack.getBottom(), matching_extruder.variant) + if self._global_container_stack.definition.getMetaDataEntry("has_variants") and matching_extruder.variant: + variant_id = self.getQualityVariantId(self._global_container_stack.definition, matching_extruder.variant) for container in containers: if container.get("variant") == variant_id: self._auto_materials_changed[str(index)] = container["id"] @@ -824,7 +824,7 @@ class MachineManager(QObject): preferred_material_name = None if old_material: preferred_material_name = old_material.getName() - preferred_material_id = self._updateMaterialContainer(self._global_container_stack.getBottom(), self._global_container_stack, containers[0], preferred_material_name).id + preferred_material_id = self._updateMaterialContainer(self._global_container_stack.definition, self._global_container_stack, containers[0], preferred_material_name).id self.setActiveMaterial(preferred_material_id) else: Logger.log("w", "While trying to set the active variant, no variant was found to replace.") @@ -941,7 +941,7 @@ class MachineManager(QObject): if not global_container_stack: return [] - global_machine_definition = quality_manager.getParentMachineDefinition(global_container_stack.getBottom()) + global_machine_definition = quality_manager.getParentMachineDefinition(global_container_stack.definition) extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks() # find qualities for extruders @@ -1093,18 +1093,14 @@ class MachineManager(QObject): @pyqtProperty(str, notify = globalContainerChanged) def activeDefinitionId(self) -> str: if self._global_container_stack: - definition = self._global_container_stack.getBottom() - if definition: - return definition.id + return self._global_container_stack.definition.id return "" @pyqtProperty(str, notify=globalContainerChanged) def activeDefinitionName(self) -> str: if self._global_container_stack: - definition = self._global_container_stack.getBottom() - if definition: - return definition.getName() + return self._global_container_stack.definition.getName() return "" @@ -1114,7 +1110,7 @@ class MachineManager(QObject): @pyqtProperty(str, notify = globalContainerChanged) def activeQualityDefinitionId(self) -> str: if self._global_container_stack: - return self.getQualityDefinitionId(self._global_container_stack.getBottom()) + return self.getQualityDefinitionId(self._global_container_stack.definition) return "" ## Get the Definition ID to use to select quality profiles for machines of the specified definition @@ -1132,7 +1128,7 @@ class MachineManager(QObject): if self._active_container_stack: variant = self._active_container_stack.variant if variant: - return self.getQualityVariantId(self._global_container_stack.getBottom(), variant) + return self.getQualityVariantId(self._global_container_stack.definition, variant) return "" ## Get the Variant ID to use to select quality profiles for variants of the specified definitions @@ -1156,7 +1152,7 @@ class MachineManager(QObject): def activeDefinitionVariantsName(self) -> str: fallback_title = catalog.i18nc("@label", "Nozzle") if self._global_container_stack: - return self._global_container_stack.getBottom().getMetaDataEntry("variants_name", fallback_title) + return self._global_container_stack.definition.getMetaDataEntry("variants_name", fallback_title) return fallback_title @@ -1165,7 +1161,7 @@ class MachineManager(QObject): container_registry = ContainerRegistry.getInstance() machine_stack = container_registry.findContainerStacks(id = machine_id) if machine_stack: - new_name = container_registry.createUniqueName("machine", machine_stack[0].getName(), new_name, machine_stack[0].getBottom().getName()) + new_name = container_registry.createUniqueName("machine", machine_stack[0].getName(), new_name, machine_stack[0].definition.getName()) machine_stack[0].setName(new_name) self.globalContainerChanged.emit() @@ -1223,7 +1219,7 @@ class MachineManager(QObject): def getDefinitionByMachineId(self, machine_id: str) -> str: containers = ContainerRegistry.getInstance().findContainerStacks(id = machine_id) if containers: - return containers[0].getBottom().getId() + return containers[0].definition.getId() @staticmethod def createMachineManager(): From 097cc72d89882b0bb05fb2627f91f5d36dbf7042 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 11 Dec 2017 09:44:56 +0100 Subject: [PATCH 73/73] Document why we're only returning full profiles It's a limitation of the quality manager that would require major refactoring there to fix. Contributes to issue CURA-4243. --- cura/Settings/ProfilesModel.py | 2 +- cura/Settings/QualityAndUserProfilesModel.py | 2 +- cura/Settings/UserProfilesModel.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/Settings/ProfilesModel.py b/cura/Settings/ProfilesModel.py index b32776f465..849d6959b9 100644 --- a/cura/Settings/ProfilesModel.py +++ b/cura/Settings/ProfilesModel.py @@ -87,7 +87,7 @@ class ProfilesModel(InstanceContainersModel): not_supported_container = ContainerRegistry.getInstance().findContainers(id = "empty_quality")[0] result.append(not_supported_container) - return {item.getId():item for item in result}, {} + return {item.getId():item for item in result}, {} #Only return true profiles for now, no metadata. The quality manager is not able to get only metadata yet. ## Re-computes the items in this model, and adds the layer height role. def _recomputeItems(self): diff --git a/cura/Settings/QualityAndUserProfilesModel.py b/cura/Settings/QualityAndUserProfilesModel.py index 8d47f1d4ab..bc81df976b 100644 --- a/cura/Settings/QualityAndUserProfilesModel.py +++ b/cura/Settings/QualityAndUserProfilesModel.py @@ -43,4 +43,4 @@ class QualityAndUserProfilesModel(ProfilesModel): result = filtered_quality_changes result.update({q.getId():q for q in quality_list}) - return result, {} + return result, {} #Only return true profiles for now, no metadata. The quality manager is not able to get only metadata yet. \ No newline at end of file diff --git a/cura/Settings/UserProfilesModel.py b/cura/Settings/UserProfilesModel.py index f69c0ff7d1..e093c6c132 100644 --- a/cura/Settings/UserProfilesModel.py +++ b/cura/Settings/UserProfilesModel.py @@ -52,7 +52,7 @@ class UserProfilesModel(ProfilesModel): (qc.getMetaDataEntry("extruder") == active_extruder.definition.getMetaDataEntry("quality_definition") or qc.getMetaDataEntry("extruder") == active_extruder.definition.getId())} - return filtered_quality_changes, {} + return filtered_quality_changes, {} #Only return true profiles for now, no metadata. The quality manager is not able to get only metadata yet. ## Called when a container changed on an extruder stack. #