From f436da867429781943473b04c723e5126434b11d Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 11 May 2017 14:00:29 +0200 Subject: [PATCH 01/17] WIP: fix project loading and container renaming CURA-3756 --- plugins/3MFReader/ThreeMFWorkspaceReader.py | 103 ++++++++++++++------ 1 file changed, 75 insertions(+), 28 deletions(-) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index e751211d04..65a4858257 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -167,6 +167,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): quality_type = "" num_settings_overriden_by_quality_changes = 0 # How many settings are changed by the quality changes num_user_settings = 0 + quality_changes_conflict = False for instance_container_file in instance_container_files: container_id = self._stripFileToId(instance_container_file) instance_container = InstanceContainer(container_id) @@ -202,7 +203,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader): file_name, cura_file_names) self._resolve_strategies = {"machine": None, "quality_changes": None, "material": None} machine_conflict = False - quality_changes_conflict = False for container_stack_file in [global_stack_file] + extruder_stack_files: container_id = self._stripFileToId(container_stack_file) serialized = archive.open(container_stack_file).read().decode("utf-8") @@ -262,6 +262,14 @@ class ThreeMFWorkspaceReader(WorkspaceReader): return WorkspaceReader.PreReadResult.cancelled self._resolve_strategies = self._dialog.getResult() + # + # There can be 3 resolve strategies coming from the dialog: + # - new: create a new container + # - override: override the existing container + # - None: There is no conflict, which means containers with the same IDs may or may not be there already. + # If they are there, there is no conflict between the them. + # In this case, you can either create a new one, or safely override the existing one. + # # Default values for k, v in self._resolve_strategies.items(): if v is None: @@ -325,7 +333,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader): global_stack_id_new = self.getNewId(global_stack_id_original) global_stack_need_rename = True - # TODO: For the moment we use pretty naive existence checking. If the ID is the same, we assume in quite a few # TODO: cases that the container loaded is the same (most notable in materials & definitions). # TODO: It might be possible that we need to add smarter checking in the future. @@ -352,21 +359,24 @@ class ThreeMFWorkspaceReader(WorkspaceReader): for material_container_file in material_container_files: container_id = self._stripFileToId(material_container_file) materials = self._container_registry.findInstanceContainers(id = container_id) + if not materials: material_container = xml_material_profile(container_id) material_container.deserialize(archive.open(material_container_file).read().decode("utf-8")) containers_to_add.append(material_container) else: - if not materials[0].isReadOnly(): # Only create new materials if they are not read only. + material_container = materials[0] + if not material_container.isReadOnly(): # Only create new materials if they are not read only. if self._resolve_strategies["material"] == "override": - materials[0].deserialize(archive.open(material_container_file).read().decode("utf-8")) + material_container.deserialize(archive.open(material_container_file).read().decode("utf-8")) elif self._resolve_strategies["material"] == "new": # Note that we *must* deserialize it with a new ID, as multiple containers will be # auto created & added. material_container = xml_material_profile(self.getNewId(container_id)) material_container.deserialize(archive.open(material_container_file).read().decode("utf-8")) containers_to_add.append(material_container) - material_containers.append(material_container) + + material_containers.append(material_container) Job.yieldThread() Logger.log("d", "Workspace loading is checking instance containers...") @@ -394,18 +404,20 @@ class ThreeMFWorkspaceReader(WorkspaceReader): # The machine is going to get a spiffy new name, so ensure that the id's of user settings match. extruder_id = instance_container.getMetaDataEntry("extruder", None) if extruder_id: - new_id = self.getNewId(extruder_id) + "_current_settings" + new_extruder_id = self.getNewId(extruder_id) + new_id = new_extruder_id + "_current_settings" instance_container._id = new_id instance_container.setName(new_id) - instance_container.setMetaDataEntry("extruder", self.getNewId(extruder_id)) + instance_container.setMetaDataEntry("extruder", new_extruder_id) containers_to_add.append(instance_container) machine_id = instance_container.getMetaDataEntry("machine", None) if machine_id: - new_id = self.getNewId(machine_id) + "_current_settings" + new_machine_id = self.getNewId(machine_id) + new_id = new_machine_id + "_current_settings" instance_container._id = new_id instance_container.setName(new_id) - instance_container.setMetaDataEntry("machine", self.getNewId(machine_id)) + instance_container.setMetaDataEntry("machine", new_machine_id) containers_to_add.append(instance_container) user_instance_containers.append(instance_container) elif container_type in ("quality_changes", "definition_changes"): @@ -558,53 +570,88 @@ class ThreeMFWorkspaceReader(WorkspaceReader): return None + # + # Replacing the old containers if resolve is "new". + # When resolve is "new", some containers will get renamed, so all the other containers that reference to those + # MUST get updated too. + # if self._resolve_strategies["machine"] == "new": # A new machine was made, but it was serialized with the wrong user container. Fix that now. for container in user_instance_containers: + # replacing the container ID for user instance containers for the extruders extruder_id = container.getMetaDataEntry("extruder", None) if extruder_id: for extruder in extruder_stacks: if extruder.getId() == extruder_id: extruder.replaceContainer(0, container) continue + + # replacing the container ID for user instance containers for the machine machine_id = container.getMetaDataEntry("machine", None) if machine_id: if global_stack.getId() == machine_id: global_stack.replaceContainer(0, container) continue - for container_type in ("quality_changes", "definition_changes"): - if self._resolve_strategies[container_type] == "new": + for changes_container_type in ("quality_changes", "definition_changes"): + if self._resolve_strategies[changes_container_type] == "new": # Quality changes needs to get a new ID, added to registry and to the right stacks - for container in quality_and_definition_changes_instance_containers: - old_id = container.getId() - container.setName(self._container_registry.uniqueName(container.getName())) + for each_changes_container in quality_and_definition_changes_instance_containers: + old_id = each_changes_container.getId() + each_changes_container.setName(self._container_registry.uniqueName(each_changes_container.getName())) # We're not really supposed to change the ID in normal cases, but this is an exception. - container._id = self.getNewId(container.getId()) + each_changes_container._id = self.getNewId(each_changes_container.getId()) # The container was not added yet, as it didn't have an unique ID. It does now, so add it. - self._container_registry.addContainer(container) + self._container_registry.addContainer(each_changes_container) - # Replace the quality/definition changes container - if container_type == "quality_changes": + # Find the old (current) changes container in the global stack + if changes_container_type == "quality_changes": old_container = global_stack.qualityChanges - elif container_type == "definition_changes": + elif changes_container_type == "definition_changes": old_container = global_stack.definitionChanges - # old_container = global_stack.findContainer({"type": container_type}) + + # sanity checks + # NOTE: The following cases SHOULD NOT happen!!!! + if not old_container: + Logger.log("e", "We try to get [%s] from the global stack [%s] but we got None instead!", + changes_container_type, global_stack.getId()) + + # Replace the quality/definition changes container if it's in the GlobalStack + # NOTE: we can get an empty container here, but the IDs will not match, + # so this comparison is fine. if old_container.getId() == old_id: - changes_index = global_stack.getContainerIndex(old_container) - global_stack.replaceContainer(changes_index, container) + if changes_container_type == "quality_changes": + global_stack.qualityChanges = each_changes_container + elif changes_container_type == "definition_changes": + global_stack.definitionChanges = each_changes_container continue - for stack in extruder_stacks: - old_container = stack.findContainer({"type": container_type}) - if old_container.getId() == old_id: - changes_index = stack.getContainerIndex(old_container) - stack.replaceContainer(changes_index, container) + # Replace the quality/definition changes container if it's in one of the ExtruderStacks + for each_extruder_stack in extruder_stacks: + changes_container = None + if changes_container_type == "quality_changes": + changes_container = each_extruder_stack.qualityChanges + elif changes_container_type == "definition_changes": + changes_container = each_extruder_stack.definitionChanges + + # sanity checks + # NOTE: The following cases SHOULD NOT happen!!!! + if not changes_container: + Logger.log("e", "We try to get [%s] from the extruder stack [%s] but we got None instead!", + changes_container_type, each_extruder_stack.getId()) + + # NOTE: we can get an empty container here, but the IDs will not match, + # so this comparison is fine. + if changes_container.getId() == old_id: + if changes_container_type == "quality_changes": + each_extruder_stack.qualityChanges = each_changes_container + elif changes_container_type == "definition_changes": + each_extruder_stack.definitionChanges = each_changes_container if self._resolve_strategies["material"] == "new": for material in material_containers: - old_material = global_stack.findContainer({"type": "material"}) + old_material = global_stack.material if old_material.getId() in self._id_mapping: material_index = global_stack.getContainerIndex(old_material) global_stack.replaceContainer(material_index, material) From bb0e7115775d67feeda7401ab4de1057c4248e4c Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 11 May 2017 14:18:58 +0200 Subject: [PATCH 02/17] WIP: fix material container renaming in project loading CURA-3756 --- plugins/3MFReader/ThreeMFWorkspaceReader.py | 28 ++++++++++++++------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 65a4858257..0d244c4492 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -650,20 +650,30 @@ class ThreeMFWorkspaceReader(WorkspaceReader): each_extruder_stack.definitionChanges = each_changes_container if self._resolve_strategies["material"] == "new": - for material in material_containers: + for each_material in material_containers: old_material = global_stack.material - if old_material.getId() in self._id_mapping: - material_index = global_stack.getContainerIndex(old_material) - global_stack.replaceContainer(material_index, material) + + # check if the old material container has been renamed to this material container ID + # if the container hasn't been renamed, we do nothing. + new_id = self._id_mapping.get(old_material.getId()) + if new_id is None or new_id != each_material.getId(): continue - for stack in extruder_stacks: - old_material = stack.findContainer({"type": "material"}) - if old_material.getId() in self._id_mapping: - material_index = stack.getContainerIndex(old_material) - stack.replaceContainer(material_index, material) + if old_material.getId() in self._id_mapping: + global_stack.material = each_material + + for each_extruder_stack in extruder_stacks: + old_material = each_extruder_stack.material + + # check if the old material container has been renamed to this material container ID + # if the container hasn't been renamed, we do nothing. + new_id = self._id_mapping.get(old_material.getId()) + if new_id is None or new_id != each_material.getId(): continue + if old_material.getId() in self._id_mapping: + each_extruder_stack.material = each_material + if extruder_stacks: for stack in extruder_stacks: ExtruderManager.getInstance().registerExtruder(stack, global_stack.getId()) From 3baec9b74221a5fa9209dada26d4695da5fb4429 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 11 May 2017 14:53:02 +0200 Subject: [PATCH 03/17] WIP: set dirty after overriding a user container with deserialize() CURA-3756 --- plugins/3MFReader/ThreeMFWorkspaceReader.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 0d244c4492..8cb34e6681 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -398,8 +398,10 @@ class ThreeMFWorkspaceReader(WorkspaceReader): if not user_containers: containers_to_add.append(instance_container) else: + instance_container = user_containers[0] if self._resolve_strategies["machine"] == "override" or self._resolve_strategies["machine"] is None: - user_containers[0].deserialize(archive.open(instance_container_file).read().decode("utf-8")) + instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8")) + instance_container.setDirty(True) elif self._resolve_strategies["machine"] == "new": # The machine is going to get a spiffy new name, so ensure that the id's of user settings match. extruder_id = instance_container.getMetaDataEntry("extruder", None) From 8a94f9425806107f73983f407c9d44f1a8ee25b0 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 11 May 2017 14:53:56 +0200 Subject: [PATCH 04/17] WIP: use .userChanges property to replace user containers in project loading CURA-3756 --- plugins/3MFReader/ThreeMFWorkspaceReader.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 8cb34e6681..2a3719a4cc 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -585,14 +585,14 @@ class ThreeMFWorkspaceReader(WorkspaceReader): if extruder_id: for extruder in extruder_stacks: if extruder.getId() == extruder_id: - extruder.replaceContainer(0, container) + extruder.userChanges = container continue # replacing the container ID for user instance containers for the machine machine_id = container.getMetaDataEntry("machine", None) if machine_id: if global_stack.getId() == machine_id: - global_stack.replaceContainer(0, container) + global_stack.userChanges = container continue for changes_container_type in ("quality_changes", "definition_changes"): From 7508a0a98155ef16a9c42fe9fdc1d5dc3f8178ee Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 12 May 2017 10:24:15 +0200 Subject: [PATCH 05/17] Changed infill buttons so they work with all languages CURA-3634 --- resources/qml/SidebarSimple.qml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index 3eaf0797e8..1e4a715095 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -125,7 +125,7 @@ Item { id: infillIcon anchors.fill: parent; - anchors.margins: UM.Theme.getSize("infill_button_margin").width + anchors.margins: 2 sourceSize.width: width sourceSize.height: width @@ -185,7 +185,7 @@ Item Component.onCompleted: { infillModel.append({ - name: catalog.i18nc("@label", "Empty"), + name: catalog.i18nc("@label", "0%"), percentage: 0, steps: 0, percentageMin: -1, @@ -196,7 +196,7 @@ Item icon: "hollow" }) infillModel.append({ - name: catalog.i18nc("@label", "Light"), + name: catalog.i18nc("@label", "20%"), percentage: 20, steps: 0, percentageMin: 0, @@ -207,7 +207,7 @@ Item icon: "sparse" }) infillModel.append({ - name: catalog.i18nc("@label", "Dense"), + name: catalog.i18nc("@label", "50%"), percentage: 50, steps: 0, percentageMin: 30, @@ -218,7 +218,7 @@ Item icon: "dense" }) infillModel.append({ - name: catalog.i18nc("@label", "Solid"), + name: catalog.i18nc("@label", "100%"), percentage: 100, steps: 0, percentageMin: 70, From 502e977c24d8fe821b3021be1bf1f40e3b79b22b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 12 May 2017 10:27:13 +0200 Subject: [PATCH 06/17] UM2E now has olson block upgrade selection as machine action CURA-3742 --- resources/definitions/ultimaker2_extended.def.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/definitions/ultimaker2_extended.def.json b/resources/definitions/ultimaker2_extended.def.json index 803e9fe896..ac9d98c5eb 100644 --- a/resources/definitions/ultimaker2_extended.def.json +++ b/resources/definitions/ultimaker2_extended.def.json @@ -11,8 +11,7 @@ "file_formats": "text/x-gcode", "icon": "icon_ultimaker2.png", "platform": "ultimaker2_platform.obj", - "platform_texture": "Ultimaker2Extendedbackplate.png", - "supported_actions": ["UpgradeFirmware"] + "platform_texture": "Ultimaker2Extendedbackplate.png" }, "overrides": { From 67aa6767c59732588c2412ddae64ca2c9e148d11 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 12 May 2017 13:23:29 +0200 Subject: [PATCH 07/17] Add .DS_Store and *.qmlc --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 527e5dc049..1db07180c0 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,10 @@ resources/firmware resources/materials LC_MESSAGES .cache +*.qmlc + +#MacOS +.DS_Store # Editors and IDEs. *kdev* From f60765c56c5ab73266009ecc56a66ded60a59ac2 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 12 May 2017 13:24:58 +0200 Subject: [PATCH 08/17] Fix workspace dialog layout so checkbox and buttons dont get hidden --- resources/qml/WorkspaceSummaryDialog.qml | 64 ++++++++++++------------ 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/resources/qml/WorkspaceSummaryDialog.qml b/resources/qml/WorkspaceSummaryDialog.qml index 7da8495da0..cbde055138 100644 --- a/resources/qml/WorkspaceSummaryDialog.qml +++ b/resources/qml/WorkspaceSummaryDialog.qml @@ -13,13 +13,10 @@ UM.Dialog { title: catalog.i18nc("@title:window", "Save Project") - width: 550 * Screen.devicePixelRatio - minimumWidth: 550 * Screen.devicePixelRatio + width: 500 + height: 400 - height: 350 * Screen.devicePixelRatio - minimumHeight: 350 * Screen.devicePixelRatio - - property int spacerHeight: 10 * Screen.devicePixelRatio + property int spacerHeight: 10 property bool dontShowAgain: true @@ -42,7 +39,6 @@ UM.Dialog Item { anchors.fill: parent - anchors.margins: 20 * Screen.devicePixelRatio UM.SettingDefinitionsModel { @@ -232,42 +228,48 @@ UM.Dialog height: spacerHeight width: height } + } + + Row + { + id: buttonRow + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + height: childrenRect.height CheckBox { id: dontShowAgainCheckbox + anchors.left: parent.left + text: catalog.i18nc("@action:label", "Don't show project summary on save again") checked: dontShowAgain } - } + Button + { + id: ok_button + anchors.right: parent.right - Button - { - id: ok_button - text: catalog.i18nc("@action:button","Save"); - enabled: true - onClicked: { - close() - yes() + text: catalog.i18nc("@action:button","Save"); + enabled: true + onClicked: { + close() + yes() + } } - anchors.bottomMargin: - 0.5 * height - anchors.bottom: parent.bottom - anchors.right: parent.right - } - Button - { - id: cancel_button - text: catalog.i18nc("@action:button","Cancel"); - enabled: true - onClicked: close() - - anchors.bottom: parent.bottom - anchors.right: ok_button.left - anchors.bottomMargin: - 0.5 * height - anchors.rightMargin:2 + Button + { + id: cancel_button + anchors.right: ok_button.left + anchors.rightMargin: 2 + text: catalog.i18nc("@action:button","Cancel"); + enabled: true + onClicked: close() + } } } } \ No newline at end of file From ef2aa3b2b212b48ca75df9da6c9bd7f139a83fd4 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 12 May 2017 13:59:31 +0200 Subject: [PATCH 09/17] Empty InstanceContainers are now correctly handled by setActiveMaterial again CURA-3789 --- cura/Settings/MachineManager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 9d66bc7030..0837c577a5 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -721,7 +721,7 @@ class MachineManager(QObject): Logger.log("w", "While trying to set the active material, no material was found to replace it.") return - if old_quality_changes and old_quality_changes.getId() == "empty_quality_changes": + if old_quality_changes and isinstance(old_quality_changes, type(self._empty_quality_changes_container)): old_quality_changes = None self.blurSettings.emit() @@ -754,7 +754,7 @@ class MachineManager(QObject): candidate_quality = quality_manager.findQualityByQualityType(quality_type, quality_manager.getWholeMachineDefinition(machine_definition), [material_container]) - if not candidate_quality or candidate_quality.getId() == "empty_quality": + if not candidate_quality or isinstance(candidate_quality, type(self._empty_quality_changes_container)): # Fall back to a quality (which must be compatible with all other extruders) new_qualities = quality_manager.findAllUsableQualitiesForMachineAndExtruders( self._global_container_stack, ExtruderManager.getInstance().getExtruderStacks()) From 9ea0f4834105c8d0921f2f3a05f58ce37340a112 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 12 May 2017 14:14:58 +0200 Subject: [PATCH 10/17] Check definition changes conflicts in preRead() CURA-3756 --- plugins/3MFReader/ThreeMFWorkspaceReader.py | 58 +++++++++++++++------ 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 2a3719a4cc..16b08e90c4 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -109,7 +109,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader): machine_type = "" variant_type_name = i18n_catalog.i18nc("@label", "Nozzle") - num_extruders = 0 # Check if there are any conflicts, so we can ask the user. archive = zipfile.ZipFile(file_name, "r") cura_file_names = [name for name in archive.namelist() if name.startswith("Cura/")] @@ -121,31 +120,41 @@ class ThreeMFWorkspaceReader(WorkspaceReader): instance_container_list = [] material_container_list = [] + # + # Read definition containers + # + machine_definition_container_count = 0 + extruder_definition_container_count = 0 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) + 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) if not definitions: definition_container = DefinitionContainer(container_id) - definition_container.deserialize(archive.open(definition_container_file).read().decode("utf-8")) + definition_container.deserialize(archive.open(each_definition_container_file).read().decode("utf-8")) else: definition_container = definitions[0] - definition_container_list.append(definition_container) - if definition_container.getMetaDataEntry("type") != "extruder": + definition_container_type = definition_container.getMetaDataEntry("type") + if definition_container_type == "machine": machine_type = definition_container.getName() variant_type_name = definition_container.getMetaDataEntry("variants_name", variant_type_name) + + machine_definition_container_count += 1 + elif definition_container_type == "extruder": + extruder_definition_container_count += 1 else: - num_extruders += 1 + Logger.log("w", "Unknown definition container type %s for %s", + definition_container_type, each_definition_container_file) Job.yieldThread() - - if num_extruders == 0: - num_extruders = 1 # No extruder stacks found, which means there is one extruder - - extruders = num_extruders * [""] + # sanity check + if machine_definition_container_count != 1: + msg = "Expecting one machine definition container but got %s" % machine_definition_container_count + Logger.log("e", msg) + raise RuntimeError(msg) material_labels = [] material_conflict = False @@ -161,20 +170,22 @@ class ThreeMFWorkspaceReader(WorkspaceReader): if materials and not materials[0].isReadOnly(): # Only non readonly materials can be in conflict material_conflict = True Job.yieldThread() + # Check if any quality_changes instance container is in conflict. instance_container_files = [name for name in cura_file_names if name.endswith(self._instance_container_suffix)] quality_name = "" quality_type = "" num_settings_overriden_by_quality_changes = 0 # How many settings are changed by the quality changes + num_settings_overriden_by_definition_changes = 0 # How many settings are changed by the definition changes num_user_settings = 0 quality_changes_conflict = False - for instance_container_file in instance_container_files: - container_id = self._stripFileToId(instance_container_file) + definition_changes_conflict = False + for each_instance_container_file in instance_container_files: + container_id = self._stripFileToId(each_instance_container_file) instance_container = InstanceContainer(container_id) # Deserialize InstanceContainer by converting read data from bytes to string - instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8")) - + instance_container.deserialize(archive.open(each_instance_container_file).read().decode("utf-8")) instance_container_list.append(instance_container) container_type = instance_container.getMetaDataEntry("type") @@ -187,6 +198,13 @@ class ThreeMFWorkspaceReader(WorkspaceReader): # Check if there really is a conflict by comparing the values if quality_changes[0] != instance_container: quality_changes_conflict = True + elif container_type == "definition_changes": + definition_name = instance_container.getName() + num_settings_overriden_by_definition_changes += len(instance_container._instances) + definition_changes = self._container_registry.findDefinitionContainers(id = container_id) + if definition_changes: + if definition_changes[0] != instance_container: + definition_changes_conflict = True elif container_type == "quality": # If the quality name is not set (either by quality or changes, set it now) # Quality changes should always override this (as they are "on top") @@ -237,9 +255,17 @@ class ThreeMFWorkspaceReader(WorkspaceReader): if not show_dialog: return WorkspaceReader.PreReadResult.accepted + # prepare data for the dialog + num_extruders = extruder_definition_container_count + if num_extruders == 0: + num_extruders = 1 # No extruder stacks found, which means there is one extruder + + extruders = num_extruders * [""] + # Show the dialog, informing the user what is about to happen. self._dialog.setMachineConflict(machine_conflict) self._dialog.setQualityChangesConflict(quality_changes_conflict) + self._dialog.setDefinitionChangesConflict(definition_changes_conflict) self._dialog.setMaterialConflict(material_conflict) self._dialog.setNumVisibleSettings(num_visible_settings) self._dialog.setQualityName(quality_name) From fe11819ce9e9b077aa7d7585b9af236bf1eb3267 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 12 May 2017 14:17:40 +0200 Subject: [PATCH 11/17] Handle "new" resolve strategy for definition&quality changes CURA-3756 --- plugins/3MFReader/ThreeMFWorkspaceReader.py | 27 ++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 16b08e90c4..1485e6422f 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -455,7 +455,32 @@ class ThreeMFWorkspaceReader(WorkspaceReader): containers_to_add.append(instance_container) else: if self._resolve_strategies[container_type] == "override": - changes_containers[0].deserialize(archive.open(instance_container_file).read().decode("utf-8")) + instance_container = changes_containers[0] + instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8")) + instance_container.setDirty(True) + elif self._resolve_strategies[container_type] == "new": + # TODO: how should we handle the case "new" for quality_changes and definition_changes? + + new_changes_container_id = self.getNewId(instance_container.getId()) + instance_container._id = new_changes_container_id + instance_container.setName(new_changes_container_id) + + # TODO: we don't know the following is correct or not, need to verify + # AND REFACTOR!!! + if self._resolve_strategies["machine"] == "new": + # The machine is going to get a spiffy new name, so ensure that the id's of user settings match. + extruder_id = instance_container.getMetaDataEntry("extruder", None) + if extruder_id: + new_extruder_id = self.getNewId(extruder_id) + instance_container.setMetaDataEntry("extruder", new_extruder_id) + + machine_id = instance_container.getMetaDataEntry("machine", None) + if machine_id: + new_machine_id = self.getNewId(machine_id) + instance_container.setMetaDataEntry("machine", new_machine_id) + + containers_to_add.append(instance_container) + elif self._resolve_strategies[container_type] is None: # The ID already exists, but nothing in the values changed, so do nothing. pass From 167f616a7c5c81344921b895a9e7028f069c7bf7 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 12 May 2017 14:34:45 +0200 Subject: [PATCH 12/17] Decreased interval of extruder timer --- cura/Settings/ExtrudersModel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Settings/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py index d2922ebcc5..7491f8aa93 100644 --- a/cura/Settings/ExtrudersModel.py +++ b/cura/Settings/ExtrudersModel.py @@ -59,7 +59,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): self.addRoleName(self.VariantRole, "variant") self._update_extruder_timer = QTimer() - self._update_extruder_timer.setInterval(250) + self._update_extruder_timer.setInterval(100) self._update_extruder_timer.setSingleShot(True) self._update_extruder_timer.timeout.connect(self.__updateExtruders) From 3996230ff9f70da1564ac52545d7f99b1e70e2f0 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 12 May 2017 15:51:35 +0200 Subject: [PATCH 13/17] Load GlobalStack before ExtruderStacks for project loading CURA-3756 --- plugins/3MFReader/ThreeMFWorkspaceReader.py | 143 ++++++++++---------- 1 file changed, 71 insertions(+), 72 deletions(-) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 1485e6422f..3b5d9dac77 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -350,6 +350,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader): extruder_stacks_added = [] container_stacks_added = [] + containers_added = [] + global_stack_id_original = self._stripFileToId(global_stack_file) global_stack_id_new = global_stack_id_original global_stack_need_rename = False @@ -497,73 +499,12 @@ class ThreeMFWorkspaceReader(WorkspaceReader): for container in containers_to_add: self._container_registry.addContainer(container) container.setDirty(True) + containers_added.append(container) # Get the stack(s) saved in the workspace. Logger.log("d", "Workspace loading is checking stacks containers...") - # load extruder stack files - try: - for index, extruder_stack_file in enumerate(extruder_stack_files): - container_id = self._stripFileToId(extruder_stack_file) - - container_stacks = self._container_registry.findContainerStacks(id = container_id) - if container_stacks: - # this container stack already exists, try to resolve - stack = container_stacks[0] - if self._resolve_strategies["machine"] == "override": - pass # do nothing - elif self._resolve_strategies["machine"] == "new": - # create a new extruder stack from this one - new_id = self.getNewId(container_id) - stack = ExtruderStack(new_id) - stack.deserialize(archive.open(extruder_stack_file).read().decode("utf-8")) - - # Ensure a unique ID and name - stack._id = new_id - - self._container_registry.addContainer(stack) - extruder_stacks_added.append(stack) - else: - if self._resolve_strategies["machine"] == "override": - global_stacks = self._container_registry.findContainerStacks(id = global_stack_id_original) - # deserialize new extruder stack over the current ones - if global_stacks: - old_extruder_stack_id = global_stacks[0].extruders[index].getId() - # HACK delete file - self._container_registry._deleteFiles(global_stacks[0].extruders[index]) - global_stacks[0].extruders[index].deserialize(archive.open(extruder_stack_file).read().decode("utf-8")) - # HACK - global_stacks[0]._extruders = global_stacks[0]._extruders[:2] - # HACK update cache - del self._container_registry._id_container_cache[old_extruder_stack_id] - new_extruder_stack_id = global_stacks[0].extruders[index].getId() - self._container_registry._id_container_cache[new_extruder_stack_id] = global_stacks[0].extruders[index] - - stack = global_stacks[0].extruders[index] - else: - Logger.log("w", "Could not find global stack, while I expected it: %s" % global_stack_id_original) - elif self._resolve_strategies["machine"] == "new": - # container not found, create a new one - stack = ExtruderStack(container_id) - stack.deserialize(archive.open(extruder_stack_file).read().decode("utf-8")) - self._container_registry.addContainer(stack) - extruder_stacks_added.append(stack) - else: - Logger.log("w", "Unknown resolve strategy: %s" % str(self._resolve_strategies["machine"])) - - if global_stack_need_rename: - if stack.getMetaDataEntry("machine"): - stack.setMetaDataEntry("machine", global_stack_id_new) - extruder_stacks.append(stack) - - except: - Logger.logException("w", "We failed to serialize the stack. Trying to clean up.") - # Something went really wrong. Try to remove any data that we added. - for container in extruder_stacks: - self._container_registry.removeContainer(container.getId()) - - return None - + # -- # load global stack file try: # Check if a stack by this ID already exists; @@ -606,22 +547,80 @@ class ThreeMFWorkspaceReader(WorkspaceReader): stack.deserialize(archive.open(global_stack_file).read().decode("utf-8")) container_stacks_added.append(stack) self._container_registry.addContainer(stack) + containers_added.append(stack) global_stack = stack Job.yieldThread() + except: + Logger.logException("w", "We failed to serialize the stack. Trying to clean up.") + # Something went really wrong. Try to remove any data that we added. + for container in containers_added: + self._container_registry.removeContainer(container.getId()) + return + + # -- + # load extruder stack files + try: + for index, extruder_stack_file in enumerate(extruder_stack_files): + container_id = self._stripFileToId(extruder_stack_file) + + container_stacks = self._container_registry.findContainerStacks(id = container_id) + if container_stacks: + # this container stack already exists, try to resolve + stack = container_stacks[0] + if self._resolve_strategies["machine"] == "override": + pass # do nothing + elif self._resolve_strategies["machine"] == "new": + # create a new extruder stack from this one + new_id = self.getNewId(container_id) + stack = ExtruderStack(new_id) + stack.deserialize(archive.open(extruder_stack_file).read().decode("utf-8")) + + # Ensure a unique ID and name + stack._id = new_id + + self._container_registry.addContainer(stack) + extruder_stacks_added.append(stack) + containers_added.append(stack) + else: + if self._resolve_strategies["machine"] == "override": + global_stacks = self._container_registry.findContainerStacks(id = global_stack_id_original) + # deserialize new extruder stack over the current ones + if global_stacks: + old_extruder_stack_id = global_stacks[0].extruders[index].getId() + # HACK delete file + self._container_registry._deleteFiles(global_stacks[0].extruders[index]) + global_stacks[0].extruders[index].deserialize(archive.open(extruder_stack_file).read().decode("utf-8")) + # HACK + global_stacks[0]._extruders = global_stacks[0]._extruders[:2] + # HACK update cache + del self._container_registry._id_container_cache[old_extruder_stack_id] + new_extruder_stack_id = global_stacks[0].extruders[index].getId() + self._container_registry._id_container_cache[new_extruder_stack_id] = global_stacks[0].extruders[index] + + stack = global_stacks[0].extruders[index] + else: + Logger.log("w", "Could not find global stack, while I expected it: %s" % global_stack_id_original) + elif self._resolve_strategies["machine"] == "new": + # container not found, create a new one + stack = ExtruderStack(container_id) + stack.deserialize(archive.open(extruder_stack_file).read().decode("utf-8")) + self._container_registry.addContainer(stack) + extruder_stacks_added.append(stack) + containers_added.append(stack) + else: + Logger.log("w", "Unknown resolve strategy: %s" % str(self._resolve_strategies["machine"])) + + if global_stack_need_rename: + if stack.getMetaDataEntry("machine"): + stack.setMetaDataEntry("machine", global_stack_id_new) + extruder_stacks.append(stack) except: Logger.logException("w", "We failed to serialize the stack. Trying to clean up.") # Something went really wrong. Try to remove any data that we added. - for container in containers_to_add: + for container in containers_added: self._container_registry.removeContainer(container.getId()) - - for container in container_stacks_added: - self._container_registry.removeContainer(container.getId()) - - for container in extruder_stacks_added: - self._container_registry.removeContainer(container.getId()) - - return None + return # # Replacing the old containers if resolve is "new". From 2b64e92a99aba7ba3b60ab8e4af3bd6adbfe8e9d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 12 May 2017 16:13:09 +0200 Subject: [PATCH 14/17] Removed unused imports --- cura/Settings/MachineManager.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 0837c577a5..f2c91f9e74 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -11,14 +11,11 @@ from UM.Application import Application from UM.Preferences import Preferences from UM.Logger import Logger from UM.Message import Message -from UM.Decorators import deprecated from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.ContainerStack import ContainerStack from UM.Settings.InstanceContainer import InstanceContainer -from UM.Settings.SettingDefinition import SettingDefinition from UM.Settings.SettingFunction import SettingFunction -from UM.Settings.Validator import ValidatorState from UM.Signal import postponeSignals import UM.FlameProfiler @@ -39,6 +36,7 @@ if TYPE_CHECKING: import os + class MachineManager(QObject): def __init__(self, parent = None): super().__init__(parent) @@ -105,8 +103,6 @@ class MachineManager(QObject): self._material_incompatible_message = Message(catalog.i18nc("@info:status", "The selected material is incompatible with the selected machine or configuration.")) - - 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 08c0779ddedaeca057f91f531556f2dfa004b9cb Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 12 May 2017 16:18:46 +0200 Subject: [PATCH 15/17] Remove unused file, context menu takes care of the multiply object dialog --- resources/qml/MultiplyObjectOptions.qml | 66 ------------------------- 1 file changed, 66 deletions(-) delete mode 100644 resources/qml/MultiplyObjectOptions.qml diff --git a/resources/qml/MultiplyObjectOptions.qml b/resources/qml/MultiplyObjectOptions.qml deleted file mode 100644 index a079369d0d..0000000000 --- a/resources/qml/MultiplyObjectOptions.qml +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2015 Ultimaker B.V. -// Cura is released under the terms of the AGPLv3 or higher. - -import QtQuick 2.2 -import QtQuick.Controls 1.1 -import QtQuick.Window 2.1 - -import UM 1.1 as UM - -UM.Dialog -{ - id: base - - //: Dialog title - title: catalog.i18nc("@title:window", "Multiply Model") - - minimumWidth: 400 * Screen.devicePixelRatio - minimumHeight: 80 * Screen.devicePixelRatio - width: minimumWidth - height: minimumHeight - - property var objectId: 0; - onAccepted: CuraApplication.multiplyObject(base.objectId, parseInt(copiesField.text)) - - property variant catalog: UM.I18nCatalog { name: "cura" } - - signal reset() - onReset: { - copiesField.text = "1"; - copiesField.selectAll(); - copiesField.focus = true; - } - - Row - { - spacing: UM.Theme.getSize("default_margin").width - - Label { - text: "Number of copies:" - anchors.verticalCenter: copiesField.verticalCenter - } - - TextField { - id: copiesField - validator: RegExpValidator { regExp: /^\d{0,2}/ } - maximumLength: 2 - } - } - - - rightButtons: - [ - Button - { - text: catalog.i18nc("@action:button","OK") - onClicked: base.accept() - enabled: base.objectId != 0 && parseInt(copiesField.text) > 0 - }, - Button - { - text: catalog.i18nc("@action:button","Cancel") - onClicked: base.reject() - } - ] -} - From 77204fc4b010506849dbc2be58f9e4000b23a7cf Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 12 May 2017 16:27:37 +0200 Subject: [PATCH 16/17] Type hinting & unused code removal --- cura/Settings/MachineManager.py | 188 ++++++-------------------------- 1 file changed, 34 insertions(+), 154 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index f2c91f9e74..57cc296bdf 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -327,7 +327,7 @@ class MachineManager(QObject): def _onInstanceContainersChanged(self, container): self._instance_container_timer.start() - def _onPropertyChanged(self, key, property_name): + def _onPropertyChanged(self, key: str, property_name: str): if property_name == "value": # Notify UI items, such as the "changed" star in profile pull down menu. self.activeStackValueChanged.emit() @@ -411,7 +411,7 @@ class MachineManager(QObject): ## Delete a user setting from the global stack and all extruder stacks. # \param key \type{str} the name of the key to delete @pyqtSlot(str) - def clearUserSettingAllCurrentStacks(self, key): + def clearUserSettingAllCurrentStacks(self, key: str): if not self._global_container_stack: return @@ -567,7 +567,7 @@ class MachineManager(QObject): # \return The layer height of the currently active quality profile. If # there is no quality profile, this returns 0. @pyqtProperty(float, notify=activeQualityChanged) - def activeQualityLayerHeight(self): + def activeQualityLayerHeight(self) -> float: if not self._global_container_stack: return 0 @@ -584,7 +584,7 @@ class MachineManager(QObject): value = value(self._global_container_stack) return value - return 0 #No quality profile. + return 0 # No quality profile. ## Get the Material ID associated with the currently active material # \returns MaterialID (string) if found, empty string otherwise @@ -606,7 +606,7 @@ class MachineManager(QObject): return "" @pyqtProperty(str, notify=activeQualityChanged) - def activeQualityName(self): + def activeQualityName(self) -> str: if self._active_container_stack and self._global_container_stack: quality = self._global_container_stack.qualityChanges if quality and not isinstance(quality, type(self._empty_quality_changes_container)): @@ -617,7 +617,7 @@ class MachineManager(QObject): return "" @pyqtProperty(str, notify=activeQualityChanged) - def activeQualityId(self): + def activeQualityId(self) -> str: if self._active_container_stack: quality = self._active_container_stack.qualityChanges if quality and not isinstance(quality, type(self._empty_quality_changes_container)): @@ -628,7 +628,7 @@ class MachineManager(QObject): return "" @pyqtProperty(str, notify=activeQualityChanged) - def globalQualityId(self): + def globalQualityId(self) -> str: if self._global_container_stack: quality = self._global_container_stack.qualityChanges if quality and not isinstance(quality, type(self._empty_quality_changes_container)): @@ -639,7 +639,7 @@ class MachineManager(QObject): return "" @pyqtProperty(str, notify = activeQualityChanged) - def activeQualityType(self): + def activeQualityType(self) -> str: if self._active_container_stack: quality = self._active_container_stack.quality if quality: @@ -647,7 +647,7 @@ class MachineManager(QObject): return "" @pyqtProperty(bool, notify = activeQualityChanged) - def isActiveQualitySupported(self): + def isActiveQualitySupported(self) -> bool: if self._active_container_stack: quality = self._active_container_stack.quality if quality: @@ -661,7 +661,7 @@ class MachineManager(QObject): # \todo Ideally, this method would be named activeQualityId(), and the other one # would be named something like activeQualityOrQualityChanges() for consistency @pyqtProperty(str, notify = activeQualityChanged) - def activeQualityContainerId(self): + def activeQualityContainerId(self) -> str: # We're using the active stack instead of the global stack in case the list of qualities differs per extruder if self._global_container_stack: quality = self._active_container_stack.quality @@ -670,7 +670,7 @@ class MachineManager(QObject): return "" @pyqtProperty(str, notify = activeQualityChanged) - def activeQualityChangesId(self): + def activeQualityChangesId(self) -> str: if self._active_container_stack: changes = self._active_container_stack.qualityChanges if changes and changes.getId() != "empty": @@ -687,7 +687,7 @@ class MachineManager(QObject): ## Copy the value of the setting of the current extruder to all other extruders as well as the global container. @pyqtSlot(str) - def copyValueToExtruders(self, key): + def copyValueToExtruders(self, key: str): if not self._active_container_stack or self._global_container_stack.getProperty("machine_extruder_count", "value") <= 1: return @@ -701,7 +701,7 @@ class MachineManager(QObject): ## Set the active material by switching out a container # Depending on from/to material+current variant, a quality profile is chosen and set. @pyqtSlot(str) - def setActiveMaterial(self, material_id): + def setActiveMaterial(self, material_id: str): with postponeSignals(*self._getContainerChangedSignals(), compress = True): containers = ContainerRegistry.getInstance().findInstanceContainers(id = material_id) if not containers or not self._active_container_stack: @@ -766,7 +766,7 @@ class MachineManager(QObject): self.setActiveQuality(new_quality_id) @pyqtSlot(str) - def setActiveVariant(self, variant_id): + def setActiveVariant(self, variant_id: str): with postponeSignals(*self._getContainerChangedSignals(), compress = True): containers = ContainerRegistry.getInstance().findInstanceContainers(id = variant_id) if not containers or not self._active_container_stack: @@ -789,7 +789,7 @@ class MachineManager(QObject): ## set the active quality # \param quality_id The quality_id of either a quality or a quality_changes @pyqtSlot(str) - def setActiveQuality(self, quality_id): + def setActiveQuality(self, quality_id: str): with postponeSignals(*self._getContainerChangedSignals(), compress = True): self.blurSettings.emit() @@ -848,7 +848,7 @@ class MachineManager(QObject): # \param quality_name \type{str} the name of the quality. # \return \type{List[Dict]} with keys "stack", "quality" and "quality_changes". @UM.FlameProfiler.profile - def determineQualityAndQualityChangesForQualityType(self, quality_type): + def determineQualityAndQualityChangesForQualityType(self, quality_type: str): quality_manager = QualityManager.getInstance() result = [] empty_quality_changes = self._empty_quality_changes_container @@ -885,7 +885,7 @@ class MachineManager(QObject): # # \param quality_changes_name \type{str} the name of the quality changes. # \return \type{List[Dict]} with keys "stack", "quality" and "quality_changes". - def _determineQualityAndQualityChangesForQualityChanges(self, quality_changes_name): + def _determineQualityAndQualityChangesForQualityChanges(self, quality_changes_name: str): result = [] quality_manager = QualityManager.getInstance() @@ -960,7 +960,7 @@ class MachineManager(QObject): Application.getInstance().discardOrKeepProfileChanges() @pyqtProperty(str, notify = activeVariantChanged) - def activeVariantName(self): + def activeVariantName(self) -> str: if self._active_container_stack: variant = self._active_container_stack.variant if variant: @@ -969,7 +969,7 @@ class MachineManager(QObject): return "" @pyqtProperty(str, notify = activeVariantChanged) - def activeVariantId(self): + def activeVariantId(self) -> str: if self._active_container_stack: variant = self._active_container_stack.variant if variant: @@ -978,7 +978,7 @@ class MachineManager(QObject): return "" @pyqtProperty(str, notify = globalContainerChanged) - def activeDefinitionId(self): + def activeDefinitionId(self) -> str: if self._global_container_stack: definition = self._global_container_stack.getBottom() if definition: @@ -987,7 +987,7 @@ class MachineManager(QObject): return "" @pyqtProperty(str, notify=globalContainerChanged) - def activeDefinitionName(self): + def activeDefinitionName(self) -> str: if self._global_container_stack: definition = self._global_container_stack.getBottom() if definition: @@ -999,7 +999,7 @@ class MachineManager(QObject): # \returns DefinitionID (string) if found, empty string otherwise # \sa getQualityDefinitionId @pyqtProperty(str, notify = globalContainerChanged) - def activeQualityDefinitionId(self): + def activeQualityDefinitionId(self) -> str: if self._global_container_stack: return self.getQualityDefinitionId(self._global_container_stack.getBottom()) return "" @@ -1008,14 +1008,14 @@ class MachineManager(QObject): # This is normally the id of the definition itself, but machines can specify a different definition to inherit qualities from # \param definition (DefinitionContainer) machine definition # \returns DefinitionID (string) if found, empty string otherwise - def getQualityDefinitionId(self, definition): + def getQualityDefinitionId(self, definition: "DefinitionContainer") -> str: return QualityManager.getInstance().getParentMachineDefinition(definition).getId() ## Get the Variant ID to use to select quality profiles for the currently active variant # \returns VariantID (string) if found, empty string otherwise # \sa getQualityVariantId @pyqtProperty(str, notify = activeVariantChanged) - def activeQualityVariantId(self): + def activeQualityVariantId(self) -> str: if self._active_container_stack: variant = self._active_container_stack.variant if variant: @@ -1026,9 +1026,9 @@ class MachineManager(QObject): # This is normally the id of the variant itself, but machines can specify a different definition # to inherit qualities from, which has consequences for the variant to use as well # \param definition (DefinitionContainer) machine definition - # \param variant (DefinitionContainer) variant definition + # \param variant (InstanceContainer) variant definition # \returns VariantID (string) if found, empty string otherwise - def getQualityVariantId(self, definition, variant): + def getQualityVariantId(self, definition: "DefinitionContainer", variant: "InstanceContainer") -> str: variant_id = variant.getId() definition_id = definition.getId() quality_definition_id = self.getQualityDefinitionId(definition) @@ -1040,7 +1040,7 @@ class MachineManager(QObject): ## Gets how the active definition calls variants # Caveat: per-definition-variant-title is currently not translated (though the fallback is) @pyqtProperty(str, notify = globalContainerChanged) - def activeDefinitionVariantsName(self): + 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) @@ -1048,7 +1048,7 @@ class MachineManager(QObject): return fallback_title @pyqtSlot(str, str) - def renameMachine(self, machine_id, new_name): + def renameMachine(self, machine_id: str, new_name: str): containers = ContainerRegistry.getInstance().findContainerStacks(id = machine_id) if containers: new_name = self._createUniqueName("machine", containers[0].getName(), new_name, containers[0].getBottom().getName()) @@ -1056,7 +1056,7 @@ class MachineManager(QObject): self.globalContainerChanged.emit() @pyqtSlot(str) - def removeMachine(self, machine_id): + def removeMachine(self, machine_id: str): # If the machine that is being removed is the currently active machine, set another machine as the active machine. activate_new_machine = (self._global_container_stack and self._global_container_stack.getId() == machine_id) @@ -1074,14 +1074,14 @@ class MachineManager(QObject): @pyqtProperty(bool, notify = globalContainerChanged) - def hasMaterials(self): + def hasMaterials(self) -> bool: if self._global_container_stack: return bool(self._global_container_stack.getMetaDataEntry("has_materials", False)) return False @pyqtProperty(bool, notify = globalContainerChanged) - def hasVariants(self): + def hasVariants(self) -> bool: if self._global_container_stack: return bool(self._global_container_stack.getMetaDataEntry("has_variants", False)) @@ -1090,7 +1090,7 @@ class MachineManager(QObject): ## Property to indicate if a machine has "specialized" material profiles. # Some machines have their own material profiles that "override" the default catch all profiles. @pyqtProperty(bool, notify = globalContainerChanged) - def filterMaterialsByMachine(self): + def filterMaterialsByMachine(self) -> bool: if self._global_container_stack: return bool(self._global_container_stack.getMetaDataEntry("has_machine_materials", False)) @@ -1099,7 +1099,7 @@ class MachineManager(QObject): ## Property to indicate if a machine has "specialized" quality profiles. # Some machines have their own quality profiles that "override" the default catch all profiles. @pyqtProperty(bool, notify = globalContainerChanged) - def filterQualityByMachine(self): + def filterQualityByMachine(self) -> bool: if self._global_container_stack: return bool(self._global_container_stack.getMetaDataEntry("has_machine_quality", False)) return False @@ -1108,7 +1108,7 @@ class MachineManager(QObject): # \param machine_id string machine id to get the definition ID of # \returns DefinitionID (string) if found, None otherwise @pyqtSlot(str, result = str) - def getDefinitionByMachineId(self, machine_id): + def getDefinitionByMachineId(self, machine_id: str) -> str: containers = ContainerRegistry.getInstance().findContainerStacks(id=machine_id) if containers: return containers[0].getBottom().getId() @@ -1117,22 +1117,6 @@ class MachineManager(QObject): def createMachineManager(engine=None, script_engine=None): return MachineManager() - def _updateVariantContainer(self, definition: "DefinitionContainer"): - if not definition.getMetaDataEntry("has_variants"): - return self._empty_variant_container - machine_definition_id = Application.getInstance().getMachineManager().getQualityDefinitionId(definition) - containers = [] - preferred_variant = definition.getMetaDataEntry("preferred_variant") - if preferred_variant: - containers = ContainerRegistry.getInstance().findInstanceContainers(type = "variant", definition = machine_definition_id, id = preferred_variant) - if not containers: - containers = ContainerRegistry.getInstance().findInstanceContainers(type = "variant", definition = machine_definition_id) - - if containers: - return containers[0] - - return self._empty_variant_container - def _updateMaterialContainer(self, definition: "DefinitionContainer", stack: "ContainerStack", variant_container: Optional["InstanceContainer"] = None, preferred_material_name: Optional[str] = None): if not definition.getMetaDataEntry("has_materials"): return self._empty_material_container @@ -1170,110 +1154,6 @@ class MachineManager(QObject): Logger.log("w", "Unable to find a material container with provided criteria, returning an empty one instead.") return self._empty_material_container - def _updateQualityContainer(self, definition: "DefinitionContainer", variant_container: "ContainerStack", material_container = None, preferred_quality_name: Optional[str] = None): - container_registry = ContainerRegistry.getInstance() - search_criteria = { "type": "quality" } - - if definition.getMetaDataEntry("has_machine_quality"): - search_criteria["definition"] = self.getQualityDefinitionId(definition) - - if definition.getMetaDataEntry("has_materials") and material_container: - search_criteria["material"] = material_container.id - else: - search_criteria["definition"] = "fdmprinter" - - if preferred_quality_name and preferred_quality_name != "empty": - search_criteria["name"] = preferred_quality_name - else: - preferred_quality = definition.getMetaDataEntry("preferred_quality") - if preferred_quality: - search_criteria["id"] = preferred_quality - - containers = container_registry.findInstanceContainers(**search_criteria) - if containers: - return containers[0] - - if "material" in search_criteria: - # First check if we can solve our material not found problem by checking if we can find quality containers - # that are assigned to the parents of this material profile. - try: - inherited_files = material_container.getInheritedFiles() - except AttributeError: # Material_container does not support inheritance. - inherited_files = [] - - if inherited_files: - for inherited_file in inherited_files: - # Extract the ID from the path we used to load the file. - search_criteria["material"] = os.path.basename(inherited_file).split(".")[0] - containers = container_registry.findInstanceContainers(**search_criteria) - if containers: - return containers[0] - # We still weren't able to find a quality for this specific material. - # Try to find qualities for a generic version of the material. - material_search_criteria = { "type": "material", "material": material_container.getMetaDataEntry("material"), "color_name": "Generic"} - if definition.getMetaDataEntry("has_machine_quality"): - if material_container: - material_search_criteria["definition"] = material_container.getDefinition().id - - if definition.getMetaDataEntry("has_variants"): - material_search_criteria["variant"] = material_container.getMetaDataEntry("variant") - else: - material_search_criteria["definition"] = self.getQualityDefinitionId(definition) - - if definition.getMetaDataEntry("has_variants") and variant_container: - material_search_criteria["variant"] = self.getQualityVariantId(definition, variant_container) - else: - material_search_criteria["definition"] = "fdmprinter" - material_containers = container_registry.findInstanceContainers(**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() - - containers = container_registry.findInstanceContainers(**search_criteria) - if containers: - return containers[0] - - if "name" in search_criteria or "id" in search_criteria: - # If a quality by this name can not be found, try a wider set of search criteria - search_criteria.pop("name", None) - search_criteria.pop("id", None) - - containers = container_registry.findInstanceContainers(**search_criteria) - if containers: - return containers[0] - - # Notify user that we were unable to find a matching quality - message = Message(catalog.i18nc("@info:status", "Unable to find a quality profile for this combination. Default settings will be used instead.")) - message.show() - return self._empty_quality_container - - ## Finds a quality-changes container to use if any other container - # changes. - # - # \param quality_type The quality type to find a quality-changes for. - # \param preferred_quality_changes_name The name of the quality-changes to - # pick, if any such quality-changes profile is available. - def _updateQualityChangesContainer(self, quality_type, preferred_quality_changes_name = None): - container_registry = ContainerRegistry.getInstance() # Cache. - search_criteria = { "type": "quality_changes" } - - search_criteria["quality"] = quality_type - if preferred_quality_changes_name: - search_criteria["name"] = preferred_quality_changes_name - - # Try to search with the name in the criteria first, since we prefer to have the correct name. - containers = container_registry.findInstanceContainers(**search_criteria) - if containers: # Found one! - return containers[0] - - if "name" in search_criteria: - del search_criteria["name"] # Not found, then drop the name requirement (if we had one) and search again. - containers = container_registry.findInstanceContainers(**search_criteria) - if containers: - return containers[0] - - return self._empty_quality_changes_container # Didn't find anything with the required quality_type. - def _onMachineNameChanged(self): self.globalContainerChanged.emit() From 7ee5a66c6448afef8c99d9338707c7454e7da870 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 12 May 2017 16:32:00 +0200 Subject: [PATCH 17/17] CuraContainerStack now overrides getBottom, so it always returns a definition --- cura/Settings/CuraContainerStack.py | 4 ++++ cura/Settings/MachineManager.py | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/cura/Settings/CuraContainerStack.py b/cura/Settings/CuraContainerStack.py index 6cffa6030b..4c5b7b08a0 100755 --- a/cura/Settings/CuraContainerStack.py +++ b/cura/Settings/CuraContainerStack.py @@ -254,6 +254,10 @@ class CuraContainerStack(ContainerStack): def definition(self) -> DefinitionContainer: return self._containers[_ContainerIndexes.Definition] + @override(ContainerStack) + def getBottom(self) -> "DefinitionContainer": + return self.definition + ## Check whether the specified setting has a 'user' value. # # A user value here is defined as the setting having a value in either diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 57cc296bdf..98526f00a5 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -33,6 +33,7 @@ from typing import TYPE_CHECKING, Optional if TYPE_CHECKING: from UM.Settings.DefinitionContainer import DefinitionContainer + from cura.Settings.GlobalStack import GlobalStack import os @@ -42,7 +43,7 @@ class MachineManager(QObject): super().__init__(parent) self._active_container_stack = None # type: ContainerStack - self._global_container_stack = None # type: ContainerStack + self._global_container_stack = None # type: GlobalStack self._error_check_timer = QTimer() self._error_check_timer.setInterval(250)