From 33c69b32f81bf63a197a34babebfc0945e194601 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 5 Jun 2015 12:10:13 +0200 Subject: [PATCH 1/9] Fix for #29 --- plugins/LayerView/LayerView.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index 576feb6cb4..3b6edc195e 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -39,6 +39,8 @@ class LayerView(View): element_counts = layer_data.getElementCounts() for layer, counts in element_counts.items(): end += sum(counts) + ## Hack to ensure the end is correct. Not quite sure what causes this + end += 2 * len(counts) if layer >= end_layer: break From 81b41b88899077d3d21e06914280cfff4706d4a2 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 4 Jun 2015 10:29:45 +0200 Subject: [PATCH 2/9] Render a transparent ghost of the selection when things are selected. Fixes Asana issue about layer view --- plugins/LayerView/LayerView.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index 3b6edc195e..82a164ecd7 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -5,6 +5,8 @@ from UM.View.View import View from UM.View.Renderer import Renderer from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Resources import Resources +from UM.Scene.Selection import Selection +from UM.Math.Color import Color ## View used to display g-code paths. class LayerView(View): @@ -23,9 +25,15 @@ class LayerView(View): self._material = renderer.createMaterial(Resources.getPath(Resources.ShadersLocation, "basic.vert"), Resources.getPath(Resources.ShadersLocation, "vertexcolor.frag")) self._material.setUniformValue("u_color", [1.0, 0.0, 0.0, 1.0]) + self._selection_material = renderer.createMaterial(Resources.getPath(Resources.ShadersLocation, "basic.vert"), Resources.getPath(Resources.ShadersLocation, "color.frag")) + self._selection_material.setUniformValue("u_color", Color(35, 35, 35, 128)) + for node in DepthFirstIterator(scene.getRoot()): if not node.render(renderer): 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: @@ -45,9 +53,9 @@ class LayerView(View): if layer >= end_layer: break - renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines, start = start, end = end) + renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines, start = start, end = end, overlay = True) else: - renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines) + renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines, overlay = True) def setLayer(self, value): self._layer_percentage = value From b5fda5b835142f0c6cdd09193e9182b21e4631d9 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 4 Jun 2015 11:12:31 +0200 Subject: [PATCH 3/9] Added exception handling to listen thread join --- 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 ad231f5207..19c5b5c07a 100644 --- a/plugins/USBPrinting/PrinterConnection.py +++ b/plugins/USBPrinting/PrinterConnection.py @@ -244,7 +244,10 @@ class PrinterConnection(SignalEmitter): self._connect_thread.join() if self._serial is not None: self.setIsConnected(False) - self._listen_thread.join() + try: + self._listen_thread.join() + except: + pass self._serial.close() self._serial = None From f57d2693fedf7e227e77b2d07840a94242863f99 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 4 Jun 2015 11:23:15 +0200 Subject: [PATCH 4/9] Updated name in menu for firmware update --- plugins/USBPrinting/USBPrinterManager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/USBPrinting/USBPrinterManager.py b/plugins/USBPrinting/USBPrinterManager.py index ba515594ae..27ff31647b 100644 --- a/plugins/USBPrinting/USBPrinterManager.py +++ b/plugins/USBPrinting/USBPrinterManager.py @@ -40,7 +40,8 @@ class USBPrinterManager(QObject, SignalEmitter, Extension): self._error_message = "" ## Add menu item to top menu of the application. - self.addMenuItem(i18n_catalog.i18n("Update Firmware"), self.updateAllFirmware) + self.setMenuName("Firmware") + self.addMenuItem(i18n_catalog.i18n("Update"), self.updateAllFirmware) pyqtError = pyqtSignal(str, arguments = ["amount"]) processingProgress = pyqtSignal(float, arguments = ["amount"]) From a53e56e282df3359f0fc2c4ad5f71f916d1e7f74 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Fri, 5 Jun 2015 13:25:04 +0200 Subject: [PATCH 5/9] Handle finished signal for the ReadMeshJob started from command line args This puts the actual mesh into the scene rather than just loading it and discarding it afterwards. Now file arguments supplied to the application actually work. Fixes "Open With" on Windows and the related Asana issue. --- cura/CuraApplication.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index f6d41a51c8..55e1e466a4 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -162,6 +162,7 @@ class CuraApplication(QtApplication): for file in self.getCommandLineOption("file", []): job = ReadMeshJob(os.path.abspath(file)) + job.finished.connect(self._onFileLoaded) job.start() self.exec_() @@ -447,3 +448,15 @@ class CuraApplication(QtApplication): def _onMessageActionTriggered(self, message, action): if action == "eject": self.getStorageDevice("LocalFileStorage").ejectRemovableDrive(message._sdcard) + + def _onFileLoaded(self, job): + mesh = job.getResult() + if mesh != None: + node = SceneNode() + + node.setSelectable(True) + node.setMeshData(mesh) + node.setName(os.path.basename(job.getFileName())) + + op = AddSceneNodeOperation(node, self.getController().getScene().getRoot()) + op.push() From a2c099a7d81526d03d0ca83f2b857a3ac9c33e62 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 5 Jun 2015 17:40:57 +0200 Subject: [PATCH 6/9] Implemented feature described by #30 --- plugins/LayerView/LayerView.py | 92 +++++++++++++++++++++++------ plugins/LayerView/LayerView.qml | 6 +- plugins/LayerView/LayerViewProxy.py | 44 ++++++++++++++ plugins/LayerView/__init__.py | 7 ++- 4 files changed, 128 insertions(+), 21 deletions(-) create mode 100644 plugins/LayerView/LayerViewProxy.py diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index 3b6edc195e..50ad534b09 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -5,6 +5,9 @@ from UM.View.View import View from UM.View.Renderer import Renderer from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Resources import Resources +from UM.Event import Event, KeyEvent +from UM.Signal import Signal +from . import LayerViewProxy ## View used to display g-code paths. class LayerView(View): @@ -13,11 +16,27 @@ class LayerView(View): self._material = None self._num_layers = 0 self._layer_percentage = 0 # what percentage of layers need to be shown (SLider gives value between 0 - 100) + self._current_layer_num = 0 + self._proxy = LayerViewProxy.LayerViewProxy() + self._controller.getScene().sceneChanged.connect(self._onSceneChanged) + self._max_layers = 10 + + def getCurrentLayer(self): + return self._current_layer_num + + def _onSceneChanged(self, node): + self.calculateMaxLayers() + + def getMaxLayers(self): + return self._max_layers def beginRendering(self): scene = self.getController().getScene() renderer = self.getRenderer() renderer.setRenderSelection(False) + + ## Recalculate num max layers + #self.calculateMaxLayers() if not self._material: self._material = renderer.createMaterial(Resources.getPath(Resources.ShadersLocation, "basic.vert"), Resources.getPath(Resources.ShadersLocation, "vertexcolor.frag")) @@ -31,26 +50,65 @@ class LayerView(View): except AttributeError: continue - if self._layer_percentage < 100: - start = 0 - end_layer = round(len(layer_data.getLayers()) * (self._layer_percentage / 100)) - end = 0 + start = 0 + end = 0 - element_counts = layer_data.getElementCounts() - for layer, counts in element_counts.items(): - end += sum(counts) - ## Hack to ensure the end is correct. Not quite sure what causes this - end += 2 * len(counts) + element_counts = layer_data.getElementCounts() + for layer, counts in element_counts.items(): + end += sum(counts) + ## Hack to ensure the end is correct. Not quite sure what causes this + end += 2 * len(counts) - if layer >= end_layer: - break + if layer >= self._current_layer_num: + break + + renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines, start = start, end = end) - renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines, start = start, end = end) - else: - renderer.queueNode(node, mesh = layer_data, material = self._material, mode = Renderer.RenderLines) - def setLayer(self, value): - self._layer_percentage = value - + if self._current_layer_num != value: + self._current_layer_num = value + if self._current_layer_num < 0: + self._current_layer_num = 0 + if self._current_layer_num > self._max_layers: + self._current_layer_num = self._max_layers + self.currentLayerNumChanged.emit() + + currentLayerNumChanged = Signal() + + def calculateMaxLayers(self): + scene = self.getController().getScene() + renderer = self.getRenderer() + if renderer and self._material: + renderer.setRenderSelection(False) + self._old_max_layers = self._max_layers + ## Recalculate num max layers + self._max_layers = 0 + 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: + continue + if self._max_layers < len(layer_data.getLayers()): + self._max_layers = len(layer_data.getLayers()) + + if self._max_layers != self._old_max_layers: + self.maxLayersChanged.emit() + + maxLayersChanged = Signal() + + ## Hackish way to ensure the proxy is already created, which ensures that the layerview.qml is already created + # as this caused some issues. + def getProxy(self, engine, script_engine): + return self._proxy + def endRendering(self): pass + + def event(self, event): + if event.type == Event.KeyPressEvent: + if event.key == KeyEvent.UpKey: + self.setLayer(self._current_layer_num + 1) + if event.key == KeyEvent.DownKey: + self.setLayer(self._current_layer_num - 1) diff --git a/plugins/LayerView/LayerView.qml b/plugins/LayerView/LayerView.qml index fc598bfb75..d8a95dff47 100644 --- a/plugins/LayerView/LayerView.qml +++ b/plugins/LayerView/LayerView.qml @@ -20,10 +20,10 @@ Item anchors.right : parent.right orientation: Qt.Vertical minimumValue: 0; - maximumValue: 100; + maximumValue: UM.LayerView.numLayers; - value: 100; - onValueChanged: UM.ActiveView.triggerAction("setLayer", value) + value: UM.LayerView.currentLayer + onValueChanged: UM.LayerView.setCurrentLayer(value) style: UM.Theme.styles.slider; } diff --git a/plugins/LayerView/LayerViewProxy.py b/plugins/LayerView/LayerViewProxy.py new file mode 100644 index 0000000000..b6a266233c --- /dev/null +++ b/plugins/LayerView/LayerViewProxy.py @@ -0,0 +1,44 @@ +from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty +from UM.Application import Application +import LayerView +class LayerViewProxy(QObject): + def __init__(self, parent = None): + super().__init__(parent) + self._current_layer = 0 + self._controller = Application.getInstance().getController() + self._controller.activeViewChanged.connect(self._onActiveViewChanged) + self._onActiveViewChanged() + + currentLayerChanged = pyqtSignal() + maxLayersChanged = pyqtSignal() + + @pyqtProperty(int, notify = maxLayersChanged) + def numLayers(self): + active_view = self._controller.getActiveView() + #print("num max layers " , active_view.getMaxLayers()) + return active_view.getMaxLayers() + #return 100 + + @pyqtProperty(int, notify = currentLayerChanged) + def currentLayer(self): + active_view = self._controller.getActiveView() + if type(active_view) == LayerView.LayerView.LayerView: + return active_view.getCurrentLayer() + + @pyqtSlot(int) + def setCurrentLayer(self, layer_num): + active_view = self._controller.getActiveView() + if type(active_view) == LayerView.LayerView.LayerView: + active_view.setLayer(layer_num) + + def _onLayerChanged(self): + self.currentLayerChanged.emit() + + def _onMaxLayersChanged(self): + self.maxLayersChanged.emit() + + def _onActiveViewChanged(self): + active_view = self._controller.getActiveView() + if type(active_view) == LayerView.LayerView.LayerView: + active_view.currentLayerNumChanged.connect(self._onLayerChanged) + active_view.maxLayersChanged.connect(self._onMaxLayersChanged) \ No newline at end of file diff --git a/plugins/LayerView/__init__.py b/plugins/LayerView/__init__.py index 93aa856175..eb3ba4cdbe 100644 --- a/plugins/LayerView/__init__.py +++ b/plugins/LayerView/__init__.py @@ -1,7 +1,8 @@ # Copyright (c) 2015 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. -from . import LayerView +from . import LayerView, LayerViewProxy +from PyQt5.QtQml import qmlRegisterType, qmlRegisterSingletonType from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") @@ -21,6 +22,10 @@ def getMetaData(): } } +def createLayerViewProxy(engine, script_engine): + return LayerViewProxy.LayerViewProxy() def register(app): + layer_view = LayerView.LayerView() + qmlRegisterSingletonType(LayerViewProxy.LayerViewProxy, "UM", 1, 0, "LayerView", layer_view.getProxy) return { "view": LayerView.LayerView() } From 8db06b8305984c614e8a54b9f2e7fe5f49b89dc4 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Fri, 5 Jun 2015 18:55:10 +0200 Subject: [PATCH 7/9] Add a text field style that can be used for generic text fields Based on the setting text field style. Contributes to Asana issue 33752130551782 --- resources/themes/cura/styles.qml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index dc2b80d2ad..b7db374da1 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -246,4 +246,32 @@ QtObject { } } } + + property Component text_field: Component { + TextFieldStyle { + textColor: UM.Theme.colors.setting_control_text; + font: UM.Theme.fonts.default; + + background: Rectangle + { + implicitHeight: control.height; + implicitWidth: control.width; + + border.width: 1; + border.color: UM.Theme.colors.setting_control_border; + + color: UM.Theme.colors.setting_validation_ok; + + Label { + anchors.right: parent.right; + anchors.rightMargin: UM.Theme.sizes.setting_unit_margin.width; + anchors.verticalCenter: parent.verticalCenter; + + text: control.unit ? control.unit : "" + color: UM.Theme.colors.setting_unit; + font: UM.Theme.fonts.default; + } + } + } + } } From d9ad54cd0d5a63545a97b29b332131e37b19b85e Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 8 Jun 2015 16:31:38 +0200 Subject: [PATCH 8/9] Rescale the current layer number based on the maximum layer This makes sure we still have the top layer selected when we receive new data. Contributes to Asana issue 34806173176751 --- plugins/LayerView/LayerView.py | 16 ++++++++++------ plugins/LayerView/LayerView.qml | 1 + 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index a7caca6a4f..7f8c04986d 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -18,10 +18,10 @@ class LayerView(View): self._material = None self._num_layers = 0 self._layer_percentage = 0 # what percentage of layers need to be shown (SLider gives value between 0 - 100) - self._current_layer_num = 0 self._proxy = LayerViewProxy.LayerViewProxy() self._controller.getScene().sceneChanged.connect(self._onSceneChanged) self._max_layers = 10 + self._current_layer_num = 10 def getCurrentLayer(self): return self._current_layer_num @@ -87,7 +87,7 @@ class LayerView(View): renderer.setRenderSelection(False) self._old_max_layers = self._max_layers ## Recalculate num max layers - self._max_layers = 0 + new_max_layers = 0 for node in DepthFirstIterator(scene.getRoot()): if not node.render(renderer): if node.getMeshData() and node.isVisible(): @@ -95,11 +95,15 @@ class LayerView(View): layer_data = node.getMeshData().layerData except AttributeError: continue - if self._max_layers < len(layer_data.getLayers()): - self._max_layers = len(layer_data.getLayers()) - - if self._max_layers != self._old_max_layers: + if new_max_layers < len(layer_data.getLayers()): + new_max_layers = len(layer_data.getLayers()) + + if new_max_layers > 0 and new_max_layers != self._old_max_layers: + self._max_layers = new_max_layers self.maxLayersChanged.emit() + + # This makes sure we update the current layer + self.setLayer(int(self._max_layers * (self._current_layer_num / self._old_max_layers))) maxLayersChanged = Signal() diff --git a/plugins/LayerView/LayerView.qml b/plugins/LayerView/LayerView.qml index d8a95dff47..f1d78d78f9 100644 --- a/plugins/LayerView/LayerView.qml +++ b/plugins/LayerView/LayerView.qml @@ -21,6 +21,7 @@ Item orientation: Qt.Vertical minimumValue: 0; maximumValue: UM.LayerView.numLayers; + stepSize: 1 value: UM.LayerView.currentLayer onValueChanged: UM.LayerView.setCurrentLayer(value) From 478babff9a99ab647b2d85f0049b87b23d366468 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 8 Jun 2015 16:40:09 +0200 Subject: [PATCH 9/9] Add a background to the tool additional controls This makes the actual text more visible and also makes the link between active tool and active tool controls more apparent. Fixes #21 --- resources/qml/Toolbar.qml | 44 +++++++++++++++++++++++++++----- resources/themes/cura/theme.json | 4 ++- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index e24d929ed7..e7cb0788c5 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -14,6 +14,23 @@ Item { width: buttons.width; height: buttons.height + panel.height; + Rectangle { + id: activeItemBackground; + + anchors.bottom: parent.bottom; + + width: UM.Theme.sizes.button.width; + height: UM.Theme.sizes.button.height * 2; + + opacity: panelBackground.opacity; + + color: UM.Theme.colors.tool_panel_background + + function setActive(new_x) { + x = new_x; + } + } + RowLayout { id: buttons; @@ -34,6 +51,7 @@ Item { checkable: true; checked: model.active; + onCheckedChanged: if (checked) activeItemBackground.setActive(x); style: UM.Theme.styles.tool_button; @@ -42,21 +60,35 @@ Item { MouseArea { anchors.fill: parent; onClicked: parent.checked ? UM.Controller.setActiveTool(null) : UM.Controller.setActiveTool(model.id); + } - } + } } } - Loader { - id: panel + UM.AngledCornerRectangle { + id: panelBackground; anchors.left: parent.left; - anchors.right: parent.right; anchors.bottom: buttons.top; anchors.bottomMargin: UM.Theme.sizes.default_margin.height; - height: childrenRect.height; + width: panel.item ? panel.width + 2 * UM.Theme.sizes.default_margin.width : 0; + height: panel.item ? panel.height + 2 * UM.Theme.sizes.default_margin.height : 0; - source: UM.ActiveTool.valid ? UM.ActiveTool.activeToolPanel : ""; + opacity: panel.item ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: 100 } } + + color: UM.Theme.colors.tool_panel_background; + cornerSize: width > 0 ? UM.Theme.sizes.default_margin.width : 0; + + Loader { + id: panel + + x: UM.Theme.sizes.default_margin.width; + y: UM.Theme.sizes.default_margin.height; + + source: UM.ActiveTool.valid ? UM.ActiveTool.activeToolPanel : ""; + } } } diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index a79636aece..eda2badd71 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -110,7 +110,9 @@ "save_button_text": [35, 35, 35, 255], "message": [205, 202, 201, 255], - "message_text": [35, 35, 35, 255] + "message_text": [35, 35, 35, 255], + + "tool_panel_background": [255, 255, 255, 255] }, "sizes": {