From 774511370466667553ad06514935d25d3bc0a113 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 19 Oct 2017 15:32:21 +0200 Subject: [PATCH 001/104] 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 002/104] 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 003/104] 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 004/104] 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 005/104] 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 006/104] 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 007/104] 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 008/104] 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 009/104] 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 010/104] 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 011/104] 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 012/104] 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 013/104] 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 014/104] 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 015/104] 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 016/104] 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 017/104] 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 018/104] 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 019/104] 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 020/104] 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 021/104] 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 022/104] 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 023/104] 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 024/104] 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 025/104] 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 026/104] 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 027/104] 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 028/104] 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 029/104] 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 030/104] 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 031/104] 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 032/104] 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 033/104] 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 034/104] 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 035/104] 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 036/104] 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 037/104] 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 038/104] 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 039/104] 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 040/104] 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 041/104] 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 042/104] 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 043/104] 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 044/104] 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 045/104] 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 046/104] 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 047/104] 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 048/104] 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 049/104] 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 050/104] 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 051/104] 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 052/104] 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 053/104] 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 054/104] 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 055/104] 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 0d9f49413e72c116f21969d970fe3a1875db9a4b Mon Sep 17 00:00:00 2001 From: Andrew Donaldson Date: Mon, 4 Dec 2017 21:16:30 +1100 Subject: [PATCH 056/104] Add Fedora to test for nvidia driver work around. --- cura_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura_app.py b/cura_app.py index d725bc1200..313b161a7d 100755 --- a/cura_app.py +++ b/cura_app.py @@ -12,7 +12,7 @@ from UM.Platform import Platform #WORKAROUND: GITHUB-88 GITHUB-385 GITHUB-612 if Platform.isLinux(): # Needed for platform.linux_distribution, which is not available on Windows and OSX # For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826 - if platform.linux_distribution()[0] in ("debian", "Ubuntu", "LinuxMint"): # TODO: Needs a "if X11_GFX == 'nvidia'" here. The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix. + if platform.linux_distribution()[0] in ("debian", "Ubuntu", "LinuxMint", "Fedora"): # TODO: Needs a "if X11_GFX == 'nvidia'" here. The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix. import ctypes from ctypes.util import find_library libGL = find_library("GL") From ac97d0d83f41717088b565c2b9f52bc9f323e4fb Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Mon, 4 Dec 2017 13:23:30 +0100 Subject: [PATCH 057/104] 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 058/104] 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 059/104] 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 060/104] 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 061/104] 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 062/104] 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 063/104] 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 064/104] 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 065/104] 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 066/104] 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 067/104] 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 068/104] 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 069/104] 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 9d2529c6d4f0ec06e11fcd533f80daf97c796155 Mon Sep 17 00:00:00 2001 From: gitname Date: Thu, 7 Dec 2017 00:54:20 -0800 Subject: [PATCH 070/104] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ba6a986093..90f17a5463 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Cura This is the new, shiny frontend for Cura. [daid/Cura](https://github.com/daid/Cura.git) is the old legacy Cura that everyone knows and loves/hates. -We re-worked the whole GUI code at Ultimaker, because the old code started to become a unmaintainable. +We re-worked the whole GUI code at Ultimaker, because the old code started to become unmaintainable. Logging Issues From a387c10686f347e4431e99977a887864550fa182 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Thu, 7 Dec 2017 09:59:00 +0100 Subject: [PATCH 071/104] 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 072/104] 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 21fffcc099f527d74f4290677a49deca6c6a861c Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 8 Dec 2017 10:40:20 +0100 Subject: [PATCH 073/104] Compare linux distro names in lower cases --- cura_app.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cura_app.py b/cura_app.py index c6fcb5cf5b..7583dd054e 100755 --- a/cura_app.py +++ b/cura_app.py @@ -12,7 +12,8 @@ from UM.Platform import Platform #WORKAROUND: GITHUB-88 GITHUB-385 GITHUB-612 if Platform.isLinux(): # Needed for platform.linux_distribution, which is not available on Windows and OSX # For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826 - if platform.linux_distribution()[0] in ("debian", "Ubuntu", "LinuxMint", "Fedora"): # TODO: Needs a "if X11_GFX == 'nvidia'" here. The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix. + linux_distro_name = platform.linux_distribution()[0].lower() + if linux_distro_name in ("debian", "ubuntu", "linuxmint", "fedora"): # TODO: Needs a "if X11_GFX == 'nvidia'" here. The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix. import ctypes from ctypes.util import find_library libGL = find_library("GL") From b4e7216f5b4744bb36093df12c37b10d4f9a60a6 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 8 Dec 2017 11:08:24 +0100 Subject: [PATCH 074/104] Always show extruder tabs in Machine Settings dialog CURA-4693 --- .../MachineSettingsAction/MachineSettingsAction.qml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.qml b/plugins/MachineSettingsAction/MachineSettingsAction.qml index f50dd9d206..b36fb989f0 100644 --- a/plugins/MachineSettingsAction/MachineSettingsAction.qml +++ b/plugins/MachineSettingsAction/MachineSettingsAction.qml @@ -22,7 +22,7 @@ Cura.MachineAction onModelChanged: { var extruderCount = base.extrudersModel.rowCount(); - base.extruderTabsCount = extruderCount > 1 ? extruderCount : 0; + base.extruderTabsCount = extruderCount; } } @@ -241,7 +241,6 @@ Cura.MachineAction UM.TooltipArea { - visible: manager.definedExtruderCount > 1 height: childrenRect.height width: childrenRect.width text: machineExtruderCountProvider.properties.description @@ -291,15 +290,6 @@ Cura.MachineAction property var afterOnEditingFinished: manager.updateMaterialForDiameter property string label: catalog.i18nc("@label", "Material diameter") } - Loader - { - id: nozzleSizeField - visible: !Cura.MachineManager.hasVariants && machineExtruderCountProvider.properties.value == 1 - sourceComponent: numericTextFieldWithUnit - property string settingKey: "machine_nozzle_size" - property string label: catalog.i18nc("@label", "Nozzle size") - property string unit: catalog.i18nc("@label", "mm") - } } } From e621ace3e49b721cea9870fae67e5ff618779092 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 8 Dec 2017 12:42:19 +0100 Subject: [PATCH 075/104] 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 8cd943949792152796859b55b84b56afeb2fc68f Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 8 Dec 2017 14:33:21 +0100 Subject: [PATCH 076/104] ignore octoprint plugin --- .gitignore | 1 + resources/qml/MonitorButton.qml | 2 +- resources/qml/SaveButton.qml | 22 +++++++++++----------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 7284597fa9..8d9ba4b2d2 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ plugins/cura-siemensnx-plugin plugins/CuraVariSlicePlugin plugins/CuraLiveScriptingPlugin plugins/CuraPrintProfileCreator +plugins/OctoPrintPlugin #Build stuff CMakeCache.txt diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index 29b00f50e6..87eceb69cd 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -18,7 +18,6 @@ Item property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands property real progress: printerConnected ? Cura.MachineManager.printerOutputDevices[0].progress : 0 - property int backendState: UM.Backend.state property bool showProgress: { // determine if we need to show the progress bar + percentage @@ -203,6 +202,7 @@ Item target: Printer onAdditionalComponentsChanged: { + print("areaId", areaId) if(areaId == "monitorButtons") { for (var component in CuraApplication.additionalComponents["monitorButtons"]) { CuraApplication.additionalComponents["monitorButtons"][component].parent = additionalComponentsRow diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 390b868a9b..25bc10d122 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -28,6 +28,10 @@ Item { return catalog.i18nc("@label:PrintjobStatus", "Please load a 3D model"); } + if (base.backendState == "undefined") { + return "" + } + switch(base.backendState) { case 1: @@ -81,7 +85,7 @@ Item { height: parent.height color: UM.Theme.getColor("progressbar_control") radius: UM.Theme.getSize("progressbar_radius").width - visible: base.backendState == 2 ? true : false + visible: (base.backendState != "undefined" && base.backendState == 2) ? true : false } } @@ -159,10 +163,8 @@ Item { tooltip: [1, 5].indexOf(UM.Backend.state) != -1 ? catalog.i18nc("@info:tooltip","Slice current printjob") : catalog.i18nc("@info:tooltip","Cancel slicing process") // 1 = not started, 2 = Processing - enabled: (base.backendState == 1 || base.backendState == 2) && base.activity == true - visible: { - return !autoSlice && (base.backendState == 1 || base.backendState == 2) && base.activity == true; - } + enabled: base.backendState != "undefined" && (base.backendState == 1 || base.backendState == 2) && base.activity == true + visible: base.backendState != "undefined" && !autoSlice && (base.backendState == 1 || base.backendState == 2) && base.activity == true property bool autoSlice height: UM.Theme.getSize("save_button_save_to_button").height @@ -235,10 +237,8 @@ Item { tooltip: UM.OutputDeviceManager.activeDeviceDescription; // 3 = done, 5 = disabled - enabled: (base.backendState == 3 || base.backendState == 5) && base.activity == true - visible: { - return autoSlice || ((base.backendState == 3 || base.backendState == 5) && base.activity == true); - } + enabled: base.backendState != "undefined" && (base.backendState == 3 || base.backendState == 5) && base.activity == true + visible: base.backendState != "undefined" && autoSlice || ((base.backendState == 3 || base.backendState == 5) && base.activity == true) property bool autoSlice height: UM.Theme.getSize("save_button_save_to_button").height @@ -315,8 +315,8 @@ Item { width: UM.Theme.getSize("save_button_save_to_button").height height: UM.Theme.getSize("save_button_save_to_button").height // 3 = Done, 5 = Disabled - enabled: (base.backendState == 3 || base.backendState == 5) && base.activity == true - visible: (devicesModel.deviceCount > 1) && (base.backendState == 3 || base.backendState == 5) && base.activity == true + enabled: base.backendState != "undefined" && (base.backendState == 3 || base.backendState == 5) && base.activity == true + visible: base.backendState != "undefined" && (devicesModel.deviceCount > 1) && (base.backendState == 3 || base.backendState == 5) && base.activity == true style: ButtonStyle { From c4d7a33c31463c56ab0cf4247c1c3e51f4cbbb6b Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 8 Dec 2017 14:56:03 +0100 Subject: [PATCH 077/104] Monitor view component loader should have width and height from parent --- plugins/MonitorStage/MonitorMainView.qml | 7 ++++++- resources/qml/MonitorButton.qml | 1 - 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/MonitorStage/MonitorMainView.qml b/plugins/MonitorStage/MonitorMainView.qml index 038403e6d3..15b05bed0a 100644 --- a/plugins/MonitorStage/MonitorMainView.qml +++ b/plugins/MonitorStage/MonitorMainView.qml @@ -8,6 +8,9 @@ import Cura 1.0 as Cura Item { + width: parent.width + height: parent.height + // We show a nice overlay on the 3D viewer when the current output device has no monitor view Rectangle { @@ -16,7 +19,6 @@ Item color: UM.Theme.getColor("viewport_overlay") width: parent.width height: parent.height - visible: monitorViewComponent.sourceComponent == null ? 1 : 0 MouseArea { @@ -30,6 +32,9 @@ Item { id: monitorViewComponent + width: parent.width + height: parent.height + property real maximumWidth: parent.width property real maximumHeight: parent.height diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index 87eceb69cd..6454e32117 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -202,7 +202,6 @@ Item target: Printer onAdditionalComponentsChanged: { - print("areaId", areaId) if(areaId == "monitorButtons") { for (var component in CuraApplication.additionalComponents["monitorButtons"]) { CuraApplication.additionalComponents["monitorButtons"][component].parent = additionalComponentsRow From 2986a17c1d64ac25eef716a1ef83029559ef4825 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 8 Dec 2017 16:02:22 +0100 Subject: [PATCH 078/104] Fix for showing printer connect button on component load, use newer CuraApplication as target --- resources/qml/Cura.qml | 7 +++---- resources/qml/MonitorButton.qml | 19 ++++++++++++------- resources/qml/Preferences/MachinesPage.qml | 19 ++++++++++++------- resources/qml/SaveButton.qml | 19 ++++++++++++------- resources/qml/Sidebar.qml | 1 - 5 files changed, 39 insertions(+), 26 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index aedf5d0fa8..5530468c4c 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -24,7 +24,7 @@ UM.MainWindow // It should be phased out in newer plugin versions. Connections { - target: Printer + target: CuraApplication onShowPrintMonitor: { if (show) { UM.Controller.setActiveStage("MonitorStage") @@ -377,7 +377,6 @@ UM.MainWindow } width: UM.Theme.getSize("sidebar").width - z: 1 source: UM.Controller.activeStage.sidebarComponent } @@ -819,7 +818,7 @@ UM.MainWindow Connections { - target: Printer + target: CuraApplication onShowMessageBox: { messageDialog.title = title @@ -865,7 +864,7 @@ UM.MainWindow Connections { - target: Printer + target: CuraApplication onRequestAddPrinter: { addMachineDialog.visible = true diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index 6454e32117..9305e2261a 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -198,14 +198,19 @@ Item spacing: UM.Theme.getSize("default_margin").width } + Component.onCompleted: { + updateAdditionalComponents("monitorButtons") + } + Connections { - target: Printer - onAdditionalComponentsChanged: - { - if(areaId == "monitorButtons") { - for (var component in CuraApplication.additionalComponents["monitorButtons"]) { - CuraApplication.additionalComponents["monitorButtons"][component].parent = additionalComponentsRow - } + target: CuraApplication + onAdditionalComponentsChanged: updateAdditionalComponents + } + + function updateAdditionalComponents (areaId) { + if(areaId == "monitorButtons") { + for (var component in CuraApplication.additionalComponents["monitorButtons"]) { + CuraApplication.additionalComponents["monitorButtons"][component].parent = additionalComponentsRow } } } diff --git a/resources/qml/Preferences/MachinesPage.qml b/resources/qml/Preferences/MachinesPage.qml index dc978c0f7a..a8e25155e1 100644 --- a/resources/qml/Preferences/MachinesPage.qml +++ b/resources/qml/Preferences/MachinesPage.qml @@ -222,14 +222,19 @@ UM.ManagementPage } } + Component.onCompleted: { + addAdditionalComponents("machinesDetailPane") + } + Connections { - target: Printer - onAdditionalComponentsChanged: - { - if(areaId == "machinesDetailPane") { - for (var component in CuraApplication.additionalComponents["machinesDetailPane"]) { - CuraApplication.additionalComponents["machinesDetailPane"][component].parent = additionalComponentsColumn - } + target: CuraApplication + onAdditionalComponentsChanged: addAdditionalComponents + } + + function addAdditionalComponents (areaId) { + if(areaId == "machinesDetailPane") { + for (var component in CuraApplication.additionalComponents["machinesDetailPane"]) { + CuraApplication.additionalComponents["machinesDetailPane"][component].parent = additionalComponentsColumn } } } diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 25bc10d122..c83e344876 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -135,14 +135,19 @@ Item { spacing: UM.Theme.getSize("default_margin").width } + Component.onCompleted: { + addAdditionalComponents("saveButton") + } + Connections { - target: Printer - onAdditionalComponentsChanged: - { - if(areaId == "saveButton") { - for (var component in CuraApplication.additionalComponents["saveButton"]) { - CuraApplication.additionalComponents["saveButton"][component].parent = additionalComponentsRow - } + target: CuraApplication + onAdditionalComponentsChanged: addAdditionalComponents + } + + function addAdditionalComponents (areaId) { + if(areaId == "saveButton") { + for (var component in CuraApplication.additionalComponents["saveButton"]) { + CuraApplication.additionalComponents["saveButton"][component].parent = additionalComponentsRow } } } diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index a459b481db..ea66aca50f 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -549,7 +549,6 @@ Rectangle visible: monitoringPrint } - SidebarTooltip { id: tooltip; From 430f3dad27b2ad6e5d1eeddd258c045ef585fa08 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 8 Dec 2017 16:57:48 +0100 Subject: [PATCH 079/104] 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 080/104] 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. # From a5cb4cd316fcea12af92762900d5942b9d2fbdb7 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Mon, 11 Dec 2017 10:53:48 +0100 Subject: [PATCH 081/104] Expand/Collapse sidebar CURA-4234 --- resources/qml/Actions.qml | 8 ++++ resources/qml/Cura.qml | 78 +++++++++++++++++++++++++++++++++++---- resources/qml/Sidebar.qml | 67 +++++++++++++++++++++++++++++++++ resources/qml/Topbar.qml | 64 -------------------------------- 4 files changed, 146 insertions(+), 71 deletions(-) diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 054a6bed09..cf3581ca0c 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -18,6 +18,7 @@ Item property alias redo: redoAction; property alias homeCamera: homeCameraAction; + property alias expandSidebar: expandSidebarAction; property alias deleteSelection: deleteSelectionAction; property alias centerSelection: centerSelectionAction; @@ -389,4 +390,11 @@ Item text: catalog.i18nc("@action:menu", "Installed plugins..."); iconName: "plugins_configure" } + + Action + { + id: expandSidebarAction; + text: catalog.i18nc("@action:inmenu menubar:view","Expand/Collapse sidebar"); + shortcut: "Ctrl+E"; + } } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 5530468c4c..37607d237d 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -17,7 +17,7 @@ UM.MainWindow id: base //: Cura application window title title: catalog.i18nc("@title:window","Ultimaker Cura"); - viewportRect: Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0) + //viewportRect: Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0) property bool showPrintMonitor: false // This connection is here to support legacy printer output devices that use the showPrintMonitor signal on Application to switch to the monitor stage @@ -34,6 +34,24 @@ UM.MainWindow } } + onWidthChanged: + { + // If slidebar is collapsed then it should be invisible + // otherwise after the main_window resize the sidebar will be fully re-drawn + if (sidebar.collapsed){ + if (sidebar.visible == true){ + sidebar.visible = false + sidebar.initialWidth = 0 + } + } + else{ + if (sidebar.visible == false){ + sidebar.visible = true + sidebar.initialWidth = UM.Theme.getSize("sidebar").width + } + } + } + Component.onCompleted: { CuraApplication.setMinimumWindowSize(UM.Theme.getSize("window_minimum_size")) @@ -49,6 +67,15 @@ UM.MainWindow // This has been fixed for QtQuick Controls 2 since the Shortcut item has a context property. Cura.Actions.parent = backgroundItem CuraApplication.purgeWindows() + + + var sidebarCollaps = UM.Preferences.getValue("general/sidebar_collaps") + + if (sidebarCollaps == true){ + sidebar.collapsed = true; + collapsSidebarAnimation.start(); + + } } Item @@ -369,16 +396,46 @@ UM.MainWindow { id: sidebar - anchors - { - top: topbar.bottom - bottom: parent.bottom - right: parent.right + property bool collapsed: false; + property var initialWidth: UM.Theme.getSize("sidebar").width; + + function callExpandOrCollapse(){ + if(collapsed){ + sidebar.visible = true + sidebar.initialWidth = UM.Theme.getSize("sidebar").width + expandSidebarAnimation.start(); + }else{ + collapsSidebarAnimation.start(); + } + collapsed = !collapsed; + UM.Preferences.setValue("general/sidebar_collaps", collapsed); } - width: UM.Theme.getSize("sidebar").width + anchors + { + top: topbar.top + bottom: parent.bottom + } + width: initialWidth + x: base.width - sidebar.width source: UM.Controller.activeStage.sidebarComponent + + NumberAnimation { + id: collapsSidebarAnimation + target: sidebar + properties: "x" + to: base.width + duration: 500 + } + + NumberAnimation { + id: expandSidebarAnimation + target: sidebar + properties: "x" + to: base.width - sidebar.width + duration: 500 + } } Loader @@ -417,6 +474,13 @@ UM.MainWindow } } + // Expand or collapse sidebar + Connections + { + target: Cura.Actions.expandSidebar + onTriggered: sidebar.callExpandOrCollapse() + } + UM.PreferencesDialog { id: preferences diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index ea66aca50f..a45303aab8 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -87,10 +87,77 @@ Rectangle } } + ToolButton + { + id: machineSelection + text: Cura.MachineManager.activeMachineName + + width: base.width + height: UM.Theme.getSize("sidebar_header").height + tooltip: Cura.MachineManager.activeMachineName + + anchors.top: base.top + //anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + style: ButtonStyle + { + background: Rectangle + { + color: + { + if(control.pressed) + { + return UM.Theme.getColor("sidebar_header_active"); + } + else if(control.hovered) + { + return UM.Theme.getColor("sidebar_header_hover"); + } + else + { + return UM.Theme.getColor("sidebar_header_bar"); + } + } + Behavior on color { ColorAnimation { duration: 50; } } + + UM.RecolorImage + { + id: downArrow + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("default_margin").width + width: UM.Theme.getSize("standard_arrow").width + height: UM.Theme.getSize("standard_arrow").height + sourceSize.width: width + sourceSize.height: width + color: UM.Theme.getColor("text_emphasis") + source: UM.Theme.getIcon("arrow_bottom") + } + Label + { + id: sidebarComboBoxLabel + color: UM.Theme.getColor("sidebar_header_text_active") + text: control.text; + elide: Text.ElideRight; + anchors.left: parent.left; + anchors.leftMargin: UM.Theme.getSize("default_margin").width * 2 + anchors.right: downArrow.left; + anchors.rightMargin: control.rightMargin; + anchors.verticalCenter: parent.verticalCenter; + font: UM.Theme.getFont("large") + } + } + label: Label {} + } + + menu: PrinterMenu { } + } + SidebarHeader { id: header width: parent.width visible: machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants + anchors.top: machineSelection.bottom onShowTooltip: base.showTooltip(item, location, text) onHideTooltip: base.hideTooltip() diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index 99910b24a2..865cd287c3 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -76,70 +76,6 @@ Rectangle ExclusiveGroup { id: topbarMenuGroup } } - ToolButton - { - id: machineSelection - text: Cura.MachineManager.activeMachineName - - width: UM.Theme.getSize("sidebar").width - height: UM.Theme.getSize("sidebar_header").height - tooltip: Cura.MachineManager.activeMachineName - - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - style: ButtonStyle - { - background: Rectangle - { - color: - { - if(control.pressed) - { - return UM.Theme.getColor("sidebar_header_active"); - } - else if(control.hovered) - { - return UM.Theme.getColor("sidebar_header_hover"); - } - else - { - return UM.Theme.getColor("sidebar_header_bar"); - } - } - Behavior on color { ColorAnimation { duration: 50; } } - - UM.RecolorImage - { - id: downArrow - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("default_margin").width - width: UM.Theme.getSize("standard_arrow").width - height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width - sourceSize.height: width - color: UM.Theme.getColor("text_emphasis") - source: UM.Theme.getIcon("arrow_bottom") - } - Label - { - id: sidebarComboBoxLabel - color: UM.Theme.getColor("sidebar_header_text_active") - text: control.text; - elide: Text.ElideRight; - anchors.left: parent.left; - anchors.leftMargin: UM.Theme.getSize("default_margin").width * 2 - anchors.right: downArrow.left; - anchors.rightMargin: control.rightMargin; - anchors.verticalCenter: parent.verticalCenter; - font: UM.Theme.getFont("large") - } - } - label: Label {} - } - - menu: PrinterMenu { } - } // View orientation Item Row From c6cdc1d1dbb649b73e66d93fe8584cf57dfcb2cf Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 11 Dec 2017 14:33:59 +0100 Subject: [PATCH 082/104] Container cfg files need to be parsed without interpolation CURA-4701 --- .../VersionUpgrade30to31/VersionUpgrade30to31.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade30to31/VersionUpgrade30to31.py b/plugins/VersionUpgrade/VersionUpgrade30to31/VersionUpgrade30to31.py index bacd3f73d5..8c5a160ff4 100644 --- a/plugins/VersionUpgrade/VersionUpgrade30to31/VersionUpgrade30to31.py +++ b/plugins/VersionUpgrade/VersionUpgrade30to31/VersionUpgrade30to31.py @@ -179,7 +179,7 @@ class VersionUpgrade30to31(VersionUpgrade): if not os.path.isfile(file_path): continue - parser = configparser.ConfigParser() + parser = configparser.ConfigParser(interpolation = None) try: parser.read([file_path]) except: @@ -213,7 +213,7 @@ class VersionUpgrade30to31(VersionUpgrade): new_filename = machine_name + "_" + "fdmextruder" + suffix - extruder_quality_changes_parser = configparser.ConfigParser() + extruder_quality_changes_parser = configparser.ConfigParser(interpolation = None) extruder_quality_changes_parser.add_section("general") extruder_quality_changes_parser["general"]["version"] = str(2) extruder_quality_changes_parser["general"]["name"] = global_quality_changes["general"]["name"] From 300f29e11bf5cb80488ec1553a9ca325b039c0d7 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Mon, 11 Dec 2017 15:30:13 +0100 Subject: [PATCH 083/104] Add expand button to menu CURA-4234 --- resources/qml/Menus/ViewMenu.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/Menus/ViewMenu.qml b/resources/qml/Menus/ViewMenu.qml index a610bb0009..c8243ad2ed 100644 --- a/resources/qml/Menus/ViewMenu.qml +++ b/resources/qml/Menus/ViewMenu.qml @@ -32,4 +32,5 @@ Menu MenuSeparator {} MenuItem { action: Cura.Actions.homeCamera; } + MenuItem { action: Cura.Actions.expandSidebar; } } From 00529f37d91bcd3e89d5c352e6b46721a17b179d Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Mon, 11 Dec 2017 18:51:53 +0100 Subject: [PATCH 084/104] After sidebar expanding the builplate stays in center of the screen CURA-4234 --- resources/qml/Cura.qml | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 37607d237d..62968d66f0 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -17,7 +17,7 @@ UM.MainWindow id: base //: Cura application window title title: catalog.i18nc("@title:window","Ultimaker Cura"); - //viewportRect: Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0) + viewportRect: Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0) property bool showPrintMonitor: false // This connection is here to support legacy printer output devices that use the showPrintMonitor signal on Application to switch to the monitor stage @@ -67,15 +67,6 @@ UM.MainWindow // This has been fixed for QtQuick Controls 2 since the Shortcut item has a context property. Cura.Actions.parent = backgroundItem CuraApplication.purgeWindows() - - - var sidebarCollaps = UM.Preferences.getValue("general/sidebar_collaps") - - if (sidebarCollaps == true){ - sidebar.collapsed = true; - collapsSidebarAnimation.start(); - - } } Item @@ -403,8 +394,10 @@ UM.MainWindow if(collapsed){ sidebar.visible = true sidebar.initialWidth = UM.Theme.getSize("sidebar").width + viewportRect = Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0) expandSidebarAnimation.start(); }else{ + viewportRect = Qt.rect(0, 0, 1, 1.0) collapsSidebarAnimation.start(); } collapsed = !collapsed; @@ -436,6 +429,17 @@ UM.MainWindow to: base.width - sidebar.width duration: 500 } + + Component.onCompleted: + { + var sidebarCollaps = UM.Preferences.getValue("general/sidebar_collaps") + + if (sidebarCollaps == true){ + sidebar.collapsed = true; + viewportRect = Qt.rect(0, 0, 1, 1.0) + collapsSidebarAnimation.start(); + } + } } Loader From fb91edd7ebb9b47a45504cc74e1a293de1e403b2 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Tue, 12 Dec 2017 10:43:19 +0100 Subject: [PATCH 085/104] Solve arrange and multiply models issue CURA-4703 --- cura/ConvexHullDecorator.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cura/ConvexHullDecorator.py b/cura/ConvexHullDecorator.py index fc72ea34de..50fa8ce7f6 100644 --- a/cura/ConvexHullDecorator.py +++ b/cura/ConvexHullDecorator.py @@ -56,11 +56,6 @@ class ConvexHullDecorator(SceneNodeDecorator): if self._node is None: return None - if getattr(self._node, "_non_printing_mesh", False): - # infill_mesh, cutting_mesh and anti_overhang_mesh do not need a convex hull - # node._non_printing_mesh is set in SettingOverrideDecorator - return None - hull = self._compute2DConvexHull() if self._global_stack and self._node: From 84aa9367c7576d3b7b1de5f0961a0204c182cf86 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 12 Dec 2017 10:54:54 +0100 Subject: [PATCH 086/104] Cleanup --- resources/qml/SaveButton.qml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index c83e344876..a8c330ada1 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -12,11 +12,10 @@ Item { id: base; UM.I18nCatalog { id: catalog; name:"cura"} - property real progress: UM.Backend.progress; - property int backendState: UM.Backend.state; - - property var backend: CuraApplication.getBackend(); - property bool activity: CuraApplication.platformActivity; + property real progress: UM.Backend.progress + property int backendState: UM.Backend.state + property var backend: CuraApplication.getBackend() + property bool activity: CuraApplication.platformActivity property alias buttonRowWidth: saveRow.width From ca413e049690772759b6e9432f7a9601717e045e Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 12 Dec 2017 11:16:40 +0100 Subject: [PATCH 087/104] Add extra checks to ensure backend exists, use same state everywhere --- resources/qml/SaveButton.qml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index a8c330ada1..d53b43e459 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -49,10 +49,12 @@ Item { } function sliceOrStopSlicing() { - if (backend != "undefined" && [1, 5].indexOf(UM.Backend.state) != -1) { - backend.forceSlice(); - } else { - backend.stopSlicing(); + if (base.backendState != "undefined" && backend !== "undefined") { + if ([1, 5].indexOf(base.backendState) != -1) { + backend.forceSlice(); + } else { + backend.stopSlicing(); + } } } @@ -165,7 +167,7 @@ Item { Button { id: prepareButton - tooltip: [1, 5].indexOf(UM.Backend.state) != -1 ? catalog.i18nc("@info:tooltip","Slice current printjob") : catalog.i18nc("@info:tooltip","Cancel slicing process") + tooltip: [1, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@info:tooltip","Slice current printjob") : catalog.i18nc("@info:tooltip","Cancel slicing process") // 1 = not started, 2 = Processing enabled: base.backendState != "undefined" && (base.backendState == 1 || base.backendState == 2) && base.activity == true visible: base.backendState != "undefined" && !autoSlice && (base.backendState == 1 || base.backendState == 2) && base.activity == true @@ -177,7 +179,7 @@ Item { anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width // 1 = not started, 5 = disabled - text: [1, 5].indexOf(UM.Backend.state) != -1 ? catalog.i18nc("@label:Printjob", "Prepare") : catalog.i18nc("@label:Printjob", "Cancel") + text: [1, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@label:Printjob", "Prepare") : catalog.i18nc("@label:Printjob", "Cancel") onClicked: { sliceOrStopSlicing(); From 6968c089db1de2c01b97cd218e4f59a7a1d6ebe2 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Tue, 12 Dec 2017 11:26:17 +0100 Subject: [PATCH 088/104] Add sidebar_collapse to the saved preferences, typos, move back machine selection to the top bar CURA-4234 --- cura/CuraApplication.py | 2 ++ resources/qml/Actions.qml | 2 +- resources/qml/Cura.qml | 10 +++--- resources/qml/Sidebar.qml | 67 --------------------------------------- resources/qml/Topbar.qml | 64 +++++++++++++++++++++++++++++++++++++ 5 files changed, 72 insertions(+), 73 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index a5ae286b1e..7925a63038 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -384,6 +384,8 @@ class CuraApplication(QtApplication): self._plugin_registry.addSupportedPluginExtension("curaplugin", "Cura Plugin") + preferences.addPreference("general/sidebar_collapse", False) + def _onEngineCreated(self): self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider()) diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index cf3581ca0c..aa185f8615 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -394,7 +394,7 @@ Item Action { id: expandSidebarAction; - text: catalog.i18nc("@action:inmenu menubar:view","Expand/Collapse sidebar"); + text: catalog.i18nc("@action:inmenu menubar:view","Expand/Collapse Sidebar"); shortcut: "Ctrl+E"; } } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 62968d66f0..ccd89eb916 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -401,12 +401,12 @@ UM.MainWindow collapsSidebarAnimation.start(); } collapsed = !collapsed; - UM.Preferences.setValue("general/sidebar_collaps", collapsed); + UM.Preferences.setValue("general/sidebar_collapse", collapsed); } anchors { - top: topbar.top + top: topbar.bottom bottom: parent.bottom } @@ -419,7 +419,7 @@ UM.MainWindow target: sidebar properties: "x" to: base.width - duration: 500 + duration: 100 } NumberAnimation { @@ -427,12 +427,12 @@ UM.MainWindow target: sidebar properties: "x" to: base.width - sidebar.width - duration: 500 + duration: 100 } Component.onCompleted: { - var sidebarCollaps = UM.Preferences.getValue("general/sidebar_collaps") + var sidebarCollaps = UM.Preferences.getValue("general/sidebar_collapse") if (sidebarCollaps == true){ sidebar.collapsed = true; diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index a45303aab8..ea66aca50f 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -87,77 +87,10 @@ Rectangle } } - ToolButton - { - id: machineSelection - text: Cura.MachineManager.activeMachineName - - width: base.width - height: UM.Theme.getSize("sidebar_header").height - tooltip: Cura.MachineManager.activeMachineName - - anchors.top: base.top - //anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - style: ButtonStyle - { - background: Rectangle - { - color: - { - if(control.pressed) - { - return UM.Theme.getColor("sidebar_header_active"); - } - else if(control.hovered) - { - return UM.Theme.getColor("sidebar_header_hover"); - } - else - { - return UM.Theme.getColor("sidebar_header_bar"); - } - } - Behavior on color { ColorAnimation { duration: 50; } } - - UM.RecolorImage - { - id: downArrow - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("default_margin").width - width: UM.Theme.getSize("standard_arrow").width - height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width - sourceSize.height: width - color: UM.Theme.getColor("text_emphasis") - source: UM.Theme.getIcon("arrow_bottom") - } - Label - { - id: sidebarComboBoxLabel - color: UM.Theme.getColor("sidebar_header_text_active") - text: control.text; - elide: Text.ElideRight; - anchors.left: parent.left; - anchors.leftMargin: UM.Theme.getSize("default_margin").width * 2 - anchors.right: downArrow.left; - anchors.rightMargin: control.rightMargin; - anchors.verticalCenter: parent.verticalCenter; - font: UM.Theme.getFont("large") - } - } - label: Label {} - } - - menu: PrinterMenu { } - } - SidebarHeader { id: header width: parent.width visible: machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants - anchors.top: machineSelection.bottom onShowTooltip: base.showTooltip(item, location, text) onHideTooltip: base.hideTooltip() diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index 865cd287c3..99910b24a2 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -76,6 +76,70 @@ Rectangle ExclusiveGroup { id: topbarMenuGroup } } + ToolButton + { + id: machineSelection + text: Cura.MachineManager.activeMachineName + + width: UM.Theme.getSize("sidebar").width + height: UM.Theme.getSize("sidebar_header").height + tooltip: Cura.MachineManager.activeMachineName + + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + style: ButtonStyle + { + background: Rectangle + { + color: + { + if(control.pressed) + { + return UM.Theme.getColor("sidebar_header_active"); + } + else if(control.hovered) + { + return UM.Theme.getColor("sidebar_header_hover"); + } + else + { + return UM.Theme.getColor("sidebar_header_bar"); + } + } + Behavior on color { ColorAnimation { duration: 50; } } + + UM.RecolorImage + { + id: downArrow + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("default_margin").width + width: UM.Theme.getSize("standard_arrow").width + height: UM.Theme.getSize("standard_arrow").height + sourceSize.width: width + sourceSize.height: width + color: UM.Theme.getColor("text_emphasis") + source: UM.Theme.getIcon("arrow_bottom") + } + Label + { + id: sidebarComboBoxLabel + color: UM.Theme.getColor("sidebar_header_text_active") + text: control.text; + elide: Text.ElideRight; + anchors.left: parent.left; + anchors.leftMargin: UM.Theme.getSize("default_margin").width * 2 + anchors.right: downArrow.left; + anchors.rightMargin: control.rightMargin; + anchors.verticalCenter: parent.verticalCenter; + font: UM.Theme.getFont("large") + } + } + label: Label {} + } + + menu: PrinterMenu { } + } // View orientation Item Row From d70bd0ebc95818f0e4ad89676c762111b4a5e167 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 12 Dec 2017 13:45:35 +0100 Subject: [PATCH 089/104] CURA-4649 Blocking printers that have localhost IP address. --- .../UM3NetworkPrinting/NetworkPrinterOutputDevicePlugin.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevicePlugin.py b/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevicePlugin.py index 46538f1af9..0d3ed52f03 100644 --- a/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevicePlugin.py @@ -43,6 +43,7 @@ class NetworkPrinterOutputDevicePlugin(QObject, OutputDevicePlugin): # List of old printer names. This is used to ensure that a refresh of zeroconf does not needlessly forces # authentication requests. self._old_printers = [] + self._excluded_addresses = ["127.0.0.1"] # Adding a list of not allowed IP addresses. At this moment, just localhost # Because the model needs to be created in the same thread as the QMLEngine, we use a signal. self.addPrinterSignal.connect(self.addPrinter) @@ -300,6 +301,9 @@ class NetworkPrinterOutputDevicePlugin(QObject, OutputDevicePlugin): if type_of_device: if type_of_device == b"printer": address = '.'.join(map(lambda n: str(n), info.address)) + if address in self._excluded_addresses: + Logger.log("d", "The IP address %s of the printer \'%s\' is not correct. Trying to reconnect.", address, name) + return False # When getting the localhost IP, then try to reconnect self.addPrinterSignal.emit(str(name), address, info.properties) else: Logger.log("w", "The type of the found device is '%s', not 'printer'! Ignoring.." % type_of_device ) From 3516d01f3d750a841e549def7787edc2348031e3 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Tue, 12 Dec 2017 15:30:19 +0100 Subject: [PATCH 090/104] Move back machine selection CURA-4234 --- cura/CuraApplication.py | 3 +- resources/qml/Cura.qml | 2 +- resources/qml/Sidebar.qml | 67 +++++++++++++++++++++++++++++++++++++++ resources/qml/Topbar.qml | 67 --------------------------------------- 4 files changed, 69 insertions(+), 70 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 7925a63038..94afec7d5a 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -312,6 +312,7 @@ class CuraApplication(QtApplication): preferences.addPreference("cura/material_settings", "{}") preferences.addPreference("view/invert_zoom", False) + preferences.addPreference("general/sidebar_collapse", False) self._need_to_show_user_agreement = not Preferences.getInstance().getValue("general/accepted_user_agreement") @@ -384,8 +385,6 @@ class CuraApplication(QtApplication): self._plugin_registry.addSupportedPluginExtension("curaplugin", "Cura Plugin") - preferences.addPreference("general/sidebar_collapse", False) - def _onEngineCreated(self): self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider()) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index ccd89eb916..8cce20d19f 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -406,7 +406,7 @@ UM.MainWindow anchors { - top: topbar.bottom + top: topbar.top bottom: parent.bottom } diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index ea66aca50f..a45303aab8 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -87,10 +87,77 @@ Rectangle } } + ToolButton + { + id: machineSelection + text: Cura.MachineManager.activeMachineName + + width: base.width + height: UM.Theme.getSize("sidebar_header").height + tooltip: Cura.MachineManager.activeMachineName + + anchors.top: base.top + //anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + style: ButtonStyle + { + background: Rectangle + { + color: + { + if(control.pressed) + { + return UM.Theme.getColor("sidebar_header_active"); + } + else if(control.hovered) + { + return UM.Theme.getColor("sidebar_header_hover"); + } + else + { + return UM.Theme.getColor("sidebar_header_bar"); + } + } + Behavior on color { ColorAnimation { duration: 50; } } + + UM.RecolorImage + { + id: downArrow + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("default_margin").width + width: UM.Theme.getSize("standard_arrow").width + height: UM.Theme.getSize("standard_arrow").height + sourceSize.width: width + sourceSize.height: width + color: UM.Theme.getColor("text_emphasis") + source: UM.Theme.getIcon("arrow_bottom") + } + Label + { + id: sidebarComboBoxLabel + color: UM.Theme.getColor("sidebar_header_text_active") + text: control.text; + elide: Text.ElideRight; + anchors.left: parent.left; + anchors.leftMargin: UM.Theme.getSize("default_margin").width * 2 + anchors.right: downArrow.left; + anchors.rightMargin: control.rightMargin; + anchors.verticalCenter: parent.verticalCenter; + font: UM.Theme.getFont("large") + } + } + label: Label {} + } + + menu: PrinterMenu { } + } + SidebarHeader { id: header width: parent.width visible: machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants + anchors.top: machineSelection.bottom onShowTooltip: base.showTooltip(item, location, text) onHideTooltip: base.hideTooltip() diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index 99910b24a2..796eb6fce5 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -46,8 +46,6 @@ Rectangle { anchors.left: logo.right anchors.leftMargin: UM.Theme.getSize("topbar_logo_right_margin").width - anchors.right: machineSelection.left - anchors.rightMargin: UM.Theme.getSize("default_margin").width spacing: UM.Theme.getSize("default_margin").width // The topbar is dynamically filled with all available stages @@ -76,71 +74,6 @@ Rectangle ExclusiveGroup { id: topbarMenuGroup } } - ToolButton - { - id: machineSelection - text: Cura.MachineManager.activeMachineName - - width: UM.Theme.getSize("sidebar").width - height: UM.Theme.getSize("sidebar_header").height - tooltip: Cura.MachineManager.activeMachineName - - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - style: ButtonStyle - { - background: Rectangle - { - color: - { - if(control.pressed) - { - return UM.Theme.getColor("sidebar_header_active"); - } - else if(control.hovered) - { - return UM.Theme.getColor("sidebar_header_hover"); - } - else - { - return UM.Theme.getColor("sidebar_header_bar"); - } - } - Behavior on color { ColorAnimation { duration: 50; } } - - UM.RecolorImage - { - id: downArrow - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("default_margin").width - width: UM.Theme.getSize("standard_arrow").width - height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width - sourceSize.height: width - color: UM.Theme.getColor("text_emphasis") - source: UM.Theme.getIcon("arrow_bottom") - } - Label - { - id: sidebarComboBoxLabel - color: UM.Theme.getColor("sidebar_header_text_active") - text: control.text; - elide: Text.ElideRight; - anchors.left: parent.left; - anchors.leftMargin: UM.Theme.getSize("default_margin").width * 2 - anchors.right: downArrow.left; - anchors.rightMargin: control.rightMargin; - anchors.verticalCenter: parent.verticalCenter; - font: UM.Theme.getFont("large") - } - } - label: Label {} - } - - menu: PrinterMenu { } - } - // View orientation Item Row { From a85bea4baedff3659421bab50e96a592872fd8da Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Tue, 12 Dec 2017 16:37:21 +0100 Subject: [PATCH 091/104] CURA-4707 fix extruder setting not triggering reslice --- plugins/CuraEngineBackend/CuraEngineBackend.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index d35df967b2..b9e8a22614 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -86,6 +86,7 @@ class CuraEngineBackend(QObject, Backend): # self._global_container_stack = None Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged) + Application.getInstance().getExtruderManager().extrudersChanged.connect(self._onGlobalStackChanged) self._onGlobalStackChanged() Application.getInstance().stacksValidationFinished.connect(self._onStackErrorCheckFinished) From e10ba065ca61a445dc39accb857e010fc785c410 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Tue, 12 Dec 2017 17:06:23 +0100 Subject: [PATCH 092/104] CURA-4707 better signal to connect solving this issue --- plugins/CuraEngineBackend/CuraEngineBackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index b9e8a22614..4581a78479 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -86,7 +86,7 @@ class CuraEngineBackend(QObject, Backend): # self._global_container_stack = None Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged) - Application.getInstance().getExtruderManager().extrudersChanged.connect(self._onGlobalStackChanged) + Application.getInstance().getExtruderManager().activeExtruderChanged.connect(self._onGlobalStackChanged) self._onGlobalStackChanged() Application.getInstance().stacksValidationFinished.connect(self._onStackErrorCheckFinished) From e14d78b32abe62ea39543fd6af6c378fdd801ea5 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 13 Dec 2017 10:16:06 +0100 Subject: [PATCH 093/104] Generate more possible machine IDs in XML material profile --- .../XmlMaterialProfile/XmlMaterialProfile.py | 350 ++++++++++-------- 1 file changed, 191 insertions(+), 159 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index c4912826ee..08530f96e2 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -212,11 +212,12 @@ class XmlMaterialProfile(InstanceContainer): for definition_id, container in machine_container_map.items(): definition = container.getDefinition() - try: - product = UM.Dictionary.findKey(product_id_map, definition_id) - except ValueError: - # An unknown product id; export it anyway - product = definition_id + + product = definition_id + for product_name, product_id_list in product_id_map.items(): + if definition_id in product_id_list: + product = product_name + break builder.start("machine") builder.start("machine_identifier", { @@ -530,106 +531,110 @@ class XmlMaterialProfile(InstanceContainer): identifiers = machine.iterfind("./um:machine_identifier", self.__namespaces) for identifier in identifiers: - 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() + machine_id_list = product_id_map.get(identifier.get("product"), []) + if not machine_id_list: + machine_id_list.append(identifier.get("product").replace(" ", "").lower()) - 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.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 - - # 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. - 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 - - 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 - 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) - - new_material._dirty = False - - if is_new_material: - containers_to_add.append(new_material) - - hotends = machine.iterfind("./um:hotend", self.__namespaces) - for hotend in hotends: - hotend_id = hotend.get("id") - if hotend_id is None: + for machine_id in machine_id_list: + definitions = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = machine_id) + if not definitions: + Logger.log("w", "No definition found for machine ID %s", machine_id) 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 = definition["id"], name = hotend_id) + definition = definitions[0] - if not variant_containers: - continue + 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. - hotend_compatibility = machine_compatibility - hotend_setting_values = {} - settings = hotend.iterfind("./um:setting", self.__namespaces) - for entry in settings: - key = entry.get("key") - if key in self.__material_settings_setting_map: - hotend_setting_values[self.__material_settings_setting_map[key]] = entry.text - elif key in self.__unmapped_settings: - if key == "hardware compatible": - hotend_compatibility = self._parseCompatibleValue(entry.text) + if machine_compatibility: + 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. + # 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. + if ContainerRegistry.getInstance().isLoaded(new_material_id): + new_material = ContainerRegistry.getInstance().findContainers(id = new_material_id)[0] + is_new_material = False else: - Logger.log("d", "Unsupported material setting %s", key) + new_material = XmlMaterialProfile(new_material_id) + is_new_material = True - new_hotend_id = self.getId() + "_" + machine_id + "_" + hotend_id.replace(" ", "_") + 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 + new_material.getMetaData()["compatible"] = machine_compatibility + new_material.getMetaData()["machine_manufacturer"] = machine_manufacturer + new_material.getMetaData()["definition"] = machine_id - # Same as machine compatibility, keep the derived material containers consistent with the parent - # material - 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 + new_material.setCachedValues(cached_machine_setting_properties) - 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.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 + new_material._dirty = False - cached_hotend_setting_properties = cached_machine_setting_properties.copy() - cached_hotend_setting_properties.update(hotend_setting_values) + if is_new_material: + containers_to_add.append(new_material) - new_hotend_material.setCachedValues(cached_hotend_setting_properties) + hotends = machine.iterfind("./um:hotend", self.__namespaces) + for hotend in hotends: + hotend_id = hotend.get("id") + if hotend_id is None: + continue - new_hotend_material._dirty = False + 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) - if is_new_material: - containers_to_add.append(new_hotend_material) + if not variant_containers: + continue + + hotend_compatibility = machine_compatibility + hotend_setting_values = {} + settings = hotend.iterfind("./um:setting", self.__namespaces) + for entry in settings: + key = entry.get("key") + if key in self.__material_settings_setting_map: + hotend_setting_values[self.__material_settings_setting_map[key]] = entry.text + elif key in self.__unmapped_settings: + if key == "hardware compatible": + hotend_compatibility = self._parseCompatibleValue(entry.text) + else: + Logger.log("d", "Unsupported material setting %s", key) + + new_hotend_id = self.getId() + "_" + machine_id + "_" + hotend_id.replace(" ", "_") + + # Same as machine compatibility, keep the derived material containers consistent with the parent + # material + 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 + + 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.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) + + new_hotend_material.setCachedValues(cached_hotend_setting_properties) + + new_hotend_material._dirty = False + + if is_new_material: + containers_to_add.append(new_hotend_material) + + # there is only one ID for a machine. Once we have reached here, it means we have already found + # a workable ID for that machine, so there is no need to continue + break for container_to_add in containers_to_add: ContainerRegistry.getInstance().addContainer(container_to_add) @@ -720,79 +725,84 @@ class XmlMaterialProfile(InstanceContainer): 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_id_list = product_id_map.get(identifier.get("product"), []) + if not machine_id_list: + machine_id_list.append(identifier.get("product").replace(" ", "").lower()) - 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["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) - - for hotend in machine.iterfind("./um:hotend", cls.__namespaces): - hotend_id = hotend.get("id") - if hotend_id is None: + for machine_id in machine_id_list: + 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] - 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) + 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. - 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) + if machine_compatibility: + new_material_id = container_id + "_" + machine_id - new_hotend_id = container_id + "_" + machine_id + "_" + hotend_id.replace(" ", "_") + # 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 = {} - # Same as machine compatibility, keep the derived material containers consistent with the parent - # material - found_materials = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = new_hotend_id) - if found_materials: - new_hotend_material_metadata = found_materials[0] - else: - new_hotend_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 + new_material_metadata["definition"] = machine_id - new_hotend_material_metadata.update(base_metadata) - 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 - new_hotend_material_metadata["definition"] = machine_id + if len(found_materials) == 0: #This is a new material. + result_metadata.append(new_material_metadata) - if len(found_materials) == 0: - result_metadata.append(new_hotend_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) + + 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().findInstanceContainersMetadata(id = new_hotend_id) + if found_materials: + new_hotend_material_metadata = found_materials[0] + else: + new_hotend_material_metadata = {} + + new_hotend_material_metadata.update(base_metadata) + 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 + new_hotend_material_metadata["definition"] = machine_id + + if len(found_materials) == 0: + result_metadata.append(new_hotend_material_metadata) + + # there is only one ID for a machine. Once we have reached here, it means we have already found + # a workable ID for that machine, so there is no need to continue + break return result_metadata @@ -818,10 +828,32 @@ class XmlMaterialProfile(InstanceContainer): # # This loads the mapping from a file. @classmethod - def getProductIdMap(cls) -> Dict[str, str]: + def getProductIdMap(cls) -> Dict[str, List[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) + product_to_id_map = json.load(f) + + # generate a few more combinations so it can be smart about finding IDs + product_to_id_map = {key: [value] for key, value in product_to_id_map.items()} + for name, id_list in product_to_id_map.items(): + name_parts = name.split(" ") + merged_name_parts = [] + for part in name_parts: + if len(part) == 0: + continue + if len(merged_name_parts) == 0: + merged_name_parts.append(part.lower()) + continue + if part.isdigit(): + # for names with digit(s) such as Ultimaker 3 Extended, we generate an ID like + # "ultimaker3_extended", ignoring the space between "Ultimaker" and "3". + merged_name_parts[-1] = merged_name_parts[-1] + part.lower() + + generated_id = "_".join(merged_name_parts) + if generated_id not in id_list: + id_list.append(generated_id) + + return product_to_id_map ## Parse the value of the "material compatible" property. @classmethod From 7cd344978149997d91d91c1f20788e42fe3de91e Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 13 Dec 2017 11:26:16 +0100 Subject: [PATCH 094/104] Fix material loading --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 08530f96e2..4de49817bc 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -632,9 +632,9 @@ class XmlMaterialProfile(InstanceContainer): if is_new_material: containers_to_add.append(new_hotend_material) - # there is only one ID for a machine. Once we have reached here, it means we have already found - # a workable ID for that machine, so there is no need to continue - break + # there is only one ID for a machine. Once we have reached here, it means we have already found + # a workable ID for that machine, so there is no need to continue + break for container_to_add in containers_to_add: ContainerRegistry.getInstance().addContainer(container_to_add) @@ -800,9 +800,9 @@ class XmlMaterialProfile(InstanceContainer): if len(found_materials) == 0: result_metadata.append(new_hotend_material_metadata) - # there is only one ID for a machine. Once we have reached here, it means we have already found - # a workable ID for that machine, so there is no need to continue - break + # there is only one ID for a machine. Once we have reached here, it means we have already found + # a workable ID for that machine, so there is no need to continue + break return result_metadata From 5c5f08e9ca3bdf84f04a7e9c6d31c73c52d3f93b Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Wed, 13 Dec 2017 11:58:59 +0100 Subject: [PATCH 095/104] Do not slice a model if it is only one on a build plane and has setting from non_printing_mesh CURA-4703 --- plugins/CuraEngineBackend/StartSliceJob.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 876b4685cc..7cfccb2b1f 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -131,12 +131,21 @@ class StartSliceJob(Job): Logger.log("w", "No objects suitable for one at a time found, or no correct order found") else: temp_list = [] + is_non_printing_mesh = False for node in DepthFirstIterator(self._scene.getRoot()): if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None: - if not getattr(node, "_outside_buildarea", False) or getattr(node, "_non_printing_mesh", False): + _non_printing_mesh = getattr(node, "_non_printing_mesh", False) + if not getattr(node, "_outside_buildarea", False) or _non_printing_mesh: temp_list.append(node) + if _non_printing_mesh: + is_non_printing_mesh = True Job.yieldThread() + #If list has one node and it has non printing settings then remove it from list + # otherwise CuraEngine will crash + if len(temp_list) == 1 and is_non_printing_mesh: + temp_list.clear() + if temp_list: object_groups.append(temp_list) From 6e6dc493f1bdd8f9027341dcc869cab295f7625e Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 13 Dec 2017 12:27:15 +0100 Subject: [PATCH 096/104] Fix material loading for unknown names --- cura/Settings/CuraContainerStack.py | 4 +- .../XmlMaterialProfile/XmlMaterialProfile.py | 54 +++++++++++-------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/cura/Settings/CuraContainerStack.py b/cura/Settings/CuraContainerStack.py index 9e30e3dd66..61c28df570 100755 --- a/cura/Settings/CuraContainerStack.py +++ b/cura/Settings/CuraContainerStack.py @@ -508,7 +508,7 @@ class CuraContainerStack(ContainerStack): def findDefaultQuality(self) -> Optional[ContainerInterface]: definition = self._getMachineDefinition() registry = ContainerRegistry.getInstance() - material_container = self.material if self.material != self._empty_instance_container else None + material_container = self.material if self.material.getId() not in (self._empty_material.getId(), self._empty_instance_container.getId()) else None search_criteria = {"type": "quality"} @@ -552,7 +552,7 @@ class CuraContainerStack(ContainerStack): material_search_criteria = {"type": "material", "material": material_container.getMetaDataEntry("material"), "color_name": "Generic"} if definition.getMetaDataEntry("has_machine_quality"): if self.material != self._empty_instance_container: - material_search_criteria["definition"] = material_container.getDefinition().id + material_search_criteria["definition"] = material_container.getMetaDataEntry("definition") if definition.getMetaDataEntry("has_variants"): material_search_criteria["variant"] = material_container.getMetaDataEntry("variant") diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 4de49817bc..e212ab9b7c 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -533,7 +533,7 @@ class XmlMaterialProfile(InstanceContainer): for identifier in identifiers: machine_id_list = product_id_map.get(identifier.get("product"), []) if not machine_id_list: - machine_id_list.append(identifier.get("product").replace(" ", "").lower()) + machine_id_list = self.getPossibleDefinitionIDsFromName(identifier.get("product")) for machine_id in machine_id_list: definitions = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = machine_id) @@ -541,6 +541,7 @@ class XmlMaterialProfile(InstanceContainer): Logger.log("w", "No definition found for machine ID %s", machine_id) continue + Logger.log("d", "Found definition for machine ID %s", machine_id) definition = definitions[0] 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. @@ -584,7 +585,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["id"], name = hotend_id) + variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = machine_id, name = hotend_id) if not variant_containers: continue @@ -617,6 +618,7 @@ class XmlMaterialProfile(InstanceContainer): new_hotend_material.getMetaData()["id"] = new_hotend_id new_hotend_material.getMetaData()["name"] = self.getName() new_hotend_material.getMetaData()["variant"] = variant_containers[0]["id"] + new_hotend_material.setDefinition(machine_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 @@ -727,13 +729,15 @@ class XmlMaterialProfile(InstanceContainer): for identifier in machine.iterfind("./um:machine_identifier", cls.__namespaces): machine_id_list = product_id_map.get(identifier.get("product"), []) if not machine_id_list: - machine_id_list.append(identifier.get("product").replace(" ", "").lower()) + machine_id_list = cls.getPossibleDefinitionIDsFromName(identifier.get("product")) for machine_id in machine_id_list: 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 + + Logger.log("d", "========= Found def for machine [%s]", machine_id) 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. @@ -823,6 +827,30 @@ class XmlMaterialProfile(InstanceContainer): else: return material_name + @classmethod + def getPossibleDefinitionIDsFromName(cls, name): + name_parts = name.lower().split(" ") + merged_name_parts = [] + for part in name_parts: + if len(part) == 0: + continue + if len(merged_name_parts) == 0: + merged_name_parts.append(part) + continue + if part.isdigit(): + # for names with digit(s) such as Ultimaker 3 Extended, we generate an ID like + # "ultimaker3_extended", ignoring the space between "Ultimaker" and "3". + merged_name_parts[-1] = merged_name_parts[-1] + part + else: + merged_name_parts.append(part) + + id_list = [name.lower().replace(" ", ""), # simply removing all spaces + name.lower().replace(" ", "_"), # simply replacing all spaces with underscores + "_".join(merged_name_parts), + ] + + return id_list + ## Gets a mapping from product names in the XML files to their definition # IDs. # @@ -832,27 +860,7 @@ class XmlMaterialProfile(InstanceContainer): 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: product_to_id_map = json.load(f) - - # generate a few more combinations so it can be smart about finding IDs product_to_id_map = {key: [value] for key, value in product_to_id_map.items()} - for name, id_list in product_to_id_map.items(): - name_parts = name.split(" ") - merged_name_parts = [] - for part in name_parts: - if len(part) == 0: - continue - if len(merged_name_parts) == 0: - merged_name_parts.append(part.lower()) - continue - if part.isdigit(): - # for names with digit(s) such as Ultimaker 3 Extended, we generate an ID like - # "ultimaker3_extended", ignoring the space between "Ultimaker" and "3". - merged_name_parts[-1] = merged_name_parts[-1] + part.lower() - - generated_id = "_".join(merged_name_parts) - if generated_id not in id_list: - id_list.append(generated_id) - return product_to_id_map ## Parse the value of the "material compatible" property. From 9ea011901132bfa16ab99cb56a39ecb7bd116493 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 13 Dec 2017 12:58:24 +0100 Subject: [PATCH 097/104] Rename 'exception' to 'error' Error is a less technical term, so it's more user friendly. --- cura/CrashHandler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/CrashHandler.py b/cura/CrashHandler.py index cb0f121d47..81eca67a46 100644 --- a/cura/CrashHandler.py +++ b/cura/CrashHandler.py @@ -59,7 +59,7 @@ class CrashHandler: self.data = dict() self.data["time_stamp"] = time.time() - Logger.log("c", "An uncaught exception has occurred!") + Logger.log("c", "An uncaught error has occurred!") for line in traceback.format_exception(exception_type, value, tb): for part in line.rstrip("\n").split("\n"): Logger.log("c", part) @@ -90,7 +90,7 @@ class CrashHandler: def _messageWidget(self): label = QLabel() - label.setText(catalog.i18nc("@label crash message", """

A fatal exception has occurred. Please send us this Crash Report to fix the problem

+ label.setText(catalog.i18nc("@label crash message", """

A fatal error has occurred. Please send us this Crash Report to fix the problem

Please use the "Send report" button to post a bug report automatically to our servers

""")) @@ -143,7 +143,7 @@ class CrashHandler: def _exceptionInfoWidget(self): group = QGroupBox() - group.setTitle(catalog.i18nc("@title:groupbox", "Exception traceback")) + group.setTitle(catalog.i18nc("@title:groupbox", "Error traceback")) layout = QVBoxLayout() text_area = QTextEdit() From 19b56404c61c4569b47dcda1b31389dd07653365 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 13 Dec 2017 13:33:05 +0100 Subject: [PATCH 098/104] Fix empty quality slider on start. It seemed that: - MachineManager was not connected to all the correct signals - After connecting to the correct signal, ProfilesModel was not yet updated when looping over it in SidebarSimple Because ProfilesModel's constructor already requests the MachineManager, we cannot do connect itemsChanged the way around as well. CURA-4707 --- cura/Settings/MachineManager.py | 19 ++++++++++++++++--- cura/Settings/ProfilesModel.py | 5 ++++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 16d42ce653..7c0adb5043 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -32,6 +32,7 @@ from .CuraStackBuilder import CuraStackBuilder from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") +from cura.Settings.ProfilesModel import ProfilesModel from typing import TYPE_CHECKING, Optional if TYPE_CHECKING: @@ -60,9 +61,11 @@ class MachineManager(QObject): self._instance_container_timer = QTimer() self._instance_container_timer.setInterval(250) self._instance_container_timer.setSingleShot(True) - self._instance_container_timer.timeout.connect(self.__onInstanceContainersChanged) + self._instance_container_timer.timeout.connect(self.__emitChangedSignals) Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged) + Application.getInstance().getContainerRegistry().containerLoadComplete.connect(self._onInstanceContainersChanged) + self._connected_to_profiles_model = False ## When the global container is changed, active material probably needs to be updated. self.globalContainerChanged.connect(self.activeMaterialChanged) @@ -333,14 +336,24 @@ class MachineManager(QObject): # on _active_container_stack. If it changes, then the properties change. self.activeQualityChanged.emit() - def __onInstanceContainersChanged(self): + def __emitChangedSignals(self): self.activeQualityChanged.emit() self.activeVariantChanged.emit() self.activeMaterialChanged.emit() self._updateStacksHaveErrors() # Prevents unwanted re-slices after changing machine self._error_check_timer.start() + def _onProfilesModelChanged(self, *args): + self.__emitChangedSignals() + def _onInstanceContainersChanged(self, container): + # This should not trigger the ProfilesModel to be created, or there will be an infinite recursion + if not self._connected_to_profiles_model and ProfilesModel.hasInstance(): + # This triggers updating the qualityModel in SidebarSimple whenever ProfilesModel is updated + Logger.log("d", "Connecting profiles model...") + ProfilesModel.getInstance().itemsChanged.connect(self._onProfilesModelChanged) + self._connected_to_profiles_model = True + self._instance_container_timer.start() def _onPropertyChanged(self, key: str, property_name: str): @@ -360,7 +373,7 @@ class MachineManager(QObject): if containers: Application.getInstance().setGlobalContainerStack(containers[0]) - self.__onInstanceContainersChanged() + self.__emitChangedSignals() @pyqtSlot(str, str) def addMachine(self, name: str, definition_id: str) -> None: diff --git a/cura/Settings/ProfilesModel.py b/cura/Settings/ProfilesModel.py index 849d6959b9..3b43e2740a 100644 --- a/cura/Settings/ProfilesModel.py +++ b/cura/Settings/ProfilesModel.py @@ -49,6 +49,10 @@ class ProfilesModel(InstanceContainersModel): ProfilesModel.__instance = cls() return ProfilesModel.__instance + @classmethod + def hasInstance(cls) -> bool: + return ProfilesModel.__instance is not None + __instance = None # type: "ProfilesModel" ## Fetch the list of containers to display. @@ -91,7 +95,6 @@ class ProfilesModel(InstanceContainersModel): ## Re-computes the items in this model, and adds the layer height role. def _recomputeItems(self): - # Some globals that we can re-use. global_container_stack = Application.getInstance().getGlobalContainerStack() if global_container_stack is None: From 4519f9b46ace2b1490dae12b08fa024a9022edfd Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 13 Dec 2017 14:04:02 +0100 Subject: [PATCH 099/104] CURA-4234 rename to cura/sidebar_collapse, fix spelling errors, styling --- cura/CuraApplication.py | 2 +- resources/qml/Cura.qml | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 46e73e6194..c4e1c416dd 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -313,7 +313,7 @@ class CuraApplication(QtApplication): preferences.addPreference("cura/material_settings", "{}") preferences.addPreference("view/invert_zoom", False) - preferences.addPreference("general/sidebar_collapse", False) + preferences.addPreference("cura/sidebar_collapse", False) self._need_to_show_user_agreement = not Preferences.getInstance().getValue("general/accepted_user_agreement") diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 8cce20d19f..91098bbb29 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -390,18 +390,18 @@ UM.MainWindow property bool collapsed: false; property var initialWidth: UM.Theme.getSize("sidebar").width; - function callExpandOrCollapse(){ - if(collapsed){ - sidebar.visible = true - sidebar.initialWidth = UM.Theme.getSize("sidebar").width - viewportRect = Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0) + function callExpandOrCollapse() { + if (collapsed) { + sidebar.visible = true; + sidebar.initialWidth = UM.Theme.getSize("sidebar").width; + viewportRect = Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0); expandSidebarAnimation.start(); - }else{ - viewportRect = Qt.rect(0, 0, 1, 1.0) - collapsSidebarAnimation.start(); + } else { + viewportRect = Qt.rect(0, 0, 1, 1.0); + collapseSidebarAnimation.start(); } collapsed = !collapsed; - UM.Preferences.setValue("general/sidebar_collapse", collapsed); + UM.Preferences.setValue("cura/sidebar_collapse", collapsed); } anchors @@ -415,7 +415,7 @@ UM.MainWindow source: UM.Controller.activeStage.sidebarComponent NumberAnimation { - id: collapsSidebarAnimation + id: collapseSidebarAnimation target: sidebar properties: "x" to: base.width @@ -432,12 +432,12 @@ UM.MainWindow Component.onCompleted: { - var sidebarCollaps = UM.Preferences.getValue("general/sidebar_collapse") + var sidebarCollapsed = UM.Preferences.getValue("cura/sidebar_collapse"); - if (sidebarCollaps == true){ + if (sidebarCollapsed) { sidebar.collapsed = true; viewportRect = Qt.rect(0, 0, 1, 1.0) - collapsSidebarAnimation.start(); + collapseSidebarAnimation.start(); } } } From 63eec848fcf9eb0b37eab27a64b42a777bce5733 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 13 Dec 2017 14:15:27 +0100 Subject: [PATCH 100/104] CURA-4234 view mode button now also moves with the sidebar --- resources/qml/Topbar.qml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index 796eb6fce5..e6cddae3cf 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -153,11 +153,21 @@ Rectangle ComboBox { id: viewModeButton + property int rightMargin: UM.Theme.getSize("sidebar").width + UM.Theme.getSize("default_margin").width; anchors { verticalCenter: parent.verticalCenter right: parent.right - rightMargin: UM.Theme.getSize("sidebar").width + UM.Theme.getSize("default_margin").width + rightMargin: rightMargin + } + + function updateMargins() { + CuraApplication.log("update margin"); + if (UM.Preferences.getValue("cura/sidebar_collapse")) { + rightMargin = UM.Theme.getSize("default_margin").width; + } else { + rightMargin = UM.Theme.getSize("sidebar").width + UM.Theme.getSize("default_margin").width; + } } style: UM.Theme.styles.combobox @@ -201,6 +211,13 @@ Rectangle } } + // Expand or collapse sidebar + Connections + { + target: Cura.Actions.expandSidebar + onTriggered: viewModeButton.updateMargins() + } + Loader { id: view_panel From 3605edc56595e0f8fa723fdf2860b09480239013 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 13 Dec 2017 14:16:35 +0100 Subject: [PATCH 101/104] CURA-4234 remove debugging logline --- resources/qml/Topbar.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index e6cddae3cf..d2f1db37b2 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -162,7 +162,6 @@ Rectangle } function updateMargins() { - CuraApplication.log("update margin"); if (UM.Preferences.getValue("cura/sidebar_collapse")) { rightMargin = UM.Theme.getSize("default_margin").width; } else { From a44cea7ac6f3572e4eed4a9c024ee8395cb635f8 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 13 Dec 2017 14:48:25 +0100 Subject: [PATCH 102/104] Update log line --- 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 e212ab9b7c..c31a60c778 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -737,7 +737,7 @@ class XmlMaterialProfile(InstanceContainer): Logger.log("w", "No definition found for machine ID %s", machine_id) continue - Logger.log("d", "========= Found def for machine [%s]", machine_id) + Logger.log("d", "Found def for machine [%s]", machine_id) 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. From 88af41769e4feb690b1561c5ab14a9b675c9a369 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Wed, 13 Dec 2017 16:04:01 +0100 Subject: [PATCH 103/104] Check multiple models with non_printing settings CURA-4703 --- plugins/CuraEngineBackend/StartSliceJob.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 7cfccb2b1f..3c1998094c 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -131,19 +131,19 @@ class StartSliceJob(Job): Logger.log("w", "No objects suitable for one at a time found, or no correct order found") else: temp_list = [] - is_non_printing_mesh = False + has_printing_mesh = False for node in DepthFirstIterator(self._scene.getRoot()): if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None: _non_printing_mesh = getattr(node, "_non_printing_mesh", False) if not getattr(node, "_outside_buildarea", False) or _non_printing_mesh: temp_list.append(node) - if _non_printing_mesh: - is_non_printing_mesh = True + if not _non_printing_mesh: + has_printing_mesh = True Job.yieldThread() - #If list has one node and it has non printing settings then remove it from list + #If the list doesn't have any model with suitable settings then clean the list # otherwise CuraEngine will crash - if len(temp_list) == 1 and is_non_printing_mesh: + if not has_printing_mesh: temp_list.clear() if temp_list: From f3a6f50e2c6d273c363fda4c8e1b1c0d8cc69ea0 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 13 Dec 2017 16:47:38 +0100 Subject: [PATCH 104/104] CURA-4234 fix topbar resize issues and removed hard coding numbers --- resources/qml/Topbar.qml | 59 ++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index d2f1db37b2..9b67856fc8 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -21,6 +21,22 @@ Rectangle property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands + property int rightMargin: UM.Theme.getSize("sidebar").width + UM.Theme.getSize("default_margin").width; + property int allItemsWidth: 0; + + function updateMarginsAndSizes() { + if (UM.Preferences.getValue("cura/sidebar_collapse")) { + rightMargin = UM.Theme.getSize("default_margin").width; + } else { + rightMargin = UM.Theme.getSize("sidebar").width + UM.Theme.getSize("default_margin").width; + } + allItemsWidth = ( + logo.width + UM.Theme.getSize("topbar_logo_right_margin").width + + UM.Theme.getSize("topbar_logo_right_margin").width + stagesMenuContainer.width + + UM.Theme.getSize("default_margin").width + viewModeButton.width + + rightMargin); + } + UM.I18nCatalog { id: catalog @@ -44,6 +60,7 @@ Rectangle Row { + id: stagesMenuContainer anchors.left: logo.right anchors.leftMargin: UM.Theme.getSize("topbar_logo_right_margin").width spacing: UM.Theme.getSize("default_margin").width @@ -85,8 +102,8 @@ Rectangle anchors { verticalCenter: base.verticalCenter - right: viewModeButton.right - rightMargin: UM.Theme.getSize("default_margin").width + viewModeButton.width + right: viewModeButton.left + rightMargin: UM.Theme.getSize("default_margin").width } // #1 3d view @@ -98,7 +115,7 @@ Rectangle onClicked:{ UM.Controller.rotateView("3d", 0); } - visible: base.width > 1100 + visible: base.width - allItemsWidth - 4 * this.width > 0; } // #2 Front view @@ -110,7 +127,7 @@ Rectangle onClicked:{ UM.Controller.rotateView("home", 0); } - visible: base.width > 1130 + visible: base.width - allItemsWidth - 3 * this.width > 0; } // #3 Top view @@ -122,7 +139,7 @@ Rectangle onClicked:{ UM.Controller.rotateView("y", 90); } - visible: base.width > 1160 + visible: base.width - allItemsWidth - 2 * this.width > 0; } // #4 Left view @@ -134,7 +151,7 @@ Rectangle onClicked:{ UM.Controller.rotateView("x", 90); } - visible: base.width > 1190 + visible: base.width - allItemsWidth - 1 * this.width > 0; } // #5 Left view @@ -146,14 +163,13 @@ Rectangle onClicked:{ UM.Controller.rotateView("x", -90); } - visible: base.width > 1210 + visible: base.width - allItemsWidth > 0; } } ComboBox { id: viewModeButton - property int rightMargin: UM.Theme.getSize("sidebar").width + UM.Theme.getSize("default_margin").width; anchors { verticalCenter: parent.verticalCenter @@ -161,14 +177,6 @@ Rectangle rightMargin: rightMargin } - function updateMargins() { - if (UM.Preferences.getValue("cura/sidebar_collapse")) { - rightMargin = UM.Theme.getSize("default_margin").width; - } else { - rightMargin = UM.Theme.getSize("sidebar").width + UM.Theme.getSize("default_margin").width; - } - } - style: UM.Theme.styles.combobox visible: UM.Controller.activeStage.stageId != "MonitorStage" @@ -210,13 +218,6 @@ Rectangle } } - // Expand or collapse sidebar - Connections - { - target: Cura.Actions.expandSidebar - onTriggered: viewModeButton.updateMargins() - } - Loader { id: view_panel @@ -233,4 +234,16 @@ Rectangle source: UM.ActiveView.valid ? UM.ActiveView.activeViewPanel : ""; } + // Expand or collapse sidebar + Connections + { + target: Cura.Actions.expandSidebar + onTriggered: updateMarginsAndSizes() + } + + Component.onCompleted: + { + updateMarginsAndSizes(); + } + }