From 7a9dbe3a3f2b29628a12db3952aa3c8a6ce90a0b Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Thu, 14 Feb 2019 14:24:34 +0100 Subject: [PATCH 1/7] Don't explode the log with errors when there's no cloud response --- plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index adff94bbbc..bdb7f38d32 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -159,6 +159,9 @@ class CloudApiClient: model: Type[CloudApiClientModel], ) -> None: def parse() -> None: + # Don't try to parse the reply if we didn't get one + if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) is None: + return status_code, response = self._parseReply(reply) self._anti_gc_callbacks.remove(parse) return self._parseModels(response, on_finished, model) From ee0eb6aac2120dd9ed1a229cc6aef7848dc8987d Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 19 Feb 2019 10:20:28 +0100 Subject: [PATCH 2/7] Don't return value of self._parseModels Because it always returns none --- plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index bdb7f38d32..87c7a50838 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -164,7 +164,8 @@ class CloudApiClient: return status_code, response = self._parseReply(reply) self._anti_gc_callbacks.remove(parse) - return self._parseModels(response, on_finished, model) + self._parseModels(response, on_finished, model) + return self._anti_gc_callbacks.append(parse) reply.finished.connect(parse) From 7300f95a4284d5c6e817da3b87540a75d08b83c8 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 22 Jul 2019 21:21:18 +0200 Subject: [PATCH 3/7] Hide incorrect tooltip The preffered solution was to change the copywriting, but since we are after stringfreeze we cannot do this. This is the next best solution to prevent user confusion. --- .../resources/qml/MonitorPrintJobCard.qml | 13 +++++++------ .../resources/qml/MonitorPrinterCard.qml | 19 ++++++++++--------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml index b863712481..ea6da9c25d 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml @@ -243,10 +243,11 @@ Item enabled: !contextMenuButton.enabled } - MonitorInfoBlurb - { - id: contextMenuDisabledInfo - text: catalog.i18nc("@info", "Please update your printer's firmware to manage the queue remotely.") - target: contextMenuButton - } + // TODO: uncomment this tooltip as soon as the required firmware is released + // MonitorInfoBlurb + // { + // id: contextMenuDisabledInfo + // text: catalog.i18nc("@info", "Please update your printer's firmware to manage the queue remotely.") + // target: contextMenuButton + // } } diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml index 1f5a4cfcb2..8562cec59d 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml @@ -81,7 +81,7 @@ Item mipmap: true } } - + Item { @@ -99,7 +99,7 @@ Item height: 18 * screenScaleFactor // TODO: Theme! width: parent.width radius: 2 * screenScaleFactor // TODO: Theme! - + Label { text: printer && printer.name ? printer.name : "" @@ -202,12 +202,13 @@ Item enabled: !contextMenuButton.enabled } - MonitorInfoBlurb - { - id: contextMenuDisabledInfo - text: catalog.i18nc("@info", "Please update your printer's firmware to manage the queue remotely.") - target: contextMenuButton - } + // TODO: uncomment this tooltip as soon as the required firmware is released + // MonitorInfoBlurb + // { + // id: contextMenuDisabledInfo + // text: catalog.i18nc("@info", "Please update your printer's firmware to manage the queue remotely.") + // target: contextMenuButton + // } CameraButton { @@ -454,4 +455,4 @@ Item id: overrideConfirmationDialog printer: base.printer } -} \ No newline at end of file +} From 827ccd5a138b4e9ef07e5dca8f195d8675a82612 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 23 Jul 2019 13:19:42 +0200 Subject: [PATCH 4/7] Fix mypy issues caused by scenenode iterator being correctly typed --- cura/CuraApplication.py | 8 ++++---- cura/PreviewPass.py | 17 ++++++++++------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index bf745b1db3..b874b539c3 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1262,7 +1262,7 @@ class CuraApplication(QtApplication): @pyqtSlot() def arrangeObjectsToAllBuildPlates(self) -> None: nodes_to_arrange = [] - for node in DepthFirstIterator(self.getController().getScene().getRoot()): # type: ignore + for node in DepthFirstIterator(self.getController().getScene().getRoot()): if not isinstance(node, SceneNode): continue @@ -1339,9 +1339,9 @@ class CuraApplication(QtApplication): return for node in nodes: - file_name = node.getMeshData().getFileName() - if file_name: - job = ReadMeshJob(file_name) + mesh_data = node.getMeshData() + if mesh_data and mesh_data.getFileName(): + job = ReadMeshJob(mesh_data.getFileName()) job._node = node # type: ignore job.finished.connect(self._reloadMeshFinished) if has_merged_nodes: diff --git a/cura/PreviewPass.py b/cura/PreviewPass.py index 49e2befd28..2fe6e7971f 100644 --- a/cura/PreviewPass.py +++ b/cura/PreviewPass.py @@ -3,6 +3,8 @@ from typing import Optional, TYPE_CHECKING +from numpy import cast + from UM.Application import Application from UM.Resources import Resources @@ -12,6 +14,7 @@ from UM.View.RenderBatch import RenderBatch from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator +from cura.Scene.CuraSceneNode import CuraSceneNode if TYPE_CHECKING: from UM.View.GL.ShaderProgram import ShaderProgram @@ -44,9 +47,9 @@ class PreviewPass(RenderPass): self._renderer = Application.getInstance().getRenderer() - self._shader = None #type: Optional[ShaderProgram] - self._non_printing_shader = None #type: Optional[ShaderProgram] - self._support_mesh_shader = None #type: Optional[ShaderProgram] + self._shader = None # type: Optional[ShaderProgram] + self._non_printing_shader = None # type: Optional[ShaderProgram] + self._support_mesh_shader = None # type: Optional[ShaderProgram] self._scene = Application.getInstance().getController().getScene() # Set the camera to be used by this render pass @@ -83,8 +86,8 @@ class PreviewPass(RenderPass): batch_support_mesh = RenderBatch(self._support_mesh_shader) # Fill up the batch with objects that can be sliced. - for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. - if hasattr(node, "_outside_buildarea") and not node._outside_buildarea: + for node in DepthFirstIterator(self._scene.getRoot()): + if hasattr(node, "_outside_buildarea") and not getattr(node, "_outside_buildarea"): if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible(): per_mesh_stack = node.callDecoration("getStack") if node.callDecoration("isNonThumbnailVisibleMesh"): @@ -94,7 +97,7 @@ class PreviewPass(RenderPass): # Support mesh uniforms = {} shade_factor = 0.6 - diffuse_color = node.getDiffuseColor() + diffuse_color = cast(CuraSceneNode, node).getDiffuseColor() diffuse_color2 = [ diffuse_color[0] * shade_factor, diffuse_color[1] * shade_factor, @@ -106,7 +109,7 @@ class PreviewPass(RenderPass): else: # Normal scene node uniforms = {} - uniforms["diffuse_color"] = prettier_color(node.getDiffuseColor()) + uniforms["diffuse_color"] = prettier_color(cast(CuraSceneNode, node).getDiffuseColor()) batch.addItem(node.getWorldTransformation(), node.getMeshData(), uniforms = uniforms) self.bind() From 0077e022cebc6b4bb7ee75cb61212d8d7f6ac671 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 23 Jul 2019 13:24:39 +0200 Subject: [PATCH 5/7] Renamed some small test classes so they don't cause pytest warnings Pytest thought that they were tests, since their name started with test --- tests/TestCuraSceneNode.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/TestCuraSceneNode.py b/tests/TestCuraSceneNode.py index d4c1809c1e..47a4dc3cb0 100644 --- a/tests/TestCuraSceneNode.py +++ b/tests/TestCuraSceneNode.py @@ -6,7 +6,7 @@ import pytest from unittest.mock import patch -class TestConvexHullDecorator(SceneNodeDecorator): +class MockedConvexHullDecorator(SceneNodeDecorator): def __init__(self): super().__init__() @@ -14,7 +14,7 @@ class TestConvexHullDecorator(SceneNodeDecorator): return Polygon([[5, 5], [-5, 5], [-5, -5], [5, -5]]) -class TestInvalidConvexHullDecorator(SceneNodeDecorator): +class InvalidConvexHullDecorator(SceneNodeDecorator): def __init__(self): super().__init__() @@ -34,16 +34,16 @@ class TestCollidesWithAreas: assert not cura_scene_node.collidesWithAreas([Polygon([[10, 10], [-10, 10], [-10, -10], [10, -10]])]) def test_convexHullIntersects(self, cura_scene_node): - cura_scene_node.addDecorator(TestConvexHullDecorator()) + cura_scene_node.addDecorator(MockedConvexHullDecorator()) assert cura_scene_node.collidesWithAreas([Polygon([[10, 10], [-10, 10], [-10, -10], [10, -10]])]) def test_convexHullNoIntersection(self, cura_scene_node): - cura_scene_node.addDecorator(TestConvexHullDecorator()) + cura_scene_node.addDecorator(MockedConvexHullDecorator()) assert not cura_scene_node.collidesWithAreas([Polygon([[60, 60], [40, 60], [40, 40], [60, 40]])]) def test_invalidConvexHull(self, cura_scene_node): - cura_scene_node.addDecorator(TestInvalidConvexHullDecorator()) + cura_scene_node.addDecorator(InvalidConvexHullDecorator()) assert not cura_scene_node.collidesWithAreas([Polygon([[10, 10], [-10, 10], [-10, -10], [10, -10]])]) From 5f74ed080f7b685c36830a85d7488604423a1534 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 23 Jul 2019 13:32:48 +0200 Subject: [PATCH 6/7] Increment SDK version to 6.2 --- cura/ApplicationMetadata.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/ApplicationMetadata.py b/cura/ApplicationMetadata.py index 73eb9bb288..eeb283a72b 100644 --- a/cura/ApplicationMetadata.py +++ b/cura/ApplicationMetadata.py @@ -9,7 +9,7 @@ DEFAULT_CURA_DISPLAY_NAME = "Ultimaker Cura" DEFAULT_CURA_VERSION = "master" DEFAULT_CURA_BUILD_TYPE = "" DEFAULT_CURA_DEBUG_MODE = False -DEFAULT_CURA_SDK_VERSION = "6.1.0" +DEFAULT_CURA_SDK_VERSION = "6.2.0" try: from cura.CuraVersion import CuraAppName # type: ignore @@ -45,4 +45,4 @@ except ImportError: # Each release has a fixed SDK version coupled with it. It doesn't make sense to make it configurable because, for # example Cura 3.2 with SDK version 6.1 will not work. So the SDK version is hard-coded here and left out of the # CuraVersion.py.in template. -CuraSDKVersion = "6.1.0" +CuraSDKVersion = "6.2.0" From d8ddcba091de0b619593aeb63c5ad5224a896a22 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 23 Jul 2019 13:40:40 +0200 Subject: [PATCH 7/7] Fix typing ignore for breath frist iterator --- cura/CuraApplication.py | 4 +-- cura/Settings/ExtruderManager.py | 2 +- cura/Settings/MachineManager.py | 2 +- .../CuraEngineBackend/CuraEngineBackend.py | 11 +++---- plugins/CuraEngineBackend/StartSliceJob.py | 30 ++++++++++++------- plugins/SimulationView/SimulationView.py | 4 +-- 6 files changed, 31 insertions(+), 22 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index b874b539c3..65534a4ccd 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1289,7 +1289,7 @@ class CuraApplication(QtApplication): def arrangeAll(self) -> None: nodes_to_arrange = [] active_build_plate = self.getMultiBuildPlateModel().activeBuildPlate - for node in DepthFirstIterator(self.getController().getScene().getRoot()): # type: ignore + for node in DepthFirstIterator(self.getController().getScene().getRoot()): if not isinstance(node, SceneNode): continue @@ -1327,7 +1327,7 @@ class CuraApplication(QtApplication): Logger.log("i", "Reloading all loaded mesh data.") nodes = [] has_merged_nodes = False - for node in DepthFirstIterator(self.getController().getScene().getRoot()): # type: ignore + for node in DepthFirstIterator(self.getController().getScene().getRoot()): if not isinstance(node, CuraSceneNode) or not node.getMeshData(): if node.getName() == "MergedMesh": has_merged_nodes = True diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 60849f0dd9..c00852cbc0 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -115,7 +115,7 @@ class ExtruderManager(QObject): selected_nodes = [] # type: List["SceneNode"] for node in Selection.getAllSelectedObjects(): if node.callDecoration("isGroup"): - for grouped_node in BreadthFirstIterator(node): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. + for grouped_node in BreadthFirstIterator(node): if grouped_node.callDecoration("isGroup"): continue diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 9d94467555..472e493cbe 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -949,7 +949,7 @@ class MachineManager(QObject): # Check to see if any objects are set to print with an extruder that will no longer exist root_node = self._application.getController().getScene().getRoot() - for node in DepthFirstIterator(root_node): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. + for node in DepthFirstIterator(root_node): if node.getMeshData(): extruder_nr = node.callDecoration("getActiveExtruderPosition") diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 6b558bc65b..ae18e76e5a 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -369,7 +369,7 @@ class CuraEngineBackend(QObject, Backend): elif job.getResult() == StartJobResult.ObjectSettingError: errors = {} - for node in DepthFirstIterator(self._application.getController().getScene().getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. + for node in DepthFirstIterator(self._application.getController().getScene().getRoot()): stack = node.callDecoration("getStack") if not stack: continue @@ -438,7 +438,7 @@ class CuraEngineBackend(QObject, Backend): if not self._application.getPreferences().getValue("general/auto_slice"): enable_timer = False - for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. + for node in DepthFirstIterator(self._scene.getRoot()): if node.callDecoration("isBlockSlicing"): enable_timer = False self.setState(BackendState.Disabled) @@ -460,7 +460,7 @@ class CuraEngineBackend(QObject, Backend): ## Return a dict with number of objects per build plate def _numObjectsPerBuildPlate(self) -> Dict[int, int]: num_objects = defaultdict(int) #type: Dict[int, int] - for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. + for node in DepthFirstIterator(self._scene.getRoot()): # Only count sliceable objects if node.callDecoration("isSliceable"): build_plate_number = node.callDecoration("getBuildPlateNumber") @@ -548,10 +548,11 @@ class CuraEngineBackend(QObject, Backend): # Clear out any old gcode self._scene.gcode_dict = {} # type: ignore - for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. + for node in DepthFirstIterator(self._scene.getRoot()): if node.callDecoration("getLayerData"): if not build_plate_numbers or node.callDecoration("getBuildPlateNumber") in build_plate_numbers: - node.getParent().removeChild(node) + # We can asume that all nodes have a parent as we're looping through the scene (and filter out root) + cast(SceneNode, node.getParent()).removeChild(node) def markSliceAll(self) -> None: for build_plate_number in range(self._application.getMultiBuildPlateModel().maxBuildPlate + 1): diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index fc4de3dfa5..72eb21c122 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -11,6 +11,7 @@ import Arcus #For typing. from UM.Job import Job from UM.Logger import Logger +from UM.Scene.SceneNode import SceneNode from UM.Settings.ContainerStack import ContainerStack #For typing. from UM.Settings.SettingRelation import SettingRelation #For typing. @@ -150,7 +151,7 @@ class StartSliceJob(Job): # Don't slice if there is a per object setting with an error value. - for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. + for node in DepthFirstIterator(self._scene.getRoot()): if not isinstance(node, CuraSceneNode) or not node.isSelectable(): continue @@ -160,15 +161,16 @@ class StartSliceJob(Job): with self._scene.getSceneLock(): # Remove old layer data. - for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. + for node in DepthFirstIterator(self._scene.getRoot()): if node.callDecoration("getLayerData") and node.callDecoration("getBuildPlateNumber") == self._build_plate_number: - node.getParent().removeChild(node) + # Singe we walk through all nodes in the scene, they always have a parent. + cast(SceneNode, node.getParent()).removeChild(node) break # Get the objects in their groups to print. object_groups = [] if stack.getProperty("print_sequence", "value") == "one_at_a_time": - for node in OneAtATimeIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. + for node in OneAtATimeIterator(self._scene.getRoot()): temp_list = [] # Node can't be printed, so don't bother sending it. @@ -183,7 +185,8 @@ class StartSliceJob(Job): children = node.getAllChildren() children.append(node) for child_node in children: - if child_node.getMeshData() and child_node.getMeshData().getVertices() is not None: + mesh_data = child_node.getMeshData() + if mesh_data and mesh_data.getVertices() is not None: temp_list.append(child_node) if temp_list: @@ -194,8 +197,9 @@ class StartSliceJob(Job): else: temp_list = [] has_printing_mesh = False - for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. - if node.callDecoration("isSliceable") and node.getMeshData() and node.getMeshData().getVertices() is not None: + for node in DepthFirstIterator(self._scene.getRoot()): + mesh_data = node.getMeshData() + if node.callDecoration("isSliceable") and mesh_data and mesh_data.getVertices() is not None: is_non_printing_mesh = bool(node.callDecoration("isNonPrintingMesh")) # Find a reason not to add the node @@ -210,7 +214,7 @@ class StartSliceJob(Job): Job.yieldThread() - #If the list doesn't have any model with suitable settings then clean the list + # If the list doesn't have any model with suitable settings then clean the list # otherwise CuraEngine will crash if not has_printing_mesh: temp_list.clear() @@ -261,10 +265,14 @@ class StartSliceJob(Job): for group in filtered_object_groups: group_message = self._slice_message.addRepeatedMessage("object_lists") - if group[0].getParent() is not None and group[0].getParent().callDecoration("isGroup"): - self._handlePerObjectSettings(group[0].getParent(), group_message) + parent = group[0].getParent() + if parent is not None and parent.callDecoration("isGroup"): + self._handlePerObjectSettings(cast(CuraSceneNode, parent), group_message) + for object in group: mesh_data = object.getMeshData() + if mesh_data is None: + continue rot_scale = object.getWorldTransformation().getTransposed().getData()[0:3, 0:3] translate = object.getWorldTransformation().getData()[:3, 3] @@ -288,7 +296,7 @@ class StartSliceJob(Job): obj.vertices = flat_verts - self._handlePerObjectSettings(object, obj) + self._handlePerObjectSettings(cast(CuraSceneNode, object), obj) Job.yieldThread() diff --git a/plugins/SimulationView/SimulationView.py b/plugins/SimulationView/SimulationView.py index 18f436b13a..20471f9763 100644 --- a/plugins/SimulationView/SimulationView.py +++ b/plugins/SimulationView/SimulationView.py @@ -218,10 +218,10 @@ class SimulationView(CuraView): if theme is not None: self._ghost_shader.setUniformValue("u_color", Color(*theme.getColor("layerview_ghost").getRgb())) - for node in DepthFirstIterator(scene.getRoot()): # type: ignore + for node in DepthFirstIterator(scene.getRoot()): # We do not want to render ConvexHullNode as it conflicts with the bottom layers. # However, it is somewhat relevant when the node is selected, so do render it then. - if type(node) is ConvexHullNode and not Selection.isSelected(node.getWatchedNode()): + if type(node) is ConvexHullNode and not Selection.isSelected(cast(ConvexHullNode, node).getWatchedNode()): continue if not node.render(renderer):