From c095cb1f226a0fdaaf9f8da5ab245cd8820266d8 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 4 Jun 2020 14:06:03 +0200 Subject: [PATCH 1/5] Converted the listview into a RowLayout I have no idea why i thought a listview was needed for this when this was made. The data shouldn't be flicable, so it makes way more sense to use a row layout CURA-7480 --- .../ConfigurationMenu/ConfigurationMenu.qml | 109 +++++++++--------- 1 file changed, 54 insertions(+), 55 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index f0ada92810..9891fc1d69 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -4,6 +4,7 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.3 import UM 1.2 as UM import Cura 1.0 as Cura @@ -37,70 +38,68 @@ Cura.ExpandablePopup headerItem: Item { // Horizontal list that shows the extruders and their materials - ListView + RowLayout { - id: extrudersList - - orientation: ListView.Horizontal anchors.fill: parent - model: extrudersModel - visible: Cura.MachineManager.activeMachine.hasMaterials - - delegate: Item + Repeater { - height: parent.height - width: Math.round(ListView.view.width / extrudersModel.count) - - // Extruder icon. Shows extruder index and has the same color as the active material. - Cura.ExtruderIcon + model: extrudersModel + delegate: Item { - id: extruderIcon - materialColor: model.color - extruderEnabled: model.enabled - height: parent.height - width: height - } + Layout.fillWidth: true + Layout.fillHeight: true - // Label for the brand of the material - Label - { - id: typeAndBrandNameLabel - - text: model.material_brand + " " + model.material - elide: Text.ElideRight - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - renderType: Text.NativeRendering - - anchors + // Extruder icon. Shows extruder index and has the same color as the active material. + Cura.ExtruderIcon { - top: extruderIcon.top - left: extruderIcon.right - leftMargin: UM.Theme.getSize("default_margin").width - right: parent.right - rightMargin: UM.Theme.getSize("default_margin").width + id: extruderIcon + materialColor: model.color + extruderEnabled: model.enabled + height: parent.height + width: height } - } - // Label that shows the name of the variant - Label - { - id: variantLabel - visible: Cura.MachineManager.activeMachine.hasVariants - - text: model.variant - elide: Text.ElideRight - font: UM.Theme.getFont("default_bold") - color: UM.Theme.getColor("text") - renderType: Text.NativeRendering - - anchors + // Label for the brand of the material + Label { - left: extruderIcon.right - leftMargin: UM.Theme.getSize("default_margin").width - top: typeAndBrandNameLabel.bottom - right: parent.right - rightMargin: UM.Theme.getSize("default_margin").width + id: typeAndBrandNameLabel + + text: model.material_brand + " " + model.material + elide: Text.ElideRight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + renderType: Text.NativeRendering + + anchors + { + top: extruderIcon.top + left: extruderIcon.right + leftMargin: UM.Theme.getSize("default_margin").width + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + } + } + // Label that shows the name of the variant + Label + { + id: variantLabel + + visible: Cura.MachineManager.activeMachine.hasVariants + + text: model.variant + elide: Text.ElideRight + font: UM.Theme.getFont("default_bold") + color: UM.Theme.getColor("text") + renderType: Text.NativeRendering + + anchors + { + left: extruderIcon.right + leftMargin: UM.Theme.getSize("default_margin").width + top: typeAndBrandNameLabel.bottom + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + } } } } From 773edfd533acf258f1d785092506c0746a93808c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 8 Jun 2020 10:24:59 +0200 Subject: [PATCH 2/5] Prevent crash when camera tool was not found --- cura/CuraApplication.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index de7da13d1c..75b1d67697 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -886,8 +886,9 @@ class CuraApplication(QtApplication): # Initialize camera tool camera_tool = controller.getTool("CameraTool") - camera_tool.setOrigin(Vector(0, 100, 0)) - camera_tool.setZoomRange(0.1, 2000) + if camera_tool: + camera_tool.setOrigin(Vector(0, 100, 0)) + camera_tool.setZoomRange(0.1, 2000) # Initialize camera animations self._camera_animation = CameraAnimation.CameraAnimation() From 83fd35424b5b75b47e26fc53e5dcb8a88a596198 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 8 Jun 2020 10:31:40 +0200 Subject: [PATCH 3/5] Store data for metadata first This should help with providing data to sentry to find out what the hell is going wrong --- cura/Machines/Models/QualitySettingsModel.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cura/Machines/Models/QualitySettingsModel.py b/cura/Machines/Models/QualitySettingsModel.py index 20d8fd20ff..c88e103f3a 100644 --- a/cura/Machines/Models/QualitySettingsModel.py +++ b/cura/Machines/Models/QualitySettingsModel.py @@ -100,7 +100,8 @@ class QualitySettingsModel(ListModel): # the settings in that quality_changes_group. if quality_changes_group is not None: container_registry = ContainerRegistry.getInstance() - global_containers = container_registry.findContainers(id = quality_changes_group.metadata_for_global["id"]) + metadata_for_global = quality_changes_group.metadata_for_global + global_containers = container_registry.findContainers(id = metadata_for_global["id"]) global_container = None if len(global_containers) == 0 else global_containers[0] extruders_containers = {pos: container_registry.findContainers(id = quality_changes_group.metadata_per_extruder[pos]["id"]) for pos in quality_changes_group.metadata_per_extruder} extruders_container = {pos: None if not containers else containers[0] for pos, containers in extruders_containers.items()} From 7e10e74e9e7433ee928a1696048834bad7e48a91 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 8 Jun 2020 17:55:07 +0200 Subject: [PATCH 4/5] Don't send print job in chunks Previously the print job was sent with multiple PUT requests. This was necessary since the request was made with the Python Requests library, which freezes the interface while it's running. By breaking the upload up in chunks, it would at least periodically keep updating the interface (4 times per second or so, depending on your internet speed and Ultimaker's). Later on, this was replaced by a QNetworkRequest which doesn't freeze the interface in a refactor, but it was still broken up in chunks. Recently that was again refactored to use Uranium's HttpRequestManager, which under the covers also uses QNetworkRequest. This also doesn't freeze while uploading, which is good. However for some reason the second chunk would always give a QNetworkReply::ProtocolUnknownError, which is a bit of a catch-all error for any HTTP status codes not supported by Qt. I was not able to figure out what was really going wrong there, but it has something to do with the content-range header and making multiple requests. Instead of fixing that, I've removed the chunking. This simplifies the code a lot and doesn't have any implications for the user, since it still doesn't freeze the interface while the network request is ongoing. It should be slightly faster to upload, and reduce load on the server too. However the disadvantage is that the original bug is probably still there if it was a bug in the HttpRequestManager. If it was a bug in the ToolPathUploader I probably deleted the bug here. Contributes to issue CURA-7488. --- .../src/Cloud/ToolPathUploader.py | 56 ++++--------------- 1 file changed, 12 insertions(+), 44 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/ToolPathUploader.py b/plugins/UM3NetworkPrinting/src/Cloud/ToolPathUploader.py index 246c33b0c7..01242c402f 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/ToolPathUploader.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/ToolPathUploader.py @@ -20,9 +20,6 @@ class ToolPathUploader: # The HTTP codes that should trigger a retry. RETRY_HTTP_CODES = {500, 502, 503, 504} - # The amount of bytes to send per request - BYTES_PER_REQUEST = 256 * 1024 - def __init__(self, http: HttpRequestManager, print_job: CloudPrintJobResponse, data: bytes, on_finished: Callable[[], Any], on_progress: Callable[[int], Any], on_error: Callable[[], Any] ) -> None: @@ -44,7 +41,6 @@ class ToolPathUploader: self._on_progress = on_progress self._on_error = on_error - self._sent_bytes = 0 self._retries = 0 self._finished = False @@ -54,23 +50,14 @@ class ToolPathUploader: return self._print_job - def _chunkRange(self) -> Tuple[int, int]: - """Determines the bytes that should be uploaded next. - - :return: A tuple with the first and the last byte to upload. - """ - last_byte = min(len(self._data), self._sent_bytes + self.BYTES_PER_REQUEST) - return self._sent_bytes, last_byte - def start(self) -> None: """Starts uploading the mesh.""" if self._finished: # reset state. - self._sent_bytes = 0 self._retries = 0 self._finished = False - self._uploadChunk() + self._upload() def stop(self): """Stops uploading the mesh, marking it as finished.""" @@ -78,26 +65,18 @@ class ToolPathUploader: Logger.log("i", "Stopped uploading") self._finished = True - def _uploadChunk(self) -> None: - """Uploads a chunk of the mesh to the cloud.""" - + def _upload(self) -> None: + """ + Uploads the print job to the cloud printer. + """ if self._finished: raise ValueError("The upload is already finished") - first_byte, last_byte = self._chunkRange() - content_range = "bytes {}-{}/{}".format(first_byte, last_byte - 1, len(self._data)) - - headers = { - "Content-Type": cast(str, self._print_job.content_type), - "Content-Range": content_range - } # type: Dict[str, str] - - Logger.log("i", "Uploading %s to %s", content_range, self._print_job.upload_url) - + Logger.log("i", "Uploading print to {upload_url}".format(upload_url = self._print_job.upload_url)) self._http.put( url = cast(str, self._print_job.upload_url), - headers_dict = headers, - data = self._data[first_byte:last_byte], + headers_dict = {"Content-Type": cast(str, self._print_job.content_type)}, + data = self._data, callback = self._finishedCallback, error_callback = self._errorCallback, upload_progress_callback = self._progressCallback @@ -111,8 +90,7 @@ class ToolPathUploader: """ Logger.log("i", "Progress callback %s / %s", bytes_sent, bytes_total) if bytes_total: - total_sent = self._sent_bytes + bytes_sent - self._on_progress(int(total_sent / len(self._data) * 100)) + self._on_progress(int(bytes_sent / len(self._data) * 100)) ## Handles an error uploading. def _errorCallback(self, reply: QNetworkReply, error: QNetworkReply.NetworkError) -> None: @@ -136,7 +114,7 @@ class ToolPathUploader: self._retries += 1 Logger.log("i", "Retrying %s/%s request %s", self._retries, self.MAX_RETRIES, reply.url().toString()) try: - self._uploadChunk() + self._upload() except ValueError: # Asynchronously it could have completed in the meanwhile. pass return @@ -148,16 +126,6 @@ class ToolPathUploader: Logger.log("d", "status_code: %s, Headers: %s, body: %s", status_code, [bytes(header).decode() for header in reply.rawHeaderList()], bytes(reply.readAll()).decode()) - self._chunkUploaded() - def _chunkUploaded(self) -> None: - """Handles a chunk of data being uploaded, starting the next chunk if needed.""" - - # We got a successful response. Let's start the next chunk or report the upload is finished. - first_byte, last_byte = self._chunkRange() - self._sent_bytes += last_byte - first_byte - if self._sent_bytes >= len(self._data): - self.stop() - self._on_finished() - else: - self._uploadChunk() + self.stop() + self._on_finished() From c6af6565a367ffb8dc8b2c65acca1076d743669c Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 8 Jun 2020 17:58:42 +0200 Subject: [PATCH 5/5] More descriptive and appropriate logging Contributes to issue CURA-7488. --- plugins/UM3NetworkPrinting/src/Cloud/ToolPathUploader.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/ToolPathUploader.py b/plugins/UM3NetworkPrinting/src/Cloud/ToolPathUploader.py index 01242c402f..3c80565fa1 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/ToolPathUploader.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/ToolPathUploader.py @@ -62,8 +62,9 @@ class ToolPathUploader: def stop(self): """Stops uploading the mesh, marking it as finished.""" - Logger.log("i", "Stopped uploading") - self._finished = True + Logger.log("i", "Finished uploading") + self._finished = True # Signal to any ongoing retries that we should stop retrying. + self._on_finished() def _upload(self) -> None: """ @@ -88,7 +89,7 @@ class ToolPathUploader: :param bytes_sent: The amount of bytes sent in the current request. :param bytes_total: The amount of bytes to send in the current request. """ - Logger.log("i", "Progress callback %s / %s", bytes_sent, bytes_total) + Logger.debug("Cloud upload progress %s / %s", bytes_sent, bytes_total) if bytes_total: self._on_progress(int(bytes_sent / len(self._data) * 100)) @@ -128,4 +129,3 @@ class ToolPathUploader: [bytes(header).decode() for header in reply.rawHeaderList()], bytes(reply.readAll()).decode()) self.stop() - self._on_finished()