From b66558f97a913e38c1dcdf6f0ecbe69eaea681d2 Mon Sep 17 00:00:00 2001 From: paukstelis Date: Sun, 19 Aug 2018 22:52:17 -0400 Subject: [PATCH 01/17] Initial commit for passing mesh names to CuraEngine --- cura/ObjectsModel.py | 18 ++++++++++++++++++ plugins/CuraEngineBackend/Cura.proto | 2 ++ plugins/CuraEngineBackend/StartSliceJob.py | 4 ++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/cura/ObjectsModel.py b/cura/ObjectsModel.py index f3c703d424..10d8e16f98 100644 --- a/cura/ObjectsModel.py +++ b/cura/ObjectsModel.py @@ -40,6 +40,9 @@ class ObjectsModel(ListModel): filter_current_build_plate = Application.getInstance().getPreferences().getValue("view/filter_current_build_plate") active_build_plate_number = self._build_plate_number group_nr = 1 + instance = 1 + namecount = [] + for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()): if not isinstance(node, SceneNode): continue @@ -55,6 +58,7 @@ class ObjectsModel(ListModel): if not node.callDecoration("isGroup"): name = node.getName() + else: name = catalog.i18nc("@label", "Group #{group_nr}").format(group_nr = str(group_nr)) group_nr += 1 @@ -63,6 +67,18 @@ class ObjectsModel(ListModel): is_outside_build_area = node.isOutsideBuildArea() else: is_outside_build_area = False + + #check if we already have an instance of the object based on name + duplicate = False + for n in namecount: + if name == n["name"]: + name = "{0}({1})".format(name, n["count"]) + node.setName(name) + n["count"] = n["count"]+1 + duplicate = True + + if not duplicate: + namecount.append({"name" : name, "count" : 1}) nodes.append({ "name": name, @@ -71,8 +87,10 @@ class ObjectsModel(ListModel): "buildPlateNumber": node_build_plate_number, "node": node }) + nodes = sorted(nodes, key=lambda n: n["name"]) self.setItems(nodes) + print(nodes) self.itemsChanged.emit() diff --git a/plugins/CuraEngineBackend/Cura.proto b/plugins/CuraEngineBackend/Cura.proto index 69612210ec..5e0f88f075 100644 --- a/plugins/CuraEngineBackend/Cura.proto +++ b/plugins/CuraEngineBackend/Cura.proto @@ -29,6 +29,8 @@ message Object bytes normals = 3; //An array of 3 floats. bytes indices = 4; //An array of ints. repeated Setting settings = 5; // Setting override per object, overruling the global settings. + //PJP + string name = 6; } message Progress diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 0ebcafdbb2..4e6c53c4fb 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -256,7 +256,7 @@ class StartSliceJob(Job): mesh_data = object.getMeshData() rot_scale = object.getWorldTransformation().getTransposed().getData()[0:3, 0:3] translate = object.getWorldTransformation().getData()[:3, 3] - + # This effectively performs a limited form of MeshData.getTransformed that ignores normals. verts = mesh_data.getVertices() verts = verts.dot(rot_scale) @@ -268,7 +268,7 @@ class StartSliceJob(Job): obj = group_message.addRepeatedMessage("objects") obj.id = id(object) - + obj.name = object.getName() indices = mesh_data.getIndices() if indices is not None: flat_verts = numpy.take(verts, indices.flatten(), axis=0) From fc9c1045c97a857627c93254a5006e8753fa5d4a Mon Sep 17 00:00:00 2001 From: paukstelis Date: Mon, 20 Aug 2018 07:40:28 -0400 Subject: [PATCH 02/17] Basic cleanup --- cura/ObjectsModel.py | 1 - plugins/CuraEngineBackend/Cura.proto | 1 - 2 files changed, 2 deletions(-) diff --git a/cura/ObjectsModel.py b/cura/ObjectsModel.py index 10d8e16f98..1ac0c6247a 100644 --- a/cura/ObjectsModel.py +++ b/cura/ObjectsModel.py @@ -90,7 +90,6 @@ class ObjectsModel(ListModel): nodes = sorted(nodes, key=lambda n: n["name"]) self.setItems(nodes) - print(nodes) self.itemsChanged.emit() diff --git a/plugins/CuraEngineBackend/Cura.proto b/plugins/CuraEngineBackend/Cura.proto index 5e0f88f075..292330576b 100644 --- a/plugins/CuraEngineBackend/Cura.proto +++ b/plugins/CuraEngineBackend/Cura.proto @@ -29,7 +29,6 @@ message Object bytes normals = 3; //An array of 3 floats. bytes indices = 4; //An array of ints. repeated Setting settings = 5; // Setting override per object, overruling the global settings. - //PJP string name = 6; } From e36f78dd355fba6c9935a2184051f78c770bf30e Mon Sep 17 00:00:00 2001 From: paukstelis Date: Fri, 24 Aug 2018 07:27:34 -0400 Subject: [PATCH 03/17] Clean up whitespace --- cura/ObjectsModel.py | 12 ++++++------ plugins/CuraEngineBackend/StartSliceJob.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cura/ObjectsModel.py b/cura/ObjectsModel.py index 1ac0c6247a..4f3d42e7fe 100644 --- a/cura/ObjectsModel.py +++ b/cura/ObjectsModel.py @@ -71,12 +71,12 @@ class ObjectsModel(ListModel): #check if we already have an instance of the object based on name duplicate = False for n in namecount: - if name == n["name"]: - name = "{0}({1})".format(name, n["count"]) - node.setName(name) - n["count"] = n["count"]+1 - duplicate = True - + if name == n["name"]: + name = "{0}({1})".format(name, n["count"]) + node.setName(name) + n["count"] = n["count"]+1 + duplicate = True + if not duplicate: namecount.append({"name" : name, "count" : 1}) diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 4e6c53c4fb..2430485e30 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -256,7 +256,7 @@ class StartSliceJob(Job): mesh_data = object.getMeshData() rot_scale = object.getWorldTransformation().getTransposed().getData()[0:3, 0:3] translate = object.getWorldTransformation().getData()[:3, 3] - + # This effectively performs a limited form of MeshData.getTransformed that ignores normals. verts = mesh_data.getVertices() verts = verts.dot(rot_scale) From 53a0abd230fb4db97ba3e86556d843185d3794bc Mon Sep 17 00:00:00 2001 From: paukstelis Date: Wed, 29 Aug 2018 17:43:22 -0400 Subject: [PATCH 04/17] Convert name check to defaultdict --- cura/ObjectsModel.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/cura/ObjectsModel.py b/cura/ObjectsModel.py index 4f3d42e7fe..8354540783 100644 --- a/cura/ObjectsModel.py +++ b/cura/ObjectsModel.py @@ -9,6 +9,7 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.SceneNode import SceneNode from UM.Scene.Selection import Selection from UM.i18n import i18nCatalog +from collections import defaultdict catalog = i18nCatalog("cura") @@ -40,9 +41,8 @@ class ObjectsModel(ListModel): filter_current_build_plate = Application.getInstance().getPreferences().getValue("view/filter_current_build_plate") active_build_plate_number = self._build_plate_number group_nr = 1 - instance = 1 - namecount = [] - + name_count_dict = defaultdict(int) + for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()): if not isinstance(node, SceneNode): continue @@ -69,16 +69,12 @@ class ObjectsModel(ListModel): is_outside_build_area = False #check if we already have an instance of the object based on name - duplicate = False - for n in namecount: - if name == n["name"]: - name = "{0}({1})".format(name, n["count"]) - node.setName(name) - n["count"] = n["count"]+1 - duplicate = True - - if not duplicate: - namecount.append({"name" : name, "count" : 1}) + name_count_dict[name] += 1 + name_count = name_count_dict[name] + + if name_count > 1: + name = "{0}({1})".format(name, name_count-1) + node.setName(name) nodes.append({ "name": name, @@ -87,7 +83,7 @@ class ObjectsModel(ListModel): "buildPlateNumber": node_build_plate_number, "node": node }) - + nodes = sorted(nodes, key=lambda n: n["name"]) self.setItems(nodes) From 9edf2b5b0bd8f4d22210ea2d3eb85e0a53806fb6 Mon Sep 17 00:00:00 2001 From: KangDroid Date: Thu, 30 Aug 2018 16:53:07 +0900 Subject: [PATCH 05/17] Re-Enable fullscreen shortcut and add menu on ViewMenu --- resources/qml/Actions.qml | 1 + resources/qml/Menus/ViewMenu.qml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index d5572298f7..cc11e609f5 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -75,6 +75,7 @@ Item Action { id:toggleFullScreenAction + shortcut: StandardKey.FullScreen; text: catalog.i18nc("@action:inmenu","Toggle Fu&ll Screen"); iconName: "view-fullscreen"; } diff --git a/resources/qml/Menus/ViewMenu.qml b/resources/qml/Menus/ViewMenu.qml index 6bbb0b1e2e..9a2e603673 100644 --- a/resources/qml/Menus/ViewMenu.qml +++ b/resources/qml/Menus/ViewMenu.qml @@ -73,4 +73,7 @@ Menu MenuSeparator {} MenuItem { action: Cura.Actions.expandSidebar; } + + MenuSeparator {} + MenuItem { action: Cura.Actions.toggleFullScreen; } } From 1467e703ae121c3f2fa02ee7258bdeba63f80c5c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 27 Sep 2018 15:16:46 +0200 Subject: [PATCH 06/17] No longer make BuildVolume set max bounds. We didn't use it anymore and it added an extra requirement for buildvolume to depend on Application --- cura/BuildVolume.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 4059283a32..9d2f5c1f90 100755 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -479,8 +479,6 @@ class BuildVolume(SceneNode): maximum = Vector(max_w - bed_adhesion_size - 1, max_h - self._raft_thickness - self._extra_z_clearance, max_d - disallowed_area_size + bed_adhesion_size - 1) ) - self._application.getController().getScene()._maximum_bounds = scale_to_max_bounds - self.updateNodeBoundaryCheck() def getBoundingBox(self) -> AxisAlignedBox: From b58c01400baf563bb537d6bc49511c898b1b9689 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 27 Sep 2018 15:28:53 +0200 Subject: [PATCH 07/17] Updated typing & documentation --- cura/Scene/ConvexHullDecorator.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/cura/Scene/ConvexHullDecorator.py b/cura/Scene/ConvexHullDecorator.py index 85d1e8e309..a78f559aa1 100644 --- a/cura/Scene/ConvexHullDecorator.py +++ b/cura/Scene/ConvexHullDecorator.py @@ -15,8 +15,6 @@ import numpy from typing import TYPE_CHECKING, Any, Optional - - if TYPE_CHECKING: from UM.Scene.SceneNode import SceneNode from cura.Settings.GlobalStack import GlobalStack @@ -35,10 +33,11 @@ class ConvexHullDecorator(SceneNodeDecorator): # Make sure the timer is created on the main thread self._recompute_convex_hull_timer = None # type: Optional[QTimer] - Application.getInstance().callLater(self.createRecomputeConvexHullTimer) + + if Application.getInstance() is not None: + Application.getInstance().callLater(self.createRecomputeConvexHullTimer) self._raft_thickness = 0.0 - # For raft thickness, DRY self._build_volume = Application.getInstance().getBuildVolume() self._build_volume.raftThicknessChanged.connect(self._onChanged) @@ -72,7 +71,7 @@ class ConvexHullDecorator(SceneNodeDecorator): def __deepcopy__(self, memo): return ConvexHullDecorator() - ## Get the unmodified 2D projected convex hull of the node + ## Get the unmodified 2D projected convex hull of the node (if any) def getConvexHull(self) -> Optional[Polygon]: if self._node is None: return None @@ -120,6 +119,7 @@ class ConvexHullDecorator(SceneNodeDecorator): return self._compute2DConvexHull() return None + ## The same as recomputeConvexHull, but using a timer if it was set. def recomputeConvexHullDelayed(self) -> None: if self._recompute_convex_hull_timer is not None: self._recompute_convex_hull_timer.start() @@ -142,13 +142,13 @@ class ConvexHullDecorator(SceneNodeDecorator): self._convex_hull_node = hull_node def _onSettingValueChanged(self, key: str, property_name: str) -> None: - if property_name != "value": #Not the value that was changed. + if property_name != "value": # Not the value that was changed. return if key in self._affected_settings: self._onChanged() if key in self._influencing_settings: - self._init2DConvexHullCache() #Invalidate the cache. + self._init2DConvexHullCache() # Invalidate the cache. self._onChanged() def _init2DConvexHullCache(self) -> None: @@ -161,7 +161,7 @@ class ConvexHullDecorator(SceneNodeDecorator): self._2d_convex_hull_mesh_world_transform = None self._2d_convex_hull_mesh_result = None - def _compute2DConvexHull(self) -> Polygon: + def _compute2DConvexHull(self) -> Optional[Polygon]: if self._node.callDecoration("isGroup"): points = numpy.zeros((0, 2), dtype=numpy.int32) for child in self._node.getChildren(): @@ -188,8 +188,6 @@ class ConvexHullDecorator(SceneNodeDecorator): else: offset_hull = None - mesh = None - world_transform = None if self._node.getMeshData(): mesh = self._node.getMeshData() world_transform = self._node.getWorldTransformation() @@ -242,10 +240,10 @@ class ConvexHullDecorator(SceneNodeDecorator): return Polygon(numpy.array(self._global_stack.getHeadAndFansCoordinates(), numpy.float32)) return Polygon() - def _compute2DConvexHeadFull(self): + def _compute2DConvexHeadFull(self) -> Polygon: return self._compute2DConvexHull().getMinkowskiHull(self._getHeadAndFans()) - def _compute2DConvexHeadMin(self): + def _compute2DConvexHeadMin(self) -> Polygon: headAndFans = self._getHeadAndFans() mirrored = headAndFans.mirror([0, 0], [0, 1]).mirror([0, 0], [1, 0]) # Mirror horizontally & vertically. head_and_fans = self._getHeadAndFans().intersectionConvexHulls(mirrored) @@ -276,7 +274,7 @@ class ConvexHullDecorator(SceneNodeDecorator): else: raise Exception("Unknown bed adhesion type. Did you forget to update the convex hull calculations for your new bed adhesion type?") - # adjust head_and_fans with extra margin + # Adjust head_and_fans with extra margin if extra_margin > 0: extra_margin_polygon = Polygon.approximatedCircle(extra_margin) poly = poly.getMinkowskiHull(extra_margin_polygon) @@ -354,7 +352,7 @@ class ConvexHullDecorator(SceneNodeDecorator): # Limit_to_extruder is set. The global stack handles this then return self._global_stack.getProperty(setting_key, prop) - ## Returns true if node is a descendant or the same as the root node. + ## Returns True if node is a descendant or the same as the root node. def __isDescendant(self, root: "SceneNode", node: "SceneNode") -> bool: if node is None: return False From d7901907aff0709c65d0f79439e073c8d3983c8d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 27 Sep 2018 15:37:08 +0200 Subject: [PATCH 08/17] Fix typing --- cura/PrinterOutput/ConfigurationModel.py | 16 ++++++++-------- cura/Scene/ConvexHullDecorator.py | 21 +++++++++++++-------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/cura/PrinterOutput/ConfigurationModel.py b/cura/PrinterOutput/ConfigurationModel.py index a3d6afd01d..b3e8373745 100644 --- a/cura/PrinterOutput/ConfigurationModel.py +++ b/cura/PrinterOutput/ConfigurationModel.py @@ -13,20 +13,20 @@ class ConfigurationModel(QObject): configurationChanged = pyqtSignal() - def __init__(self): + def __init__(self) -> None: super().__init__() - self._printer_type = None + self._printer_type = "" self._extruder_configurations = [] # type: List[ExtruderConfigurationModel] - self._buildplate_configuration = None + self._buildplate_configuration = "" def setPrinterType(self, printer_type): self._printer_type = printer_type @pyqtProperty(str, fset = setPrinterType, notify = configurationChanged) - def printerType(self): + def printerType(self) -> str: return self._printer_type - def setExtruderConfigurations(self, extruder_configurations): + def setExtruderConfigurations(self, extruder_configurations: List[ExtruderConfigurationModel]): if self._extruder_configurations != extruder_configurations: self._extruder_configurations = extruder_configurations @@ -39,16 +39,16 @@ class ConfigurationModel(QObject): def extruderConfigurations(self): return self._extruder_configurations - def setBuildplateConfiguration(self, buildplate_configuration): + def setBuildplateConfiguration(self, buildplate_configuration: str) -> None: self._buildplate_configuration = buildplate_configuration @pyqtProperty(str, fset = setBuildplateConfiguration, notify = configurationChanged) - def buildplateConfiguration(self): + def buildplateConfiguration(self) -> str: return self._buildplate_configuration ## This method is intended to indicate whether the configuration is valid or not. # The method checks if the mandatory fields are or not set - def isValid(self): + def isValid(self) -> bool: if not self._extruder_configurations: return False for configuration in self._extruder_configurations: diff --git a/cura/Scene/ConvexHullDecorator.py b/cura/Scene/ConvexHullDecorator.py index a78f559aa1..31e21df6bf 100644 --- a/cura/Scene/ConvexHullDecorator.py +++ b/cura/Scene/ConvexHullDecorator.py @@ -78,7 +78,7 @@ class ConvexHullDecorator(SceneNodeDecorator): hull = self._compute2DConvexHull() - if self._global_stack and self._node: + if self._global_stack and self._node and hull is not None: # Parent can be None if node is just loaded. if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and (self._node.getParent() is None or not self._node.getParent().callDecoration("isGroup")): hull = hull.getMinkowskiHull(Polygon(numpy.array(self._global_stack.getProperty("machine_head_polygon", "value"), numpy.float32))) @@ -240,17 +240,22 @@ class ConvexHullDecorator(SceneNodeDecorator): return Polygon(numpy.array(self._global_stack.getHeadAndFansCoordinates(), numpy.float32)) return Polygon() - def _compute2DConvexHeadFull(self) -> Polygon: - return self._compute2DConvexHull().getMinkowskiHull(self._getHeadAndFans()) + def _compute2DConvexHeadFull(self) -> Optional[Polygon]: + convex_hull = self._compute2DConvexHeadFull() + if convex_hull: + return convex_hull.getMinkowskiHull(self._getHeadAndFans()) + return None - def _compute2DConvexHeadMin(self) -> Polygon: - headAndFans = self._getHeadAndFans() - mirrored = headAndFans.mirror([0, 0], [0, 1]).mirror([0, 0], [1, 0]) # Mirror horizontally & vertically. + def _compute2DConvexHeadMin(self) -> Optional[Polygon]: + head_and_fans = self._getHeadAndFans() + mirrored = head_and_fans.mirror([0, 0], [0, 1]).mirror([0, 0], [1, 0]) # Mirror horizontally & vertically. head_and_fans = self._getHeadAndFans().intersectionConvexHulls(mirrored) # Min head hull is used for the push free - min_head_hull = self._compute2DConvexHull().getMinkowskiHull(head_and_fans) - return min_head_hull + convex_hull = self._compute2DConvexHeadFull() + if convex_hull: + return convex_hull.getMinkowskiHull(head_and_fans) + return None ## Compensate given 2D polygon with adhesion margin # \return 2D polygon with added margin From 889035ebfadb50cb567860b50824a98fc348ac45 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 27 Sep 2018 15:42:12 +0200 Subject: [PATCH 09/17] Fixed typo --- cura/PrinterOutput/ConfigurationModel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/PrinterOutput/ConfigurationModel.py b/cura/PrinterOutput/ConfigurationModel.py index b3e8373745..89e609c913 100644 --- a/cura/PrinterOutput/ConfigurationModel.py +++ b/cura/PrinterOutput/ConfigurationModel.py @@ -26,7 +26,7 @@ class ConfigurationModel(QObject): def printerType(self) -> str: return self._printer_type - def setExtruderConfigurations(self, extruder_configurations: List[ExtruderConfigurationModel]): + def setExtruderConfigurations(self, extruder_configurations: List["ExtruderConfigurationModel"]): if self._extruder_configurations != extruder_configurations: self._extruder_configurations = extruder_configurations From c15f8aa6935736bb1ed90c877a0fd2399d65d707 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 27 Sep 2018 15:54:23 +0200 Subject: [PATCH 10/17] Move printer type checking to where it belongs; inside the UM3 plugin. --- .../PrinterOutput/NetworkedPrinterOutputDevice.py | 15 +-------------- .../src/UM3OutputDevicePlugin.py | 13 +++++++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index 94f86f19a3..d9c5707a03 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -53,21 +53,8 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): self._sending_gcode = False self._compressing_gcode = False self._gcode = [] # type: List[str] - self._connection_state_before_timeout = None # type: Optional[ConnectionState] - printer_type = self._properties.get(b"machine", b"").decode("utf-8") - printer_type_identifiers = { - "9066": "ultimaker3", - "9511": "ultimaker3_extended", - "9051": "ultimaker_s5" - } - self._printer_type = "Unknown" - for key, value in printer_type_identifiers.items(): - if printer_type.startswith(key): - self._printer_type = value - break - def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None: raise NotImplementedError("requestWrite needs to be implemented") @@ -341,7 +328,7 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): @pyqtProperty(str, constant = True) def printerType(self) -> str: - return self._printer_type + return self._properties.get(b"printer_type", b"Unknown").decode("utf-8") ## IP adress of this printer @pyqtProperty(str, constant = True) diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index f4749a6747..9c070f2de2 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -260,6 +260,19 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): # or "Legacy" UM3 device. cluster_size = int(properties.get(b"cluster_size", -1)) + printer_type = properties.get(b"machine", b"").decode("utf-8") + printer_type_identifiers = { + "9066": "ultimaker3", + "9511": "ultimaker3_extended", + "9051": "ultimaker_s5" + } + + for key, value in printer_type_identifiers.items(): + if printer_type.startswith(key): + properties[b"printer_type"] = bytes(value, encoding="utf8") + break + else: + properties[b"printer_type"] = b"Unknown" if cluster_size >= 0: device = ClusterUM3OutputDevice.ClusterUM3OutputDevice(name, address, properties) else: From 7310a677ced78acab279bfc2a8677ebe1b4bd084 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 27 Sep 2018 16:07:18 +0200 Subject: [PATCH 11/17] Clean up more code This fixes some typing and moves a property to protected, as it should be --- cura/Machines/ContainerNode.py | 13 +++++-------- cura/Machines/MaterialManager.py | 1 - .../Models/SettingVisibilityPresetsModel.py | 4 ++-- cura/Machines/QualityChangesGroup.py | 4 ++-- cura/Machines/QualityNode.py | 8 ++++---- 5 files changed, 13 insertions(+), 17 deletions(-) diff --git a/cura/Machines/ContainerNode.py b/cura/Machines/ContainerNode.py index 0d44c7c4a3..eef1c63127 100644 --- a/cura/Machines/ContainerNode.py +++ b/cura/Machines/ContainerNode.py @@ -9,9 +9,6 @@ from UM.ConfigurationErrorMessage import ConfigurationErrorMessage from UM.Logger import Logger from UM.Settings.InstanceContainer import InstanceContainer -if TYPE_CHECKING: - from cura.Machines.QualityGroup import QualityGroup - ## # A metadata / container combination. Use getContainer() to get the container corresponding to the metadata. @@ -24,11 +21,11 @@ if TYPE_CHECKING: # This is used in Variant, Material, and Quality Managers. # class ContainerNode: - __slots__ = ("_metadata", "container", "children_map") + __slots__ = ("_metadata", "_container", "children_map") def __init__(self, metadata: Optional[Dict[str, Any]] = None) -> None: self._metadata = metadata - self.container = None + self._container = None # type: Optional[InstanceContainer] self.children_map = OrderedDict() # type: ignore # This is because it's children are supposed to override it. ## Get an entry value from the metadata @@ -50,7 +47,7 @@ class ContainerNode: Logger.log("e", "Cannot get container for a ContainerNode without metadata.") return None - if self.container is None: + if self._container is None: container_id = self._metadata["id"] from UM.Settings.ContainerRegistry import ContainerRegistry container_list = ContainerRegistry.getInstance().findInstanceContainers(id = container_id) @@ -59,9 +56,9 @@ class ContainerNode: error_message = ConfigurationErrorMessage.getInstance() error_message.addFaultyContainers(container_id) return None - self.container = container_list[0] + self._container = container_list[0] - return self.container + return self._container def __str__(self) -> str: return "%s[%s]" % (self.__class__.__name__, self.getMetaDataEntry("id")) diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index 0ca9047620..be97fbc161 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -21,7 +21,6 @@ from .VariantType import VariantType if TYPE_CHECKING: from UM.Settings.DefinitionContainer import DefinitionContainer - from UM.Settings.InstanceContainer import InstanceContainer from cura.Settings.GlobalStack import GlobalStack from cura.Settings.ExtruderStack import ExtruderStack diff --git a/cura/Machines/Models/SettingVisibilityPresetsModel.py b/cura/Machines/Models/SettingVisibilityPresetsModel.py index 3062e83889..d5fa51d20a 100644 --- a/cura/Machines/Models/SettingVisibilityPresetsModel.py +++ b/cura/Machines/Models/SettingVisibilityPresetsModel.py @@ -58,7 +58,7 @@ class SettingVisibilityPresetsModel(ListModel): break return result - def _populate(self): + def _populate(self) -> None: from cura.CuraApplication import CuraApplication items = [] for file_path in Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.SettingVisibilityPreset): @@ -147,7 +147,7 @@ class SettingVisibilityPresetsModel(ListModel): def activePreset(self) -> str: return self._active_preset_item["id"] - def _onPreferencesChanged(self, name: str): + def _onPreferencesChanged(self, name: str) -> None: if name != "general/visible_settings": return diff --git a/cura/Machines/QualityChangesGroup.py b/cura/Machines/QualityChangesGroup.py index 3dcf2ab1c8..7844b935dc 100644 --- a/cura/Machines/QualityChangesGroup.py +++ b/cura/Machines/QualityChangesGroup.py @@ -24,9 +24,9 @@ class QualityChangesGroup(QualityGroup): ConfigurationErrorMessage.getInstance().addFaultyContainers(node.getMetaDataEntry("id")) return - if extruder_position is None: #Then we're a global quality changes profile. + if extruder_position is None: # Then we're a global quality changes profile. self.node_for_global = node - else: #This is an extruder's quality changes profile. + else: # This is an extruder's quality changes profile. self.nodes_for_extruders[extruder_position] = node def __str__(self) -> str: diff --git a/cura/Machines/QualityNode.py b/cura/Machines/QualityNode.py index a821a1e15d..991388a4bd 100644 --- a/cura/Machines/QualityNode.py +++ b/cura/Machines/QualityNode.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Optional, Dict, cast +from typing import Optional, Dict, cast, Any from .ContainerNode import ContainerNode from .QualityChangesGroup import QualityChangesGroup @@ -12,21 +12,21 @@ from .QualityChangesGroup import QualityChangesGroup # class QualityNode(ContainerNode): - def __init__(self, metadata: Optional[dict] = None) -> None: + def __init__(self, metadata: Optional[Dict[str, Any]] = None) -> None: super().__init__(metadata = metadata) self.quality_type_map = {} # type: Dict[str, QualityNode] # quality_type -> QualityNode for InstanceContainer def getChildNode(self, child_key: str) -> Optional["QualityNode"]: return self.children_map.get(child_key) - def addQualityMetadata(self, quality_type: str, metadata: dict): + def addQualityMetadata(self, quality_type: str, metadata: Dict[str, Any]): if quality_type not in self.quality_type_map: self.quality_type_map[quality_type] = QualityNode(metadata) def getQualityNode(self, quality_type: str) -> Optional["QualityNode"]: return self.quality_type_map.get(quality_type) - def addQualityChangesMetadata(self, quality_type: str, metadata: dict): + def addQualityChangesMetadata(self, quality_type: str, metadata: Dict[str, Any]): if quality_type not in self.quality_type_map: self.quality_type_map[quality_type] = QualityNode() quality_type_node = self.quality_type_map[quality_type] From f585afe77bdff9653784242005e1728b972028d3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 27 Sep 2018 17:31:45 +0200 Subject: [PATCH 12/17] Fix spacing --- cura/Settings/GlobalStack.py | 1 + tests/Settings/TestGlobalStack.py | 43 +++++++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py index e2f7df41ea..517b45eb98 100755 --- a/cura/Settings/GlobalStack.py +++ b/cura/Settings/GlobalStack.py @@ -23,6 +23,7 @@ from .CuraContainerStack import CuraContainerStack if TYPE_CHECKING: from cura.Settings.ExtruderStack import ExtruderStack + ## Represents the Global or Machine stack and its related containers. # class GlobalStack(CuraContainerStack): diff --git a/tests/Settings/TestGlobalStack.py b/tests/Settings/TestGlobalStack.py index f8052aa4bb..0f1579f78b 100755 --- a/tests/Settings/TestGlobalStack.py +++ b/tests/Settings/TestGlobalStack.py @@ -15,6 +15,7 @@ import UM.Settings.SettingDefinition #To add settings to the definition. from cura.Settings.cura_empty_instance_containers import empty_container + ## Gets an instance container with a specified container type. # # \param container_type The type metadata for the instance container. @@ -24,22 +25,27 @@ def getInstanceContainer(container_type) -> InstanceContainer: container.setMetaDataEntry("type", container_type) return container + class DefinitionContainerSubClass(DefinitionContainer): def __init__(self): super().__init__(container_id = "SubDefinitionContainer") + class InstanceContainerSubClass(InstanceContainer): def __init__(self, container_type): super().__init__(container_id = "SubInstanceContainer") self.setMetaDataEntry("type", container_type) + #############################START OF TEST CASES################################ + ## Tests whether adding a container is properly forbidden. def test_addContainer(global_stack): with pytest.raises(InvalidOperationError): global_stack.addContainer(unittest.mock.MagicMock()) + ## Tests adding extruders to the global stack. def test_addExtruder(global_stack): mock_definition = unittest.mock.MagicMock() @@ -67,6 +73,7 @@ def test_addExtruder(global_stack): # global_stack.addExtruder(unittest.mock.MagicMock()) assert len(global_stack.extruders) == 2 #Didn't add the faulty extruder. + #Tests setting user changes profiles to invalid containers. @pytest.mark.parametrize("container", [ getInstanceContainer(container_type = "wrong container type"), @@ -77,6 +84,7 @@ def test_constrainUserChangesInvalid(container, global_stack): with pytest.raises(InvalidContainerError): #Invalid container, should raise an error. global_stack.userChanges = container + #Tests setting user changes profiles. @pytest.mark.parametrize("container", [ getInstanceContainer(container_type = "user"), @@ -85,6 +93,7 @@ def test_constrainUserChangesInvalid(container, global_stack): def test_constrainUserChangesValid(container, global_stack): global_stack.userChanges = container #Should not give an error. + #Tests setting quality changes profiles to invalid containers. @pytest.mark.parametrize("container", [ getInstanceContainer(container_type = "wrong container type"), @@ -95,6 +104,7 @@ def test_constrainQualityChangesInvalid(container, global_stack): with pytest.raises(InvalidContainerError): #Invalid container, should raise an error. global_stack.qualityChanges = container + #Test setting quality changes profiles. @pytest.mark.parametrize("container", [ getInstanceContainer(container_type = "quality_changes"), @@ -103,6 +113,7 @@ def test_constrainQualityChangesInvalid(container, global_stack): def test_constrainQualityChangesValid(container, global_stack): global_stack.qualityChanges = container #Should not give an error. + #Tests setting quality profiles to invalid containers. @pytest.mark.parametrize("container", [ getInstanceContainer(container_type = "wrong container type"), @@ -113,6 +124,7 @@ def test_constrainQualityInvalid(container, global_stack): with pytest.raises(InvalidContainerError): #Invalid container, should raise an error. global_stack.quality = container + #Test setting quality profiles. @pytest.mark.parametrize("container", [ getInstanceContainer(container_type = "quality"), @@ -121,6 +133,7 @@ def test_constrainQualityInvalid(container, global_stack): def test_constrainQualityValid(container, global_stack): global_stack.quality = container #Should not give an error. + #Tests setting materials to invalid containers. @pytest.mark.parametrize("container", [ getInstanceContainer(container_type = "wrong container type"), @@ -131,6 +144,7 @@ def test_constrainMaterialInvalid(container, global_stack): with pytest.raises(InvalidContainerError): #Invalid container, should raise an error. global_stack.material = container + #Test setting materials. @pytest.mark.parametrize("container", [ getInstanceContainer(container_type = "material"), @@ -139,6 +153,7 @@ def test_constrainMaterialInvalid(container, global_stack): def test_constrainMaterialValid(container, global_stack): global_stack.material = container #Should not give an error. + #Tests setting variants to invalid containers. @pytest.mark.parametrize("container", [ getInstanceContainer(container_type = "wrong container type"), @@ -149,6 +164,7 @@ def test_constrainVariantInvalid(container, global_stack): with pytest.raises(InvalidContainerError): #Invalid container, should raise an error. global_stack.variant = container + #Test setting variants. @pytest.mark.parametrize("container", [ getInstanceContainer(container_type = "variant"), @@ -157,6 +173,7 @@ def test_constrainVariantInvalid(container, global_stack): def test_constrainVariantValid(container, global_stack): global_stack.variant = container #Should not give an error. + #Tests setting definition changes profiles to invalid containers. @pytest.mark.parametrize("container", [ getInstanceContainer(container_type = "wrong container type"), @@ -167,6 +184,7 @@ def test_constrainDefinitionChangesInvalid(container, global_stack): with pytest.raises(InvalidContainerError): #Invalid container, should raise an error. global_stack.definitionChanges = container + #Test setting definition changes profiles. @pytest.mark.parametrize("container", [ getInstanceContainer(container_type = "definition_changes"), @@ -175,6 +193,7 @@ def test_constrainDefinitionChangesInvalid(container, global_stack): def test_constrainDefinitionChangesValid(container, global_stack): global_stack.definitionChanges = container #Should not give an error. + #Tests setting definitions to invalid containers. @pytest.mark.parametrize("container", [ getInstanceContainer(container_type = "wrong class"), @@ -184,6 +203,7 @@ def test_constrainDefinitionInvalid(container, global_stack): with pytest.raises(InvalidContainerError): #Invalid container, should raise an error. global_stack.definition = container + #Test setting definitions. @pytest.mark.parametrize("container", [ DefinitionContainer(container_id = "DefinitionContainer"), @@ -192,6 +212,7 @@ def test_constrainDefinitionInvalid(container, global_stack): def test_constrainDefinitionValid(container, global_stack): global_stack.definition = container #Should not give an error. + ## Tests whether deserialising completes the missing containers with empty ones. The initial containers are just the # definition and the definition_changes (that cannot be empty after CURA-5281) def test_deserializeCompletesEmptyContainers(global_stack): @@ -207,6 +228,7 @@ def test_deserializeCompletesEmptyContainers(global_stack): continue assert global_stack.getContainer(container_type_index) == empty_container #All others need to be empty. + ## Tests whether an instance container with the wrong type gets removed when deserialising. def test_deserializeRemovesWrongInstanceContainer(global_stack): global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "wrong type") @@ -217,6 +239,7 @@ def test_deserializeRemovesWrongInstanceContainer(global_stack): assert global_stack.quality == global_stack._empty_instance_container #Replaced with empty. + ## Tests whether a container with the wrong class gets removed when deserialising. def test_deserializeRemovesWrongContainerClass(global_stack): global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = DefinitionContainer(container_id = "wrong class") @@ -227,6 +250,7 @@ def test_deserializeRemovesWrongContainerClass(global_stack): assert global_stack.quality == global_stack._empty_instance_container #Replaced with empty. + ## Tests whether an instance container in the definition spot results in an error. def test_deserializeWrongDefinitionClass(global_stack): global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Definition] = getInstanceContainer(container_type = "definition") #Correct type but wrong class. @@ -235,6 +259,7 @@ def test_deserializeWrongDefinitionClass(global_stack): with pytest.raises(UM.Settings.ContainerStack.InvalidContainerStackError): #Must raise an error that there is no definition container. global_stack.deserialize("") + ## Tests whether an instance container with the wrong type is moved into the correct slot by deserialising. def test_deserializeMoveInstanceContainer(global_stack): global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Quality] = getInstanceContainer(container_type = "material") #Not in the correct spot. @@ -246,6 +271,7 @@ def test_deserializeMoveInstanceContainer(global_stack): assert global_stack.quality == empty_container assert global_stack.material != empty_container + ## Tests whether a definition container in the wrong spot is moved into the correct spot by deserialising. def test_deserializeMoveDefinitionContainer(global_stack): global_stack._containers[cura.Settings.CuraContainerStack._ContainerIndexes.Material] = DefinitionContainer(container_id = "some definition") #Not in the correct spot. @@ -256,6 +282,7 @@ def test_deserializeMoveDefinitionContainer(global_stack): assert global_stack.material == empty_container assert global_stack.definition != empty_container + ## Tests whether getProperty properly applies the stack-like behaviour on its containers. def test_getPropertyFallThrough(global_stack): #A few instance container mocks to put in the stack. @@ -298,6 +325,7 @@ def test_getPropertyFallThrough(global_stack): global_stack.userChanges = mock_layer_heights[container_indexes.UserChanges] assert global_stack.getProperty("layer_height", "value") == container_indexes.UserChanges + ## In definitions, test whether having no resolve allows us to find the value. def test_getPropertyNoResolveInDefinition(global_stack): value = unittest.mock.MagicMock() #Just sets the value for bed temperature. @@ -307,6 +335,7 @@ def test_getPropertyNoResolveInDefinition(global_stack): global_stack.definition = value assert global_stack.getProperty("material_bed_temperature", "value") == 10 #No resolve, so fall through to value. + ## In definitions, when the value is asked and there is a resolve function, it must get the resolve first. def test_getPropertyResolveInDefinition(global_stack): resolve_and_value = unittest.mock.MagicMock() #Sets the resolve and value for bed temperature. @@ -316,6 +345,7 @@ def test_getPropertyResolveInDefinition(global_stack): global_stack.definition = resolve_and_value assert global_stack.getProperty("material_bed_temperature", "value") == 7.5 #Resolve wins in the definition. + ## In instance containers, when the value is asked and there is a resolve function, it must get the value first. def test_getPropertyResolveInInstance(global_stack): container_indices = cura.Settings.CuraContainerStack._ContainerIndexes @@ -342,6 +372,7 @@ def test_getPropertyResolveInInstance(global_stack): global_stack.userChanges = instance_containers[container_indices.UserChanges] assert global_stack.getProperty("material_bed_temperature", "value") == 5 + ## Tests whether the value in instances gets evaluated before the resolve in definitions. def test_getPropertyInstancesBeforeResolve(global_stack): value = unittest.mock.MagicMock() #Sets just the value. @@ -356,6 +387,7 @@ def test_getPropertyInstancesBeforeResolve(global_stack): assert global_stack.getProperty("material_bed_temperature", "value") == 10 + ## Tests whether the hasUserValue returns true for settings that are changed in the user-changes container. def test_hasUserValueUserChanges(global_stack): container = unittest.mock.MagicMock() @@ -367,6 +399,7 @@ def test_hasUserValueUserChanges(global_stack): assert not global_stack.hasUserValue("infill_sparse_density") assert not global_stack.hasUserValue("") + ## Tests whether the hasUserValue returns true for settings that are changed in the quality-changes container. def test_hasUserValueQualityChanges(global_stack): container = unittest.mock.MagicMock() @@ -378,6 +411,7 @@ def test_hasUserValueQualityChanges(global_stack): assert not global_stack.hasUserValue("infill_sparse_density") assert not global_stack.hasUserValue("") + ## Tests whether a container in some other place on the stack is correctly not recognised as user value. def test_hasNoUserValue(global_stack): container = unittest.mock.MagicMock() @@ -387,21 +421,25 @@ def test_hasNoUserValue(global_stack): assert not global_stack.hasUserValue("layer_height") #However this container is quality, so it's not a user value. + ## Tests whether inserting a container is properly forbidden. def test_insertContainer(global_stack): with pytest.raises(InvalidOperationError): global_stack.insertContainer(0, unittest.mock.MagicMock()) + ## Tests whether removing a container is properly forbidden. def test_removeContainer(global_stack): with pytest.raises(InvalidOperationError): global_stack.removeContainer(unittest.mock.MagicMock()) + ## Tests whether changing the next stack is properly forbidden. def test_setNextStack(global_stack): with pytest.raises(InvalidOperationError): global_stack.setNextStack(unittest.mock.MagicMock()) + ## Tests setting properties directly on the global stack. @pytest.mark.parametrize("key, property, value", [ ("layer_height", "value", 0.1337), @@ -415,6 +453,7 @@ def test_setPropertyUser(key, property, value, global_stack): user_changes.getMetaDataEntry = unittest.mock.MagicMock(return_value = "user") global_stack.userChanges = user_changes - global_stack.setProperty(key, property, value) #The actual test. + global_stack.setProperty(key, property, value) # The actual test. - global_stack.userChanges.setProperty.assert_called_once_with(key, property, value, None, False) #Make sure that the user container gets a setProperty call. \ No newline at end of file + # Make sure that the user container gets a setProperty call. + global_stack.userChanges.setProperty.assert_called_once_with(key, property, value, None, False) \ No newline at end of file From dc2c074bc0b5297bf80a20d962bda36cf12c75e5 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 16 Aug 2018 15:34:05 +0200 Subject: [PATCH 13/17] Verbose output for Linux CI --- Jenkinsfile | 44 ++++++++++++++++++++++++++++++++++++++++--- cmake/CuraTests.cmake | 2 +- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 4f755dcae2..35f07d3987 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -52,10 +52,48 @@ parallel_nodes(['linux && cura', 'windows && cura']) { // Try and run the unit tests. If this stage fails, we consider the build to be "unstable". stage('Unit Test') { - try { + if (isUnix()) { + // For Linux to show everything + def branch = env.BRANCH_NAME + if(!fileExists("${env.CURA_ENVIRONMENT_PATH}/${branch}")) { + branch = "master" + } + def uranium_dir = get_workspace_dir("Ultimaker/Uranium/${branch}") + + try { + sh """ + cd .. + export PYTHONPATH=.:"${uranium_dir}" + ${env.CURA_ENVIRONMENT_PATH}/${branch}/bin/pytest -x --verbose --full-trace --capture=no ./tests + """ + } catch(e) { + currentBuild.result = "UNSTABLE" + } + } + else { + // For Windows make('test') - } catch(e) { - currentBuild.result = "UNSTABLE" + } + } + + stage('Code Style') { + if (isUnix()) { + // For Linux to show everything + def branch = env.BRANCH_NAME + if(!fileExists("${env.CURA_ENVIRONMENT_PATH}/${branch}")) { + branch = "master" + } + def uranium_dir = get_workspace_dir("Ultimaker/Uranium/${branch}") + + try { + sh """ + cd .. + export PYTHONPATH=.:"${uranium_dir}" + ${env.CURA_ENVIRONMENT_PATH}/${branch}/bin/python3 run_mypy.py + """ + } catch(e) { + currentBuild.result = "UNSTABLE" + } } } } diff --git a/cmake/CuraTests.cmake b/cmake/CuraTests.cmake index 801f054bc3..30794ed608 100644 --- a/cmake/CuraTests.cmake +++ b/cmake/CuraTests.cmake @@ -34,7 +34,7 @@ function(cura_add_test) if (NOT ${test_exists}) add_test( NAME ${_NAME} - COMMAND ${PYTHON_EXECUTABLE} -m pytest --junitxml=${CMAKE_BINARY_DIR}/junit-${_NAME}.xml ${_DIRECTORY} + COMMAND ${PYTHON_EXECUTABLE} -m pytest --verbose --full-trace --capture=no --no-print-log --junitxml=${CMAKE_BINARY_DIR}/junit-${_NAME}.xml ${_DIRECTORY} ) set_tests_properties(${_NAME} PROPERTIES ENVIRONMENT LANG=C) set_tests_properties(${_NAME} PROPERTIES ENVIRONMENT "PYTHONPATH=${_PYTHONPATH}") From 84bad92f10bbb2175f9b5315338420f0dc63e01f Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 28 Sep 2018 09:57:28 +0200 Subject: [PATCH 14/17] Verbose output for Windows CI --- Jenkinsfile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 35f07d3987..274e383ffa 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -72,7 +72,12 @@ parallel_nodes(['linux && cura', 'windows && cura']) { } else { // For Windows - make('test') + try { + // This also does code style checks. + bat 'ctest -V' + } catch(e) { + currentBuild.result = "UNSTABLE" + } } } From 4a4b0960520de86f538f7848fed16385740b6881 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 28 Sep 2018 11:53:03 +0200 Subject: [PATCH 15/17] Only send uppercase g-code via custom commands Because most printers don't understand anything other than uppercase. Fixes #4178. --- cura/PrinterOutput/GenericOutputController.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/PrinterOutput/GenericOutputController.py b/cura/PrinterOutput/GenericOutputController.py index e6310e5bff..95e65b2f0b 100644 --- a/cura/PrinterOutput/GenericOutputController.py +++ b/cura/PrinterOutput/GenericOutputController.py @@ -66,7 +66,7 @@ class GenericOutputController(PrinterOutputController): self._output_device.sendCommand("G28 Z") def sendRawCommand(self, printer: "PrinterOutputModel", command: str): - self._output_device.sendCommand(command) + self._output_device.sendCommand(command.upper()) #Most printers only understand uppercase g-code commands. def setJobState(self, job: "PrintJobOutputModel", state: str): if state == "pause": From ef5f9bb0d45d4a8972b5b975873d0400fce9f794 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 28 Sep 2018 12:01:20 +0200 Subject: [PATCH 16/17] Improve warning when saving g-code before slicing This terminology is more consistent with what the rest of the interface uses. Discovered during work on #4112. --- plugins/GCodeWriter/GCodeWriter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/GCodeWriter/GCodeWriter.py b/plugins/GCodeWriter/GCodeWriter.py index 5d5e3578cd..3e5bf59e73 100644 --- a/plugins/GCodeWriter/GCodeWriter.py +++ b/plugins/GCodeWriter/GCodeWriter.py @@ -70,7 +70,7 @@ class GCodeWriter(MeshWriter): active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate scene = Application.getInstance().getController().getScene() if not hasattr(scene, "gcode_dict"): - self.setInformation(catalog.i18nc("@warning:status", "Please generate G-code before saving.")) + self.setInformation(catalog.i18nc("@warning:status", "Please prepare G-code before exporting.")) return False gcode_dict = getattr(scene, "gcode_dict") gcode_list = gcode_dict.get(active_build_plate, None) @@ -86,7 +86,7 @@ class GCodeWriter(MeshWriter): stream.write(settings) return True - self.setInformation(catalog.i18nc("@warning:status", "Please generate G-code before saving.")) + self.setInformation(catalog.i18nc("@warning:status", "Please prepare G-code before exporting.")) return False ## Create a new container with container 2 as base and container 1 written over it. From a68a591c18c68ab5e8271a4628e5a1f23d7883b1 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 28 Sep 2018 13:07:18 +0200 Subject: [PATCH 17/17] Correct typo leading to infinite recursion. --- cura/Scene/ConvexHullDecorator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Scene/ConvexHullDecorator.py b/cura/Scene/ConvexHullDecorator.py index 31e21df6bf..aca5d866be 100644 --- a/cura/Scene/ConvexHullDecorator.py +++ b/cura/Scene/ConvexHullDecorator.py @@ -241,7 +241,7 @@ class ConvexHullDecorator(SceneNodeDecorator): return Polygon() def _compute2DConvexHeadFull(self) -> Optional[Polygon]: - convex_hull = self._compute2DConvexHeadFull() + convex_hull = self._compute2DConvexHull() if convex_hull: return convex_hull.getMinkowskiHull(self._getHeadAndFans()) return None