From 53a94c23e17ec711b3723c6b58b17fd651ff573c Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Wed, 8 Jul 2015 18:01:03 +0200 Subject: [PATCH 1/9] 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 2/9] 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 ae89ef37c05058c0d1c375b64dc2977076f37d49 Mon Sep 17 00:00:00 2001 From: Tamara Hogenhout Date: Thu, 9 Jul 2015 16:27:31 +0200 Subject: [PATCH 3/9] 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 4/9] 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 5/9] 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 6/9] 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 7/9] 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 8/9] 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 9/9] 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):