From b28bfc960276d4ae927125822308a18d294acb69 Mon Sep 17 00:00:00 2001 From: Kurt Loeffler Date: Wed, 13 Jan 2016 20:00:30 -0800 Subject: [PATCH] Reworked UI so that it matches 15.04 UI, and made each field in the UI do the same thing that they do in 15.04. --- plugins/ImageReader/ConfigUI.qml | 196 ++++++++++++++++++++------- plugins/ImageReader/ImageReader.py | 25 +++- plugins/ImageReader/ImageReaderUI.py | 75 ++++++++-- 3 files changed, 235 insertions(+), 61 deletions(-) diff --git a/plugins/ImageReader/ConfigUI.qml b/plugins/ImageReader/ConfigUI.qml index ebd2d36bb0..08b5b44db1 100644 --- a/plugins/ImageReader/ConfigUI.qml +++ b/plugins/ImageReader/ConfigUI.qml @@ -10,13 +10,13 @@ import UM 1.1 as UM UM.Dialog { - width: 250*Screen.devicePixelRatio; - minimumWidth: 250*Screen.devicePixelRatio; - maximumWidth: 250*Screen.devicePixelRatio; + width: 350*Screen.devicePixelRatio; + minimumWidth: 350*Screen.devicePixelRatio; + maximumWidth: 350*Screen.devicePixelRatio; - height: 200*Screen.devicePixelRatio; - minimumHeight: 200*Screen.devicePixelRatio; - maximumHeight: 200*Screen.devicePixelRatio; + height: 220*Screen.devicePixelRatio; + minimumHeight: 220*Screen.devicePixelRatio; + maximumHeight: 220*Screen.devicePixelRatio; modality: Qt.Modal @@ -30,58 +30,158 @@ UM.Dialog Layout.fillWidth: true columnSpacing: 16 rowSpacing: 4 - columns: 2 + columns: 1 - Text { - text: catalog.i18nc("@action:label","Size (mm)") + UM.TooltipArea { Layout.fillWidth:true - } - TextField { - id: size - focus: true - validator: DoubleValidator {notation: DoubleValidator.StandardNotation; bottom: 1; top: 500;} - text: "120" - onTextChanged: { manager.onSizeChanged(text) } + height: childrenRect.height + text: catalog.i18nc("@info:tooltip","The maximum distance of each pixel from \"Base.\"") + Row { + width: parent.width + height: childrenRect.height + + Text { + text: catalog.i18nc("@action:label","Height (mm)") + width: 150 + anchors.verticalCenter: parent.verticalCenter + } + + TextField { + id: peak_height + objectName: "Peak_Height" + validator: DoubleValidator {notation: DoubleValidator.StandardNotation; bottom: -500; top: 500;} + width: 180 + onTextChanged: { manager.onPeakHeightChanged(text) } + } + } } - Text { - text: catalog.i18nc("@action:label","Base Height (mm)") + UM.TooltipArea { Layout.fillWidth:true - } - TextField { - id: base_height - validator: DoubleValidator {notation: DoubleValidator.StandardNotation; bottom: 0; top: 500;} - text: "2" - onTextChanged: { manager.onBaseHeightChanged(text) } - } + height: childrenRect.height + text: catalog.i18nc("@info:tooltip","The base height from the build plate in millimeters.") + Row { + width: parent.width + height: childrenRect.height - Text { - text: catalog.i18nc("@action:label","Peak Height (mm)") + Text { + text: catalog.i18nc("@action:label","Base (mm)") + width: 150 + anchors.verticalCenter: parent.verticalCenter + } + + TextField { + id: base_height + objectName: "Base_Height" + validator: DoubleValidator {notation: DoubleValidator.StandardNotation; bottom: 0; top: 500;} + width: 180 + onTextChanged: { manager.onBaseHeightChanged(text) } + } + } + } + + UM.TooltipArea { Layout.fillWidth:true - } - TextField { - id: peak_height - validator: DoubleValidator {notation: DoubleValidator.StandardNotation; bottom: 0; top: 500;} - text: "12" - onTextChanged: { manager.onPeakHeightChanged(text) } - } + height: childrenRect.height + text: catalog.i18nc("@info:tooltip","The width in millimeters on the build plate.") + Row { + width: parent.width + height: childrenRect.height - Text { - text: catalog.i18nc("@action:label","Smoothing") + Text { + text: catalog.i18nc("@action:label","Width (mm)") + width: 150 + anchors.verticalCenter: parent.verticalCenter + } + + TextField { + id: width + objectName: "Width" + focus: true + validator: DoubleValidator {notation: DoubleValidator.StandardNotation; bottom: 1; top: 500;} + width: 180 + onTextChanged: { manager.onWidthChanged(text) } + } + } + } + + UM.TooltipArea { Layout.fillWidth:true - } - Rectangle { - width: 100 - height: 20 - color: "transparent" + height: childrenRect.height + text: catalog.i18nc("@info:tooltip","The depth in millimeters on the build plate") + Row { + width: parent.width + height: childrenRect.height - Slider { - id: smoothing - maximumValue: 100.0 - stepSize: 1.0 - value: 1 - width: 100 - onValueChanged: { manager.onSmoothingChanged(value) } + Text { + text: catalog.i18nc("@action:label","Depth (mm)") + width: 150 + anchors.verticalCenter: parent.verticalCenter + } + TextField { + id: depth + objectName: "Depth" + focus: true + validator: DoubleValidator {notation: DoubleValidator.StandardNotation; bottom: 1; top: 500;} + width: 180 + onTextChanged: { manager.onDepthChanged(text) } + } + } + } + + UM.TooltipArea { + Layout.fillWidth:true + height: childrenRect.height + text: catalog.i18nc("@info:tooltip","By default, white pixels represent high points on the mesh and black pixels represent low points on the mesh. Change this option to reverse the behavior such that black pixels represent high points on the mesh and white pixels represent low points on the mesh.") + Row { + width: parent.width + height: childrenRect.height + + //Empty label so 2 column layout works. + Text { + text: "" + width: 150 + anchors.verticalCenter: parent.verticalCenter + } + ComboBox { + id: image_color_invert + objectName: "Image_Color_Invert" + model: [ catalog.i18nc("@action:label","Lighter is higher"), catalog.i18nc("@action:label","Darker is higher") ] + width: 180 + onCurrentIndexChanged: { manager.onImageColorInvertChanged(currentIndex) } + } + } + } + + UM.TooltipArea { + Layout.fillWidth:true + height: childrenRect.height + text: catalog.i18nc("@info:tooltip","The amount of smoothing to apply to the image.") + Row { + width: parent.width + height: childrenRect.height + + Text { + text: catalog.i18nc("@action:label","Smoothing") + width: 150 + anchors.verticalCenter: parent.verticalCenter + } + + Rectangle { + width: 180 + height: 20 + Layout.fillWidth:true + color: "transparent" + + Slider { + id: smoothing + objectName: "Smoothing" + maximumValue: 100.0 + stepSize: 1.0 + width: 180 + onValueChanged: { manager.onSmoothingChanged(value) } + } + } } } } diff --git a/plugins/ImageReader/ImageReader.py b/plugins/ImageReader/ImageReader.py index f12b6355c7..c7475819b2 100644 --- a/plugins/ImageReader/ImageReader.py +++ b/plugins/ImageReader/ImageReader.py @@ -23,6 +23,20 @@ class ImageReader(MeshReader): self._ui = ImageReaderUI(self) def preRead(self, file_name): + img = QImage(file_name) + + if img.isNull(): + Logger.log("e", "Image is corrupt.") + return MeshReader.PreReadResult.failed + + width = img.width() + depth = img.height() + + largest = max(width, depth) + width = width/largest*self._ui.defaultWidth + depth = depth/largest*self._ui.defaultDepth + + self._ui.setWidthAndDepth(width, depth) self._ui.showConfigUI() self._ui.waitForUIToClose() @@ -31,9 +45,10 @@ class ImageReader(MeshReader): return MeshReader.PreReadResult.accepted def read(self, file_name): - return self._generateSceneNode(file_name, self._ui.size, self._ui.peak_height, self._ui.base_height, self._ui.smoothing, 512) + size = max(self._ui.getWidth(), self._ui.getDepth()) + return self._generateSceneNode(file_name, size, self._ui.peak_height, self._ui.base_height, self._ui.smoothing, 512, self._ui.image_color_invert) - def _generateSceneNode(self, file_name, xz_size, peak_height, base_height, blur_iterations, max_size): + def _generateSceneNode(self, file_name, xz_size, peak_height, base_height, blur_iterations, max_size, image_color_invert): mesh = None scene_node = None @@ -56,9 +71,10 @@ class ImageReader(MeshReader): img = img.scaled(width, height, Qt.IgnoreAspectRatio) base_height = max(base_height, 0) + peak_height = max(peak_height, -base_height) xz_size = max(xz_size, 1) - scale_vector = Vector(xz_size, max(peak_height - base_height, -base_height), xz_size) + scale_vector = Vector(xz_size, peak_height, xz_size) if width > height: scale_vector.setZ(scale_vector.z * aspect) @@ -92,6 +108,9 @@ class ImageReader(MeshReader): Job.yieldThread() + if image_color_invert: + height_data = 1-height_data + for i in range(0, blur_iterations): copy = numpy.pad(height_data, ((1, 1), (1, 1)), mode='edge') diff --git a/plugins/ImageReader/ImageReaderUI.py b/plugins/ImageReader/ImageReaderUI.py index e14bd7acda..519d73d5a7 100644 --- a/plugins/ImageReader/ImageReaderUI.py +++ b/plugins/ImageReader/ImageReaderUI.py @@ -24,15 +24,32 @@ class ImageReaderUI(QObject): self._ui_view = None self.show_config_ui_trigger.connect(self._actualShowConfigUI) - # There are corresponding values for these fields in ConfigUI.qml. - # If you change the values here, consider updating ConfigUI.qml as well. - self.size = 120 - self.base_height = 2 - self.peak_height = 12 + self.defaultWidth = 120 + self.defaultDepth = 120 + + self._aspect = 1 + self._width = self.defaultWidth + self._depth = self.defaultDepth + + self.base_height = 1 + self.peak_height = 10 self.smoothing = 1 + self.image_color_invert = False; self._ui_lock = threading.Lock() self._cancelled = False + self._disable_size_callbacks = False + + def setWidthAndDepth(self, width, depth): + self._aspect = width/depth + self._width = width + self._depth = depth + + def getWidth(self): + return self._width + + def getDepth(self): + return self._depth def getCancelled(self): return self._cancelled @@ -47,10 +64,20 @@ class ImageReaderUI(QObject): self.show_config_ui_trigger.emit() def _actualShowConfigUI(self): + self._disable_size_callbacks = True + if self._ui_view is None: self._createConfigUI() self._ui_view.show() + self._ui_view.findChild(QObject, "Width").setProperty("text", str(self._width)) + self._ui_view.findChild(QObject, "Depth").setProperty("text", str(self._depth)) + self._disable_size_callbacks = False + + self._ui_view.findChild(QObject, "Base_Height").setProperty("text", str(self.base_height)) + self._ui_view.findChild(QObject, "Peak_Height").setProperty("text", str(self.peak_height)) + self._ui_view.findChild(QObject, "Smoothing").setProperty("value", self.smoothing) + def _createConfigUI(self): if self._ui_view is None: Logger.log("d", "Creating ImageReader config UI") @@ -62,6 +89,8 @@ class ImageReaderUI(QObject): self._ui_view.setFlags(self._ui_view.flags() & ~Qt.WindowCloseButtonHint & ~Qt.WindowMinimizeButtonHint & ~Qt.WindowMaximizeButtonHint); + self._disable_size_callbacks = False + @pyqtSlot() def onOkButtonClicked(self): self._cancelled = False @@ -75,11 +104,30 @@ class ImageReaderUI(QObject): self._ui_lock.release() @pyqtSlot(str) - def onSizeChanged(self, value): - if (len(value) > 0): - self.size = float(value) - else: - self.size = 0 + def onWidthChanged(self, value): + if self._ui_view and not self._disable_size_callbacks: + if (len(value) > 0): + self._width = float(value) + else: + self._width = 0 + + self._depth = self._width/self._aspect + self._disable_size_callbacks = True + self._ui_view.findChild(QObject, "Depth").setProperty("text", str(self._depth)) + self._disable_size_callbacks = False + + @pyqtSlot(str) + def onDepthChanged(self, value): + if self._ui_view and not self._disable_size_callbacks: + if (len(value) > 0): + self._depth = float(value) + else: + self._depth = 0 + + self._width = self._depth*self._aspect + self._disable_size_callbacks = True + self._ui_view.findChild(QObject, "Width").setProperty("text", str(self._width)) + self._disable_size_callbacks = False @pyqtSlot(str) def onBaseHeightChanged(self, value): @@ -98,3 +146,10 @@ class ImageReaderUI(QObject): @pyqtSlot(float) def onSmoothingChanged(self, value): self.smoothing = int(value) + + @pyqtSlot(int) + def onImageColorInvertChanged(self, value): + if (value == 1): + self.image_color_invert = True + else: + self.image_color_invert = False