From 4295a85f6a2a90f7e29abbd747b9bba92de9edf0 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 20 Jul 2016 10:53:04 +0200 Subject: [PATCH 01/66] Split Initial Layer Speed in two It's now two settings: Initial Layer Print Speed (for the extrusion moves) and Initial Layer Travel Speed (for the non-extrusion moves). Contributes to issue CURA-1507. --- resources/definitions/fdmprinter.def.json | 42 +++++++++++++++++++---- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index d4933be5bb..ca09f3ec23 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1511,14 +1511,44 @@ }, "speed_layer_0": { "label": "Initial Layer Speed", - "description": "The print speed for the initial layer. A lower value is advised to improve adhesion to the build plate.", + "description": "The speed for the initial layer. A lower value is advised to improve adhesion to the build plate.", "unit": "mm/s", "type": "float", "default_value": 30, "minimum_value": "0.1", "maximum_value": "299792458000", "maximum_value_warning": "300", - "settable_per_mesh": true + "settable_per_mesh": true, + "children": + { + "speed_print_layer_0": + { + "label": "Initial Layer Print Speed", + "description": "The speed of printing for the initial layer. A lower value is advised to improve adhesion to the build plate.", + "unit": "mm/s", + "type": "float", + "default_value": 30, + "value": "speed_layer_0", + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "300", + "settable_per_mesh": true + }, + "speed_travel_layer_0": + { + "label": "Initial Layer Travel Speed", + "description": "The speed of travel moves in the initial layer. A lower value is advised to prevent pulling previously printed parts away from the build plate.", + "unit": "mm/s", + "type": "float", + "default_value": 60, + "value": "speed_layer_0 * 2", + "minimum_value": "0.1", + "maximum_value": "299792458000", + "maximum_value_warning": "300", + "settable_per_mesh": true, + "settable_per_extruder": true + } + } }, "skirt_speed": { "label": "Skirt Speed", @@ -1545,8 +1575,8 @@ "settable_per_mesh": false, "settable_per_extruder": false }, - - + + "acceleration_enabled": { "label": "Enable Acceleration Control", "description": "Enables adjusting the print head acceleration. Increasing the accelerations can reduce printing time at the cost of print quality.", @@ -1733,8 +1763,8 @@ "settable_per_mesh": false }, - - + + "jerk_enabled": { "label": "Enable Jerk Control", "description": "Enables adjusting the jerk of print head when the velocity in the X or Y axis changes. Increasing the jerk can reduce printing time at the cost of print quality.", From 336dd406b15bff2e7a530e41592ef03d60f4c2bb Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 26 Jul 2016 11:58:25 +0200 Subject: [PATCH 02/66] Add a reusable mechanism for plugins to add controls to designated areas in the GUI contributes to CURA-271 --- cura/CuraApplication.py | 22 +++++++++++++++++++++- resources/qml/SaveButton.qml | 26 +++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index a6d0a3b827..9dc5987cb7 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -128,6 +128,8 @@ class CuraApplication(QtApplication): self._machine_action_manager = MachineActionManager.MachineActionManager() self._machine_manager = None # This is initialized on demand. + self._additional_components = {} # Components to add to certain areas in the interface + super().__init__(name = "cura", version = CuraVersion, buildtype = CuraBuildType) self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png"))) @@ -876,4 +878,22 @@ class CuraApplication(QtApplication): self.getMainWindow().setMinimumSize(size) def getBuildVolume(self): - return self._volume \ No newline at end of file + return self._volume + + additionalComponentsChanged = pyqtSignal(str, arguments = ["areaId"]) + + @pyqtProperty("QVariantMap", notify = additionalComponentsChanged) + def additionalComponents(self): + return self._additional_components + + ## Add a component to a list of components to be reparented to another area in the GUI. + # The actual reparenting is done by the area itself. + # \param area_id \type{str} Identifying name of the area to which the component should be reparented + # \param component \type{QQuickComponent} The component that should be reparented + @pyqtSlot(str, "QVariant") + def addAdditionalComponent(self, area_id, component): + if area_id not in self._additional_components: + self._additional_components[area_id] = [] + self._additional_components[area_id].append(component) + + self.additionalComponentsChanged.emit(area_id) \ No newline at end of file diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 8b95de15ee..9ea6e181bb 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -84,6 +84,27 @@ Rectangle { anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.left: parent.left + Row { + id: additionalComponentsRow + anchors.top: parent.top + anchors.right: saveToButton.visible ? saveToButton.left : parent.right + anchors.rightMargin: UM.Theme.getSize("default_margin").width + + spacing: UM.Theme.getSize("default_margin").width + } + + Connections { + target: Printer + onAdditionalComponentsChanged: + { + if(areaId == "saveButton") { + for (var component in Printer.additionalComponents["saveButton"]) { + Printer.additionalComponents["saveButton"][component].parent = additionalComponentsRow + } + } + } + } + Button { id: saveToButton @@ -102,8 +123,7 @@ Rectangle { } style: ButtonStyle { - background: - Rectangle + background: Rectangle { border.width: UM.Theme.getSize("default_lining").width border.color: !control.enabled ? UM.Theme.getColor("action_button_disabled_border") : @@ -126,7 +146,7 @@ Rectangle { text: control.text; } } - label: Item { } + label: Item { } } } From d64eff9947a4a9a116cb302af59e61fe632d28a9 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 26 Jul 2016 18:54:55 +0200 Subject: [PATCH 03/66] Fix updating the Per Object Settings color swatch CURA-1971 --- plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index 79f10f508f..dc0574d14d 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -47,7 +47,9 @@ Item { id: extruders_model onRowsInserted: extruderSelector.visible = extruders_model.rowCount() > 1 onModelReset: extruderSelector.visible = extruders_model.rowCount() > 1 + onModelChanged: extruderSelector.color = extruders_model.getItem(extruderSelector.currentIndex).colour } + property string color: extruders_model.getItem(extruderSelector.currentIndex).colour visible: extruders_model.rowCount() > 1 textRole: "name" width: UM.Theme.getSize("setting_control").width @@ -88,7 +90,7 @@ Item { anchors.leftMargin: UM.Theme.getSize("default_lining").width anchors.verticalCenter: parent.verticalCenter - color: extruders_model.getItem(extruderSelector.currentIndex).colour + color: extruderSelector.color border.width: UM.Theme.getSize("default_lining").width border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : UM.Theme.getColor("setting_control_border") } @@ -136,6 +138,7 @@ Item { if(extruders_model.getItem(i).id == UM.ActiveTool.properties.getValue("SelectedActiveExtruder")) { extruderSelector.currentIndex = i; + extruderSelector.color = extruders_model.getItem(i).colour; return; } } From d96ce760f8828c14ece3b42ad6668f6f55c94905 Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Tue, 26 Jul 2016 20:21:00 +0200 Subject: [PATCH 04/66] CURA-1223: 3MFReader: Doing selftest and fail on broken files This commit adds a selftest before the "result" gets returned. It should break on this/these functions and popup a message about using the online repair tool. I compared the content of both files for a long time, googled about the format, but wasn't able to find a proper fix. More routines will likely need to be added here, but with those I have it is working so far very well. Sadly it is not possible to override the default message, so two messages will appear. Additionally, the URL of the link is not clickable/executable from the UX. Just acting like normal text. Contributes to CURA-1223 --- plugins/3MFReader/ThreeMFReader.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index 57d76b2783..bf25ccd440 100644 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -9,9 +9,13 @@ from UM.Math.Vector import Vector from UM.Scene.SceneNode import SceneNode from UM.Scene.GroupDecorator import GroupDecorator from UM.Math.Quaternion import Quaternion - from UM.Job import Job +from UM.Message import Message +from UM.i18n import i18nCatalog +catalog = i18nCatalog("cura") + + import math import zipfile @@ -116,4 +120,11 @@ class ThreeMFReader(MeshReader): except Exception as e: Logger.log("e", "exception occured in 3mf reader: %s", e) + try: # Selftest - There might be more functions that should fail + result.getBoundingBox() + except: + message = Message(catalog.i18nc("@info:status", "Your 3MF file seems to be broken. Please visit https://modelrepair.azurewebsites.net/ and try to repair your model!"), lifetime = 0) + message.show() + return None + return result From 762ea15e785cef44ab14460c41044a8bc589236c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 27 Jul 2016 09:54:02 +0200 Subject: [PATCH 05/66] Global inherited settings now use target (instead of active) stack to copy values Fixes multiple issues with support settings not being updated properly --- cura/Settings/MachineManager.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index ca09cbc5dc..5bc0ff65fa 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -269,13 +269,16 @@ class MachineManager(QObject): if property_name == "global_inherits_stack": if self._active_container_stack and self._active_container_stack != self._global_container_stack: # Update the global user value when the "global_inherits_stack" function points to a different stack - stack_index = int(self._active_container_stack.getProperty(key, property_name)) - extruder_stacks = [stack for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())] + extruder_stacks = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())) + target_stack_position = int(self._active_container_stack.getProperty(key, "global_inherits_stack")) + if target_stack_position == -1: # Prevent -1 from selecting wrong stack. + target_stack = self._active_container_stack + else: + target_stack = extruder_stacks[target_stack_position] - if len(extruder_stacks) > stack_index: - new_value = extruder_stacks[stack_index].getProperty(key, "value") - if self._global_container_stack.getProperty(key, "value") != new_value: - self._global_container_stack.getTop().setProperty(key, "value", new_value) + new_value = target_stack.getProperty(key, "value") + if self._global_container_stack.getProperty(key, "value") != new_value: + self._global_container_stack.getTop().setProperty(key, "value", new_value) if property_name == "validationState": if self._global_stack_valid: @@ -549,7 +552,7 @@ class MachineManager(QObject): return "" @pyqtSlot(str, str) - def renameQualityContainer(self, container_id, new_name): + def renameQualityContainer(self, container_id, nbalew_name): containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id, type = "quality") if containers: new_name = self._createUniqueName("quality", containers[0].getName(), new_name, From 5da3cd34e9bb759ca18aec6ff2768c43bf4f1e69 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 27 Jul 2016 10:56:13 +0200 Subject: [PATCH 06/66] Improve inheritance function for speed_travel_layer_0 This makes the speed_travel_layer_0 by default have the same ratio to the parent print speed as the travel speed has on other layers. --- resources/definitions/fdmprinter.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index ca09f3ec23..62c8505558 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1541,7 +1541,7 @@ "unit": "mm/s", "type": "float", "default_value": 60, - "value": "speed_layer_0 * 2", + "value": "speed_layer_0 * speed_travel / speed_print", "minimum_value": "0.1", "maximum_value": "299792458000", "maximum_value_warning": "300", From 1e2147522deab8e4d64e4422dc1095731c8afe61 Mon Sep 17 00:00:00 2001 From: Simon Edwards Date: Wed, 27 Jul 2016 11:00:47 +0200 Subject: [PATCH 07/66] Increase the size of the backend log. Contributes to CURA-1509 Cura in slicing loop, Arcus Error (8) --- plugins/CuraEngineBackend/CuraEngineBackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index aedc91f130..bf68a6cb78 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -92,7 +92,7 @@ class CuraEngineBackend(Backend): self._always_restart = True #Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness. self._process_layers_job = None #The currently active job to process layers, or None if it is not processing layers. - self._backend_log_max_lines = 200 # Maximal count of lines to buffer + self._backend_log_max_lines = 20000 # Maximum number of lines to buffer self._error_message = None #Pop-up message that shows errors. self.backendQuit.connect(self._onBackendQuit) From 4175e51dbb1f8707c05910872c0dd628baacce62 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 27 Jul 2016 14:00:42 +0200 Subject: [PATCH 08/66] Corrected gantry height for ultimaker2.json (extended and go inherit). CURA-1979 Made same change in resources/definitions/ultimaker2.def.json as in the original resources/machines/ultimaker2.json --- resources/definitions/ultimaker2.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/ultimaker2.def.json b/resources/definitions/ultimaker2.def.json index 3712963f23..d8e73a7e14 100644 --- a/resources/definitions/ultimaker2.def.json +++ b/resources/definitions/ultimaker2.def.json @@ -57,7 +57,7 @@ "default_value": 2 }, "gantry_height": { - "default_value": 55 + "default_value": 48 }, "machine_use_extruder_offset_to_offset_coords": { "default_value": true From afed6eed93e2ebb11bb0183ca08aa6e8d4fef8f1 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 27 Jul 2016 14:14:13 +0200 Subject: [PATCH 09/66] Print montior also handles wait_for_cleanup correctly now --- resources/qml/MonitorButton.qml | 6 +++++- resources/qml/Sidebar.qml | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index af163a39e2..7a87eb4f60 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -22,7 +22,7 @@ Rectangle { if(!printerConnected) return UM.Theme.getColor("status_offline") - else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing" || Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print") + else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing" || Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print" || Cura.MachineManager.printerOutputDevices[0].jobState == "wait_cleanup" ) return UM.Theme.getColor("status_busy") else if(Cura.MachineManager.printerOutputDevices[0].jobState == "ready" || Cura.MachineManager.printerOutputDevices[0].jobState == "") return UM.Theme.getColor("status_ready") @@ -53,6 +53,10 @@ Rectangle { return catalog.i18nc("@label:", "Preparing...") } + else if(Cura.MachineManager.printerOutputDevices[0].jobState == "wait_cleanup") + { + return catalog.i18nc("@label:", "Waiting for cleanup...") + } else { return " " diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 53a2375394..1542e24f5d 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -108,7 +108,7 @@ Rectangle iconSource: { if(!printerConnected) return UM.Theme.getIcon("tab_monitor") - else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing" || Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print") + else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing" || Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print" || Cura.MachineManager.printerOutputDevices[0].jobState == "wait_cleanup" ) return UM.Theme.getIcon("tab_monitor_busy") else if(Cura.MachineManager.printerOutputDevices[0].jobState == "ready" || Cura.MachineManager.printerOutputDevices[0].jobState == "") return UM.Theme.getIcon("tab_monitor_connected") From cd26794155ee73ea6b10bfb7e179c23892e52afb Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Wed, 27 Jul 2016 17:27:57 +0200 Subject: [PATCH 10/66] JSON fix: retrieve globalish support settings from support extruder (CURA-2003) --- resources/definitions/fdmprinter.def.json | 47 +++++++++-------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 950c856a6e..f8bd162c27 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -630,9 +630,8 @@ "type": "float", "enabled": "support_enable", "value": "line_width", - "global_inherits_stack": "support_extruder_nr", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true }, "support_roof_line_width": { @@ -645,9 +644,8 @@ "type": "float", "enabled": "support_roof_enable", "value": "line_width", - "global_inherits_stack": "support_extruder_nr", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true }, "prime_tower_line_width": { @@ -1444,10 +1442,9 @@ "maximum_value_warning": "150", "default_value": 60, "value": "speed_print", - "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": false, - "settable_per_extruder": false, + "settable_per_extruder": true, "children": { "speed_support_infill": @@ -1461,10 +1458,9 @@ "maximum_value": "299792458000", "maximum_value_warning": "150", "value": "speed_support", - "global_inherits_stack": "support_extruder_nr", "enabled": "support_enable", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true }, "speed_support_roof": { @@ -1478,9 +1474,8 @@ "maximum_value_warning": "150", "enabled": "support_roof_enable and support_enable", "value": "speed_support / 1.5", - "global_inherits_stack": "support_extruder_nr", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true } } }, @@ -1650,10 +1645,9 @@ "maximum_value_warning": "10000", "default_value": 3000, "value": "acceleration_print", - "global_inherits_stack": "support_extruder_nr", "enabled": "acceleration_enabled and support_enable", "settable_per_mesh": false, - "settable_per_extruder": false, + "settable_per_extruder": true, "children": { "acceleration_support_infill": { "label": "Support Infill Acceleration", @@ -1662,13 +1656,12 @@ "type": "float", "default_value": 3000, "value": "acceleration_support", - "global_inherits_stack": "support_extruder_nr", "minimum_value": "0.1", "minimum_value_warning": "100", "maximum_value_warning": "10000", "enabled": "acceleration_enabled and support_enable", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true }, "acceleration_support_roof": { "label": "Support Roof Acceleration", @@ -1677,13 +1670,12 @@ "type": "float", "default_value": 3000, "value": "acceleration_support", - "global_inherits_stack": "support_extruder_nr", "minimum_value": "0.1", "minimum_value_warning": "100", "maximum_value_warning": "10000", "enabled": "acceleration_enabled and support_roof_enable and support_enable", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true } } }, @@ -1841,10 +1833,9 @@ "maximum_value_warning": "50", "default_value": 20, "value": "jerk_print", - "global_inherits_stack": "support_extruder_nr", "enabled": "jerk_enabled and support_enable", "settable_per_mesh": false, - "settable_per_extruder": false, + "settable_per_extruder": true, "children": { "jerk_support_infill": { "label": "Support Infill Jerk", @@ -1853,13 +1844,12 @@ "type": "float", "default_value": 20, "value": "jerk_support", - "global_inherits_stack": "support_extruder_nr", "minimum_value": "0.1", "minimum_value_warning": "5", "maximum_value_warning": "50", "enabled": "jerk_enabled and support_enable", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true }, "jerk_support_roof": { "label": "Support Roof Jerk", @@ -1868,13 +1858,12 @@ "type": "float", "default_value": 20, "value": "jerk_support", - "global_inherits_stack": "support_extruder_nr", "minimum_value": "0.1", "minimum_value_warning": "5", "maximum_value_warning": "50", "enabled": "jerk_enabled and support_roof_enable and support_enable", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true } } }, @@ -2180,7 +2169,7 @@ "default_value": "zigzag", "enabled": "support_enable", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true }, "support_connect_zigzags": { @@ -2190,7 +2179,7 @@ "default_value": true, "enabled": "support_enable and (support_pattern == \"zigzag\")", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true }, "support_infill_rate": { @@ -2203,7 +2192,7 @@ "default_value": 15, "enabled": "support_enable", "settable_per_mesh": false, - "settable_per_extruder": false, + "settable_per_extruder": true, "children": { "support_line_distance": { @@ -2216,7 +2205,7 @@ "enabled": "support_enable", "value": "(support_line_width * 100) / support_infill_rate * (2 if support_pattern == \"grid\" else (3 if support_pattern == \"triangles\" else 1))", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true } } }, @@ -2389,7 +2378,7 @@ "maximum_value_warning": "100", "enabled":"support_roof_enable and support_enable", "settable_per_mesh": false, - "settable_per_extruder": false, + "settable_per_extruder": true, "children": { "support_roof_line_distance": @@ -2403,7 +2392,7 @@ "value": "0 if support_roof_density == 0 else (support_roof_line_width * 100) / support_roof_density * (2 if support_roof_pattern == \"grid\" else (3 if support_roof_pattern == \"triangles\" else 1))", "enabled": "support_roof_enable and support_enable", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true } } }, @@ -2423,7 +2412,7 @@ "default_value": "concentric", "enabled": "support_roof_enable and support_enable", "settable_per_mesh": false, - "settable_per_extruder": false + "settable_per_extruder": true }, "support_use_towers": { From bac3f73e6db141554af50bf6b281c429da47c5a1 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 27 Jul 2016 17:42:15 +0200 Subject: [PATCH 11/66] Check ConvexHullHead vs ConvexHullHead collision. CURA-1776 --- cura/PlatformPhysics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index d2a848dd72..91d2b1a1ef 100644 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -95,11 +95,11 @@ class PlatformPhysics: # Get the overlap distance for both convex hulls. If this returns None, there is no intersection. head_hull = node.callDecoration("getConvexHullHead") if head_hull: - overlap = head_hull.intersectsPolygon(other_node.callDecoration("getConvexHull")) + overlap = head_hull.intersectsPolygon(other_node.callDecoration("getConvexHullHead")) if not overlap: other_head_hull = other_node.callDecoration("getConvexHullHead") if other_head_hull: - overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_head_hull) + overlap = node.callDecoration("getConvexHullHead").intersectsPolygon(other_head_hull) else: own_convex_hull = node.callDecoration("getConvexHull") other_convex_hull = other_node.callDecoration("getConvexHull") From 6af0f28f32e776e971d04cd3e15b01f75319425d Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 27 Jul 2016 17:50:25 +0200 Subject: [PATCH 12/66] Only show draft shield height if draft shield enabled We must've missed that one previously. Contributes to issue CURA-1295. --- resources/definitions/fdmprinter.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index f8bd162c27..2df6c295a4 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -3348,7 +3348,7 @@ "maximum_value_warning": "9999", "default_value": 0, "value": "9999 if draft_shield_height_limitation == 'full' and draft_shield_enabled else 0.0", - "enabled": "draft_shield_height_limitation == \"limited\"", + "enabled": "draft_shield_enabled and draft_shield_height_limitation == \"limited\"", "settable_per_mesh": false, "settable_per_extruder": false }, From 823ab61cc577eb072a61cb3bd7cb524e1b4561d3 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 27 Jul 2016 18:02:31 +0200 Subject: [PATCH 13/66] Tweak convex hull node and transparent object shader Now we render at least a vague hint at lighting instead of nothing at all. --- cura/ConvexHullNode.py | 2 +- resources/shaders/transparent_object.shader | 34 ++++++++------------- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/cura/ConvexHullNode.py b/cura/ConvexHullNode.py index 703dfb0bed..232b30e317 100644 --- a/cura/ConvexHullNode.py +++ b/cura/ConvexHullNode.py @@ -23,7 +23,7 @@ class ConvexHullNode(SceneNode): self._original_parent = parent # Color of the drawn convex hull - self._color = Color(35, 35, 35, 192) + self._color = Color(0.4, 0.4, 0.4, 1.0) # The y-coordinate of the convex hull mesh. Must not be 0, to prevent z-fighting. self._mesh_height = 0.1 diff --git a/resources/shaders/transparent_object.shader b/resources/shaders/transparent_object.shader index a3790901bc..cd27a40769 100644 --- a/resources/shaders/transparent_object.shader +++ b/resources/shaders/transparent_object.shader @@ -1,7 +1,7 @@ [shaders] vertex = - uniform highp mat4 u_viewProjectionMatrix; uniform highp mat4 u_modelMatrix; + uniform highp mat4 u_viewProjectionMatrix; uniform highp mat4 u_normalMatrix; attribute highp vec4 a_vertex; @@ -10,7 +10,6 @@ vertex = varying highp vec3 v_vertex; varying highp vec3 v_normal; - varying highp vec2 v_uvs; void main() { @@ -19,56 +18,47 @@ vertex = v_vertex = world_space_vert.xyz; v_normal = (u_normalMatrix * normalize(a_normal)).xyz; - - v_uvs = a_uvs; } fragment = uniform mediump vec4 u_ambientColor; uniform mediump vec4 u_diffuseColor; uniform highp vec3 u_lightPosition; - uniform highp vec3 u_viewPosition; + uniform mediump float u_opacity; - uniform sampler2D u_texture; varying highp vec3 v_vertex; varying highp vec3 v_normal; - varying highp vec2 v_uvs; void main() { - // Copied from platform.shader, removed texture - mediump vec4 final_color = vec4(0.0); + mediump vec4 finalColor = vec4(0.0); /* Ambient Component */ - final_color += u_ambientColor; + finalColor += u_ambientColor; highp vec3 normal = normalize(v_normal); - highp vec3 light_dir = normalize(u_lightPosition - v_vertex); + highp vec3 lightDir = normalize(u_lightPosition - v_vertex); /* Diffuse Component */ - highp float n_dot_l = clamp(dot(normal, light_dir), 0.0, 1.0); - final_color += (n_dot_l * u_diffuseColor); + highp float NdotL = clamp(abs(dot(normal, lightDir)), 0.0, 1.0); + finalColor += (NdotL * u_diffuseColor); - final_color.a = u_opacity; - - gl_FragColor = final_color; + gl_FragColor = finalColor; + gl_FragColor.a = u_opacity; } [defaults] -u_ambientColor = [0.3, 0.3, 0.3, 1.0] -u_diffuseColor = [1.0, 1.0, 1.0, 1.0] +u_ambientColor = [0.1, 0.1, 0.1, 1.0] +u_diffuseColor = [0.4, 0.4, 0.4, 1.0] u_opacity = 0.5 -u_texture = 0 [bindings] -u_viewProjectionMatrix = view_projection_matrix u_modelMatrix = model_matrix +u_viewProjectionMatrix = view_projection_matrix u_normalMatrix = normal_matrix u_lightPosition = light_0_position -u_viewPosition = camera_position [attributes] a_vertex = vertex a_normal = normal -a_uvs = uv0 From 7971ffdc0828377810e3c36fc93408a344cf8fc3 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 27 Jul 2016 18:02:50 +0200 Subject: [PATCH 14/66] Properly ignore Arcus debug messages --- plugins/CuraEngineBackend/CuraEngineBackend.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index bf68a6cb78..aa6f2b0807 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -252,6 +252,9 @@ class CuraEngineBackend(Backend): return super()._onSocketError(error) + if error.getErrorCode() == Arcus.ErrorCode.Debug: + return + self._terminate() if error.getErrorCode() not in [Arcus.ErrorCode.BindFailedError, Arcus.ErrorCode.ConnectionResetError, Arcus.ErrorCode.Debug]: From 4dbe895c15d8c4ffbebee18947580a73840c642d Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Wed, 27 Jul 2016 20:16:12 +0200 Subject: [PATCH 15/66] 3MF_Reader: Optionally use cElementTree This should make reading the XML faster --- plugins/3MFReader/ThreeMFReader.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index bf25ccd440..71fe8857b1 100644 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -19,7 +19,10 @@ catalog = i18nCatalog("cura") import math import zipfile -import xml.etree.ElementTree as ET +try: + import xml.etree.cElementTree as ET +except ImportError: + import xml.etree.ElementTree as ET ## Base implementation for reading 3MF files. Has no support for textures. Only loads meshes! class ThreeMFReader(MeshReader): From d4861ee6269d658be42599b6e6d1459fe1dd7561 Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Wed, 27 Jul 2016 20:21:35 +0200 Subject: [PATCH 16/66] CURA-1223: Checking whether the bounding box is correct Adds an additional check, like suggested by @awhiemstra. Contributes to CURA-1223 --- plugins/3MFReader/ThreeMFReader.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index 71fe8857b1..deb6153ee3 100644 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -124,7 +124,8 @@ class ThreeMFReader(MeshReader): Logger.log("e", "exception occured in 3mf reader: %s", e) try: # Selftest - There might be more functions that should fail - result.getBoundingBox() + boundingBox = result.getBoundingBox() + boundingBox.isValid() except: message = Message(catalog.i18nc("@info:status", "Your 3MF file seems to be broken. Please visit https://modelrepair.azurewebsites.net/ and try to repair your model!"), lifetime = 0) message.show() From 9f280717d880cb9d132f43a47aef133f64b5bf76 Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Wed, 27 Jul 2016 21:14:21 +0200 Subject: [PATCH 17/66] Clean up __init__ --- plugins/3MFReader/ThreeMFReader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index deb6153ee3..74db9fe946 100644 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -27,7 +27,7 @@ except ImportError: ## Base implementation for reading 3MF files. Has no support for textures. Only loads meshes! class ThreeMFReader(MeshReader): def __init__(self): - super(ThreeMFReader, self).__init__() + super().__init__() self._supported_extensions = [".3mf"] self._namespaces = { From d99827109ab836963d523322ef00b0e6cf9675bd Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Wed, 27 Jul 2016 21:20:55 +0200 Subject: [PATCH 18/66] Revert "Clean up __init__" This reverts commit 44c64c4ac766fc7e085d338cb1f21c949851c9ba. --- plugins/3MFReader/ThreeMFReader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index 74db9fe946..deb6153ee3 100644 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -27,7 +27,7 @@ except ImportError: ## Base implementation for reading 3MF files. Has no support for textures. Only loads meshes! class ThreeMFReader(MeshReader): def __init__(self): - super().__init__() + super(ThreeMFReader, self).__init__() self._supported_extensions = [".3mf"] self._namespaces = { From 5f540a7a78d004b2642cfc275b8a56e5f9676dff Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Wed, 27 Jul 2016 21:22:30 +0200 Subject: [PATCH 19/66] Revert "Revert "Clean up __init__"" This reverts commit d99827109ab836963d523322ef00b0e6cf9675bd. --- plugins/3MFReader/ThreeMFReader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index deb6153ee3..74db9fe946 100644 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -27,7 +27,7 @@ except ImportError: ## Base implementation for reading 3MF files. Has no support for textures. Only loads meshes! class ThreeMFReader(MeshReader): def __init__(self): - super(ThreeMFReader, self).__init__() + super().__init__() self._supported_extensions = [".3mf"] self._namespaces = { From b9857a697576db4a311c792bb57130f03f7c9904 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 26 Jul 2016 17:10:17 +0200 Subject: [PATCH 20/66] Partially solve setting the UMO heated bed kit option CURA-580 --- plugins/UltimakerMachineActions/UMOUpgradeSelection.py | 3 +++ .../UMOUpgradeSelectionMachineAction.qml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/UltimakerMachineActions/UMOUpgradeSelection.py b/plugins/UltimakerMachineActions/UMOUpgradeSelection.py index 2e13ec50d5..5d00fd5e3a 100644 --- a/plugins/UltimakerMachineActions/UMOUpgradeSelection.py +++ b/plugins/UltimakerMachineActions/UMOUpgradeSelection.py @@ -12,6 +12,9 @@ class UMOUpgradeSelection(MachineAction): super().__init__("UMOUpgradeSelection", catalog.i18nc("@action", "Select upgrades")) self._qml_url = "UMOUpgradeSelectionMachineAction.qml" + def _reset(self): + self.heatedBedChanged.emit() + heatedBedChanged = pyqtSignal() @pyqtProperty(bool, notify = heatedBedChanged) diff --git a/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml b/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml index d960bcf976..234e53c0a6 100644 --- a/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml +++ b/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml @@ -44,7 +44,7 @@ Cura.MachineAction text: catalog.i18nc("@label", "Heated bed (official kit or self-built)") checked: manager.hasHeatedBed - onClicked: manager.hasHeatedBed ? manager.removeHeatedBed() : manager.addHeatedBed() + onClicked: checked ? manager.addHeatedBed() : manager.removeHeatedBed() } UM.I18nCatalog { id: catalog; name: "cura"; } From dc6d415cf5b2e5a0ac49e207f4159e16fbf275fe Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Thu, 28 Jul 2016 09:13:35 +0200 Subject: [PATCH 21/66] Disabled duplicate material, because it has a lot of issues. CURA-1969. --- resources/qml/Preferences/MaterialsPage.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index aaaa4f5e9d..9da5522762 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -67,6 +67,8 @@ UM.ManagementPage enabled: base.currentItem != null && base.currentItem.id != Cura.MachineManager.activeMaterialId onClicked: Cura.MachineManager.setActiveMaterial(base.currentItem.id) }, + /* + // disabled because it has a lot of issues Button { text: catalog.i18nc("@action:button", "Duplicate"); @@ -89,7 +91,7 @@ UM.ManagementPage Cura.MachineManager.setActiveMaterial(material_id) } - }, + }, */ Button { text: catalog.i18nc("@action:button", "Remove"); From 1147e915c19f033e632329df63a4174a83b45d28 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 28 Jul 2016 09:27:20 +0200 Subject: [PATCH 22/66] Setting a instance to the same state no longer causes display to break CURA-1985 --- plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index dc0574d14d..75cf522c14 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -246,6 +246,7 @@ Item { key: model.key watchedProperties: [ "value", "enabled", "validationState" ] storeIndex: 0 + removeUnusedValue: false } } } From c6d1caaf0f0c64da1d9d2b0abcd92964847f5ba3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 28 Jul 2016 11:47:56 +0200 Subject: [PATCH 23/66] Convex hull is no longer needlessly re-calculated CURA-2002 --- cura/ConvexHullDecorator.py | 5 +++-- cura/PlatformPhysics.py | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/ConvexHullDecorator.py b/cura/ConvexHullDecorator.py index d864f4288b..783d74e41b 100644 --- a/cura/ConvexHullDecorator.py +++ b/cura/ConvexHullDecorator.py @@ -30,9 +30,10 @@ class ConvexHullDecorator(SceneNodeDecorator): def setNode(self, node): previous_node = self._node + # Disconnect from previous node signals if previous_node is not None and node is not previous_node: - previous_node.transformationChanged.connect(self._onChanged) - previous_node.parentChanged.connect(self._onChanged) + previous_node.transformationChanged.disconnect(self._onChanged) + previous_node.parentChanged.disconnect(self._onChanged) super().setNode(node) diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index 91d2b1a1ef..56daaddc18 100644 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -71,7 +71,6 @@ class PlatformPhysics: # If there is no convex hull for the node, start calculating it and continue. if not node.getDecorator(ConvexHullDecorator): node.addDecorator(ConvexHullDecorator()) - node.callDecoration("recomputeConvexHull") if Preferences.getInstance().getValue("physics/automatic_push_free"): # Check for collisions between convex hulls From cab1dc7a13416d1a72592962e1708ad365702051 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 28 Jul 2016 12:35:36 +0200 Subject: [PATCH 24/66] Fix merge mistake --- cura/CuraApplication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index a6d0a3b827..1142783c9c 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -98,7 +98,7 @@ class CuraApplication(QtApplication): SettingDefinition.addSupportedProperty("settable_per_meshgroup", DefinitionPropertyType.Any, default = True) SettingDefinition.addSupportedProperty("settable_globally", DefinitionPropertyType.Any, default = True) SettingDefinition.addSupportedProperty("global_inherits_stack", DefinitionPropertyType.Function, default = "-1") - SettingDefinition.addSettingType("extruder", int, str, Validator) + SettingDefinition.addSettingType("extruder", None, str, Validator) ## Add the 4 types of profiles to storage. Resources.addStorageType(self.ResourceTypes.QualityInstanceContainer, "quality") From a1bd8f59edef4c8ad6815314ea35dce677367155 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 28 Jul 2016 13:14:07 +0200 Subject: [PATCH 25/66] Slicing no longer occurs when stack is invalid CURA-1998 --- cura/Settings/MachineManager.py | 27 ++++++++++--------- .../CuraEngineBackend/CuraEngineBackend.py | 2 +- plugins/CuraEngineBackend/StartSliceJob.py | 2 +- resources/qml/Actions.qml | 4 +-- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 5bc0ff65fa..9e09ad83be 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -26,7 +26,7 @@ class MachineManager(QObject): self._global_container_stack = None Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged) - self._global_stack_valid = None + self._active_stack_valid = None self._onGlobalContainerChanged() ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged) @@ -74,7 +74,7 @@ class MachineManager(QObject): activeStackChanged = pyqtSignal() globalValueChanged = pyqtSignal() # Emitted whenever a value inside global container is changed. - globalValidationChanged = pyqtSignal() # Emitted whenever a validation inside global container is changed + activeValidationChanged = pyqtSignal() # Emitted whenever a validation inside active container is changed blurSettings = pyqtSignal() # Emitted to force fields in the advanced sidebar to un-focus, so they update properly @@ -281,16 +281,17 @@ class MachineManager(QObject): self._global_container_stack.getTop().setProperty(key, "value", new_value) if property_name == "validationState": - if self._global_stack_valid: + if self._active_stack_valid: changed_validation_state = self._active_container_stack.getProperty(key, property_name) if changed_validation_state in (UM.Settings.ValidatorState.Exception, UM.Settings.ValidatorState.MaximumError, UM.Settings.ValidatorState.MinimumError): - self._global_stack_valid = False - self.globalValidationChanged.emit() + self._active_stack_valid = False + self.activeValidationChanged.emit() else: has_errors = self._checkStackForErrors(self._active_container_stack) if not has_errors: - self._global_stack_valid = True - self.globalValidationChanged.emit() + self._active_stack_valid = True + self.activeValidationChanged.emit() + def _onGlobalContainerChanged(self): if self._global_container_stack: self._global_container_stack.nameChanged.disconnect(self._onMachineNameChanged) @@ -313,8 +314,6 @@ class MachineManager(QObject): self._global_container_stack.nameChanged.connect(self._onMachineNameChanged) self._global_container_stack.containersChanged.connect(self._onInstanceContainersChanged) self._global_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged) - self._global_stack_valid = not self._checkStackForErrors(self._global_container_stack) - self.globalValidationChanged.emit() material = self._global_container_stack.findContainer({"type": "material"}) material.nameChanged.connect(self._onMaterialNameChanged) @@ -332,6 +331,8 @@ class MachineManager(QObject): self._active_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged) else: self._active_container_stack = self._global_container_stack + self._active_stack_valid = not self._checkStackForErrors(self._active_container_stack) + self.activeValidationChanged.emit() def _onInstanceContainersChanged(self, container): container_type = container.getMetaDataEntry("type") @@ -436,11 +437,11 @@ class MachineManager(QObject): return len(user_settings) != 0 ## Check if the global profile does not contain error states - # Note that the _global_stack_valid is cached due to performance issues + # Note that the _active_stack_valid is cached due to performance issues # Calling _checkStackForErrors on every change is simply too expensive - @pyqtProperty(bool, notify = globalValidationChanged) - def isGlobalStackValid(self): - return bool(self._global_stack_valid) + @pyqtProperty(bool, notify = activeValidationChanged) + def isActiveStackValid(self): + return bool(self._active_stack_valid) @pyqtProperty(str, notify = activeStackChanged) def activeUserProfileId(self): diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index aa6f2b0807..8028df5923 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -117,7 +117,7 @@ class CuraEngineBackend(Backend): # \return list of commands and args / parameters. def getEngineCommand(self): json_path = Resources.getPath(Resources.DefinitionContainers, "fdmprinter.def.json") - return [Preferences.getInstance().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", json_path, "-vv"] + return [Preferences.getInstance().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", json_path, ""] ## Emitted when we get a message containing print duration and material amount. This also implies the slicing has finished. # \param time The amount of time the print will take. diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index a726e239e0..ebc4495365 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -71,7 +71,7 @@ class StartSliceJob(Job): return # Don't slice if there is a setting with an error value. - if self._checkStackForErrors(stack): + if not Application.getInstance().getMachineManager().isActiveStackValid: self.setResult(StartJobResult.SettingError) return diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 67bc5fe149..c443c38875 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -119,7 +119,7 @@ Item Action { id: updateProfileAction; - enabled: Cura.MachineManager.isGlobalStackValid && Cura.MachineManager.hasUserSettings && !Cura.MachineManager.isReadOnly(Cura.MachineManager.activeQualityId) + enabled: Cura.MachineManager.isActiveStackValid && Cura.MachineManager.hasUserSettings && !Cura.MachineManager.isReadOnly(Cura.MachineManager.activeQualityId) text: catalog.i18nc("@action:inmenu menubar:profile","&Update profile with current settings"); onTriggered: Cura.MachineManager.updateQualityContainerFromUserContainer() } @@ -135,7 +135,7 @@ Item Action { id: addProfileAction; - enabled: Cura.MachineManager.isGlobalStackValid && Cura.MachineManager.hasUserSettings + enabled: Cura.MachineManager.isActiveStackValid && Cura.MachineManager.hasUserSettings text: catalog.i18nc("@action:inmenu menubar:profile","&Create profile from current settings..."); } From 40932a3ad212e87529abcaa8a34328b33abd7072 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 28 Jul 2016 13:07:14 +0200 Subject: [PATCH 26/66] Set default of draft shield height to 10mm This is a fairly arbitrary default until we have tests from materials. The draft shield height is now only used by the engine if the draft shield is enabled and limited. Contributes to issue CURA-1295. --- resources/definitions/fdmprinter.def.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 2df6c295a4..669d0bcef2 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -3346,8 +3346,8 @@ "type": "float", "minimum_value": "0", "maximum_value_warning": "9999", - "default_value": 0, - "value": "9999 if draft_shield_height_limitation == 'full' and draft_shield_enabled else 0.0", + "default_value": 10, + "value": "10", "enabled": "draft_shield_enabled and draft_shield_height_limitation == \"limited\"", "settable_per_mesh": false, "settable_per_extruder": false From 1bbf9879214f821bfb5f9aab40bb9089bf317473 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 28 Jul 2016 13:45:11 +0200 Subject: [PATCH 27/66] Fix SettingComboBox and SettingExtruder in some contexts Use propertyProvider instead of provider; fixes comboboxes in PostProcessing plugin. --- resources/qml/Settings/SettingComboBox.qml | 6 +++--- resources/qml/Settings/SettingExtruder.qml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/qml/Settings/SettingComboBox.qml b/resources/qml/Settings/SettingComboBox.qml index 5308e45f5a..283e309e6a 100644 --- a/resources/qml/Settings/SettingComboBox.qml +++ b/resources/qml/Settings/SettingComboBox.qml @@ -86,18 +86,18 @@ SettingItem } } - onActivated: { forceActiveFocus(); provider.setPropertyValue("value", definition.options[index].key) } + onActivated: { forceActiveFocus(); propertyProvider.setPropertyValue("value", definition.options[index].key) } onModelChanged: updateCurrentIndex(); Connections { - target: provider + target: propertyProvider onPropertiesChanged: control.updateCurrentIndex() } function updateCurrentIndex() { for(var i = 0; i < definition.options.length; ++i) { - if(definition.options[i].key == provider.properties.value) { + if(definition.options[i].key == propertyProvider.properties.value) { currentIndex = i; return; } diff --git a/resources/qml/Settings/SettingExtruder.qml b/resources/qml/Settings/SettingExtruder.qml index 72c5299b15..d88fc9c457 100644 --- a/resources/qml/Settings/SettingExtruder.qml +++ b/resources/qml/Settings/SettingExtruder.qml @@ -105,13 +105,13 @@ SettingItem onActivated: { forceActiveFocus(); - provider.setPropertyValue("value", extruders_model.getItem(index).index) + propertyProvider.setPropertyValue("value", extruders_model.getItem(index).index) } onModelChanged: updateCurrentIndex(); Connections { - target: provider + target: propertyProvider onPropertiesChanged: control.updateCurrentIndex(); } @@ -119,7 +119,7 @@ SettingItem { for(var i = 0; i < extruders_model.rowCount(); ++i) { - if(extruders_model.getItem(i).index == provider.properties.value) + if(extruders_model.getItem(i).index == propertyProvider.properties.value) { currentIndex = i; return; From ce462ace17a2730c108215f414ff5b1b37b50c19 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Thu, 28 Jul 2016 13:48:43 +0200 Subject: [PATCH 28/66] Layout. CURA-1707 --- cura/ConvexHullNode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/ConvexHullNode.py b/cura/ConvexHullNode.py index 232b30e317..8e5acf9518 100644 --- a/cura/ConvexHullNode.py +++ b/cura/ConvexHullNode.py @@ -77,7 +77,7 @@ class ConvexHullNode(SceneNode): convex_hull_head = self._node.callDecoration("getConvexHullHead") if convex_hull_head: convex_hull_head_builder = MeshBuilder() - convex_hull_head_builder.addConvexPolygon(convex_hull_head.getPoints(), self._mesh_height-self._thickness) + convex_hull_head_builder.addConvexPolygon(convex_hull_head.getPoints(), self._mesh_height - self._thickness) self._convex_hull_head_mesh = convex_hull_head_builder.build() if not node: From 0794ddb58e278888b47dccd85af67f680272c55e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 28 Jul 2016 14:01:57 +0200 Subject: [PATCH 29/66] Backend now listens to all machine extruders for changes CURA-1999 --- .../CuraEngineBackend/CuraEngineBackend.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 8028df5923..73054fb4dc 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -13,9 +13,11 @@ from UM.Resources import Resources from UM.Settings.Validator import ValidatorState #To find if a setting is in an error state. We can't slice then. from UM.Platform import Platform + import cura.Settings from cura.OneAtATimeIterator import OneAtATimeIterator +from cura.Settings.ExtruderManager import ExtruderManager from . import ProcessSlicedLayersJob from . import ProcessGCodeJob from . import StartSliceJob @@ -391,22 +393,35 @@ class CuraEngineBackend(Backend): if self._global_container_stack: self._global_container_stack.propertyChanged.disconnect(self._onSettingChanged) self._global_container_stack.containersChanged.disconnect(self._onChanged) + extruders = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())) + if extruders: + for extruder in extruders: + extruder.propertyChanged.disconnect(self._onSettingChanged) self._global_container_stack = Application.getInstance().getGlobalContainerStack() if self._global_container_stack: self._global_container_stack.propertyChanged.connect(self._onSettingChanged) #Note: Only starts slicing when the value changed. self._global_container_stack.containersChanged.connect(self._onChanged) + extruders = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())) + if extruders: + for extruder in extruders: + extruder.propertyChanged.connect(self._onSettingChanged) self._onActiveExtruderChanged() self._onChanged() def _onActiveExtruderChanged(self): + if self._global_container_stack: + # Connect all extruders of the active machine. This might cause a few connects that have already happend, + # but that shouldn't cause issues as only new / unique connections are added. + extruders = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())) + if extruders: + for extruder in extruders: + extruder.propertyChanged.connect(self._onSettingChanged) if self._active_extruder_stack: - self._active_extruder_stack.propertyChanged.disconnect(self._onSettingChanged) self._active_extruder_stack.containersChanged.disconnect(self._onChanged) self._active_extruder_stack = cura.Settings.ExtruderManager.getInstance().getActiveExtruderStack() if self._active_extruder_stack: - self._active_extruder_stack.propertyChanged.connect(self._onSettingChanged) # Note: Only starts slicing when the value changed. self._active_extruder_stack.containersChanged.connect(self._onChanged) From fe205b51f966a4e06ec7c149ca254bdaf7ff8072 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 28 Jul 2016 14:45:49 +0200 Subject: [PATCH 30/66] Per object settings now use correct stack CURA-1934 --- cura/Settings/SettingOverrideDecorator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cura/Settings/SettingOverrideDecorator.py b/cura/Settings/SettingOverrideDecorator.py index 24360ed992..f1e34a939a 100644 --- a/cura/Settings/SettingOverrideDecorator.py +++ b/cura/Settings/SettingOverrideDecorator.py @@ -75,6 +75,7 @@ class SettingOverrideDecorator(SceneNodeDecorator): # \param extruder_stack_id The new extruder stack to print with. def setActiveExtruder(self, extruder_stack_id): self._extruder_stack = extruder_stack_id + self._updateNextStack() self.activeExtruderChanged.emit() def getStack(self): From 3d47e329a7214fe3687636ab733e9a06bb7996ca Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Thu, 28 Jul 2016 14:57:17 +0200 Subject: [PATCH 31/66] Fix convex hull being updated after removing CPU eating monster. CURA-2002. --- cura/ConvexHullDecorator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/ConvexHullDecorator.py b/cura/ConvexHullDecorator.py index 783d74e41b..5185579633 100644 --- a/cura/ConvexHullDecorator.py +++ b/cura/ConvexHullDecorator.py @@ -287,5 +287,5 @@ class ConvexHullDecorator(SceneNodeDecorator): _affected_settings = [ "adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", - "raft_surface_thickness", "raft_airgap", "print_sequence", + "raft_surface_thickness", "raft_airgap", "raft_margin", "print_sequence", "skirt_gap", "skirt_line_count", "skirt_brim_line_width", "skirt_distance"] From 467f971dac2707e07a9f8d80eddad7a4b0ec1945 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 28 Jul 2016 15:26:59 +0200 Subject: [PATCH 32/66] When sending per object settings, we now also check if we need to send anything at all Adding a setting and then removing it caused some weird behavior issues CURA-1988 --- plugins/CuraEngineBackend/StartSliceJob.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index ebc4495365..26cf71ec7e 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -211,7 +211,9 @@ class StartSliceJob(Job): def _handlePerObjectSettings(self, node, message): stack = node.callDecoration("getStack") - if stack: + # Check if the node has a stack attached to it and the stack has any settings in the top container. + if stack and stack.getTop().getAllKeys(): + # Because we want to use inheritance correctly, we send all settings as seen from the per object stack. for key in stack.getAllKeys(): setting = message.addRepeatedMessage("settings") setting.name = key From 570a67556a3c14c3c6f68a71e80f74d69fbffba3 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 28 Jul 2016 18:06:14 +0200 Subject: [PATCH 33/66] Fix exporting and importing materials on OSX OSX's file dialog is stupid and does not understand extensions with a . in them. So instead just use everything after the last . Fixes CURA-1987 --- cura/Settings/ContainerManager.py | 17 +++++++++++++++-- resources/qml/Preferences/MaterialsPage.qml | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index 9184db109a..82be7c480f 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -244,6 +244,7 @@ class ContainerManager(QObject): if not type_name or entry["type"] == type_name: filters.append(filter_string) + filters.append("All Files (*)") return filters ## Export a container to a file @@ -280,6 +281,9 @@ class ContainerManager(QObject): return { "status": "error", "message": "Container not found"} container = containers[0] + if UM.Platform.isOSX() and "." in file_url: + file_url = file_url[:file_url.rfind(".")] + for suffix in mime_type.suffixes: if file_url.endswith(suffix): break @@ -301,7 +305,7 @@ class ContainerManager(QObject): with UM.SaveFile(file_url, "w") as f: f.write(contents) - return { "status": "success", "message": "Succesfully exported container"} + return { "status": "success", "message": "Succesfully exported container", "path": file_url} ## Imports a profile from a file # @@ -371,11 +375,20 @@ class ContainerManager(QObject): "container": container_type } - suffix_list = "*." + mime_type.preferredSuffix + suffix = mime_type.preferredSuffix + if UM.Platform.isOSX() and "." in suffix: + # OSX's File dialog is stupid and does not allow selecting files with a . in its name + suffix = suffix[suffix.index(".") + 1:] + + suffix_list = "*." + suffix for suffix in mime_type.suffixes: if suffix == mime_type.preferredSuffix: continue + if UM.Platform.isOSX() and "." in suffix: + # OSX's File dialog is stupid and does not allow selecting files with a . in its name + suffix = suffix[suffix.index("."):] + suffix_list += ", *." + suffix name_filter = "{0} ({1})".format(mime_type.comment, suffix_list) diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index 9da5522762..f4a8df1dcf 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -255,7 +255,7 @@ UM.ManagementPage else if(result.status == "success") { messageDialog.icon = StandardIcon.Information - messageDialog.text = catalog.i18nc("@info:status", "Successfully exported material to %1").arg(fileUrl) + messageDialog.text = catalog.i18nc("@info:status", "Successfully exported material to %1").arg(result.path) messageDialog.open() } CuraApplication.setDefaultPath("dialog_material_path", folder) From 14d4b1f881d74abd817f9ec3209ff066b8a50b70 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 28 Jul 2016 18:05:03 +0200 Subject: [PATCH 34/66] Fix setting the default path on OSX Contributes to CURA-1987 --- cura/CuraApplication.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index c51207862e..aa9478e491 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -203,7 +203,7 @@ class CuraApplication(QtApplication): "dialog_profile_path", "dialog_material_path"]: - Preferences.getInstance().addPreference("local_file/%s" % key, "~/") + Preferences.getInstance().addPreference("local_file/%s" % key, os.path.expanduser("~/")) Preferences.getInstance().setDefault("local_file/last_used_type", "text/x-gcode") @@ -346,6 +346,7 @@ class CuraApplication(QtApplication): @pyqtSlot(str, result = QUrl) def getDefaultPath(self, key): default_path = Preferences.getInstance().getValue("local_file/%s" % key) + print(default_path) return QUrl.fromLocalFile(default_path) @pyqtSlot(str, str) @@ -896,4 +897,4 @@ class CuraApplication(QtApplication): self._additional_components[area_id] = [] self._additional_components[area_id].append(component) - self.additionalComponentsChanged.emit(area_id) \ No newline at end of file + self.additionalComponentsChanged.emit(area_id) From 8fb6c9939a5d9f9299acea6287506a7d258afb8a Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 28 Jul 2016 18:14:38 +0200 Subject: [PATCH 35/66] Add "offline" styling to sidebar/monitor CURA-1851 --- resources/qml/MonitorButton.qml | 7 ++++++- resources/qml/Sidebar.qml | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index 7a87eb4f60..4a33b347d3 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -30,6 +30,8 @@ Rectangle return UM.Theme.getColor("status_paused") else if (Cura.MachineManager.printerOutputDevices[0].jobState == "error") return UM.Theme.getColor("status_stopped") + else if (Cura.MachineManager.printerOutputDevices[0].jobState == "offline") + return UM.Theme.getColor("status_offline") else return UM.Theme.getColor("text") } @@ -41,7 +43,10 @@ Rectangle { if(!printerConnected) { - return catalog.i18nc("@label:", "Please check your printer connections") + return catalog.i18nc("@label:", "Not connected to a printer") + } else if(Cura.MachineManager.printerOutputDevices[0].jobState == "offline") + { + return catalog.i18nc("@label:", "Lost connection with the printer") } else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing") { return catalog.i18nc("@label:", "Printing...") diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 1542e24f5d..8ab776af6a 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -116,6 +116,8 @@ Rectangle return UM.Theme.getIcon("tab_monitor_paused") else if (Cura.MachineManager.printerOutputDevices[0].jobState == "error") return UM.Theme.getIcon("tab_monitor_stopped") + else if (Cura.MachineManager.printerOutputDevices[0].jobState == "offline") + return UM.Theme.getIcon("tab_monitor_offline") else return UM.Theme.getIcon("tab_monitor") } From 1b432b7b4f9fe0f52d50161589540b17e22cf14d Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Thu, 28 Jul 2016 18:30:48 +0200 Subject: [PATCH 36/66] Removing debug print Contributes to CURA-1987 --- cura/CuraApplication.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index aa9478e491..bc4378feff 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -346,7 +346,6 @@ class CuraApplication(QtApplication): @pyqtSlot(str, result = QUrl) def getDefaultPath(self, key): default_path = Preferences.getInstance().getValue("local_file/%s" % key) - print(default_path) return QUrl.fromLocalFile(default_path) @pyqtSlot(str, str) From 27f160c22034a9856833613a14c8bb3d2fb7a67d Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Thu, 28 Jul 2016 19:52:43 +0200 Subject: [PATCH 37/66] JSON fix: made retraction_hop_only_when_collides and retraction_hop not children of retraction_hop_enabled retraction_hop_enabled was used by the engine, so it should never become disabled --- resources/definitions/fdmprinter.def.json | 46 +++++++++++------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 4017ab3a69..c9763571e2 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1236,30 +1236,28 @@ "default_value": false, "enabled": "retraction_enable", "settable_per_mesh": false, - "settable_per_extruder": true, - "children": { - "retraction_hop_only_when_collides": { - "label": "Z Hop Only Over Printed Parts", - "description": "Only perform a Z Hop when moving over printed parts which cannot be avoided by horizontal motion by Avoid Printed Parts when Traveling.", - "type": "bool", - "default_value": false, - "enabled": "retraction_enable and retraction_hop_enabled and travel_avoid_other_parts", - "settable_per_mesh": false, - "settable_per_extruder": true - }, - "retraction_hop": { - "label": "Z Hop Height", - "description": "The height difference when performing a Z Hop.", - "unit": "mm", - "type": "float", - "default_value": 1, - "minimum_value_warning": "-0.0001", - "maximum_value_warning": "10", - "enabled": "retraction_enable and retraction_hop_enabled", - "settable_per_mesh": false, - "settable_per_extruder": true - } - } + "settable_per_extruder": true + }, + "retraction_hop_only_when_collides": { + "label": "Z Hop Only Over Printed Parts", + "description": "Only perform a Z Hop when moving over printed parts which cannot be avoided by horizontal motion by Avoid Printed Parts when Traveling.", + "type": "bool", + "default_value": false, + "enabled": "retraction_enable and retraction_hop_enabled and travel_avoid_other_parts", + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "retraction_hop": { + "label": "Z Hop Height", + "description": "The height difference when performing a Z Hop.", + "unit": "mm", + "type": "float", + "default_value": 1, + "minimum_value_warning": "-0.0001", + "maximum_value_warning": "10", + "enabled": "retraction_enable and retraction_hop_enabled", + "settable_per_mesh": false, + "settable_per_extruder": true }, "material_standby_temperature": { From 4b192db1bfdc1ae16b009ea96954f58a9fc712a3 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Thu, 28 Jul 2016 20:02:22 +0200 Subject: [PATCH 38/66] JSON: extruders cannot be more than 16 (CuraEngine hardcoded value) (CURA-2015) --- resources/definitions/fdmprinter.def.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index c9763571e2..e7445b6061 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -150,6 +150,8 @@ "label": "Number extruders", "description": "Number of extruder trains. An extruder train is the combination of a feeder, bowden tube, and nozzle.", "default_value": 1, + "minimum_value": "1", + "maximum_value": "16", "type": "int", "settable_per_mesh": false, "settable_per_extruder": false, From b5d2533820d670e162defbdec7da692f7c2bedc3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 29 Jul 2016 09:59:57 +0200 Subject: [PATCH 39/66] G-code writer no longer uses duplicate for flattening profile Spent to much time fixing this already, so we now just make a new profile by hand based on the profiles to be flattened CURA-1933 --- plugins/GCodeWriter/GCodeWriter.py | 51 ++++++++++++------------------ 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/plugins/GCodeWriter/GCodeWriter.py b/plugins/GCodeWriter/GCodeWriter.py index 11c5dc5f84..8e3be15ffe 100644 --- a/plugins/GCodeWriter/GCodeWriter.py +++ b/plugins/GCodeWriter/GCodeWriter.py @@ -9,6 +9,8 @@ import UM.Settings.ContainerRegistry from cura.CuraApplication import CuraApplication from cura.Settings.ExtruderManager import ExtruderManager +from UM.Settings.InstanceContainer import InstanceContainer + import re #For escaping characters in the settings. import json @@ -61,6 +63,20 @@ class GCodeWriter(MeshWriter): return False + ## Create a new container with container 2 as base and container 1 written over it. + def _createFlattenedContainerInstance(self, instance_container1, instance_container2): + flat_container = InstanceContainer(instance_container2.getName()) + flat_container.setDefinition(instance_container2.getDefinition()) + flat_container.setMetaData(instance_container2.getMetaData()) + + for key in instance_container2.getAllKeys(): + flat_container.setProperty(key, "value", instance_container2.getProperty(key, "value")) + + for key in instance_container1.getAllKeys(): + flat_container.setProperty(key, "value", instance_container1.getProperty(key, "value")) + return flat_container + + ## Serialises a container stack to prepare it for writing at the end of the # g-code. # @@ -74,38 +90,13 @@ class GCodeWriter(MeshWriter): prefix_length = len(prefix) container_with_profile = stack.findContainer({"type": "quality"}) - machine_manager = CuraApplication.getInstance().getMachineManager() - - # Duplicate the current quality profile and update it with any user settings. - flat_quality_id = machine_manager.duplicateContainer(container_with_profile.getId()) - - flat_quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = flat_quality_id)[0] - flat_quality._dirty = False - user_settings = stack.getTop() - - # We don't want to send out any signals, so disconnect them. - flat_quality.propertyChanged.disconnectAll() - - for key in user_settings.getAllKeys(): - flat_quality.setProperty(key, "value", user_settings.getProperty(key, "value")) - - serialized = flat_quality.serialize() - + flat_global_container = self._createFlattenedContainerInstance(stack.getTop(),container_with_profile) + serialized = flat_global_container.serialize() data = {"global_quality": serialized} - manager = ExtruderManager.getInstance() - for extruder in manager.getMachineExtruders(stack.getId()): + + for extruder in ExtruderManager.getInstance().getMachineExtruders(stack.getId()): extruder_quality = extruder.findContainer({"type": "quality"}) - - flat_extruder_quality_id = machine_manager.duplicateContainer(extruder_quality.getId()) - flat_extruder_quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id=flat_extruder_quality_id)[0] - flat_extruder_quality._dirty = False - extruder_user_settings = extruder.getTop() - - # We don't want to send out any signals, so disconnect them. - flat_extruder_quality.propertyChanged.disconnectAll() - - for key in extruder_user_settings.getAllKeys(): - flat_extruder_quality.setProperty(key, "value", extruder_user_settings.getProperty(key, "value")) + flat_extruder_quality = self._createFlattenedContainerInstance(extruder.getTop(), extruder_quality) extruder_serialized = flat_extruder_quality.serialize() data.setdefault("extruder_quality", []).append(extruder_serialized) From 1ed0503e027edd0dbaa1da95c261f8ce42aa9f9f Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 29 Jul 2016 10:37:03 +0200 Subject: [PATCH 40/66] Backspace now triggers same action as delete CURA-1891 --- resources/qml/Cura.qml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index d0870991d2..45137db5cc 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -54,10 +54,7 @@ UM.MainWindow Keys.onPressed: { if (event.key == Qt.Key_Backspace) { - if(objectContextMenu.objectId != 0) - { - Printer.deleteObject(objectContextMenu.objectId); - } + Cura.Actions.deleteSelection.trigger() } } From 21d4e9b8942532744dde1d149beb1358d760c723 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 29 Jul 2016 11:00:28 +0200 Subject: [PATCH 41/66] Delete selection now also removed group nodes when they only have one child left CURA-1891 --- cura/CuraApplication.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index bc4378feff..2de70b7304 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -555,12 +555,19 @@ class CuraApplication(QtApplication): def deleteSelection(self): if not self.getController().getToolsEnabled(): return - + removed_group_nodes = [] op = GroupedOperation() nodes = Selection.getAllSelectedObjects() for node in nodes: op.addOperation(RemoveSceneNodeOperation(node)) - + group_node = node.getParent() + if group_node and group_node.callDecoration("isGroup") and group_node not in removed_group_nodes: + remaining_nodes_in_group = list(set(group_node.getChildren()) - set(nodes)) + if len(remaining_nodes_in_group) == 1: + removed_group_nodes.append(group_node) + remaining_nodes_in_group[0].translate(group_node.getPosition()) + remaining_nodes_in_group[0].setParent(group_node.getParent()) + op.addOperation(RemoveSceneNodeOperation(group_node)) op.push() pass From 03aa4f9c6a4e856079f1f4624b23ce80ae3586d0 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 29 Jul 2016 11:05:11 +0200 Subject: [PATCH 42/66] Undo removing object from group now works correctly CURA-1891 --- cura/CuraApplication.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 2de70b7304..9c3e2511c1 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -565,8 +565,7 @@ class CuraApplication(QtApplication): remaining_nodes_in_group = list(set(group_node.getChildren()) - set(nodes)) if len(remaining_nodes_in_group) == 1: removed_group_nodes.append(group_node) - remaining_nodes_in_group[0].translate(group_node.getPosition()) - remaining_nodes_in_group[0].setParent(group_node.getParent()) + op.addOperation(SetParentOperation(remaining_nodes_in_group[0], group_node.getParent())) op.addOperation(RemoveSceneNodeOperation(group_node)) op.push() @@ -593,8 +592,7 @@ class CuraApplication(QtApplication): op.push() if group_node: if len(group_node.getChildren()) == 1 and group_node.callDecoration("isGroup"): - group_node.getChildren()[0].translate(group_node.getPosition()) - group_node.getChildren()[0].setParent(group_node.getParent()) + op.addOperation(SetParentOperation(group_node.getChildren()[0], group_node.getParent())) op = RemoveSceneNodeOperation(group_node) op.push() From d1a6420e1b33770733883ae6f0d7954b090bbf3e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 29 Jul 2016 11:07:16 +0200 Subject: [PATCH 43/66] Delete all object now clears Selection CURA-1891 --- cura/CuraApplication.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 9c3e2511c1..a646d687f6 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -657,6 +657,7 @@ class CuraApplication(QtApplication): op.addOperation(RemoveSceneNodeOperation(node)) op.push() + Selection.clear() ## Reset all translation on nodes with mesh data. @pyqtSlot() From f584b49af2d0f1db850ebaad9d100849c1ef4fd5 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 29 Jul 2016 11:27:03 +0200 Subject: [PATCH 44/66] Add "Close" button to (single) machine actions dialog CURA-2019 --- .../BedLevelMachineAction.qml | 22 +++---------------- .../UMOCheckupMachineAction.qml | 19 +++------------- .../UpgradeFirmwareMachineAction.qml | 22 ++++++------------- resources/qml/Preferences/MachinesPage.qml | 6 +++++ 4 files changed, 19 insertions(+), 50 deletions(-) diff --git a/plugins/UltimakerMachineActions/BedLevelMachineAction.qml b/plugins/UltimakerMachineActions/BedLevelMachineAction.qml index d043c20df5..602e945922 100644 --- a/plugins/UltimakerMachineActions/BedLevelMachineAction.qml +++ b/plugins/UltimakerMachineActions/BedLevelMachineAction.qml @@ -47,39 +47,23 @@ Cura.MachineAction text: catalog.i18nc("@label", "For every position; insert a piece of paper under the nozzle and adjust the print bed height. The print bed height is right when the paper is slightly gripped by the tip of the nozzle.") } - Item + Row { id: bedlevelingWrapper anchors.top: bedlevelingText.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.horizontalCenter: parent.horizontalCenter - height: skipBedlevelingButton.height - width: bedlevelingButton.width + skipBedlevelingButton.width + UM.Theme.getSize("default_margin").height < bedLevelMachineAction.width ? bedlevelingButton.width + skipBedlevelingButton.width + UM.Theme.getSize("default_margin").height : bedLevelMachineAction.width + width: childrenRect.width + spacing: UM.Theme.getSize("default_margin").width Button { id: bedlevelingButton - anchors.top: parent.top - anchors.left: parent.left text: catalog.i18nc("@action:button","Move to Next Position"); onClicked: { manager.moveToNextLevelPosition() } } - - Button - { - id: skipBedlevelingButton - anchors.top: parent.width < bedLevelMachineAction.width ? parent.top : bedlevelingButton.bottom - anchors.topMargin: parent.width < bedLevelMachineAction.width ? 0 : UM.Theme.getSize("default_margin").height/2 - anchors.left: parent.width < bedLevelMachineAction.width ? bedlevelingButton.right : parent.left - anchors.leftMargin: parent.width < bedLevelMachineAction.width ? UM.Theme.getSize("default_margin").width : 0 - text: catalog.i18nc("@action:button","Skip bed leveling"); - onClicked: - { - manager.setFinished() - } - } } } } diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml index 14ed1e2c51..27953874cd 100644 --- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml +++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml @@ -39,19 +39,17 @@ Cura.MachineAction text: catalog.i18nc("@label", "It's a good idea to do a few sanity checks on your Ultimaker. You can skip this step if you know your machine is functional"); } - Item + Row { id: startStopButtons anchors.top: pageDescription.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.horizontalCenter: parent.horizontalCenter - height: childrenRect.height - width: startCheckButton.width + skipCheckButton.width + UM.Theme.getSize("default_margin").height < checkupMachineAction.width ? startCheckButton.width + skipCheckButton.width + UM.Theme.getSize("default_margin").height : checkupMachineAction.width + width: childrenRect.width + spacing: UM.Theme.getSize("default_margin").width Button { id: startCheckButton - anchors.top: parent.top - anchors.left: parent.left text: catalog.i18nc("@action:button","Start Printer Check"); onClicked: { @@ -60,17 +58,6 @@ Cura.MachineAction manager.startCheck() } } - - Button - { - id: skipCheckButton - anchors.top: parent.width < checkupMachineAction.width ? parent.top : startCheckButton.bottom - anchors.topMargin: parent.width < checkupMachineAction.width ? 0 : UM.Theme.getSize("default_margin").height/2 - anchors.left: parent.width < checkupMachineAction.width ? startCheckButton.right : parent.left - anchors.leftMargin: parent.width < checkupMachineAction.width ? UM.Theme.getSize("default_margin").width : 0 - text: catalog.i18nc("@action:button", "Skip Printer Check"); - onClicked: manager.setFinished() - } } Item diff --git a/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.qml b/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.qml index 37e4eae2d3..0c9b80c010 100644 --- a/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.qml +++ b/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.qml @@ -56,29 +56,21 @@ Cura.MachineAction wrapMode: Text.WordWrap text: catalog.i18nc("@label", "Cura requires these new features and thus your firmware will most likely need to be upgraded. You can do so now."); } - Item + Row { anchors.top: upgradeText2.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.horizontalCenter: parent.horizontalCenter - width: upgradeButton.width + skipUpgradeButton.width + UM.Theme.getSize("default_margin").height < upgradeFirmwareMachineAction.width ? upgradeButton.width + skipUpgradeButton.width + UM.Theme.getSize("default_margin").height : upgradeFirmwareMachineAction.width + width: childrenRect.width + spacing: UM.Theme.getSize("default_margin").width Button { id: upgradeButton - anchors.top: parent.top - anchors.left: parent.left text: catalog.i18nc("@action:button","Upgrade to Marlin Firmware"); - onClicked: Cura.USBPrinterManager.updateAllFirmware() - } - Button - { - id: skipUpgradeButton - anchors.top: parent.width < upgradeFirmwareMachineAction.width ? parent.top : upgradeButton.bottom - anchors.topMargin: parent.width < upgradeFirmwareMachineAction.width ? 0 : UM.Theme.getSize("default_margin").height / 2 - anchors.left: parent.width < upgradeFirmwareMachineAction.width ? upgradeButton.right : parent.left - anchors.leftMargin: parent.width < upgradeFirmwareMachineAction.width ? UM.Theme.getSize("default_margin").width : 0 - text: catalog.i18nc("@action:button", "Skip Upgrade"); - onClicked: manager.setFinished() + onClicked: + { + Cura.USBPrinterManager.updateAllFirmware() + } } } } diff --git a/resources/qml/Preferences/MachinesPage.qml b/resources/qml/Preferences/MachinesPage.qml index 0dc3bb32eb..b057a33d52 100644 --- a/resources/qml/Preferences/MachinesPage.qml +++ b/resources/qml/Preferences/MachinesPage.qml @@ -107,6 +107,12 @@ UM.ManagementPage contents = content; content.onCompleted.connect(hide) } + rightButtons: Button + { + text: catalog.i18nc("@action:button", "Close") + iconName: "dialog-close" + onClicked: actionDialog.accept() + } } Row From 8d59d7187f4054ee450a662b2d497160ec2430bf Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 29 Jul 2016 13:01:03 +0200 Subject: [PATCH 45/66] Deleting old toplayer data is now only done when new data is complete. This prevents the pretty annoying blinking behavior that we had before --- plugins/LayerView/LayerView.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index 87e1a30834..5dd84392b4 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -126,7 +126,6 @@ class LayerView(View): if self._current_layer_num > self._max_layers: self._current_layer_num = self._max_layers - self.resetLayerData() self._startUpdateTopLayers() self.currentLayerNumChanged.emit() @@ -199,7 +198,7 @@ class LayerView(View): if not job.getResult(): return - + self.resetLayerData() # Reset the layer data only when job is done. Doing it now prevents "blinking" data. self._current_layer_mesh = job.getResult().get("layers") self._current_layer_jumps = job.getResult().get("jumps") self._controller.getScene().sceneChanged.emit(self._controller.getScene().getRoot()) From b9a1d50e0583c736fe27679a4db44935c2edba3d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 29 Jul 2016 13:17:41 +0200 Subject: [PATCH 46/66] Added option to only display top layers in layerview --- plugins/LayerView/LayerView.py | 9 +++++---- resources/qml/Preferences/GeneralPage.qml | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index 5dd84392b4..358a357d3e 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -45,10 +45,11 @@ class LayerView(View): self._activity = False Preferences.getInstance().addPreference("view/top_layer_count", 5) + Preferences.getInstance().addPreference("view/only_show_top_layers", False) Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged) self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count")) - + self._only_show_top_layers = bool(Preferences.getInstance().getValue("view/only_show_top_layers")) self._busy = False def getActivity(self): @@ -100,7 +101,7 @@ class LayerView(View): continue # Render all layers below a certain number as line mesh instead of vertices. - if self._current_layer_num - self._solid_layers > -1: + if self._current_layer_num - self._solid_layers > -1 and not self._only_show_top_layers: start = 0 end = 0 element_counts = layer_data.getElementCounts() @@ -206,12 +207,12 @@ class LayerView(View): self._top_layers_job = None def _onPreferencesChanged(self, preference): - if preference != "view/top_layer_count": + if preference != "view/top_layer_count" and preference != "view/only_show_top_layers": return self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count")) + self._only_show_top_layers = bool(Preferences.getInstance().getValue("view/only_show_top_layers")) - self.resetLayerData() self._startUpdateTopLayers() class _CreateTopLayersJob(Job): diff --git a/resources/qml/Preferences/GeneralPage.qml b/resources/qml/Preferences/GeneralPage.qml index 7e5b4efadc..223b7eda85 100644 --- a/resources/qml/Preferences/GeneralPage.qml +++ b/resources/qml/Preferences/GeneralPage.qml @@ -192,6 +192,7 @@ UM.PreferencesPage } } + UM.TooltipArea { width: childrenRect.width; height: childrenRect.height; @@ -215,6 +216,19 @@ UM.PreferencesPage } } } + UM.TooltipArea { + width: childrenRect.width + height: childrenRect.height + text: catalog.i18nc("@info:tooltip", "Should only the top layers be displayed in layerview?") + + CheckBox + { + id: topLayersOnlyCheckbox + text: catalog.i18nc("@option:check", "Only display top layer(s) in layer view") + checked: boolCheck(UM.Preferences.getValue("view/only_show_top_layers")) + onCheckedChanged: UM.Preferences.setValue("view/only_show_top_layers", checked) + } + } Item { From c1d9c1ea125aee1b61a745f2231706a10297ae31 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 29 Jul 2016 14:29:35 +0200 Subject: [PATCH 47/66] Add button to start bedleveling wizard Prevents the printer from homing unexpectedly --- .../BedLevelMachineAction.py | 6 ++++++ .../BedLevelMachineAction.qml | 20 +++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/plugins/UltimakerMachineActions/BedLevelMachineAction.py b/plugins/UltimakerMachineActions/BedLevelMachineAction.py index 7dad841340..12df7d6843 100644 --- a/plugins/UltimakerMachineActions/BedLevelMachineAction.py +++ b/plugins/UltimakerMachineActions/BedLevelMachineAction.py @@ -18,6 +18,11 @@ class BedLevelMachineAction(MachineAction): pass def _reset(self): + self._bed_level_position = 0 + pass + + @pyqtSlot() + def startBedLeveling(self): self._bed_level_position = 0 printer_output_devices = self._getPrinterOutputDevices() if printer_output_devices: @@ -52,4 +57,5 @@ class BedLevelMachineAction(MachineAction): output_device.moveHead(0, 0, -3) self._bed_level_position += 1 elif self._bed_level_position >= 3: + output_device.sendCommand("M18") # Turn off all motors so the user can move the axes self.setFinished() \ No newline at end of file diff --git a/plugins/UltimakerMachineActions/BedLevelMachineAction.qml b/plugins/UltimakerMachineActions/BedLevelMachineAction.qml index 602e945922..c7c4074120 100644 --- a/plugins/UltimakerMachineActions/BedLevelMachineAction.qml +++ b/plugins/UltimakerMachineActions/BedLevelMachineAction.qml @@ -55,13 +55,29 @@ Cura.MachineAction anchors.horizontalCenter: parent.horizontalCenter width: childrenRect.width spacing: UM.Theme.getSize("default_margin").width + + Button + { + id: startBedLevelingButton + text: catalog.i18nc("@action:button","Start Bed Leveling") + onClicked: + { + startBedLevelingButton.visible = false; + bedlevelingButton.visible = true; + checkupMachineAction.heatupHotendStarted = false; + checkupMachineAction.heatupBedStarted = false; + manager.startCheck(); + } + } + Button { id: bedlevelingButton - text: catalog.i18nc("@action:button","Move to Next Position"); + text: catalog.i18nc("@action:button","Move to Next Position") + visible: false onClicked: { - manager.moveToNextLevelPosition() + manager.moveToNextLevelPosition(); } } } From 2576aaca6265e1de0523e0461657aadf08837521 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 29 Jul 2016 14:30:36 +0200 Subject: [PATCH 48/66] Hide start check button once the check has started --- plugins/UltimakerMachineActions/UMOCheckupMachineAction.py | 1 + .../UltimakerMachineActions/UMOCheckupMachineAction.qml | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py index b46c92a82c..355b4dd0c1 100644 --- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py +++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py @@ -155,6 +155,7 @@ class UMOCheckupMachineAction(MachineAction): if output_devices: self._output_device = output_devices[0] try: + self._output_device.sendCommand("M18") # Turn off all motors so the user can move the axes self._output_device.startPollEndstop() self._output_device.bedTemperatureChanged.connect(self.bedTemperatureChanged) self._output_device.hotendTemperaturesChanged.connect(self.hotendTemperatureChanged) diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml index 27953874cd..0bdbc27527 100644 --- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml +++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml @@ -53,9 +53,10 @@ Cura.MachineAction text: catalog.i18nc("@action:button","Start Printer Check"); onClicked: { - checkupMachineAction.heatupHotendStarted = false - checkupMachineAction.heatupBedStarted = false - manager.startCheck() + checkupMachineAction.heatupHotendStarted = false; + checkupMachineAction.heatupBedStarted = false; + manager.startCheck(); + startCheckButton.visible = false; } } } From 7ff1b937e32869aee32fd81d24043a4e1b2ec654 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 29 Jul 2016 15:19:50 +0200 Subject: [PATCH 49/66] Perobject settings now use correct stacks to calculate values CURA-1754 --- plugins/CuraEngineBackend/StartSliceJob.py | 2 +- plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 26cf71ec7e..6e7ccba6ee 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -212,7 +212,7 @@ class StartSliceJob(Job): def _handlePerObjectSettings(self, node, message): stack = node.callDecoration("getStack") # Check if the node has a stack attached to it and the stack has any settings in the top container. - if stack and stack.getTop().getAllKeys(): + if stack: # Because we want to use inheritance correctly, we send all settings as seen from the per object stack. for key in stack.getAllKeys(): setting = message.addRepeatedMessage("settings") diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index 75cf522c14..482fb73b0f 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -127,7 +127,11 @@ Item { } } } - + Component.onCompleted: + { + // Ensure primary extruder is set as active + UM.ActiveTool.setProperty("SelectedActiveExtruder", extruders_model.getItem(0).id) + } onActivated: UM.ActiveTool.setProperty("SelectedActiveExtruder", extruders_model.getItem(index).id); onModelChanged: updateCurrentIndex(); From fa78cf2d397b6df5261ab5eeee111bc88ea4574e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 29 Jul 2016 15:44:34 +0200 Subject: [PATCH 50/66] No longer use brute force approach for per object CURA-1754 --- plugins/CuraEngineBackend/StartSliceJob.py | 23 ++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 6e7ccba6ee..8f4f47ef50 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -13,6 +13,7 @@ from UM.Scene.SceneNode import SceneNode from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Settings.Validator import ValidatorState +from UM.Settings.SettingRelation import RelationType from cura.OneAtATimeIterator import OneAtATimeIterator @@ -213,9 +214,27 @@ class StartSliceJob(Job): stack = node.callDecoration("getStack") # Check if the node has a stack attached to it and the stack has any settings in the top container. if stack: - # Because we want to use inheritance correctly, we send all settings as seen from the per object stack. - for key in stack.getAllKeys(): + # Check all settings for relations, so we can also calculate the correct values for dependant settings. + changed_setting_keys = set(stack.getTop().getAllKeys()) + for key in stack.getTop().getAllKeys(): + instance = stack.getTop().getInstance(key) + self._addRelations(changed_setting_keys, instance.definition.relations) + Job.yieldThread() + + # Ensure that the engine is aware what the build extruder is + changed_setting_keys.add("extruder_nr") + + # Get values for all changed settings + for key in changed_setting_keys: setting = message.addRepeatedMessage("settings") setting.name = key setting.value = str(stack.getProperty(key, "value")).encode("utf-8") Job.yieldThread() + + def _addRelations(self, relations_set, relations): + for relation in filter(lambda r: r.role == "value", relations): + if relation.type == RelationType.RequiresTarget: + continue + + relations_set.add(relation.target.key) + self._addRelations(relations_set, relation.target.relations) \ No newline at end of file From 34840d42bdb2ef9ba2bd6b60e82ab2a6bc1726ef Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 29 Jul 2016 16:38:27 +0200 Subject: [PATCH 51/66] Sort printers by category instead of by manufacturer Always show Ultimaker on top. CURA-1593 --- resources/qml/AddMachineDialog.qml | 65 ++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/resources/qml/AddMachineDialog.qml b/resources/qml/AddMachineDialog.qml index 38221030ea..350435d1ca 100644 --- a/resources/qml/AddMachineDialog.qml +++ b/resources/qml/AddMachineDialog.qml @@ -16,12 +16,23 @@ UM.Dialog { id: base title: catalog.i18nc("@title:window", "Add Printer") - property string activeManufacturer: "Ultimaker"; + property string preferredCategory: "Ultimaker" + property string activeCategory: preferredCategory + + onVisibilityChanged: + { + // Reset selection and machine name + if (visible) { + activeCategory = preferredCategory; + machineList.currentIndex = 0; + machineName.text = getMachineName(); + } + } signal machineAdded(string id) function getMachineName() { - var name = machineList.model.getItem(machineList.currentIndex).name + var name = machineList.model.get(machineList.currentIndex).name return name } @@ -36,16 +47,32 @@ UM.Dialog right: parent.right; bottom: parent.bottom; } + ListView { id: machineList - model: UM.DefinitionContainersModel + model: ListModel { - id: machineDefinitionsModel - filter: {"visible":true} + id: sortedMachineDefinitionsModel + Component.onCompleted: { + // DefinitionContainersModel is sorted alphabetically, but we want the preferred + // category on top so we create a custom-sorted ListModel from it. + var items = []; + for(var i in machineDefinitionsModel.items) { + var item = machineDefinitionsModel.getItem(i); + if (item["category"] == preferredCategory) + sortedMachineDefinitionsModel.append(item); + else + items.push(item); + } + for(var i in items) { + sortedMachineDefinitionsModel.append(items[i]); + } + } } - section.property: "manufacturer" + + section.property: "category" section.delegate: Button { text: section @@ -76,16 +103,25 @@ UM.Dialog sourceSize.width: width sourceSize.height: width color: palette.windowText - source: base.activeManufacturer == section ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_right") + source: base.activeCategory == section ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_right") } } } onClicked: { - base.activeManufacturer = section; - machineList.currentIndex = machineList.model.find("manufacturer", section) - machineName.text = getMachineName() + base.activeCategory = section; + if (machineList.model.get(machineList.currentIndex).category != section) { + // Find the first machine from this category + for(var i = 0; i < sortedMachineDefinitionsModel.count; i++) { + var item = sortedMachineDefinitionsModel.get(i); + if (item.category == section) { + machineList.currentIndex = i; + break; + } + } + } + machineName.text = getMachineName(); } } @@ -114,7 +150,7 @@ UM.Dialog states: State { name: "collapsed"; - when: base.activeManufacturer != model.manufacturer; + when: base.activeCategory != model.category; PropertyChanges { target: machineButton; opacity: 0; height: 0; } } @@ -161,7 +197,7 @@ UM.Dialog onClicked: { base.visible = false - var item = machineList.model.getItem(machineList.currentIndex); + var item = machineList.model.get(machineList.currentIndex); Cura.MachineManager.addMachine(machineName.text, item.id) base.machineAdded(item.id) // Emit signal that the user added a machine. } @@ -174,6 +210,11 @@ UM.Dialog id: catalog; name: "cura"; } + UM.DefinitionContainersModel + { + id: machineDefinitionsModel + filter: { "visible": true } + } SystemPalette { id: palette } ExclusiveGroup { id: printerGroup; } } From 0a75aa7496bc78439180bb7eb4120ea5401da13a Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 29 Jul 2016 16:40:58 +0200 Subject: [PATCH 52/66] Fix category of PrintrBot simple definition CURA-1593 --- resources/definitions/printrbot_simple.def.json | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/definitions/printrbot_simple.def.json b/resources/definitions/printrbot_simple.def.json index 0116ba6244..a1963fe20e 100644 --- a/resources/definitions/printrbot_simple.def.json +++ b/resources/definitions/printrbot_simple.def.json @@ -7,6 +7,7 @@ "visible": true, "author": "Calvindog717", "manufacturer": "PrintrBot", + "category": "Other", "file_formats": "text/x-gcode" }, From 228f5be35aa3143feff35d96f55d3d5ba15a65f5 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 29 Jul 2016 17:13:30 +0200 Subject: [PATCH 53/66] Fix order of machine actions on manage printers page MachineActionManager now uses lists instead of dicts because dicts don't maintain the order. --- cura/MachineActionManager.py | 10 ++++++---- resources/definitions/ultimaker_original.def.json | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cura/MachineActionManager.py b/cura/MachineActionManager.py index b50bb95e7f..6061a2e49b 100644 --- a/cura/MachineActionManager.py +++ b/cura/MachineActionManager.py @@ -57,9 +57,10 @@ class MachineActionManager(QObject): def addRequiredAction(self, definition_id, action_key): if action_key in self._machine_actions: if definition_id in self._required_actions: - self._required_actions[definition_id] |= {self._machine_actions[action_key]} + if self._machine_actions[action_key] not in self._required_actions[definition_id]: + self._required_actions[definition_id].append(self._machine_actions[action_key]) else: - self._required_actions[definition_id] = {self._machine_actions[action_key]} + self._required_actions[definition_id] = [self._machine_actions[action_key]] else: raise UnknownMachineActionError("Action %s, which is required for %s is not known." % (action_key, definition_id)) @@ -67,9 +68,10 @@ class MachineActionManager(QObject): def addSupportedAction(self, definition_id, action_key): if action_key in self._machine_actions: if definition_id in self._supported_actions: - self._supported_actions[definition_id] |= {self._machine_actions[action_key]} + if self._machine_actions[action_key] not in self._supported_actions[definition_id]: + self._supported_actions[definition_id].append(self._machine_actions[action_key]) else: - self._supported_actions[definition_id] = {self._machine_actions[action_key]} + self._supported_actions[definition_id] = [self._machine_actions[action_key]] else: Logger.log("w", "Unable to add %s to %s, as the action is not recognised", action_key, definition_id) diff --git a/resources/definitions/ultimaker_original.def.json b/resources/definitions/ultimaker_original.def.json index d2174ece80..0815aeee02 100644 --- a/resources/definitions/ultimaker_original.def.json +++ b/resources/definitions/ultimaker_original.def.json @@ -15,7 +15,7 @@ "preferred_material": "*pla*", "preferred_quality": "*normal*", "first_start_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel"], - "supported_actions": ["UMOCheckup", "UpgradeFirmware", "BedLevel", "UMOUpgradeSelection"] + "supported_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel", "UpgradeFirmware"] }, "overrides": { From 562363ec16fb7a93048c37285ffaa6e8a2a1d075 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 29 Jul 2016 17:24:12 +0200 Subject: [PATCH 54/66] Updated documentation --- plugins/CuraEngineBackend/StartSliceJob.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 8f4f47ef50..2fd7d110bf 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -25,6 +25,7 @@ class StartJobResult(IntEnum): SettingError = 3 NothingToSlice = 4 + ## Formatter class that handles token expansion in start/end gcod class GcodeStartEndFormatter(Formatter): def get_value(self, key, args, kwargs): # [CodeStyle: get_value is an overridden function from the Formatter class] @@ -38,6 +39,7 @@ class GcodeStartEndFormatter(Formatter): Logger.log("w", "Incorrectly formatted placeholder '%s' in start/end gcode", key) return "{" + str(key) + "}" + ## Job class that builds up the message of scene data to send to CuraEngine. class StartSliceJob(Job): def __init__(self, slice_message): @@ -124,6 +126,9 @@ class StartSliceJob(Job): if temp_list: object_groups.append(temp_list) + # There are cases when there is nothing to slice. This can happen due to one at a time slicing not being + # able to find a possible sequence or because there are no objects on the build plate (or they are outside + # the build volume) if not object_groups: self.setResult(StartJobResult.NothingToSlice) return @@ -172,6 +177,7 @@ class StartSliceJob(Job): Logger.logException("w", "Unable to do token replacement on start/end gcode") return str(value).encode("utf-8") + ## Create extruder message from stack def _buildExtruderMessage(self, stack): message = self._slice_message.addRepeatedMessage("extruders") message.id = int(stack.getMetaDataEntry("position")) @@ -210,6 +216,9 @@ class StartSliceJob(Job): else: setting_message.value = str(value).encode("utf-8") + ## Check if a node has per object settings and ensure that they are set correctly in the message + # \param node \type{SceneNode} Node to check. + # \param message object_lists message to put the per object settings in def _handlePerObjectSettings(self, node, message): stack = node.callDecoration("getStack") # Check if the node has a stack attached to it and the stack has any settings in the top container. @@ -231,6 +240,9 @@ class StartSliceJob(Job): setting.value = str(stack.getProperty(key, "value")).encode("utf-8") Job.yieldThread() + ## Recursive function to put all settings that require eachother for value changes in a list + # \param relations_set \type{set} Set of keys (strings) of settings that are influenced + # \param relations list of relation objects that need to be checked. def _addRelations(self, relations_set, relations): for relation in filter(lambda r: r.role == "value", relations): if relation.type == RelationType.RequiresTarget: From 4a5b2465aec1dd9980ffbc0adade06172ca59a53 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 29 Jul 2016 17:25:36 +0200 Subject: [PATCH 55/66] Fixed process sliced layer job not deleting old data --- plugins/CuraEngineBackend/ProcessSlicedLayersJob.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py index c2f73cf5b7..3bdcd60009 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py @@ -56,10 +56,9 @@ class ProcessSlicedLayersJob(Job): ## Remove old layer data (if any) for node in DepthFirstIterator(self._scene.getRoot()): - if type(node) is SceneNode and node.getMeshData(): - if node.callDecoration("getLayerData"): - self._scene.getRoot().removeChild(node) - Job.yieldThread() + if node.callDecoration("getLayerData"): + node.getParent().removeChild(node) + break if self._abort_requested: if self._progress: self._progress.hide() From 03aa9cf8d3b7e6b74749709e56f4483387c6200b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 29 Jul 2016 17:28:58 +0200 Subject: [PATCH 56/66] Codestyle --- .../CuraEngineBackend/CuraEngineBackend.py | 54 ++++++++++--------- .../ProcessSlicedLayersJob.py | 20 ++++--- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 73054fb4dc..6b74887036 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -42,7 +42,8 @@ class CuraEngineBackend(Backend): def __init__(self): super().__init__() - # Find out where the engine is located, and how it is called. This depends on how Cura is packaged and which OS we are running on. + # Find out where the engine is located, and how it is called. + # This depends on how Cura is packaged and which OS we are running on. default_engine_location = os.path.join(Application.getInstallPrefix(), "bin", "CuraEngine") if hasattr(sys, "frozen"): default_engine_location = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "CuraEngine") @@ -61,7 +62,7 @@ class CuraEngineBackend(Backend): self._stored_layer_data = [] self._stored_optimized_layer_data = [] - #Triggers for when to (re)start slicing: + # Triggers for when to (re)start slicing: self._global_container_stack = None Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged) self._onGlobalStackChanged() @@ -70,15 +71,15 @@ class CuraEngineBackend(Backend): cura.Settings.ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderChanged) self._onActiveExtruderChanged() - #When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired. - #This timer will group them up, and only slice for the last setting changed signal. - #TODO: Properly group propertyChanged signals by whether they are triggered by the same user interaction. + # When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired. + # This timer will group them up, and only slice for the last setting changed signal. + # TODO: Properly group propertyChanged signals by whether they are triggered by the same user interaction. self._change_timer = QTimer() self._change_timer.setInterval(500) self._change_timer.setSingleShot(True) self._change_timer.timeout.connect(self.slice) - #Listeners for receiving messages from the back-end. + # Listeners for receiving messages from the back-end. self._message_handlers["cura.proto.Layer"] = self._onLayerMessage self._message_handlers["cura.proto.LayerOptimized"] = self._onOptimizedLayerMessage self._message_handlers["cura.proto.Progress"] = self._onProgressMessage @@ -88,19 +89,19 @@ class CuraEngineBackend(Backend): self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage self._start_slice_job = None - self._slicing = False #Are we currently slicing? - self._restart = False #Back-end is currently restarting? - self._enabled = True #Should we be slicing? Slicing might be paused when, for instance, the user is dragging the mesh around. - self._always_restart = True #Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness. - self._process_layers_job = None #The currently active job to process layers, or None if it is not processing layers. + self._slicing = False # Are we currently slicing? + self._restart = False # Back-end is currently restarting? + self._enabled = True # Should we be slicing? Slicing might be paused when, for instance, the user is dragging the mesh around. + self._always_restart = True # Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness. + self._process_layers_job = None # The currently active job to process layers, or None if it is not processing layers. - self._backend_log_max_lines = 20000 # Maximum number of lines to buffer - self._error_message = None #Pop-up message that shows errors. + self._backend_log_max_lines = 20000 # Maximum number of lines to buffer + self._error_message = None # Pop-up message that shows errors. self.backendQuit.connect(self._onBackendQuit) self.backendConnected.connect(self._onBackendConnected) - #When a tool operation is in progress, don't slice. So we need to listen for tool operations. + # When a tool operation is in progress, don't slice. So we need to listen for tool operations. Application.getInstance().getController().toolOperationStarted.connect(self._onToolOperationStarted) Application.getInstance().getController().toolOperationStopped.connect(self._onToolOperationStopped) @@ -121,7 +122,8 @@ class CuraEngineBackend(Backend): json_path = Resources.getPath(Resources.DefinitionContainers, "fdmprinter.def.json") return [Preferences.getInstance().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", json_path, ""] - ## Emitted when we get a message containing print duration and material amount. This also implies the slicing has finished. + ## Emitted when we get a message containing print duration and material amount. + # This also implies the slicing has finished. # \param time The amount of time the print will take. # \param material_amount The amount of material the print will use. printDurationMessage = Signal() @@ -135,7 +137,7 @@ class CuraEngineBackend(Backend): ## Perform a slice of the scene. def slice(self): self._slice_start_time = time() - if not self._enabled or not self._global_container_stack: #We shouldn't be slicing. + if not self._enabled or not self._global_container_stack: # We shouldn't be slicing. # try again in a short time self._change_timer.start() return @@ -145,10 +147,10 @@ class CuraEngineBackend(Backend): self._stored_layer_data = [] self._stored_optimized_layer_data = [] - if self._slicing: #We were already slicing. Stop the old job. + if self._slicing: # We were already slicing. Stop the old job. self._terminate() - if self._process_layers_job: #We were processing layers. Stop that, the layers are going to change soon. + if self._process_layers_job: # We were processing layers. Stop that, the layers are going to change soon. self._process_layers_job.abort() self._process_layers_job = None @@ -185,7 +187,7 @@ class CuraEngineBackend(Backend): self._process.terminate() Logger.log("d", "Engine process is killed. Received return code %s", self._process.wait()) self._process = None - except Exception as e: # terminating a process that is already terminating causes an exception, silently ignore this. + except Exception as e: # terminating a process that is already terminating causes an exception, silently ignore this. Logger.log("d", "Exception occurred while trying to kill the engine %s", str(e)) ## Event handler to call when the job to initiate the slicing process is @@ -267,7 +269,7 @@ class CuraEngineBackend(Backend): # \param instance The setting instance that has changed. # \param property The property of the setting instance that has changed. def _onSettingChanged(self, instance, property): - if property == "value": #Only reslice if the value has changed. + if property == "value": # Only reslice if the value has changed. self._onChanged() ## Called when a sliced layer data message is received from the engine. @@ -318,7 +320,7 @@ class CuraEngineBackend(Backend): ## Called when a print time message is received from the engine. # - # \param message The protobuf message containing the print time and + # \param message The protobuff message containing the print time and # material amount per extruder def _onPrintTimeMaterialEstimates(self, message): material_amounts = [] @@ -353,8 +355,8 @@ class CuraEngineBackend(Backend): # # \param tool The tool that the user is using. def _onToolOperationStarted(self, tool): - self._terminate() # Do not continue slicing once a tool has started - self._enabled = False # Do not reslice when a tool is doing it's 'thing' + self._terminate() # Do not continue slicing once a tool has started + self._enabled = False # Do not reslice when a tool is doing it's 'thing' ## Called when the user stops using some tool. # @@ -362,13 +364,13 @@ class CuraEngineBackend(Backend): # # \param tool The tool that the user was using. def _onToolOperationStopped(self, tool): - self._enabled = True # Tool stop, start listening for changes again. + self._enabled = True # Tool stop, start listening for changes again. ## Called when the user changes the active view mode. def _onActiveViewChanged(self): if Application.getInstance().getController().getActiveView(): view = Application.getInstance().getController().getActiveView() - if view.getPluginId() == "LayerView": #If switching to layer view, we should process the layers if that hasn't been done yet. + if view.getPluginId() == "LayerView": # If switching to layer view, we should process the layers if that hasn't been done yet. self._layer_view_active = True # There is data and we're not slicing at the moment # if we are slicing, there is no need to re-calculate the data as it will be invalid in a moment. @@ -401,7 +403,7 @@ class CuraEngineBackend(Backend): self._global_container_stack = Application.getInstance().getGlobalContainerStack() if self._global_container_stack: - self._global_container_stack.propertyChanged.connect(self._onSettingChanged) #Note: Only starts slicing when the value changed. + self._global_container_stack.propertyChanged.connect(self._onSettingChanged) # Note: Only starts slicing when the value changed. self._global_container_stack.containersChanged.connect(self._onChanged) extruders = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())) if extruders: diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py index 3bdcd60009..7443340c5b 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py @@ -73,7 +73,7 @@ class ProcessSlicedLayersJob(Job): # instead simply offset all other layers so the lowest layer is always 0. min_layer_number = 0 for layer in self._layers: - if(layer.id < min_layer_number): + if layer.id < min_layer_number: min_layer_number = layer.id current_layer = 0 @@ -97,7 +97,7 @@ class ProcessSlicedLayersJob(Job): points = numpy.fromstring(polygon.points, dtype="f4") # Convert bytearray to numpy array if polygon.point_type == 0: # Point2D points = points.reshape((-1,2)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly. - else: # Point3D + else: # Point3D points = points.reshape((-1,3)) line_widths = numpy.fromstring(polygon.line_width, dtype="f4") # Convert bytearray to numpy array @@ -107,15 +107,14 @@ class ProcessSlicedLayersJob(Job): # This uses manual array creation + copy rather than numpy.insert since this is # faster. new_points = numpy.empty((len(points), 3), numpy.float32) - if polygon.point_type == 0: # Point2D - new_points[:,0] = points[:,0] - new_points[:,1] = layer.height/1000 # layer height value is in backend representation - new_points[:,2] = -points[:,1] + if polygon.point_type == 0: # Point2D + new_points[:, 0] = points[:, 0] + new_points[:, 1] = layer.height / 1000 # layer height value is in backend representation + new_points[:, 2] = -points[:, 1] else: # Point3D - new_points[:,0] = points[:,0] - new_points[:,1] = points[:,2] - new_points[:,2] = -points[:,1] - + new_points[:, 0] = points[:, 0] + new_points[:, 1] = points[:, 2] + new_points[:, 2] = -points[:, 1] this_poly = LayerPolygon.LayerPolygon(layer_data, extruder, line_types, new_points, line_widths) this_poly.buildCache() @@ -184,4 +183,3 @@ class ProcessSlicedLayersJob(Job): else: if self._progress: self._progress.hide() - From 6e3488142e87b470d04c935e0de8c6b90159eaae Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 29 Jul 2016 17:32:57 +0200 Subject: [PATCH 57/66] Removed lifetime from slice error messages so they use the (longer) default --- plugins/CuraEngineBackend/CuraEngineBackend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 6b74887036..a822512218 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -208,7 +208,7 @@ class CuraEngineBackend(Backend): if job.getResult() == StartSliceJob.StartJobResult.SettingError: if Application.getInstance().getPlatformActivity: - self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. Please check your setting values for errors."), lifetime = 10) + self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. Please check your setting values for errors.")) self._error_message.show() self.backendStateChange.emit(BackendState.Error) else: @@ -217,7 +217,7 @@ class CuraEngineBackend(Backend): if job.getResult() == StartSliceJob.StartJobResult.NothingToSlice: if Application.getInstance().getPlatformActivity: - self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. No suitable objects found."), lifetime = 10) + self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. No suitable objects found.")) self._error_message.show() self.backendStateChange.emit(BackendState.Error) else: From 1182a3c87e907da16c965ed7620bb3cc2ecd9afc Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 29 Jul 2016 17:36:07 +0200 Subject: [PATCH 58/66] Fix render color for single-extrusion printers CURA-1992 --- plugins/SolidView/SolidView.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py index 2182a0748a..1e9721169a 100644 --- a/plugins/SolidView/SolidView.py +++ b/plugins/SolidView/SolidView.py @@ -57,25 +57,28 @@ class SolidView(View): # renderer.queueNode(scene.getRoot(), mesh = node.getBoundingBoxMesh(),mode = Renderer.RenderLines) uniforms = {} - if self._extruders_model.rowCount() > 0: + if self._extruders_model.rowCount() == 0: + material = Application.getInstance().getGlobalContainerStack().findContainer({ "type": "material" }) + material_color = material.getMetaDataEntry("color_code", default = self._extruders_model.defaultColours[0]) if material else self._extruders_model.defaultColours[0] + else: # Get color to render this mesh in from ExtrudersModel extruder_index = 0 extruder_id = node.callDecoration("getActiveExtruder") if extruder_id: extruder_index = max(0, self._extruders_model.find("id", extruder_id)) - extruder_color = self._extruders_model.getItem(extruder_index)["colour"] - try: - # Colors are passed as rgb hex strings (eg "#ffffff"), and the shader needs - # an rgba list of floats (eg [1.0, 1.0, 1.0, 1.0]) - uniforms["diffuse_color"] = [ - int(extruder_color[1:3], 16) / 255, - int(extruder_color[3:5], 16) / 255, - int(extruder_color[5:7], 16) / 255, - 1.0 - ] - except ValueError: - pass + material_color = self._extruders_model.getItem(extruder_index)["colour"] + try: + # Colors are passed as rgb hex strings (eg "#ffffff"), and the shader needs + # an rgba list of floats (eg [1.0, 1.0, 1.0, 1.0]) + uniforms["diffuse_color"] = [ + int(material_color[1:3], 16) / 255, + int(material_color[3:5], 16) / 255, + int(material_color[5:7], 16) / 255, + 1.0 + ] + except ValueError: + pass if hasattr(node, "_outside_buildarea"): if node._outside_buildarea: From 102a0793e6982d29fe952d7af01f57789f3de144 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 29 Jul 2016 17:38:35 +0200 Subject: [PATCH 59/66] Codestyle & documentation --- plugins/LayerView/LayerView.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index 358a357d3e..f60d492ec7 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -11,6 +11,7 @@ from UM.Math.Color import Color from UM.Mesh.MeshBuilder import MeshBuilder from UM.Job import Job from UM.Preferences import Preferences +from UM.Logger import Logger from UM.View.RenderBatch import RenderBatch from UM.View.GL.OpenGL import OpenGL @@ -34,7 +35,7 @@ class LayerView(View): self._shader = None self._selection_shader = None self._num_layers = 0 - self._layer_percentage = 0 # what percentage of layers need to be shown (SLider gives value between 0 - 100) + self._layer_percentage = 0 # what percentage of layers need to be shown (Slider gives value between 0 - 100) self._proxy = LayerViewProxy.LayerViewProxy() self._controller.getScene().getRoot().childrenChanged.connect(self._onSceneChanged) self._max_layers = 0 @@ -43,6 +44,7 @@ class LayerView(View): self._current_layer_jumps = None self._top_layers_job = None self._activity = False + self._old_max_layers = 0 Preferences.getInstance().addPreference("view/top_layer_count", 5) Preferences.getInstance().addPreference("view/only_show_top_layers", False) @@ -133,7 +135,6 @@ class LayerView(View): def calculateMaxLayers(self): scene = self.getController().getScene() - renderer = self.getRenderer() # TODO: @UnusedVariable self._activity = True self._old_max_layers = self._max_layers @@ -215,6 +216,7 @@ class LayerView(View): self._startUpdateTopLayers() + class _CreateTopLayersJob(Job): def __init__(self, scene, layer_number, solid_layers): super().__init__() @@ -242,20 +244,20 @@ class _CreateTopLayersJob(Job): try: layer = layer_data.getLayer(layer_number).createMesh() - except Exception as e: - print(e) + except Exception: + Logger.logException("w", "An exception occurred while creating layer mesh.") return if not layer or layer.getVertices() is None: continue - layer_mesh.addIndices(layer_mesh._vertex_count+layer.getIndices()) + layer_mesh.addIndices(layer_mesh.getVertexCount() + layer.getIndices()) layer_mesh.addVertices(layer.getVertices()) # Scale layer color by a brightness factor based on the current layer number # This will result in a range of 0.5 - 1.0 to multiply colors by. - brightness = numpy.ones((1,4), dtype=numpy.float32) * (2.0 - (i / self._solid_layers)) / 2.0 - brightness[0, 3] = 1.0; + brightness = numpy.ones((1, 4), dtype=numpy.float32) * (2.0 - (i / self._solid_layers)) / 2.0 + brightness[0, 3] = 1.0 layer_mesh.addColors(layer.getColors() * brightness) if self._cancel: @@ -271,7 +273,7 @@ class _CreateTopLayersJob(Job): if not jump_mesh or jump_mesh.getVertices() is None: jump_mesh = None - self.setResult({ "layers": layer_mesh.build(), "jumps": jump_mesh }) + self.setResult({"layers": layer_mesh.build(), "jumps": jump_mesh}) def cancel(self): self._cancel = True From ed1d12d21d7d3e6cc2af69eae83083fef6e63b60 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 29 Jul 2016 17:43:38 +0200 Subject: [PATCH 60/66] Hide linked settings icon for single extrusion printers CURA-2020 --- resources/qml/Settings/SettingItem.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Settings/SettingItem.qml b/resources/qml/Settings/SettingItem.qml index eb6c37f73c..dae9953690 100644 --- a/resources/qml/Settings/SettingItem.qml +++ b/resources/qml/Settings/SettingItem.qml @@ -138,7 +138,7 @@ Item { { id: linkedSettingIcon; - visible: base.settablePerExtruder != "True" && base.showLinkedSettingIcon + visible: Cura.MachineManager.activeStackId != Cura.MachineManager.activeMachineId && base.settablePerExtruder != "True" && base.showLinkedSettingIcon height: parent.height; width: height; From 74d21a9053f5f6eb4c7093eb3348cc29d2031c9a Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 29 Jul 2016 19:15:05 +0200 Subject: [PATCH 61/66] Only draw boundingbox wireframe on selected groups CURA-1492 --- plugins/SolidView/SolidView.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py index 1e9721169a..0329fb53e2 100644 --- a/plugins/SolidView/SolidView.py +++ b/plugins/SolidView/SolidView.py @@ -3,6 +3,7 @@ from UM.View.View import View from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator +from UM.Scene.Selection import Selection from UM.Resources import Resources from UM.Application import Application from UM.Preferences import Preferences @@ -87,7 +88,7 @@ class SolidView(View): renderer.queueNode(node, shader = self._enabled_shader, uniforms = uniforms) else: renderer.queueNode(node, material = self._enabled_shader, uniforms = uniforms) - if node.callDecoration("isGroup"): + if node.callDecoration("isGroup") and Selection.isSelected(node): renderer.queueNode(scene.getRoot(), mesh = node.getBoundingBoxMesh(), mode = Renderer.RenderLines) def endRendering(self): From e01c8a4f106f69583280d12ac8642b2177e36d72 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 29 Jul 2016 19:25:40 +0200 Subject: [PATCH 62/66] Add Select All functionality --- cura/CuraApplication.py | 18 +++++++++++++++++- resources/qml/Actions.qml | 11 +++++++++++ resources/qml/Cura.qml | 3 +++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index a646d687f6..53d509b71c 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -633,7 +633,23 @@ class CuraApplication(QtApplication): if node: op = SetTransformOperation(node, Vector()) op.push() - + + ## Select all nodes containing mesh data in the scene. + @pyqtSlot() + def selectAll(self): + if not self.getController().getToolsEnabled(): + return + + Selection.clear() + for node in DepthFirstIterator(self.getController().getScene().getRoot()): + if type(node) is not SceneNode: + continue + if not node.getMeshData() and not node.callDecoration("isGroup"): + continue # Node that doesnt have a mesh and is not a group. + if node.getParent() and node.getParent().callDecoration("isGroup"): + continue # Grouped nodes don't need resetting as their parent (the group) is resetted) + Selection.add(node) + ## Delete all nodes containing mesh data in the scene. @pyqtSlot() def deleteAll(self): diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index c443c38875..ed88433e33 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -27,6 +27,7 @@ Item property alias multiplyObject: multiplyObjectAction; + property alias selectAll: selectAllAction; property alias deleteAll: deleteAllAction; property alias reloadAll: reloadAllAction; property alias resetAllTranslation: resetAllTranslationAction; @@ -230,6 +231,16 @@ Item iconName: "edit-duplicate" } + Action + { + id: selectAllAction; + text: catalog.i18nc("@action:inmenu menubar:edit","&Select All Objects"); + enabled: UM.Controller.toolsEnabled; + iconName: "edit-select-all"; + shortcut: "Ctrl+A"; + onTriggered: Printer.selectAll(); + } + Action { id: deleteAllAction; diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 45137db5cc..ae72353e0e 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -118,6 +118,7 @@ UM.MainWindow MenuItem { action: Cura.Actions.undo; } MenuItem { action: Cura.Actions.redo; } MenuSeparator { } + MenuItem { action: Cura.Actions.selectAll; } MenuItem { action: Cura.Actions.deleteSelection; } MenuItem { action: Cura.Actions.deleteAll; } MenuItem { action: Cura.Actions.resetAllTranslation; } @@ -537,6 +538,7 @@ UM.MainWindow MenuItem { action: Cura.Actions.deleteObject; } MenuItem { action: Cura.Actions.multiplyObject; } MenuSeparator { } + MenuItem { action: Cura.Actions.selectAll; } MenuItem { action: Cura.Actions.deleteAll; } MenuItem { action: Cura.Actions.reloadAll; } MenuItem { action: Cura.Actions.resetAllTranslation; } @@ -589,6 +591,7 @@ UM.MainWindow Menu { id: contextMenu; + MenuItem { action: Cura.Actions.selectAll; } MenuItem { action: Cura.Actions.deleteAll; } MenuItem { action: Cura.Actions.reloadAll; } MenuItem { action: Cura.Actions.resetAllTranslation; } From fb96950762c4e5235dd12fd071a95c5ef02dd70d Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 29 Jul 2016 21:50:49 +0200 Subject: [PATCH 63/66] Don't set extruder_nr per object for single extrusion printers CURA-1754 --- plugins/CuraEngineBackend/StartSliceJob.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 2fd7d110bf..a96b9ef857 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -231,7 +231,8 @@ class StartSliceJob(Job): Job.yieldThread() # Ensure that the engine is aware what the build extruder is - changed_setting_keys.add("extruder_nr") + if stack.getProperty("machine_extruder_count", "value") > 1: + changed_setting_keys.add("extruder_nr") # Get values for all changed settings for key in changed_setting_keys: From b21a1f311acaeaeb4760b9edf632af92e1c6c12a Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 29 Jul 2016 16:22:18 +0200 Subject: [PATCH 64/66] Send all global_inherits_stack to engine Only the settings for which global_inherits_stack is set should be sent to the engine. Contributes to issue CURA-2011. --- plugins/CuraEngineBackend/Cura.proto | 9 ++++++++- plugins/CuraEngineBackend/StartSliceJob.py | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/plugins/CuraEngineBackend/Cura.proto b/plugins/CuraEngineBackend/Cura.proto index 0c4803cc19..289c0a98a0 100644 --- a/plugins/CuraEngineBackend/Cura.proto +++ b/plugins/CuraEngineBackend/Cura.proto @@ -13,6 +13,7 @@ message Slice repeated ObjectList object_lists = 1; // The meshgroups to be printed one after another SettingList global_settings = 2; // The global settings used for the whole print job repeated Extruder extruders = 3; // The settings sent to each extruder object + repeated SettingExtruder global_inherits_stack = 4; //From which stack the setting would inherit if not defined in a stack. } message Extruder @@ -108,8 +109,14 @@ message Setting { bytes value = 2; // The value of the setting } +message SettingExtruder { + string name = 1; //The setting key. + + int32 extruder = 2; //From which extruder stack the setting should inherit. +} + message GCodePrefix { - bytes data = 2; // Header string to be prenpended before the rest of the gcode sent from the engine + bytes data = 2; //Header string to be prepended before the rest of the g-code sent from the engine. } message SlicingFinished { diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index a96b9ef857..689346448d 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -134,6 +134,7 @@ class StartSliceJob(Job): return self._buildGlobalSettingsMessage(stack) + self._buildGlobalInheritsStackMessage(stack) for extruder_stack in cura.Settings.ExtruderManager.getInstance().getMachineExtruders(stack.getId()): self._buildExtruderMessage(extruder_stack) @@ -216,6 +217,21 @@ class StartSliceJob(Job): else: setting_message.value = str(value).encode("utf-8") + ## Sends for some settings which extruder they should fallback to if not + # set. + # + # This is only set for settings that have the global_inherits_stack + # property. + # + # \param stack The global stack with all settings, from which to read the + # global_inherits_stack property. + def _buildGlobalInheritsStackMessage(self, stack): + for key in stack.getAllKeys(): + if stack.hasProperty(key, "global_inherits_stack"): + setting_extruder = self._slice_message.addRepeatedMessage("global_inherits_stack") + setting_extruder.name = key + setting_extruder.extruder = int(stack.getProperty(key, "global_inherits_stack")) + ## Check if a node has per object settings and ensure that they are set correctly in the message # \param node \type{SceneNode} Node to check. # \param message object_lists message to put the per object settings in From c2201eb8149921eb9009b4577ab1bad2d0c8d556 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 29 Jul 2016 17:34:22 +0200 Subject: [PATCH 65/66] Send setting extruder only when extruder is valid Apparently hasProperty always returns True. Fine. I'll just check the values then... Contributes to issue CURA-2011. --- plugins/CuraEngineBackend/StartSliceJob.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 689346448d..5b948c90ab 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -227,10 +227,11 @@ class StartSliceJob(Job): # global_inherits_stack property. def _buildGlobalInheritsStackMessage(self, stack): for key in stack.getAllKeys(): - if stack.hasProperty(key, "global_inherits_stack"): + extruder = int(stack.getProperty(key, "global_inherits_stack")) + if extruder >= 0: #Set to a specific extruder. setting_extruder = self._slice_message.addRepeatedMessage("global_inherits_stack") setting_extruder.name = key - setting_extruder.extruder = int(stack.getProperty(key, "global_inherits_stack")) + setting_extruder.extruder = extruder ## Check if a node has per object settings and ensure that they are set correctly in the message # \param node \type{SceneNode} Node to check. From 96ac27d1c3d38a3323e85c267baf7262cfebaeb7 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 1 Aug 2016 10:50:04 +0200 Subject: [PATCH 66/66] Remove 3MF repair message CURA-1223 --- plugins/3MFReader/ThreeMFReader.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index 74db9fe946..ea3ca30118 100644 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -11,11 +11,6 @@ from UM.Scene.GroupDecorator import GroupDecorator from UM.Math.Quaternion import Quaternion from UM.Job import Job -from UM.Message import Message -from UM.i18n import i18nCatalog -catalog = i18nCatalog("cura") - - import math import zipfile @@ -123,12 +118,10 @@ class ThreeMFReader(MeshReader): except Exception as e: Logger.log("e", "exception occured in 3mf reader: %s", e) - try: # Selftest - There might be more functions that should fail + try: # Selftest - There might be more functions that should fail boundingBox = result.getBoundingBox() boundingBox.isValid() except: - message = Message(catalog.i18nc("@info:status", "Your 3MF file seems to be broken. Please visit https://modelrepair.azurewebsites.net/ and try to repair your model!"), lifetime = 0) - message.show() return None - - return result + + return result