From ef73453874886b0b6a8820bb757652891e9740e3 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 15 Feb 2019 14:36:58 +0100 Subject: [PATCH 01/10] Fix height in the about dialog to show the logo correctly --- resources/qml/Dialogs/AboutDialog.qml | 54 ++++++++++++++------------ resources/themes/cura-light/theme.json | 2 +- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/resources/qml/Dialogs/AboutDialog.qml b/resources/qml/Dialogs/AboutDialog.qml index 604cbc16ba..584903dd60 100644 --- a/resources/qml/Dialogs/AboutDialog.qml +++ b/resources/qml/Dialogs/AboutDialog.qml @@ -21,41 +21,45 @@ UM.Dialog Rectangle { + id: header width: parent.width + 2 * margin // margin from Dialog.qml - height: version.y + version.height + margin + height: childrenRect.height + topPadding anchors.top: parent.top - anchors.topMargin: - margin + anchors.topMargin: -margin anchors.horizontalCenter: parent.horizontalCenter + property real topPadding: UM.Theme.getSize("wide_margin").height + color: UM.Theme.getColor("main_window_header_background") - } - Image - { - id: logo - width: (base.minimumWidth * 0.85) | 0 - source: UM.Theme.getImage("logo") - sourceSize.width: width - sourceSize.height: height + Image + { + id: logo + width: (base.minimumWidth * 0.85) | 0 + height: (width * (UM.Theme.getSize("logo").height / UM.Theme.getSize("logo").width)) | 0 + source: UM.Theme.getImage("logo") + sourceSize.width: width + sourceSize.height: height - anchors.top: parent.top - anchors.topMargin: ((base.minimumWidth - width) / 2) | 0 - anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: parent.topPadding + anchors.horizontalCenter: parent.horizontalCenter - UM.I18nCatalog{id: catalog; name: "cura"} - } + UM.I18nCatalog{id: catalog; name: "cura"} + } - Label - { - id: version + Label + { + id: version - text: catalog.i18nc("@label","version: %1").arg(UM.Application.version) - font: UM.Theme.getFont("large_bold") - color: UM.Theme.getColor("button_text") - anchors.right : logo.right - anchors.top: logo.bottom - anchors.topMargin: (UM.Theme.getSize("default_margin").height / 2) | 0 + text: catalog.i18nc("@label","version: %1").arg(UM.Application.version) + font: UM.Theme.getFont("large_bold") + color: UM.Theme.getColor("button_text") + anchors.right : logo.right + anchors.top: logo.bottom + anchors.topMargin: (UM.Theme.getSize("default_margin").height / 2) | 0 + } } Label @@ -67,7 +71,7 @@ UM.Dialog text: catalog.i18nc("@label","End-to-end solution for fused filament 3D printing.") font: UM.Theme.getFont("system") wrapMode: Text.WordWrap - anchors.top: version.bottom + anchors.top: header.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 573fe8bcfa..92308537dd 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -473,7 +473,7 @@ "default_lining": [0.08, 0.08], "default_arrow": [0.8, 0.8], - "logo": [8, 2.4], + "logo": [8, 1.75], "wide_margin": [2.0, 2.0], "thick_margin": [1.71, 1.43], From f521fae1525f5f7cf2479e5e38444995abb9bb8d Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 15 Feb 2019 15:11:09 +0100 Subject: [PATCH 02/10] Fix call_on_qt_thread decorator CURA-6225 Do thread check in the wrapper function, not outside. --- cura/Utils/Threading.py | 9 ++++++++- plugins/UFPWriter/UFPWriter.py | 6 +++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/cura/Utils/Threading.py b/cura/Utils/Threading.py index 3cd6200513..550a5421ff 100644 --- a/cura/Utils/Threading.py +++ b/cura/Utils/Threading.py @@ -1,3 +1,4 @@ +import functools import threading from cura.CuraApplication import CuraApplication @@ -6,7 +7,7 @@ from cura.CuraApplication import CuraApplication # # HACK: # -# In project loading, when override the existing machine is selected, the stacks and containers that are correctly +# In project loading, when override the existing machine is selected, the stacks and containers that are currently # active in the system will be overridden at runtime. Because the project loading is done in a different thread than # the Qt thread, something else can kick in the middle of the process. One of them is the rendering. It will access # the current stacks and container, which have not completely been updated yet, so Cura will crash in this case. @@ -22,7 +23,13 @@ class InterCallObject: def call_on_qt_thread(func): + @functools.wraps(func) def _call_on_qt_thread_wrapper(*args, **kwargs): + # If the current thread is the main thread, which is the Qt thread, directly call the function. + current_thread = threading.current_thread() + if isinstance(current_thread, threading._MainThread): + return func(*args, **kwargs) + def _handle_call(ico, *args, **kwargs): ico.result = func(*args, **kwargs) ico.finish_event.set() diff --git a/plugins/UFPWriter/UFPWriter.py b/plugins/UFPWriter/UFPWriter.py index c0db104c82..a686837b30 100644 --- a/plugins/UFPWriter/UFPWriter.py +++ b/plugins/UFPWriter/UFPWriter.py @@ -44,8 +44,12 @@ class UFPWriter(MeshWriter): # trigger loading other containers. Because those loaded containers are QtObjects, they must be created on the # Qt thread. The File read/write operations right now are executed on separated threads because they are scheduled # by the Job class. - @call_on_qt_thread def write(self, stream, nodes, mode = MeshWriter.OutputMode.BinaryMode): + print("------------> self _write = ", self._write) + return self._write(stream, nodes, mode = mode) + + @call_on_qt_thread + def _write(self, stream, nodes, mode = MeshWriter.OutputMode.BinaryMode): archive = VirtualFile() archive.openStream(stream, "application/x-ufp", OpenMode.WriteOnly) From 3e819f56fc96d541c6d7a9b1c33057789541f25d Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 15 Feb 2019 15:14:10 +0100 Subject: [PATCH 03/10] Remove debug code CURA-6225 --- plugins/UFPWriter/UFPWriter.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/UFPWriter/UFPWriter.py b/plugins/UFPWriter/UFPWriter.py index a686837b30..c0db104c82 100644 --- a/plugins/UFPWriter/UFPWriter.py +++ b/plugins/UFPWriter/UFPWriter.py @@ -44,12 +44,8 @@ class UFPWriter(MeshWriter): # trigger loading other containers. Because those loaded containers are QtObjects, they must be created on the # Qt thread. The File read/write operations right now are executed on separated threads because they are scheduled # by the Job class. - def write(self, stream, nodes, mode = MeshWriter.OutputMode.BinaryMode): - print("------------> self _write = ", self._write) - return self._write(stream, nodes, mode = mode) - @call_on_qt_thread - def _write(self, stream, nodes, mode = MeshWriter.OutputMode.BinaryMode): + def write(self, stream, nodes, mode = MeshWriter.OutputMode.BinaryMode): archive = VirtualFile() archive.openStream(stream, "application/x-ufp", OpenMode.WriteOnly) From e80eccaea32cec6cd1192fc643cadf08d28851ac Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 15 Feb 2019 16:46:47 +0100 Subject: [PATCH 04/10] Manually create printer properties for cloud output device --- .../src/Cloud/CloudOutputDevice.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 33968beb6d..223d33c7b8 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -61,8 +61,19 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # \param cluster: The device response received from the cloud API. # \param parent: The optional parent of this output device. def __init__(self, api_client: CloudApiClient, cluster: CloudClusterResponse, parent: QObject = None) -> None: + + # The following properties are expected on each networked output device. + # Because the cloud connection does not off all of these, we manually construct this version here. + # An example of why this is needed is the selection of the compatible file type when exporting the tool path. + properties = { + b"address": b"", + b"name": cluster.host_name.encode(), + b"firmware_version": cluster.host_version.encode(), + b"printer_type": b"" + } + super().__init__(device_id = cluster.cluster_id, address = "", - connection_type = ConnectionType.CloudConnection, properties = {}, parent = parent) + connection_type = ConnectionType.CloudConnection, properties = properties, parent = parent) self._api = api_client self._cluster = cluster From 119c868650fa68b1c0d9b1555a9b9af46f300c8f Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 15 Feb 2019 16:52:11 +0100 Subject: [PATCH 05/10] Add a test for the device properties --- .../tests/Cloud/TestCloudOutputDevice.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py index 14b1f4feba..418e3fe5cb 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py @@ -21,6 +21,7 @@ class TestCloudOutputDevice(TestCase): JOB_ID = "ABCDefGHIjKlMNOpQrSTUvYxWZ0-1234567890abcDE=" HOST_NAME = "ultimakersystem-ccbdd30044ec" HOST_GUID = "e90ae0ac-1257-4403-91ee-a44c9b7e8050" + HOST_VERSION = "5.2.0" STATUS_URL = "{}/connect/v1/clusters/{}/status".format(CuraCloudAPIRoot, CLUSTER_ID) PRINT_URL = "{}/connect/v1/clusters/{}/print/{}".format(CuraCloudAPIRoot, CLUSTER_ID, JOB_ID) @@ -36,7 +37,7 @@ class TestCloudOutputDevice(TestCase): patched_method.start() self.cluster = CloudClusterResponse(self.CLUSTER_ID, self.HOST_GUID, self.HOST_NAME, is_online=True, - status="active") + status="active", host_version=self.HOST_VERSION) self.network = NetworkManagerMock() self.account = MagicMock(isLoggedIn=True, accessToken="TestAccessToken") @@ -56,6 +57,11 @@ class TestCloudOutputDevice(TestCase): for patched_method in self.patches: patched_method.stop() + # We test for these in order to make sure the correct file type is selected depending on the firmware version. + def test_properties(self): + self.assertEqual(self.device.firmwareVersion, self.HOST_VERSION) + self.assertEqual(self.device.name, self.HOST_NAME) + def test_status(self): self.device._update() self.network.flushReplies() From 897c932e8002900691da3b2884187b964ae2de51 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 15 Feb 2019 17:03:38 +0100 Subject: [PATCH 06/10] Fix test for cloud output device ufp uploading --- .../tests/Cloud/TestCloudOutputDevice.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py index 418e3fe5cb..6a0b87c4da 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py @@ -120,7 +120,7 @@ class TestCloudOutputDevice(TestCase): def test_print_to_cloud(self): active_machine_mock = self.app.getGlobalContainerStack.return_value - active_machine_mock.getMetaDataEntry.side_effect = {"file_formats": "application/gzip"}.get + active_machine_mock.getMetaDataEntry.side_effect = {"file_formats": "application/x-ufp"}.get request_upload_response = parseFixture("putJobUploadResponse") request_print_response = parseFixture("postJobPrintResponse") @@ -130,6 +130,10 @@ class TestCloudOutputDevice(TestCase): file_handler = MagicMock() file_handler.getSupportedFileTypesWrite.return_value = [{ + "extension": "ufp", + "mime_type": "application/x-ufp", + "mode": 2 + }, { "extension": "gcode.gz", "mime_type": "application/gzip", "mode": 2, @@ -138,15 +142,11 @@ class TestCloudOutputDevice(TestCase): lambda stream, nodes: stream.write(str(nodes).encode()) scene_nodes = [SceneNode()] - expected_mesh = str(scene_nodes).encode() self.device.requestWrite(scene_nodes, file_handler=file_handler, file_name="FileName") self.network.flushReplies() self.assertEqual( - {"data": {"content_type": "application/gzip", "file_size": len(expected_mesh), "job_name": "FileName"}}, + {"data": {"content_type": "application/x-ufp", "job_name": "FileName"}}, json.loads(self.network.getRequestBody("PUT", self.REQUEST_UPLOAD_URL).decode()) ) - self.assertEqual(expected_mesh, - self.network.getRequestBody("PUT", request_upload_response["data"]["upload_url"])) - self.assertIsNone(self.network.getRequestBody("POST", self.PRINT_URL)) From 3f11cb911dcd5ac5e4a5524df83f9ef7a0b063a1 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 15 Feb 2019 17:06:32 +0100 Subject: [PATCH 07/10] Add filesize back to expected result --- plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py index 6a0b87c4da..c0b4919314 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py @@ -146,7 +146,7 @@ class TestCloudOutputDevice(TestCase): self.network.flushReplies() self.assertEqual( - {"data": {"content_type": "application/x-ufp", "job_name": "FileName"}}, + {"data": {"content_type": "application/x-ufp", "file_size": 52, "job_name": "FileName"}}, json.loads(self.network.getRequestBody("PUT", self.REQUEST_UPLOAD_URL).decode()) ) self.assertIsNone(self.network.getRequestBody("POST", self.PRINT_URL)) From 11cf409d71f550c88ae457f57d444930196199e5 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 15 Feb 2019 17:07:45 +0100 Subject: [PATCH 08/10] Fix codestyle --- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 223d33c7b8..7b5add276a 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -67,8 +67,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # An example of why this is needed is the selection of the compatible file type when exporting the tool path. properties = { b"address": b"", - b"name": cluster.host_name.encode(), - b"firmware_version": cluster.host_version.encode(), + b"name": cluster.host_name.encode() if cluster.host_name else b"", + b"firmware_version": cluster.host_version.encode() if cluster.host_version else b"", b"printer_type": b"" } From f25fefdbcb5121ed696d4d0b1de0879e13f60dd5 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 15 Feb 2019 17:24:03 +0100 Subject: [PATCH 09/10] Add expected mesh back to test --- .../UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py index c0b4919314..c4d891302e 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py @@ -142,11 +142,14 @@ class TestCloudOutputDevice(TestCase): lambda stream, nodes: stream.write(str(nodes).encode()) scene_nodes = [SceneNode()] + expected_mesh = str(scene_nodes).encode() self.device.requestWrite(scene_nodes, file_handler=file_handler, file_name="FileName") self.network.flushReplies() self.assertEqual( - {"data": {"content_type": "application/x-ufp", "file_size": 52, "job_name": "FileName"}}, + {"data": {"content_type": "application/x-ufp", "file_size": len(expected_mesh), "job_name": "FileName"}}, json.loads(self.network.getRequestBody("PUT", self.REQUEST_UPLOAD_URL).decode()) ) + self.assertEqual(expected_mesh, + self.network.getRequestBody("PUT", request_upload_response["data"]["upload_url"])) self.assertIsNone(self.network.getRequestBody("POST", self.PRINT_URL)) From 222f8e6cdb11c3dfe44cbaa07cec8035ad9424d4 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 18 Feb 2019 10:38:39 +0100 Subject: [PATCH 10/10] Prevent crash for backup plugin if there is no internet connection --- plugins/CuraDrive/src/DriveApiService.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/plugins/CuraDrive/src/DriveApiService.py b/plugins/CuraDrive/src/DriveApiService.py index 7c1f8faa83..6a828e32d6 100644 --- a/plugins/CuraDrive/src/DriveApiService.py +++ b/plugins/CuraDrive/src/DriveApiService.py @@ -40,10 +40,13 @@ class DriveApiService: if not access_token: Logger.log("w", "Could not get access token.") return [] - - backup_list_request = requests.get(self.BACKUP_URL, headers = { - "Authorization": "Bearer {}".format(access_token) - }) + try: + backup_list_request = requests.get(self.BACKUP_URL, headers = { + "Authorization": "Bearer {}".format(access_token) + }) + except requests.exceptions.ConnectionError: + Logger.log("w", "Unable to connect with the server.") + return [] # HTTP status 300s mean redirection. 400s and 500s are errors. # Technically 300s are not errors, but the use case here relies on "requests" to handle redirects automatically.