From f00568ecd8be91f1e3c4019157410ce44c64da18 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Wed, 8 Jul 2015 17:02:42 +0200 Subject: [PATCH 01/22] feature: draft protection screen --- resources/settings/fdmprinter.json | 92 ++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index fc0ea6fa7c..bada17d09f 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -1280,6 +1280,98 @@ } } }, + "shield": { + "label": "Shielding", + "visible": true, + "icon": "category_shield", + "settings": { + "ooze_shield_enabled": { + "label": "Enable Ooze Shield", + "description": "Enable exterior ooze shield. This will create a shell around the object which is likely to wipe a second nozzle if it's at the same height as the first nozzle.", + "type": "boolean", + "default": false + }, + "ooze_shield_angle": { + "label": "Ooze Shield Angle", + "description": "The maximum angle a part in the ooze shield will have. With 0 degrees being vertical, and 90 degrees being horizontal. A smaller angle leads to less failed ooze shields, but more material.", + "unit": "°", + "type": "float", + "min_value": 0.0, + "max_value": 90.0, + "default": 60.0, + "visible": false, + "active_if": { + "setting": "ooze_shield_enabled", + "value": true + } + }, + "ooze_shield_dist": { + "label": "Ooze Shields Distance", + "description": "Distance of the ooze shield from the print, in the X/Y directions.", + "unit": "mm", + "type": "float", + "min_value": 0.0, + "max_value_warning": 30.0, + "default": 2.0, + "visible": false, + "active_if": { + "setting": "ooze_shield_enabled", + "value": true + } + }, + "draft_shield_enabled": { + "label": "Enable Draft Shield", + "description": "Enable exterior draft shield. This will create a wall around the object which traps (hot) air and shields against gusts of wind. Especially useful for materials which warp easily.", + "type": "boolean", + "default": false + }, + "draft_shield_dist": { + "label": "Draft Shield X/Y Distance", + "description": "Distance of the draft shield from the print, in the X/Y directions.", + "unit": "mm", + "type": "float", + "min_value": 0.0, + "max_value_warning": 100.0, + "default": 10.0, + "visible": false, + "active_if": { + "setting": "draft_shield_enabled", + "value": true + } + }, + "draft_shield_height_limitation": { + "label": "Draft Shield Limitation", + "description": "Whether to limit the height of the draft shield", + "type": "enum", + "options": [ + "Full", + "Limited" + ], + "default": "Full", + "visible": false, + "inherit_function": "Full", + "active_if": { + "setting": "draft_shield_enabled", + "value": true + } + }, + "draft_shield_height": { + "label": "Draft Shield Height", + "description": "Height limitation on the draft shield. Above this height no draft shield will be printed.", + "unit": "mm", + "type": "float", + "min_value": 0.0, + "max_value_warning": 30.0, + "default": 0.0, + "inherit_function": "9999 if draft_shield_height_limitation == 'Full' && draft_shield_enabled else 0.0", + "visible": false, + "active_if": { + "setting": "draft_shield_height_limitation", + "value": "Limited" + } + } + } + }, "support": { "label": "Support", "visible": true, From 53a94c23e17ec711b3723c6b58b17fd651ff573c Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Wed, 8 Jul 2015 18:01:03 +0200 Subject: [PATCH 02/22] Adds layer count Brings back the layer count, the min & max. And it does not display these untill Cura is done slicing. Fixes #117 --- plugins/LayerView/LayerView.py | 12 ++++-- plugins/LayerView/LayerView.qml | 3 +- plugins/LayerView/LayerViewProxy.py | 12 +++++- resources/themes/cura/styles.qml | 67 +++++++++++++++++++++++++++++ resources/themes/cura/theme.json | 1 + 5 files changed, 90 insertions(+), 5 deletions(-) diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index 617dda411a..13e389f247 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -27,9 +27,13 @@ class LayerView(View): self._max_layers = 10 self._current_layer_num = 10 self._current_layer_mesh = None + self._activity = False self._solid_layers = 5 + def getActivity(self): + return self._activity + def getCurrentLayer(self): return self._current_layer_num @@ -114,13 +118,14 @@ class LayerView(View): self._current_layer_mesh = None self.currentLayerNumChanged.emit() - + currentLayerNumChanged = Signal() - + def calculateMaxLayers(self): scene = self.getController().getScene() renderer = self.getRenderer() if renderer and self._material: + self._activity = True renderer.setRenderSelection(False) self._old_max_layers = self._max_layers ## Recalculate num max layers @@ -138,10 +143,11 @@ class LayerView(View): if new_max_layers > 0 and new_max_layers != self._old_max_layers: self._max_layers = new_max_layers self.maxLayersChanged.emit() + self._current_layer_num = self._max_layers # This makes sure we update the current layer self.setLayer(int(self._max_layers * (self._current_layer_num / self._old_max_layers))) - + maxLayersChanged = Signal() ## Hackish way to ensure the proxy is already created, which ensures that the layerview.qml is already created diff --git a/plugins/LayerView/LayerView.qml b/plugins/LayerView/LayerView.qml index f1d78d78f9..3f6e0deb37 100644 --- a/plugins/LayerView/LayerView.qml +++ b/plugins/LayerView/LayerView.qml @@ -18,6 +18,7 @@ Item width: 10 height: 250 anchors.right : parent.right + anchors.rightMargin: UM.Theme.sizes.default_margin.width * 2 orientation: Qt.Vertical minimumValue: 0; maximumValue: UM.LayerView.numLayers; @@ -26,6 +27,6 @@ Item value: UM.LayerView.currentLayer onValueChanged: UM.LayerView.setCurrentLayer(value) - style: UM.Theme.styles.slider; + style: UM.LayerView.getLayerActivity ? UM.Theme.styles.layerViewSlider : UM.Theme.styles.slider } } diff --git a/plugins/LayerView/LayerViewProxy.py b/plugins/LayerView/LayerViewProxy.py index b6a266233c..77672d1744 100644 --- a/plugins/LayerView/LayerViewProxy.py +++ b/plugins/LayerView/LayerViewProxy.py @@ -11,7 +11,13 @@ class LayerViewProxy(QObject): currentLayerChanged = pyqtSignal() maxLayersChanged = pyqtSignal() - + activityChanged = pyqtSignal() + + @pyqtProperty(bool, notify = activityChanged) + def getLayerActivity(self): + active_view = self._controller.getActiveView() + return active_view.getActivity() + @pyqtProperty(int, notify = maxLayersChanged) def numLayers(self): active_view = self._controller.getActiveView() @@ -30,9 +36,13 @@ class LayerViewProxy(QObject): active_view = self._controller.getActiveView() if type(active_view) == LayerView.LayerView.LayerView: active_view.setLayer(layer_num) + + def _layerActivityChanged(self): + self.activityChanged.emit() def _onLayerChanged(self): self.currentLayerChanged.emit() + self._layerActivityChanged() def _onMaxLayersChanged(self): self.maxLayersChanged.emit() diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index dedabd3b96..a46f36009d 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -272,6 +272,7 @@ QtObject { } handle: UM.AngledCornerRectangle { + id: scrollViewHandle implicitWidth: UM.Theme.sizes.scrollbar.width; cornerSize: UM.Theme.sizes.scrollbar.width; @@ -367,6 +368,72 @@ QtObject { } } + property Component layerViewSlider: Component { + SliderStyle { + groove: Rectangle { + id: layerSliderGroove + implicitWidth: control.width; + implicitHeight: UM.Theme.sizes.slider_groove.height; + + color: UM.Theme.colors.slider_groove; + border.width: 1; + border.color: UM.Theme.colors.slider_groove_border; + Rectangle { + anchors { + left: parent.left; + top: parent.top; + bottom: parent.bottom; + } + color: UM.Theme.colors.slider_groove_fill; + width: (control.value / (control.maximumValue - control.minimumValue)) * parent.width; + } + Label { + id: maxValueLabel + text: control.maximumValue + 1 + transformOrigin: Item.BottomLeft + rotation: 90 + x: parent.x + parent.width - maxValueLabel.height + y: parent.y + } + Label { + id: minValueLabel + text: '1' + transformOrigin: Item.BottomLeft + rotation: 90 + x: parent.x + y: parent.y + } + } + handle: Rectangle { + id: layerSliderControl + width: UM.Theme.sizes.slider_handle.width; + height: UM.Theme.sizes.slider_handle.height; + color: control.hovered ? UM.Theme.colors.slider_handle_hover : UM.Theme.colors.slider_handle; + Behavior on color { ColorAnimation { duration: 50; } } + Label { + id: valueLabel + text: control.value + 1 + anchors.bottom: layerSliderControl.bottom + anchors.right: layerSliderControl.left + anchors.bottomMargin: parent.width + UM.Theme.sizes.default_margin.width + transformOrigin: Item.BottomRight + rotation: 90 + Rectangle { + width: (parent.width + UM.Theme.sizes.tooltip_margins.width) < 35 ? 35 : parent.width + UM.Theme.sizes.tooltip_margins.width + height: parent.height + UM.Theme.sizes.tooltip_margins.height + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + z: parent.z - 1 + color: UM.Theme.colors.slider_text_background + border.width: 1 + border.color: UM.Theme.colors.slider_groove_fill; + + } + } + } + } + } + property Component text_field: Component { TextFieldStyle { textColor: UM.Theme.colors.setting_control_text; diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index 8cd574b22c..910081e829 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -100,6 +100,7 @@ "slider_groove_fill": [160, 163, 171, 255], "slider_handle": [12, 169, 227, 255], "slider_handle_hover": [34, 150, 190, 255], + "slider_text_background": [255, 255, 255, 255], "checkbox": [255, 255, 255, 255], "checkbox_hover": [245, 245, 245, 255], From ab07b0205f2eac284cf07ab4028459364390d370 Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Wed, 8 Jul 2015 18:05:50 +0200 Subject: [PATCH 03/22] tiniest detail concerning the padding of the layer count label Fixes #117 --- resources/themes/cura/styles.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index a46f36009d..91a8d512e0 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -420,7 +420,7 @@ QtObject { rotation: 90 Rectangle { width: (parent.width + UM.Theme.sizes.tooltip_margins.width) < 35 ? 35 : parent.width + UM.Theme.sizes.tooltip_margins.width - height: parent.height + UM.Theme.sizes.tooltip_margins.height + height: parent.height + (UM.Theme.sizes.tooltip_margins.height / 2) anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter z: parent.z - 1 From 43d1aead769c109e3e21a33127c321611c174346 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Thu, 9 Jul 2015 11:38:15 +0200 Subject: [PATCH 04/22] included retraction_extra_prime_amount --- resources/settings/fdmprinter.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index bada17d09f..95ef82bd72 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -673,6 +673,19 @@ "value": true } }, + "retraction_extra_prime_amount": { + "label": "Retraction Extra Prime Amount", + "description": "The amount of material extruded after unretracting. During a retracted travel material might get lost and so we need to compensate for this.", + "unit": "mm", + "type": "float", + "default": 0.0, + "visible": false, + "inherit": false, + "active_if": { + "setting": "retraction_enable", + "value": true + } + }, "retraction_min_travel": { "label": "Retraction Minimum Travel", "description": "The minimum distance of travel needed for a retraction to happen at all. This helps ensure you do not get a lot of retractions in a small area.", From ae89ef37c05058c0d1c375b64dc2977076f37d49 Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Thu, 9 Jul 2015 16:27:31 +0200 Subject: [PATCH 05/22] Enlarge the zone where you can use scrolling to look trough the layer mode. Also make it more visible fixes #118 --- plugins/LayerView/LayerView.py | 4 +++- plugins/LayerView/LayerView.qml | 30 ++++++++++++++++++++++++++++-- resources/themes/cura/styles.qml | 12 +++++++++--- resources/themes/cura/theme.json | 4 ++++ 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index 13e389f247..cad15047e3 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -146,9 +146,11 @@ class LayerView(View): self._current_layer_num = self._max_layers # This makes sure we update the current layer - self.setLayer(int(self._max_layers * (self._current_layer_num / self._old_max_layers))) + self.setLayer(int(self._max_layers)) + self.currentLayerNumChanged.emit() maxLayersChanged = Signal() + currentLayerNumChanged = Signal() ## Hackish way to ensure the proxy is already created, which ensures that the layerview.qml is already created # as this caused some issues. diff --git a/plugins/LayerView/LayerView.qml b/plugins/LayerView/LayerView.qml index 3f6e0deb37..4d0da440d1 100644 --- a/plugins/LayerView/LayerView.qml +++ b/plugins/LayerView/LayerView.qml @@ -15,10 +15,11 @@ Item Slider { + id: slider width: 10 height: 250 anchors.right : parent.right - anchors.rightMargin: UM.Theme.sizes.default_margin.width * 2 + anchors.rightMargin: UM.Theme.sizes.slider_layerview_margin.width orientation: Qt.Vertical minimumValue: 0; maximumValue: UM.LayerView.numLayers; @@ -27,6 +28,31 @@ Item value: UM.LayerView.currentLayer onValueChanged: UM.LayerView.setCurrentLayer(value) - style: UM.LayerView.getLayerActivity ? UM.Theme.styles.layerViewSlider : UM.Theme.styles.slider + style: UM.Theme.styles.layerViewSlider + } + Rectangle { + anchors.right: parent.right + y: -UM.Theme.sizes.slider_layerview_background_extension.height + z: slider.z - 1 + width: UM.Theme.sizes.button.width + height: UM.Theme.sizes.slider_layerview_background_extension.height + color: UM.Theme.colors.slider_text_background + } + UM.AngledCornerRectangle { + anchors.right : parent.right + anchors.verticalCenter: parent.verticalCenter + z: slider.z - 1 + cornerSize: UM.Theme.sizes.default_margin.width; + width: UM.Theme.sizes.slider_layerview_background.width + height: slider.height + UM.Theme.sizes.default_margin.height * 2 + color: UM.Theme.colors.slider_text_background + MouseArea { + id: sliderMouseArea + property double manualStepSize: slider.maximumValue / 11 + anchors.fill: parent + onWheel: { + slider.value = wheel.angleDelta.y < 0 ? slider.value - sliderMouseArea.manualStepSize : slider.value + sliderMouseArea.manualStepSize + } + } } } diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index 91a8d512e0..e40552cd32 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -368,7 +368,7 @@ QtObject { } } - property Component layerViewSlider: Component { + property Component layerViewSlider: Component { SliderStyle { groove: Rectangle { id: layerSliderGroove @@ -389,19 +389,23 @@ QtObject { } Label { id: maxValueLabel + visible: UM.LayerView.getLayerActivity ? true : false text: control.maximumValue + 1 + font: control.maximumValue > 998 ? UM.Theme.fonts.small : UM.Theme.fonts.default transformOrigin: Item.BottomLeft rotation: 90 x: parent.x + parent.width - maxValueLabel.height - y: parent.y + y: control.maximumValue > 998 ? parent.y + UM.Theme.sizes.slider_layerview_smalltext_margin.width : parent.y } Label { id: minValueLabel + visible: UM.LayerView.getLayerActivity ? true : false text: '1' + font: control.maximumValue > 998 ? UM.Theme.fonts.small : UM.Theme.fonts.default transformOrigin: Item.BottomLeft rotation: 90 x: parent.x - y: parent.y + y: control.maximumValue > 998 ? parent.y + UM.Theme.sizes.slider_layerview_smalltext_margin.width : parent.y } } handle: Rectangle { @@ -412,10 +416,12 @@ QtObject { Behavior on color { ColorAnimation { duration: 50; } } Label { id: valueLabel + visible: UM.LayerView.getLayerActivity ? true : false text: control.value + 1 anchors.bottom: layerSliderControl.bottom anchors.right: layerSliderControl.left anchors.bottomMargin: parent.width + UM.Theme.sizes.default_margin.width + font: UM.Theme.fonts.default transformOrigin: Item.BottomRight rotation: 90 Rectangle { diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index 910081e829..5b247045b8 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -155,6 +155,10 @@ "slider_groove": [0.5, 0.5], "slider_handle": [1.5, 1.5], + "slider_layerview_background": [6.0, 0.0], + "slider_layerview_smalltext_margin": [0.3, 0.00], + "slider_layerview_background_extension": [0.0, 2.2], + "slider_layerview_margin": [3.0, 3.0], "checkbox": [1.5, 1.5], From a83bcedb2261d9b4a16e2dbd58b6faa9bb611f11 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 9 Jul 2015 16:27:22 +0200 Subject: [PATCH 06/22] Catch errors when trying to close the connection thread Contributes to #82 --- plugins/USBPrinting/PrinterConnection.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/USBPrinting/PrinterConnection.py b/plugins/USBPrinting/PrinterConnection.py index a4867e656a..7ef6c759ac 100644 --- a/plugins/USBPrinting/PrinterConnection.py +++ b/plugins/USBPrinting/PrinterConnection.py @@ -255,7 +255,10 @@ class PrinterConnection(SignalEmitter): ## Close the printer connection def close(self): if self._connect_thread.isAlive(): - self._connect_thread.join() + try: + self._connect_thread.join() + except: + pass if self._serial is not None: self.setIsConnected(False) try: From 20874d88ad1d3f8e6ffbb97a64009f84ed0f8f68 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 9 Jul 2015 16:30:12 +0200 Subject: [PATCH 07/22] Properly close all open USB connections on shut down Contributes to #82 --- plugins/USBPrinting/USBPrinterManager.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/USBPrinting/USBPrinterManager.py b/plugins/USBPrinting/USBPrinterManager.py index cb06113f60..5b889d30ce 100644 --- a/plugins/USBPrinting/USBPrinterManager.py +++ b/plugins/USBPrinting/USBPrinterManager.py @@ -46,6 +46,8 @@ class USBPrinterManager(QObject, SignalEmitter, Extension): ## Add menu item to top menu of the application. self.setMenuName("Firmware") self.addMenuItem(i18n_catalog.i18n("Update Firmware"), self.updateAllFirmware) + + Application.getInstance().applicationShuttingDown.connect(self._onApplicationShuttingDown) pyqtError = pyqtSignal(str, arguments = ["error"]) processingProgress = pyqtSignal(float, arguments = ["amount"]) @@ -292,3 +294,7 @@ class USBPrinterManager(QObject, SignalEmitter, Extension): else: base_list = base_list + glob.glob("/dev/ttyUSB*") + glob.glob("/dev/ttyACM*") + glob.glob("/dev/cu.*") + glob.glob("/dev/tty.usb*") + glob.glob("/dev/rfcomm*") + glob.glob("/dev/serial/by-id/*") return base_list + + def _onApplicationShuttingDown(self): + for connection in self._printer_connections: + connection.close() From b458a4c6e35aa6ecdd626f044b6fdbc0efdc44d4 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 9 Jul 2015 16:31:06 +0200 Subject: [PATCH 08/22] Correct a copy-paste error in getConnectionList Contributes to #82 --- plugins/USBPrinting/USBPrinterManager.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/USBPrinting/USBPrinterManager.py b/plugins/USBPrinting/USBPrinterManager.py index 5b889d30ce..0609869059 100644 --- a/plugins/USBPrinting/USBPrinterManager.py +++ b/plugins/USBPrinting/USBPrinterManager.py @@ -282,17 +282,17 @@ class USBPrinterManager(QObject, SignalEmitter, Extension): i = 0 while True: values = winreg.EnumValue(key, i) - if not base_list or "USBSER" in values[0]: + if not only_list_usb or "USBSER" in values[0]: base_list += [values[1]] i += 1 except Exception as e: pass - - if base_list: - base_list = base_list + glob.glob("/dev/ttyUSB*") + glob.glob("/dev/ttyACM*") + glob.glob("/dev/cu.usb*") - base_list = filter(lambda s: "Bluetooth" not in s, base_list) # Filter because mac sometimes puts them in the list else: - base_list = base_list + glob.glob("/dev/ttyUSB*") + glob.glob("/dev/ttyACM*") + glob.glob("/dev/cu.*") + glob.glob("/dev/tty.usb*") + glob.glob("/dev/rfcomm*") + glob.glob("/dev/serial/by-id/*") + if only_list_usb: + base_list = base_list + glob.glob("/dev/ttyUSB*") + glob.glob("/dev/ttyACM*") + glob.glob("/dev/cu.usb*") + base_list = filter(lambda s: "Bluetooth" not in s, base_list) # Filter because mac sometimes puts them in the list + else: + base_list = base_list + glob.glob("/dev/ttyUSB*") + glob.glob("/dev/ttyACM*") + glob.glob("/dev/cu.*") + glob.glob("/dev/tty.usb*") + glob.glob("/dev/rfcomm*") + glob.glob("/dev/serial/by-id/*") return base_list def _onApplicationShuttingDown(self): From ba80cdba67a9ae4d67bd5924a36dc5299dc0b333 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 9 Jul 2015 16:31:44 +0200 Subject: [PATCH 09/22] Write to the correct variable so bed temperature is properly updated Contributes to #82 --- plugins/USBPrinting/USBPrinterManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/USBPrinting/USBPrinterManager.py b/plugins/USBPrinting/USBPrinterManager.py index 0609869059..066ae585da 100644 --- a/plugins/USBPrinting/USBPrinterManager.py +++ b/plugins/USBPrinting/USBPrinterManager.py @@ -172,7 +172,7 @@ class USBPrinterManager(QObject, SignalEmitter, Extension): ## Callback for bed temperature change def onBedTemperature(self, serial_port,temperature): - self._bed_temperature = temperature + self._bed_temp = temperature self.pyqtBedTemperature.emit(temperature) ## Callback for error From f4153fa63ec30bcc9442729abbf47032e7770975 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Fri, 10 Jul 2015 10:51:54 +0200 Subject: [PATCH 10/22] Only process the layer data if the layer view is active. Contributes to #109 --- .../CuraEngineBackend/CuraEngineBackend.py | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index ab7b4cebf3..d5e948d0cd 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -37,6 +37,12 @@ class CuraEngineBackend(Backend): self._scene = Application.getInstance().getController().getScene() self._scene.sceneChanged.connect(self._onSceneChanged) + # Workaround to disable layer view processing if layer view is not active. + self._layer_view_active = False + Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged) + self._onActiveViewChanged() + self._stored_layer_data = None + self._settings = None Application.getInstance().activeMachineChanged.connect(self._onActiveMachineChanged) self._onActiveMachineChanged() @@ -150,7 +156,7 @@ class CuraEngineBackend(Backend): obj = msg.objects.add() obj.id = id(object) - verts = numpy.array(mesh_data.getVertices(), copy=True) + verts = numpy.array(mesh_data.getVertices()) verts[:,[1,2]] = verts[:,[2,1]] verts[:,1] *= -1 obj.vertices = verts.tostring() @@ -188,8 +194,11 @@ class CuraEngineBackend(Backend): def _onSlicedObjectListMessage(self, message): if self._save_polygons: - job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(message) - job.start() + if self._layer_view_active: + job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(message) + job.start() + else : + self._stored_layer_data = message def _onProgressMessage(self, message): if message.amount >= 0.99: @@ -248,3 +257,14 @@ class CuraEngineBackend(Backend): def _onToolOperationStopped(self, tool): self._enabled = True self._onChanged() + + def _onActiveViewChanged(self): + if Application.getInstance().getController().getActiveView(): + view = Application.getInstance().getController().getActiveView() + if view.getPluginId() == "LayerView": + self._layer_view_active = True + if self._stored_layer_data: + job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(self._stored_layer_data) + job.start() + else: + self._layer_view_active = False From 8fa0468787b2a375668c67c789be49bec696e48e Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Fri, 10 Jul 2015 11:14:59 +0200 Subject: [PATCH 11/22] Also add the parent class' command line arguments Contributes to #96 --- cura/CuraApplication.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 5e2ba4a08a..f5a89df498 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -110,6 +110,7 @@ class CuraApplication(QtApplication): self._plugin_registry.loadPlugin("CuraEngineBackend") def addCommandLineOptions(self, parser): + super().addCommandLineOptions(parser) parser.add_argument("file", nargs="*", help="Files to load after starting the application.") def run(self): From 968508b6e85919c62ce93520f7141f6ade755544 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Fri, 10 Jul 2015 13:13:52 +0200 Subject: [PATCH 12/22] feat: infill wipe dist; wireframe restructure & renaming; bugfix: draft_shield_height inherit_function --- resources/settings/fdmprinter.json | 104 ++++++++++++++++------------- 1 file changed, 56 insertions(+), 48 deletions(-) diff --git a/resources/settings/fdmprinter.json b/resources/settings/fdmprinter.json index 95ef82bd72..f5b5ee21a7 100644 --- a/resources/settings/fdmprinter.json +++ b/resources/settings/fdmprinter.json @@ -389,7 +389,15 @@ "description": "The amount of overlap between the infill and the walls. A slight overlap allows the walls to connect firmly to the infill.", "unit": "%", "type": "float", - "default": 15.0, + "default": 10.0, + "visible": false + }, + "infill_wipe_dist": { + "label": "Infill Wipe Distance", + "description": "Distance of a travel move inserted after every infill line, to make the infill stick to the walls better. This option is imilar to infill overlap, but without extrusion and only on one end of the infill line.", + "unit": "mm", + "type": "float", + "default": 0.04, "visible": false }, "fill_sparse_thickness": { @@ -1376,7 +1384,7 @@ "min_value": 0.0, "max_value_warning": 30.0, "default": 0.0, - "inherit_function": "9999 if draft_shield_height_limitation == 'Full' && draft_shield_enabled else 0.0", + "inherit_function": "9999 if draft_shield_height_limitation == 'Full' and draft_shield_enabled else 0.0", "visible": false, "active_if": { "setting": "draft_shield_height_limitation", @@ -1688,8 +1696,33 @@ "default": false, "visible": false }, + "wireframe_height": { + "label": "WP Connection Height", + "description": "The height of the upward and diagonally downward lines between two horizontal parts. This determines the overall density of the net structure. Only applies to Wire Printing.", + "type": "float", + "unit": "mm", + "default": 3.0, + "visible": false, + "active_if": { + "setting": "wireframe_enabled", + "value": true + } + }, + "wireframe_roof_inset": { + "label": "WP Roof Inset Distance", + "description": "The distance covered when making a connection from a roof outline inward. Only applies to Wire Printing.", + "type": "float", + "unit": "mm", + "default": 3.0, + "visible": false, + "active_if": { + "setting": "wireframe_enabled", + "value": true + }, + "inherit_function": "wireframe_height" + }, "wireframe_printspeed": { - "label": "Wire Printing speed", + "label": "WP speed", "description": "Speed at which the nozzle moves when extruding material. Only applies to Wire Printing.", "unit": "mm/s", "type": "float", @@ -1701,7 +1734,7 @@ }, "children": { "wireframe_printspeed_bottom": { - "label": "Wire Bottom Printing Speed", + "label": "WP Bottom Printing Speed", "description": "Speed of printing the first layer, which is the only layer touching the build platform. Only applies to Wire Printing.", "unit": "mm/s", "type": "float", @@ -1714,7 +1747,7 @@ } }, "wireframe_printspeed_up": { - "label": "Wire Upward Printing Speed", + "label": "WP Upward Printing Speed", "description": "Speed of printing a line upward 'in thin air'. Only applies to Wire Printing.", "unit": "mm/s", "type": "float", @@ -1727,7 +1760,7 @@ } }, "wireframe_printspeed_down": { - "label": "Wire Downward Printing Speed", + "label": "WP Downward Printing Speed", "description": "Speed of printing a line diagonally downward. Only applies to Wire Printing.", "unit": "mm/s", "type": "float", @@ -1740,7 +1773,7 @@ } }, "wireframe_printspeed_flat": { - "label": "Wire Horizontal Printing Speed", + "label": "WP Horizontal Printing Speed", "description": "Speed of printing the horizontal contours of the object. Only applies to Wire Printing.", "unit": "mm/s", "type": "float", @@ -1755,7 +1788,7 @@ } }, "wireframe_flow": { - "label": "Wire Printing Flow", + "label": "WP Flow", "description": "Flow compensation: the amount of material extruded is multiplied by this value. Only applies to Wire Printing.", "unit": "%", "default": 100.0, @@ -1767,7 +1800,7 @@ }, "children": { "wireframe_flow_connection": { - "label": "Wire Connection Flow", + "label": "WP Connection Flow", "description": "Flow compensation when going up or down. Only applies to Wire Printing.", "unit": "%", "default": 100.0, @@ -1779,7 +1812,7 @@ } }, "wireframe_flow_flat": { - "label": "Wire Flat Flow", + "label": "WP Flat Flow", "description": "Flow compensation when printing flat lines. Only applies to Wire Printing.", "unit": "%", "default": 100.0, @@ -1793,7 +1826,7 @@ } }, "wireframe_top_delay": { - "label": "Wire Printing Top Delay", + "label": "WP Top Delay", "description": "Delay time after an upward move, so that the upward line can harden. Only applies to Wire Printing.", "unit": "sec", "type": "float", @@ -1805,7 +1838,7 @@ } }, "wireframe_bottom_delay": { - "label": "Wire Printing Bottom Delay", + "label": "WP Bottom Delay", "description": "Delay time after a downward move. Only applies to Wire Printing. Only applies to Wire Printing.", "unit": "sec", "type": "float", @@ -1817,7 +1850,7 @@ } }, "wireframe_flat_delay": { - "label": "Wire Printing Flat Delay", + "label": "WP Flat Delay", "description": "Delay time between two horizontal segments. Introducing such a delay can cause better adhesion to previous layers at the connection points, while too large delay times cause sagging. Only applies to Wire Printing.", "unit": "sec", "type": "float", @@ -1829,7 +1862,7 @@ } }, "wireframe_up_half_speed": { - "label": "Wire Printing Ease Upward", + "label": "WP Ease Upward", "description": "Distance of an upward move which is extruded with half speed.\nThis can cause better adhesion to previous layers, while not heating the material in those layers too much. Only applies to Wire Printing.", "type": "float", "unit": "mm", @@ -1841,7 +1874,7 @@ } }, "wireframe_top_jump": { - "label": "Wire Printing Knot Size", + "label": "WP Knot Size", "description": "Creates a small knot at the top of an upward line, so that the consecutive horizontal layer has a better chance to connect to it. Only applies to Wire Printing.", "type": "float", "unit": "mm", @@ -1853,7 +1886,7 @@ } }, "wireframe_fall_down": { - "label": "Wire Printing Fall Down", + "label": "WP Fall Down", "description": "Distance with which the material falls down after an upward extrusion. This distance is compensated for. Only applies to Wire Printing.", "type": "float", "unit": "mm", @@ -1865,7 +1898,7 @@ } }, "wireframe_drag_along": { - "label": "Wire Printing Drag along", + "label": "WP Drag along", "description": "Distance with which the material of an upward extrusion is dragged along with the diagonally downward extrusion. This distance is compensated for. Only applies to Wire Printing.", "type": "float", "unit": "mm", @@ -1877,7 +1910,7 @@ } }, "wireframe_strategy": { - "label": "Wire Printing Strategy", + "label": "WP Strategy", "description": "Strategy for making sure two consecutive layers connect at each connection point. Retraction lets the upward lines harden in the right position, but may cause filament grinding. A knot can be made at the end of an upward line to heighten the chance of connecting to it and to let the line cool; however it may require slow printing speeds. Another strategy is to compensate for the sagging of the top of an upward line; however, the lines won't always fall down as predicted.", "type": "enum", "options": [ @@ -1893,7 +1926,7 @@ } }, "wireframe_straight_before_down": { - "label": "Wire Printing Straighten Downward Lines", + "label": "WP Straighten Downward Lines", "description": "Percentage of a diagonally downward line which is covered by a horizontal line piece. This can prevent sagging of the top most point of upward lines. Only applies to Wire Printing.", "type": "float", "unit": "%", @@ -1905,7 +1938,7 @@ } }, "wireframe_roof_fall_down": { - "label": "Wire Printing Roof Fall Down", + "label": "WP Roof Fall Down", "description": "The distance which horizontal roof lines printed 'in thin air' fall down when being printed. This distance is compensated for. Only applies to Wire Printing.", "type": "float", "unit": "mm", @@ -1917,7 +1950,7 @@ } }, "wireframe_roof_drag_along": { - "label": "Wire Printing Roof Drag Along", + "label": "WP Roof Drag Along", "description": "The distance of the end piece of an inward line which gets dragged along when going back to the outer outline of the roof. This distance is compensated for. Only applies to Wire Printing.", "type": "float", "unit": "mm", @@ -1929,7 +1962,7 @@ } }, "wireframe_roof_outer_delay": { - "label": "Wire Printing Roof Outer Delay", + "label": "WP Roof Outer Delay", "description": "Time spent at the outer perimeters of hole which is to become a roof. Larger times can ensure a better connection. Only applies to Wire Printing.", "type": "boolean", "unit": "sec", @@ -1941,33 +1974,8 @@ "value": true } }, - "wireframe_height": { - "label": "Wire Printing Connection Height", - "description": "The height of the upward and diagonally downward lines between two horizontal parts. Only applies to Wire Printing.", - "type": "float", - "unit": "mm", - "default": 3.0, - "visible": false, - "active_if": { - "setting": "wireframe_enabled", - "value": true - } - }, - "wireframe_roof_inset": { - "label": "Wire Printing Roof Inset Distance", - "description": "The distance covered when making a connection from a roof outline inward. Only applies to Wire Printing.", - "type": "float", - "unit": "mm", - "default": 3.0, - "visible": false, - "active_if": { - "setting": "wireframe_enabled", - "value": true - }, - "inherit_function": "wireframe_height" - }, "wireframe_nozzle_clearance": { - "label": "Wire Printing Nozzle Clearance", + "label": "WP Nozzle Clearance", "description": "Distance between the nozzle and horizontally downward lines. Larger clearance results in diagonally downward lines with a less steep angle, which in turn results in less upward connections with the next layer. Only applies to Wire Printing.", "type": "float", "unit": "mm", From 3e27c8b791f9da9fc877e773d3d69477d175cfa0 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Fri, 10 Jul 2015 15:58:34 +0200 Subject: [PATCH 13/22] Do not store files that fail to load in recent files Contributes to Ultimaker/Cura#103 --- cura/CuraApplication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index f5a89df498..3e7ae252c8 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -523,7 +523,7 @@ class CuraApplication(QtApplication): op.push() def _onJobFinished(self, job): - if type(job) is not ReadMeshJob: + if type(job) is not ReadMeshJob or not job.getResult(): return f = QUrl.fromLocalFile(job.getFileName()) From 6859481fd48deb87a3fcb3d3bc77d8a9031f1aa0 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Fri, 10 Jul 2015 16:02:01 +0200 Subject: [PATCH 14/22] Send M104 to set the temperature to 0 This makes it possible to continue communication after cancelling a print. Contributes to #82 --- plugins/USBPrinting/PrinterConnection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/USBPrinting/PrinterConnection.py b/plugins/USBPrinting/PrinterConnection.py index 7ef6c759ac..9d3c3334ef 100644 --- a/plugins/USBPrinting/PrinterConnection.py +++ b/plugins/USBPrinting/PrinterConnection.py @@ -468,7 +468,7 @@ class PrinterConnection(SignalEmitter): # Turn of temperatures self._sendCommand("M140 S0") - self._sendCommand("M109 S0") + self._sendCommand("M104 S0") self._is_printing = False ## Check if the process did not encounter an error yet. From a5f00f30ca90d02fbf082abbcebf5ceaf4d23d25 Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Fri, 10 Jul 2015 17:50:15 +0200 Subject: [PATCH 15/22] Create functions that get & set platform activity The platform activity is determined by whether there is a mesh in the build platform or not Contributes to #128 --- cura/CuraApplication.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 5e2ba4a08a..4021cd2fc8 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -74,6 +74,7 @@ class CuraApplication(QtApplication): self._print_information = None self._i18n_catalog = None self._previous_active_tool = None + self._platform_activity = False self.activeMachineChanged.connect(self._onActiveMachineChanged) @@ -215,6 +216,31 @@ class CuraApplication(QtApplication): self._previous_active_tool = None requestAddPrinter = pyqtSignal() + activityChanged = pyqtSignal() + + @pyqtProperty(bool, notify = activityChanged) + def getPlatformActivity(self): + return self._platform_activity + + @pyqtSlot(bool) + def setPlatformActivity(self, activity): + ##Sets the _platform_activity variable on true or false depending on whether there is a mesh on the platform + if activity == True: + self._platform_activity = activity + elif activity == False: + nodes = [] + for node in DepthFirstIterator(self.getController().getScene().getRoot()): + if type(node) is not SceneNode or not node.getMeshData(): + continue + nodes.append(node) + i = 0 + for node in nodes: + if not node.getMeshData(): + continue + i += 1 + if i <= 1: ## i == 0 when the meshes are removed using the deleteAll function; i == 1 when the last remaining mesh is removed using the deleteObject function + self._platform_activity = activity + self.activityChanged.emit() ## Remove an object from the scene @pyqtSlot("quint64") @@ -227,6 +253,7 @@ class CuraApplication(QtApplication): if object: op = RemoveSceneNodeOperation(object) op.push() + self.setPlatformActivity(False) ## Create a number of copies of existing object. @pyqtSlot("quint64", int) @@ -267,7 +294,6 @@ class CuraApplication(QtApplication): if type(node) is not SceneNode or not node.getMeshData(): continue nodes.append(node) - if nodes: op = GroupedOperation() @@ -275,6 +301,7 @@ class CuraApplication(QtApplication): op.addOperation(RemoveSceneNodeOperation(node)) op.push() + self.setPlatformActivity(False) ## Reset all translation on nodes with mesh data. @pyqtSlot() From c4b923eb743d0f80e8ac762375b33ef70043b40e Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Fri, 10 Jul 2015 17:52:15 +0200 Subject: [PATCH 16/22] Sets the platform activity on true when a model is loaded Also solved a problem with deleteSelection trigger contributes to #128 --- cura/BuildVolume.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index cf085bfe61..1556ee8d22 100644 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -33,6 +33,7 @@ class BuildVolume(SceneNode): self.setCalculateBoundingBox(False) + def setWidth(self, width): self._width = width From a687c9c89eda846c9754866a87c434240cda0743 Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Fri, 10 Jul 2015 18:41:24 +0200 Subject: [PATCH 17/22] Sets the platform activity on true when a model is loaded Also solved a problem with the deleteSelection trigger contributes to #128 --- resources/qml/Cura.qml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 226f4f59a9..f8e80367eb 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -41,7 +41,10 @@ UM.MainWindow { var path = modelData.toString() return (index + 1) + ". " + path.slice(path.lastIndexOf("/") + 1); } - onTriggered: UM.MeshFileHandler.readLocalFile(modelData); + onTriggered: { + UM.MeshFileHandler.readLocalFile(modelData); + Printer.setPlatformActivity(true) + } } onObjectAdded: fileMenu.insertItem(index, object) onObjectRemoved: fileMenu.removeItem(object) @@ -318,7 +321,11 @@ UM.MainWindow { redo.onTriggered: UM.OperationStack.redo(); redo.enabled: UM.OperationStack.canRedo; - deleteSelection.onTriggered: UM.Controller.removeSelection(); + deleteSelection.onTriggered: { + if(objectContextMenu.objectId != 0) { + Printer.deleteObject(objectContextMenu.objectId); + } + } deleteObject.onTriggered: { if(objectContextMenu.objectId != 0) { @@ -408,6 +415,7 @@ UM.MainWindow { onAccepted: { UM.MeshFileHandler.readLocalFile(fileUrl) + Printer.setPlatformActivity(true) } } From 81aa047ae38cea1268a704770c83c2980566b4e6 Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Fri, 10 Jul 2015 18:43:47 +0200 Subject: [PATCH 18/22] Adds an idle-state for the safebutton When there is no model, the safebutton goes back into the idle state contributes to #128 --- resources/qml/SaveButton.qml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 4708995308..004b523336 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -14,6 +14,7 @@ Rectangle { property Action saveAction; property real progress: UM.Backend.progress; + property bool activity: Printer.getPlatformActivity; Behavior on progress { NumberAnimation { duration: 250; } } property string currentDevice: "local_file" @@ -76,7 +77,7 @@ Rectangle { color: UM.Theme.colors.save_button_estimated_text; font: UM.Theme.fonts.small; text: - if(base.progress < 0) { + if(base.activity == false) { //: Save button label return qsTr("Please load a 3D model"); } else if (base.progress < 0.99) { @@ -97,7 +98,7 @@ Rectangle { anchors.leftMargin: UM.Theme.sizes.save_button_text_margin.width; color: UM.Theme.colors.save_button_printtime_text; font: UM.Theme.fonts.small; - visible: base.progress < 0.99 ? false : true + visible: base.activity == false || base.progress < 0.99 ? false : true text: (!base.printDuration || !base.printDuration.valid) ? "" : base.printDuration.getDisplayString(UM.DurationFormat.Long); } Label { @@ -107,11 +108,10 @@ Rectangle { anchors.leftMargin: UM.Theme.sizes.save_button_text_margin.width; color: base.printDuration.days > 0 ? UM.Theme.colors.save_button_estimated_text : UM.Theme.colors.save_button_printtime_text; font: UM.Theme.fonts.small; - property bool mediumLengthDuration: base.printDuration.hours > 9 && base.printMaterialAmount > 9.99 && base.printDuration.days == 0 width: mediumLengthDuration ? 50 : undefined elide: mediumLengthDuration ? Text.ElideRight : Text.ElideNone - visible: base.progress < 0.99 ? false : true + visible: base.activity == false || base.progress < 0.99 ? false : true //: Print material amount save button label text: base.printMaterialAmount < 0 ? "" : qsTr("%1m material").arg(base.printMaterialAmount); } @@ -125,7 +125,7 @@ Rectangle { } width: Math.max(infoBox.width * base.progress); color: UM.Theme.colors.save_button_active - visible: base.progress > 0.99 ? false : true + visible: progress > 0.99 ? false : true } Button { @@ -135,7 +135,7 @@ Rectangle { anchors.left: parent.left anchors.leftMargin: UM.Theme.sizes.default_margin.width; tooltip: '' - enabled: progress >= 0.99; + enabled: progress > 0.99 && base.activity == true width: infoBox.width/6*4.5 height: UM.Theme.sizes.save_button_save_to_button.height From 608283f5f42bcc057f0751a811d1922f22ed6fde Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Fri, 10 Jul 2015 19:28:19 +0200 Subject: [PATCH 19/22] Adds an idle-state for the layerview slider When there is no model, the layerview slider goes back into the idle state fixes #128 --- resources/themes/cura/styles.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index e40552cd32..e7794746f5 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -389,7 +389,7 @@ QtObject { } Label { id: maxValueLabel - visible: UM.LayerView.getLayerActivity ? true : false + visible: UM.LayerView.getLayerActivity && Printer.getPlatformActivity ? true : false text: control.maximumValue + 1 font: control.maximumValue > 998 ? UM.Theme.fonts.small : UM.Theme.fonts.default transformOrigin: Item.BottomLeft @@ -399,7 +399,7 @@ QtObject { } Label { id: minValueLabel - visible: UM.LayerView.getLayerActivity ? true : false + visible: UM.LayerView.getLayerActivity && Printer.getPlatformActivity ? true : false text: '1' font: control.maximumValue > 998 ? UM.Theme.fonts.small : UM.Theme.fonts.default transformOrigin: Item.BottomLeft @@ -416,7 +416,7 @@ QtObject { Behavior on color { ColorAnimation { duration: 50; } } Label { id: valueLabel - visible: UM.LayerView.getLayerActivity ? true : false + visible: UM.LayerView.getLayerActivity && Printer.getPlatformActivity ? true : false text: control.value + 1 anchors.bottom: layerSliderControl.bottom anchors.right: layerSliderControl.left From fb7b7ca7b8817c94408fa0a66127aebdd791a6fe Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 13 Jul 2015 13:49:07 +0200 Subject: [PATCH 20/22] Do not cause "dictionary changed size during iteration" errors when changing view Connecting to a signal while it is emitting causes errors. --- plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py b/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py index 6930d9da71..89c5fc5e23 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py @@ -22,15 +22,15 @@ class ProcessSlicedObjectListJob(Job): super().__init__() self._message = message self._scene = Application.getInstance().getController().getScene() - self._progress = None - Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged) def run(self): if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView": self._progress = Message(catalog.i18nc("Layers View mode", "Layers"), 0, False, 0) self._progress.show() + Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged) + objectIdMap = {} new_node = SceneNode() ## Put all nodes in a dict identified by ID From c6f5ceaf5649a7145db984f4207dff221c0480f9 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 13 Jul 2015 14:03:40 +0200 Subject: [PATCH 21/22] LayerData now works by using decorators --- plugins/CuraEngineBackend/LayerDataDecorator.py | 12 ++++++++++++ .../ProcessSlicedObjectListJob.py | 14 ++++++++++---- plugins/LayerView/LayerView.py | 13 ++++++------- 3 files changed, 28 insertions(+), 11 deletions(-) create mode 100644 plugins/CuraEngineBackend/LayerDataDecorator.py diff --git a/plugins/CuraEngineBackend/LayerDataDecorator.py b/plugins/CuraEngineBackend/LayerDataDecorator.py new file mode 100644 index 0000000000..81ce241be5 --- /dev/null +++ b/plugins/CuraEngineBackend/LayerDataDecorator.py @@ -0,0 +1,12 @@ +from UM.Scene.SceneNodeDecorator import SceneNodeDecorator + +class LayerDataDecorator(SceneNodeDecorator): + def __init__(self): + super().__init__() + self._layer_data = None + + def getLayerData(self): + return self._layer_data + + def setLayerData(self, layer_data): + self._layer_data = layer_data \ No newline at end of file diff --git a/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py b/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py index 89c5fc5e23..25564ed8f5 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py @@ -11,6 +11,7 @@ from UM.Message import Message from UM.i18n import i18nCatalog from . import LayerData +from . import LayerDataDecorator import numpy import struct @@ -36,7 +37,8 @@ class ProcessSlicedObjectListJob(Job): ## Put all nodes in a dict identified by ID for node in DepthFirstIterator(self._scene.getRoot()): if type(node) is SceneNode and node.getMeshData(): - if hasattr(node.getMeshData(), "layerData"): + if node.callDecoration("getLayerData"): + #if hasattr(node.getMeshData(), "layerData"): self._scene.getRoot().removeChild(node) else: objectIdMap[id(node)] = node @@ -83,14 +85,18 @@ class ProcessSlicedObjectListJob(Job): # We are done processing all the layers we got from the engine, now create a mesh out of the data layerData.build() - mesh.layerData = layerData if self._progress: self._progress.setProgress(100) - + + #Add layerdata decorator to scene node to indicate that the node has layerdata + decorator = LayerDataDecorator.LayerDataDecorator() + decorator.setLayerData(layerData) + new_node.addDecorator(decorator) + new_node.setMeshData(mesh) new_node.setParent(self._scene.getRoot()) - + view = Application.getInstance().getController().getActiveView() if view.getPluginId() == "LayerView": view.resetLayerData() diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index cad15047e3..1caf504ad1 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -68,10 +68,8 @@ class LayerView(View): if node.getMeshData() and node.isVisible(): if Selection.isSelected(node): renderer.queueNode(node, material = self._selection_material, transparent = True) - - try: - layer_data = node.getMeshData().layerData - except AttributeError: + layer_data = node.callDecoration("getLayerData") + if not layer_data: continue # Render all layers below a certain number as line mesh instead of vertices. @@ -133,10 +131,11 @@ class LayerView(View): for node in DepthFirstIterator(scene.getRoot()): if not node.render(renderer): if node.getMeshData() and node.isVisible(): - try: - layer_data = node.getMeshData().layerData - except AttributeError: + + layer_data = node.callDecoration("getLayerData") + if not layer_data: continue + if new_max_layers < len(layer_data.getLayers()): new_max_layers = len(layer_data.getLayers()) - 1 From 2683fa0958be738e62e72d68f41fe17f6491b007 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 14 Jul 2015 10:25:08 +0200 Subject: [PATCH 22/22] Added grouping action --- cura/CuraApplication.py | 12 ++++++++++++ resources/qml/Actions.qml | 9 +++++++++ resources/qml/Cura.qml | 6 ++++++ 3 files changed, 27 insertions(+) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 3beafb9284..7c1034059b 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -22,6 +22,7 @@ from UM.Math.Polygon import Polygon from UM.Scene.BoxRenderer import BoxRenderer from UM.Scene.Selection import Selection +from UM.Scene.GroupDecorator import GroupDecorator from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation @@ -416,6 +417,17 @@ class CuraApplication(QtApplication): return self.getActiveMachine().setSettingValueByKey(key, value) + + + @pyqtSlot() + def groupSelected(self): + group_node = SceneNode() + group_decorator = GroupDecorator() + group_node.addDecorator(group_decorator) + group_node.setParent(self.getController().getScene().getRoot()) + for node in Selection.getAllSelectedObjects(): + node.setParent(group_node) + ## Add an output device that can be written to. # diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index b29b260f28..a98d94b62c 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -3,6 +3,7 @@ import QtQuick 2.2 import QtQuick.Controls 1.1 +import UM 1.0 as UM Item { property alias open: openAction; @@ -16,6 +17,7 @@ Item { property alias deleteObject: deleteObjectAction; property alias centerObject: centerObjectAction; + property alias groupObjects: groupObjectsAction; property alias multiplyObject: multiplyObjectAction; property alias splitObject: splitObjectAction; @@ -120,6 +122,13 @@ Item { //: Center object action text: qsTr("Center Object on Platform"); } + + Action + { + id: groupObjectsAction + text: qsTr("Group objects"); + enabled: UM.Scene.numObjectsSelected > 1 ? true: false + } Action { id: multiplyObjectAction; diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index f8e80367eb..514ea77bb3 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -347,6 +347,11 @@ UM.MainWindow { objectContextMenu.objectId = 0; } } + + groupObjects.onTriggered: + { + Printer.groupSelected() + } deleteAll.onTriggered: Printer.deleteAll() resetAllTranslation.onTriggered: Printer.resetAllTranslation() @@ -373,6 +378,7 @@ UM.MainWindow { MenuItem { action: actions.deleteObject; } MenuItem { action: actions.multiplyObject; } MenuItem { action: actions.splitObject; } + MenuItem { action: actions.groupObjects;} MenuSeparator { } MenuItem { action: actions.deleteAll; } MenuItem { action: actions.reloadAll; }