From d568531aed1565ce1f4c65a902d9f2030f4a5b9d Mon Sep 17 00:00:00 2001 From: adripo Date: Thu, 11 Jun 2020 17:14:53 +0200 Subject: [PATCH 01/22] Fixed next XY return position after pause --- plugins/PostProcessingPlugin/scripts/PauseAtHeight.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py b/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py index db66cc10fb..6673c790da 100644 --- a/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py +++ b/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py @@ -204,10 +204,11 @@ class PauseAtHeight(Script): """Get the X and Y values for a layer (will be used to get X and Y of the layer after the pause).""" lines = layer.split("\n") for line in lines: - if self.getValue(line, "X") is not None and self.getValue(line, "Y") is not None: - x = self.getValue(line, "X") - y = self.getValue(line, "Y") - return x, y + if not line.startswith("M205"): + if self.getValue(line, "X") is not None and self.getValue(line, "Y") is not None: + x = self.getValue(line, "X") + y = self.getValue(line, "Y") + return x, y return 0, 0 def execute(self, data: List[str]) -> List[str]: From 2fc06069baf87d396a0d850085002cf26c6d56c9 Mon Sep 17 00:00:00 2001 From: adripo Date: Thu, 11 Jun 2020 17:38:06 +0200 Subject: [PATCH 02/22] Added whitelist G0..G4 instead of blacklist --- plugins/PostProcessingPlugin/scripts/PauseAtHeight.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py b/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py index 6673c790da..8ed1bb8942 100644 --- a/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py +++ b/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py @@ -204,7 +204,7 @@ class PauseAtHeight(Script): """Get the X and Y values for a layer (will be used to get X and Y of the layer after the pause).""" lines = layer.split("\n") for line in lines: - if not line.startswith("M205"): + if line.startswith(("G0", "G1", "G2", "G3")): if self.getValue(line, "X") is not None and self.getValue(line, "Y") is not None: x = self.getValue(line, "X") y = self.getValue(line, "Y") From 637f14fdad23ff579ccf0ae4ef2d520d708e97cb Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 11 Jun 2020 19:46:25 +0200 Subject: [PATCH 03/22] Add setting: Minimum slope steepness for stair-stepping. part of fixing CURA-6989 (and tickets such as CURA-6747) --- resources/definitions/fdmprinter.def.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 85d18078e9..f81bcf05b3 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -4474,6 +4474,19 @@ "enabled": "support_enable or support_meshes_present", "settable_per_mesh": true }, + "support_bottom_stair_step_min_slope": + { + "label": "Support Stair Step Minimum Slope Angle", + "description": "The minimum slope of the area for stair-stepping to take effect. Low values should make support easier to remove on shallower slopes, but really low values may result in some very counter-intuitive results on other parts of the model.", + "unit": "°", + "type": "float", + "default_value": 10.0, + "limit_to_extruder": "support_bottom_extruder_nr if support_bottom_enable else support_infill_extruder_nr", + "minimum_value": "0.01", + "maximum_value": "89.99", + "enabled": "support_enable or support_meshes_present", + "settable_per_mesh": true + }, "support_join_distance": { "label": "Support Join Distance", From f4dc1805948db0e9fc5b6183ba2551cb6be24612 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 12 Jun 2020 11:31:13 +0200 Subject: [PATCH 04/22] Fix default value min_skin_width_for_expansion. At some point max_skin_angle_for_expansion s deafult value was updated, but the default value for its child, min_skin_width_for_expansion wasn't altered to reflect that. --- resources/definitions/fdmprinter.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index f81bcf05b3..3a698e0e2f 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -2082,7 +2082,7 @@ "description": "Skin areas narrower than this are not expanded. This avoids expanding the narrow skin areas that are created when the model surface has a slope close to the vertical.", "unit": "mm", "type": "float", - "default_value": 2.24, + "default_value": 0, "value": "top_layers * layer_height / math.tan(math.radians(max_skin_angle_for_expansion))", "minimum_value": "0", "enabled": "(top_layers > 0 or bottom_layers > 0) and (top_skin_expand_distance > 0 or bottom_skin_expand_distance > 0)", From 30ad6e1445b0a4c68046e2df567c86758d05a87e Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 12 Jun 2020 18:39:18 +0200 Subject: [PATCH 05/22] On crash report unbundled plugins + version numbers. 3T-sessions have their own internal tickets now! See CURA-7509 for this one. --- cura/CrashHandler.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cura/CrashHandler.py b/cura/CrashHandler.py index a2c9ede8ef..4c0dd4855b 100644 --- a/cura/CrashHandler.py +++ b/cura/CrashHandler.py @@ -215,6 +215,16 @@ class CrashHandler: locale.getdefaultlocale()[0] self.data["locale_cura"] = self.cura_locale + try: + from cura.CuraApplication import CuraApplication + plugins = CuraApplication.getInstance().getPluginRegistry() + self.data["plugins"] = { + plugin_id: plugins.getMetaData(plugin_id)["plugin"]["version"] + for plugin_id in plugins.getInstalledPlugins() if not plugins.isBundledPlugin(plugin_id) + } + except: + self.data["plugins"] = {"[FAILED]": "0.0.0"} + crash_info = "" + catalog.i18nc("@label Cura version number", "Cura version") + ": " + str(self.cura_version) + "
" crash_info += "" + catalog.i18nc("@label", "Cura language") + ": " + str(self.cura_locale) + "
" crash_info += "" + catalog.i18nc("@label", "OS language") + ": " + str(self.data["locale_os"]) + "
" @@ -238,6 +248,8 @@ class CrashHandler: scope.set_tag("locale_cura", self.cura_locale) scope.set_tag("is_enterprise", ApplicationMetadata.IsEnterpriseVersion) + scope.set_context("plugins", self.data["plugins"]) + scope.set_user({"id": str(uuid.getnode())}) return group From 3b6ac5eb8d6ea1f35d88e1d0a2ef32c52edaf9b2 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Mon, 15 Jun 2020 15:01:14 +0200 Subject: [PATCH 06/22] Add host_guid to the cloud printers' metadata Since the host_guid is unique to the printer, it is more reliable for identifying the cloud printers. This comes in handy when the cloud printer is removed from the account and re-added. With the host_guid, the printer that is added again can be properly identified as an existing Cura cloud printer, and be linked to the original. To achieve that, the META_CLUSTER_ID of the printer is updated with the new one that is generated when the printer is added again to the account. CURA-7505 --- .../src/Cloud/CloudOutputDeviceManager.py | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 33c9caba05..e473ae7cc3 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -31,6 +31,7 @@ class CloudOutputDeviceManager: """ META_CLUSTER_ID = "um_cloud_cluster_id" + META_HOST_GUID = "host_guid" META_NETWORK_KEY = "um_network_key" SYNC_SERVICE_NAME = "CloudOutputDeviceManager" @@ -113,13 +114,18 @@ class CloudOutputDeviceManager: all_clusters = {c.cluster_id: c for c in clusters} # type: Dict[str, CloudClusterResponse] online_clusters = {c.cluster_id: c for c in clusters if c.is_online} # type: Dict[str, CloudClusterResponse] - # Add the new printers in Cura. If a printer was previously added and is rediscovered, set its metadata to - # reflect that and mark the printer not removed from the account + # Add the new printers in Cura. for device_id, cluster_data in all_clusters.items(): if device_id not in self._remote_clusters: new_clusters.append(cluster_data) - if device_id in self._um_cloud_printers and not parseBool(self._um_cloud_printers[device_id].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")): - self._um_cloud_printers[device_id].setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, True) + if device_id in self._um_cloud_printers: + # Existing cloud printers may not have the host_guid meta-data entry. If that's the case, add it. + if not self._um_cloud_printers[device_id].getMetaDataEntry(self.META_HOST_GUID, None): + self._um_cloud_printers[device_id].setMetaDataEntry(self.META_HOST_GUID, cluster_data.host_guid) + # If a printer was previously not linked to the account and is rediscovered, mark the printer as linked + # to the current account + if not parseBool(self._um_cloud_printers[device_id].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")): + self._um_cloud_printers[device_id].setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, True) self._onDevicesDiscovered(new_clusters) # Hide the current removed_printers_message, if there is any @@ -161,11 +167,21 @@ class CloudOutputDeviceManager: """ new_devices = [] remote_clusters_added = False + host_guid_map = {machine.getMetaDataEntry(self.META_HOST_GUID): device_cluster_id + for device_cluster_id, machine in self._um_cloud_printers.items() + if machine.getMetaDataEntry(self.META_HOST_GUID)} + machine_manager = CuraApplication.getInstance().getMachineManager() + for cluster_data in clusters: device = CloudOutputDevice(self._api, cluster_data) - # Create a machine if we don't already have it. Do not make it the active machine. - machine_manager = CuraApplication.getInstance().getMachineManager() + if cluster_data.host_guid in host_guid_map: + machine = machine_manager.getMachine(device.printerType, {self.META_HOST_GUID: cluster_data.host_guid}) + # Update the META_CLUSTER_ID of the machine in case it has been changed (e.g. if the printer was + # removed and re-added to the account). + if machine and machine.getMetaDataEntry(self.META_CLUSTER_ID) != device.key: + machine.setMetaDataEntry(self.META_CLUSTER_ID, device.key) + # Create a machine if we don't already have it. Do not make it the active machine. # We only need to add it if it wasn't already added by "local" network or by cloud. if machine_manager.getMachine(device.printerType, {self.META_CLUSTER_ID: device.key}) is None \ and machine_manager.getMachine(device.printerType, {self.META_NETWORK_KEY: cluster_data.host_name + "*"}) is None: # The host name is part of the network key. @@ -378,6 +394,7 @@ class CloudOutputDeviceManager: def _setOutputDeviceMetadata(self, device: CloudOutputDevice, machine: GlobalStack): machine.setName(device.name) machine.setMetaDataEntry(self.META_CLUSTER_ID, device.key) + machine.setMetaDataEntry(self.META_HOST_GUID, device.clusterData.host_guid) machine.setMetaDataEntry("group_name", device.name) machine.setMetaDataEntry("group_size", device.clusterSize) machine.setMetaDataEntry("removal_warning", self.I18N_CATALOG.i18nc( From 8abaf69873bfc8b1d285b26fd803481356070465 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Wed, 17 Jun 2020 16:11:18 +0200 Subject: [PATCH 07/22] Update CloudOutputDevice of the machine when it is rediscovered Even though the metadata of the machine was updated when it was rediscovered, the new cluster id was not updated in all its references (such as in the _remote_clusters and in the _um_cloud_printers dicts). This commit fixes that issue by properly updating all the entries that depend on the machine's cluster id. CURA-7505 --- .../src/Cloud/CloudOutputDeviceManager.py | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index e473ae7cc3..bde324df24 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -174,12 +174,13 @@ class CloudOutputDeviceManager: for cluster_data in clusters: device = CloudOutputDevice(self._api, cluster_data) + # If the machine already existed before, it will be present in the host_guid_map if cluster_data.host_guid in host_guid_map: machine = machine_manager.getMachine(device.printerType, {self.META_HOST_GUID: cluster_data.host_guid}) - # Update the META_CLUSTER_ID of the machine in case it has been changed (e.g. if the printer was - # removed and re-added to the account). if machine and machine.getMetaDataEntry(self.META_CLUSTER_ID) != device.key: - machine.setMetaDataEntry(self.META_CLUSTER_ID, device.key) + # If the retrieved device has a different cluster_id than the existing machine, bring the existing + # machine up-to-date. + self._updateOutdatedMachine(outdated_machine = machine, new_cloud_output_device = device) # Create a machine if we don't already have it. Do not make it the active machine. # We only need to add it if it wasn't already added by "local" network or by cloud. @@ -266,6 +267,31 @@ class CloudOutputDeviceManager: ) message.setText(message_text) + def _updateOutdatedMachine(self, outdated_machine: GlobalStack, new_cloud_output_device: CloudOutputDevice) -> None: + """ + Update the cloud metadata of a pre-existing machine that is rediscovered (e.g. if the printer was removed and + re-added to the account) and delete the old CloudOutputDevice related to this machine. + + :param outdated_machine: The cloud machine that needs to be brought up-to-date with the new data received from + the account + :param new_cloud_output_device: The new CloudOutputDevice that should be linked to the pre-existing machine + :return: None + """ + old_cluster_id = outdated_machine.getMetaDataEntry(self.META_CLUSTER_ID) + outdated_machine.setMetaDataEntry(self.META_CLUSTER_ID, new_cloud_output_device.key) + outdated_machine.setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, True) + # Cleanup the remainings of the old CloudOutputDevice(old_cluster_id) + self._um_cloud_printers[new_cloud_output_device.key] = self._um_cloud_printers.pop(old_cluster_id) + output_device_manager = CuraApplication.getInstance().getOutputDeviceManager() + if old_cluster_id in output_device_manager.getOutputDeviceIds(): + output_device_manager.removeOutputDevice(old_cluster_id) + if old_cluster_id in self._remote_clusters: + # We need to close the device so that it stops checking for its status + self._remote_clusters[old_cluster_id].close() + del self._remote_clusters[old_cluster_id] + self._remote_clusters[new_cloud_output_device.key] = new_cloud_output_device + + def _devicesRemovedFromAccount(self, removed_device_ids: Set[str]) -> None: """ Removes the CloudOutputDevice from the received device ids and marks the specific printers as "removed from From 930d92869a4b891df239d7aed72fa866643264cb Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 18 Jun 2020 11:03:40 +0200 Subject: [PATCH 08/22] Fix syncing empty material with cloud For some reason the cloud gives a 0000 guid when the material is empty. --- 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 423df167cd..3f69bcb934 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1377,8 +1377,8 @@ class MachineManager(QObject): for extruder_configuration in configuration.extruderConfigurations: # We support "" or None, since the cloud uses None instead of empty strings - extruder_has_hotend = extruder_configuration.hotendID and extruder_configuration.hotendID != "" - extruder_has_material = extruder_configuration.material.guid and extruder_configuration.material.guid != "" + extruder_has_hotend = extruder_configuration.hotendID not in ["", None] + extruder_has_material = extruder_configuration.material.guid not in [None, "", "00000000-0000-0000-0000-000000000000"] # If the machine doesn't have a hotend or material, disable this extruder if not extruder_has_hotend or not extruder_has_material: From 466ec7c42a1241805924ec5a167d023e0e678273 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 18 Jun 2020 11:08:17 +0200 Subject: [PATCH 09/22] Remove duplicated variable It was holding the same data as extruders_to_disable --- cura/Settings/MachineManager.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 3f69bcb934..f9ccc5940f 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1368,7 +1368,6 @@ class MachineManager(QObject): with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue): self.switchPrinterType(configuration.printerType) - disabled_used_extruder_position_set = set() extruders_to_disable = set() # If an extruder that's currently used to print a model gets disabled due to the syncing, we need to show @@ -1396,7 +1395,6 @@ class MachineManager(QObject): self._global_container_stack.extruderList[int(position)].setEnabled(False) need_to_show_message = True - disabled_used_extruder_position_set.add(int(position)) else: machine_node = ContainerTree.getInstance().machines.get(self._global_container_stack.definition.getId()) @@ -1427,7 +1425,7 @@ class MachineManager(QObject): # Show human-readable extruder names such as "Extruder Left", "Extruder Front" instead of "Extruder 1, 2, 3". extruder_names = [] - for extruder_position in sorted(disabled_used_extruder_position_set): + for extruder_position in sorted(extruders_to_disable): extruder_stack = self._global_container_stack.extruderList[int(extruder_position)] extruder_name = extruder_stack.definition.getName() extruder_names.append(extruder_name) From 364769d821013a861467c6f07755aef3ebe923be Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Thu, 18 Jun 2020 13:49:50 +0200 Subject: [PATCH 10/22] Remove only the hidden printers when removing a printers When printers share the same group_id, they are removed, regardless if they are hidden or not. This was resulting in sometimes removing multiple printers when removing a printer that was created by a project file. This PR fixes that by actually checking whether the printer to be removed is hidden. CURA-7522 --- 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 f9ccc5940f..7953db8f76 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -745,7 +745,7 @@ class MachineManager(QObject): # If the printer that is being removed is a network printer, the hidden printers have to be also removed group_id = metadata.get("group_id", None) if group_id: - metadata_filter = {"group_id": group_id} + metadata_filter = {"group_id": group_id, "hidden": True} hidden_containers = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter) if hidden_containers: # This reuses the method and remove all printers recursively From 18f3d187d5d1bbfece646c444ab3817cef7a758e Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Thu, 18 Jun 2020 14:08:35 +0200 Subject: [PATCH 11/22] Remove extra newlines in printers added from account message Since \n are now replaced by
, the \n should be removed from this specific message. Required after the changes in https://github.com/Ultimaker/Uranium/commit/efaad4f6c82e9e06fc1c8c829eea077958db6dbf --- .../src/Cloud/CloudOutputDeviceManager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index da200c7fc2..04180e6aec 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -239,13 +239,13 @@ class CloudOutputDeviceManager: num_hidden = len(new_devices) - max_disp_devices + 1 device_name_list = ["
  • {} ({})
  • ".format(device.name, device.printerTypeName) for device in new_devices[0:num_hidden]] device_name_list.append(self.I18N_CATALOG.i18nc("info:hidden list items", "
  • ... and {} others
  • ", num_hidden)) - device_names = "\n".join(device_name_list) + device_names = "".join(device_name_list) else: - device_names = "\n".join(["
  • {} ({})
  • ".format(device.name, device.printerTypeName) for device in new_devices]) + device_names = "".join(["
  • {} ({})
  • ".format(device.name, device.printerTypeName) for device in new_devices]) message_text = self.I18N_CATALOG.i18nc( "info:status", - "Cloud printers added from your account:\n
      {}
    ", + "Cloud printers added from your account:
      {}
    ", device_names ) message.setText(message_text) From 85bfb0222878475a140a041a3b0ad088fd257859 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 18 Jun 2020 14:31:28 +0200 Subject: [PATCH 12/22] Show an error if trying to open a project file with unknown machine Previously it would crash. Fixes Sentry issue CURA-Y1. --- plugins/3MFReader/ThreeMFWorkspaceReader.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 2d257bb4b4..eaac225e00 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -651,7 +651,13 @@ class ThreeMFWorkspaceReader(WorkspaceReader): self._container_registry.addContainer(global_stack) else: # Find the machine - global_stack = self._container_registry.findContainerStacks(name = self._machine_info.name, type = "machine")[0] + global_stacks = self._container_registry.findContainerStacks(name = self._machine_info.name, type = "machine") + if not global_stacks: + message = Message(i18n_catalog.i18nc("@info:error Don't translate the XML tag !", "Project file {0} is made using profiles that are unknown to this version of Ultimaker Cura.", file_name)) + message.show() + self.setWorkspaceName("") + return [], {} + global_stack = global_stacks[0] extruder_stacks = self._container_registry.findContainerStacks(machine = global_stack.getId(), type = "extruder_train") extruder_stack_dict = {stack.getMetaDataEntry("position"): stack for stack in extruder_stacks} From f8a88642c2258fefd9d5c4be717d64a3b4adbd68 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 18 Jun 2020 14:53:18 +0200 Subject: [PATCH 13/22] Don't crash when failing to create Toolbox.qml dialogue Basically we should never be raising the master Exception class, really. The enclosing class has error handling in case this returns None already, so this fix is easy. Contributes to Sentry issue CURA-Y5. --- cura/Scene/CuraSceneNode.py | 6 +++--- plugins/Toolbox/src/Toolbox.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cura/Scene/CuraSceneNode.py b/cura/Scene/CuraSceneNode.py index 7337a3a32f..c22277a4b0 100644 --- a/cura/Scene/CuraSceneNode.py +++ b/cura/Scene/CuraSceneNode.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019 Ultimaker B.V. +# Copyright (c) 2020 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from copy import deepcopy @@ -119,9 +119,9 @@ class CuraSceneNode(SceneNode): self._aabb = None if self._mesh_data: self._aabb = self._mesh_data.getExtents(self.getWorldTransformation()) - else: # If there is no mesh_data, use a boundingbox that encompasses the local (0,0,0) + else: # If there is no mesh_data, use a bounding box that encompasses the local (0,0,0) position = self.getWorldPosition() - self._aabb = AxisAlignedBox(minimum=position, maximum=position) + self._aabb = AxisAlignedBox(minimum = position, maximum = position) for child in self.getAllChildren(): if child.callDecoration("isNonPrintingMesh"): diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 0ad9f7c89c..876ca586a7 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019 Ultimaker B.V. +# Copyright (c) 2020 Ultimaker B.V. # Toolbox is released under the terms of the LGPLv3 or higher. import json @@ -232,7 +232,7 @@ class Toolbox(QObject, Extension): "licenseModel": self._license_model }) if not dialog: - raise Exception("Failed to create Marketplace dialog") + return None return dialog def _convertPluginMetadata(self, plugin_data: Dict[str, Any]) -> Optional[Dict[str, Any]]: From f0002e265ba4bc753d6d26fbca3be4bc216061af Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 18 Jun 2020 17:39:58 +0200 Subject: [PATCH 14/22] Mark recently constructed stacks as not dirty The stacks would get marked dirty for setting some metadata, but since thats part of their construction, they aren't really dirty. Previously, every single stack that was loaded would be marked as dirty, which causes unneeded saving. --- cura/Settings/CuraContainerStack.py | 2 ++ cura/Settings/ExtruderStack.py | 2 ++ cura/Settings/GlobalStack.py | 2 ++ 3 files changed, 6 insertions(+) diff --git a/cura/Settings/CuraContainerStack.py b/cura/Settings/CuraContainerStack.py index 36548ed5de..4595bf3996 100755 --- a/cura/Settings/CuraContainerStack.py +++ b/cura/Settings/CuraContainerStack.py @@ -60,6 +60,8 @@ class CuraContainerStack(ContainerStack): import cura.CuraApplication #Here to prevent circular imports. self.setMetaDataEntry("setting_version", cura.CuraApplication.CuraApplication.SettingVersion) + self.setDirty(False) + # This is emitted whenever the containersChanged signal from the ContainerStack base class is emitted. pyqtContainersChanged = pyqtSignal() diff --git a/cura/Settings/ExtruderStack.py b/cura/Settings/ExtruderStack.py index 7520d436e9..bb35b336c7 100644 --- a/cura/Settings/ExtruderStack.py +++ b/cura/Settings/ExtruderStack.py @@ -32,6 +32,8 @@ class ExtruderStack(CuraContainerStack): self.propertiesChanged.connect(self._onPropertiesChanged) + self.setDirty(False) + enabledChanged = pyqtSignal() @override(ContainerStack) diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py index 929c567921..a9164d0fb9 100755 --- a/cura/Settings/GlobalStack.py +++ b/cura/Settings/GlobalStack.py @@ -55,6 +55,8 @@ class GlobalStack(CuraContainerStack): # properties. So we need to tie them together like this. self.metaDataChanged.connect(self.configuredConnectionTypesChanged) + self.setDirty(False) + extrudersChanged = pyqtSignal() configuredConnectionTypesChanged = pyqtSignal() From d0a84828ff05299c978553a60a5241edbb550fbf Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 18 Jun 2020 17:59:35 +0200 Subject: [PATCH 15/22] Remove unneeded call to autosave trigger on initializaiton --- cura/AutoSave.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cura/AutoSave.py b/cura/AutoSave.py index 2c1dbe4a84..d80e34771e 100644 --- a/cura/AutoSave.py +++ b/cura/AutoSave.py @@ -31,7 +31,6 @@ class AutoSave: self._change_timer.timeout.connect(self._onTimeout) self._application.globalContainerStackChanged.connect(self._onGlobalStackChanged) self._onGlobalStackChanged() - self._triggerTimer() def _triggerTimer(self, *args: Any) -> None: if not self._saving: From ea0c8ff9bc2b0a51dc098170d33ff4648f97defd Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 18 Jun 2020 18:05:07 +0200 Subject: [PATCH 16/22] Implement setDirty for mockContainer --- tests/Settings/MockContainer.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Settings/MockContainer.py b/tests/Settings/MockContainer.py index bb99710ef6..9c20f55405 100644 --- a/tests/Settings/MockContainer.py +++ b/tests/Settings/MockContainer.py @@ -153,6 +153,9 @@ class MockContainer(ContainerInterface, UM.PluginObject.PluginObject): def isDirty(self): return True + def setDirty(self, dirty): + pass + metaDataChanged = Signal() propertyChanged = Signal() containersChanged = Signal() From cb657ac45ae74815d5caa9774b35a5194629acc7 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 19 Jun 2020 15:34:13 +0200 Subject: [PATCH 17/22] Some minor stylechanges in tests --- tests/Settings/TestDefinitionContainer.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/Settings/TestDefinitionContainer.py b/tests/Settings/TestDefinitionContainer.py index 8622db26ee..d434066d10 100644 --- a/tests/Settings/TestDefinitionContainer.py +++ b/tests/Settings/TestDefinitionContainer.py @@ -12,7 +12,7 @@ from unittest.mock import patch, MagicMock import UM.Settings.ContainerRegistry #To create empty instance containers. import UM.Settings.ContainerStack #To set the container registry the container stacks use. from UM.Settings.DefinitionContainer import DefinitionContainer #To check against the class of DefinitionContainer. - +from UM.VersionUpgradeManager import FilesDataUpdateResult from UM.Resources import Resources Resources.addSearchPath(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "resources"))) @@ -36,6 +36,7 @@ def definition_container(): assert result.getId() == uid return result + @pytest.mark.parametrize("file_path", definition_filepaths) def test_definitionIds(file_path): """ @@ -45,6 +46,7 @@ def test_definitionIds(file_path): definition_id = os.path.basename(file_path).split(".")[0] assert " " not in definition_id # Definition IDs are not allowed to have spaces. + @pytest.mark.parametrize("file_path", definition_filepaths) def test_noCategory(file_path): """ @@ -57,6 +59,7 @@ def test_noCategory(file_path): metadata = DefinitionContainer.deserializeMetadata(json, "test_container_id") assert "category" not in metadata[0] + @pytest.mark.parametrize("file_path", machine_filepaths) def test_validateMachineDefinitionContainer(file_path, definition_container): """Tests all definition containers""" @@ -65,13 +68,12 @@ def test_validateMachineDefinitionContainer(file_path, definition_container): if file_name == "fdmprinter.def.json" or file_name == "fdmextruder.def.json": return # Stop checking, these are root files. - from UM.VersionUpgradeManager import FilesDataUpdateResult - mocked_vum = MagicMock() mocked_vum.updateFilesData = lambda ct, v, fdl, fnl: FilesDataUpdateResult(ct, v, fdl, fnl) with patch("UM.VersionUpgradeManager.VersionUpgradeManager.getInstance", MagicMock(return_value = mocked_vum)): assertIsDefinitionValid(definition_container, file_path) + def assertIsDefinitionValid(definition_container, file_path): with open(file_path, encoding = "utf-8") as data: json = data.read() @@ -86,6 +88,7 @@ def assertIsDefinitionValid(definition_container, file_path): if "platform_texture" in metadata[0]: assert metadata[0]["platform_texture"] in all_images + @pytest.mark.parametrize("file_path", definition_filepaths) def test_validateOverridingDefaultValue(file_path: str): """Tests whether setting values are not being hidden by parent containers. @@ -189,7 +192,9 @@ def test_noId(file_path: str): @pytest.mark.parametrize("file_path", extruder_filepaths) def test_extruderMatch(file_path: str): - """Verifies that extruders say that they work on the same extruder_nr as what is listed in their machine definition.""" + """ + Verifies that extruders say that they work on the same extruder_nr as what is listed in their machine definition. + """ extruder_id = os.path.basename(file_path).split(".")[0] with open(file_path, encoding = "utf-8") as f: From f8e648fcb3860826f7ddead5a19fa582e0637014 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 19 Jun 2020 16:04:59 +0200 Subject: [PATCH 18/22] Add the no response config --- .github/no-response.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/no-response.yml diff --git a/.github/no-response.yml b/.github/no-response.yml new file mode 100644 index 0000000000..a386aaa7ba --- /dev/null +++ b/.github/no-response.yml @@ -0,0 +1,13 @@ +# Configuration for probot-no-response - https://github.com/probot/no-response + +# Number of days of inactivity before an Issue is closed for lack of response +daysUntilClose: 14 +# Label requiring a response +responseRequiredLabel: 'Status: Needs Info' +# Comment to post when closing an Issue for lack of response. Set to `false` to disable +closeComment: > + This issue has been automatically closed because there has been no response + to our request for more information from the original author. With only the + information that is currently in the issue, we don't have enough information + to take action. Please reach out if you have or find the answers we need so + that we can investigate further. From ba8cb4ce4a9be1bd4f1ccf19e519fc5560b5fdbc Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 19 Jun 2020 15:35:04 +0200 Subject: [PATCH 19/22] Don't pre-load engine before other plug-ins This was a fix for the post-processing plug-in that's no longer required. It was also only applied when running from source, so obviously it's not required or we'd have seen it in the builds. --- cura/CuraApplication.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index dfb5c6cac1..bae212917a 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -756,7 +756,6 @@ class CuraApplication(QtApplication): if not hasattr(sys, "frozen"): self._plugin_registry.addPluginLocation(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "plugins")) self._plugin_registry.loadPlugin("ConsoleLogger") - self._plugin_registry.loadPlugin("CuraEngineBackend") self._plugin_registry.loadPlugins() From 9cce800d42e74841ab5f5292b4f5fda2500ee07b Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 19 Jun 2020 16:14:10 +0200 Subject: [PATCH 20/22] Run CTest in parallel (#7952) * Don't pre-load engine before other plug-ins This was a fix for the post-processing plug-in that's no longer required. It was also only applied when running from source, so obviously it's not required or we'd have seen it in the builds. * Run automated tests in parallel Done during Turbo Testing and Tooling. * Pass number of processors via ctest command itself More reliable apparently because it's a CMake variable that doesn't get cached. It shouldn't get cached or set in the CMake script because it's not the same for every computer. Done during Turbo Testing and Tooling. --- docker/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/build.sh b/docker/build.sh index a500663c64..39632b348b 100755 --- a/docker/build.sh +++ b/docker/build.sh @@ -67,4 +67,4 @@ cmake3 \ -DBUILD_TESTS=ON \ .. make -ctest3 --output-on-failure -T Test +ctest3 -j4 --output-on-failure -T Test From 61def4ba255840cb32f17cc3d2297bcb074358d3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 19 Jun 2020 16:59:27 +0200 Subject: [PATCH 21/22] Prevent crash when requested polygon is too large CURA-6P --- cura/Arranging/Arrange.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cura/Arranging/Arrange.py b/cura/Arranging/Arrange.py index 4333cbd74a..c9d3498c7b 100644 --- a/cura/Arranging/Arrange.py +++ b/cura/Arranging/Arrange.py @@ -80,8 +80,11 @@ class Arrange: # After scaling (like up to 0.1 mm) the node might not have points if not points.size: continue - - shape_arr = ShapeArray.fromPolygon(points, scale = scale) + try: + shape_arr = ShapeArray.fromPolygon(points, scale = scale) + except ValueError: + Logger.logException("w", "Unable to create polygon") + continue arranger.place(0, 0, shape_arr) # If a build volume was set, add the disallowed areas From d0d7e671edbfec5abeaace16721a7744d649b4b9 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Tue, 23 Jun 2020 10:56:48 +0200 Subject: [PATCH 22/22] Make the deletion of the definitionChanges safer Just in case findContainerStacks returns an empty list --- cura/Settings/MachineManager.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 7953db8f76..1934befd66 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -738,8 +738,9 @@ class MachineManager(QObject): containers = CuraContainerRegistry.getInstance().findInstanceContainersMetadata(type = "user", machine = machine_id) for container in containers: CuraContainerRegistry.getInstance().removeContainer(container["id"]) - machine_stack = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine", name = machine_id)[0] - CuraContainerRegistry.getInstance().removeContainer(machine_stack.definitionChanges.getId()) + machine_stacks = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine", name = machine_id) + if machine_stacks: + CuraContainerRegistry.getInstance().removeContainer(machine_stacks[0].definitionChanges.getId()) CuraContainerRegistry.getInstance().removeContainer(machine_id) # If the printer that is being removed is a network printer, the hidden printers have to be also removed