diff --git a/python/plugins/processing/gui/AlgorithmDialog.py b/python/plugins/processing/gui/AlgorithmDialog.py index d13331af150..c5698edb2a6 100644 --- a/python/plugins/processing/gui/AlgorithmDialog.py +++ b/python/plugins/processing/gui/AlgorithmDialog.py @@ -29,7 +29,7 @@ from qgis.PyQt.QtCore import Qt from qgis.PyQt.QtWidgets import QMessageBox, QApplication, QPushButton, QWidget, QVBoxLayout from qgis.PyQt.QtGui import QCursor, QColor, QPalette -from qgis.core import QgsMapLayerRegistry +from qgis.core import QgsMapLayerRegistry, QgsExpressionContext, QgsExpressionContextUtils, QgsExpression from processing.core.ProcessingLog import ProcessingLog from processing.core.ProcessingConfig import ProcessingConfig @@ -102,8 +102,8 @@ class AlgorithmDialog(AlgorithmDialogBase): continue if not self.setParamValue( param, self.mainWidget.valueItems[param.name]): - raise AlgorithmDialogBase.InvalidParameterValue(param, - self.mainWidget.valueItems[param.name]) + raise AlgorithmDialogBase.InvalidParameterValue( + param, self.mainWidget.valueItems[param.name]) for param in params: if isinstance(param, ParameterExtent): @@ -121,6 +121,19 @@ class AlgorithmDialog(AlgorithmDialogBase): return True + def evaluateExpression(self, text): + context = QgsExpressionContext() + context.appendScope(QgsExpressionContextUtils.globalScope()) + context.appendScope(QgsExpressionContextUtils.projectScope()) + exp = QgsExpression(text) + if exp.hasParserError(): + raise Exception(exp.parserErrorString()) + result = exp.evaluate(context) + if exp.hasEvalError(): + raise ValueError(exp.evalErrorString()) + return result + + def setParamValue(self, param, widget, alg=None): if isinstance(param, ParameterRaster): return param.setValue(widget.getValue()) @@ -159,7 +172,12 @@ class AlgorithmDialog(AlgorithmDialogBase): if param.multiline: return param.setValue(unicode(widget.toPlainText())) else: - return param.setValue(unicode(widget.text())) + text = widget.text() + try: + text = self.evaluateExpression(text) + except: + return False + return param.setValue(text) elif isinstance(param, ParameterGeometryPredicate): return param.setValue(widget.value()) else: diff --git a/python/plugins/processing/gui/NumberInputPanel.py b/python/plugins/processing/gui/NumberInputPanel.py index 145e1ccd0b2..27b7298cd60 100644 --- a/python/plugins/processing/gui/NumberInputPanel.py +++ b/python/plugins/processing/gui/NumberInputPanel.py @@ -29,9 +29,15 @@ import os from qgis.PyQt import uic from qgis.PyQt.QtCore import pyqtSignal +from PyQt.QtGui import QDialog from math import log10, floor -from processing.gui.NumberInputDialog import NumberInputDialog +from qgis.core import (QgsDataSourceURI, QgsCredentials, QgsExpressionContext, + QgsExpressionContextUtils, QgsExpression, QgsRasterLayer, + QgsExpressionContextScope) +from qgis.gui import QgsEncodingFileDialog, QgsExpressionBuilderDialog +from qgis.utils import iface +from processing.tools import dataobjects pluginPath = os.path.split(os.path.dirname(__file__))[0] WIDGET, BASE = uic.loadUiType( @@ -46,6 +52,7 @@ class NumberInputPanel(BASE, WIDGET): super(NumberInputPanel, self).__init__(None) self.setupUi(self) + self.spnValue.setExpressionsEnabled(True) self.isInteger = isInteger if self.isInteger: self.spnValue.setDecimals(0) @@ -74,15 +81,65 @@ class NumberInputPanel(BASE, WIDGET): self.spnValue.setValue(0) self.spnValue.setClearValue(0) - self.btnCalc.clicked.connect(self.showNumberInputDialog) + self.btnCalc.setFixedHeight(self.spnValue.height()) + + self.btnCalc.clicked.connect(self.showExpressionsBuilder) self.spnValue.valueChanged.connect(lambda: self.hasChanged.emit()) - def showNumberInputDialog(self): - dlg = NumberInputDialog(self.isInteger) - dlg.exec_() - if dlg.value is not None: - self.spnValue.setValue(dlg.value) + def showExpressionsBuilder(self): + context = self.expressionContext() + dlg = QgsExpressionBuilderDialog(None, self.spnValue.text(), self, "generic", context) + dlg.setWindowTitle(self.tr("Expression based input")); + if dlg.exec_() == QDialog.Accepted: + exp = QgsExpression(dlg.expressionText()) + if not exp.hasParserError(): + result = exp.evaluate(context) + if not exp.hasEvalError(): + try: + self.spnValue.setValue(float(result)) + except: + pass + + def expressionContext(self): + context = QgsExpressionContext() + context.appendScope(QgsExpressionContextUtils.globalScope()) + context.appendScope(QgsExpressionContextUtils.projectScope()) + processingScope = QgsExpressionContextScope() + layers = dataobjects.getAllLayers() + for layer in layers: + name = layer.name() + processingScope.setVariable("%s_minx" % name, layer.extent().xMinimum()) + processingScope.setVariable("%s_miny" % name, layer.extent().yMinimum()) + processingScope.setVariable("%s_maxx" % name, layer.extent().xMaximum()) + processingScope.setVariable("%s_maxy" % name, layer.extent().yMaximum()) + if isinstance(layer, QgsRasterLayer): + cellsize = (layer.extent().xMaximum() + - layer.extent().xMinimum()) / layer.width() + processingScope.setVariable("%s_cellsize" % name, cellsize) + + layers = dataobjects.getRasterLayers() + for layer in layers: + for i in range(layer.bandCount()): + stats = layer.dataProvider().bandStatistics(i + 1) + processingScope.setVariable("%s_band%i_avg" % (name, i + 1), stats.mean) + processingScope.setVariable("%s_band%i_stddev" % (name, i + 1), stats.stdDev) + processingScope.setVariable("%s_band%i_min" % (name, i + 1), stats.minimumValue) + processingScope.setVariable("%s_band%i_max" % (name, i + 1), stats.maximumValue) + + extent = iface.mapCanvas().extent() + processingScope.setVariable("canvasextent_minx", extent.xMinimum()) + processingScope.setVariable("canvasextent_miny", extent.yMinimum()) + processingScope.setVariable("canvasextent_maxx", extent.xMaximum()) + processingScope.setVariable("canvasextent_maxy", extent.yMaximum()) + + extent = iface.mapCanvas().fullExtent() + processingScope.setVariable("fullextent_minx", extent.xMinimum()) + processingScope.setVariable("fullextent_miny", extent.yMinimum()) + processingScope.setVariable("fullextent_maxx", extent.xMaximum()) + processingScope.setVariable("fullextent_maxy", extent.yMaximum()) + context.appendScope(processingScope) + return context def getValue(self): return self.spnValue.value() diff --git a/python/plugins/processing/gui/OutputSelectionPanel.py b/python/plugins/processing/gui/OutputSelectionPanel.py index 06b7a82880e..68447f45e2f 100644 --- a/python/plugins/processing/gui/OutputSelectionPanel.py +++ b/python/plugins/processing/gui/OutputSelectionPanel.py @@ -32,8 +32,9 @@ from qgis.PyQt import uic from qgis.PyQt.QtCore import QCoreApplication, QSettings from qgis.PyQt.QtWidgets import QDialog, QMenu, QAction, QFileDialog from qgis.PyQt.QtGui import QCursor -from qgis.gui import QgsEncodingFileDialog -from qgis.core import QgsDataSourceURI, QgsCredentials +from qgis.gui import QgsEncodingFileDialog, QgsExpressionBuilderDialog +from qgis.core import QgsDataSourceURI, QgsCredentials, QgsExpressionContext,\ + QgsExpressionContextUtils, QgsExpression, QgsExpressionContextScope from processing.core.ProcessingConfig import ProcessingConfig from processing.core.outputs import OutputVector @@ -78,6 +79,11 @@ class OutputSelectionPanel(BASE, WIDGET): actionSaveToFile.triggered.connect(self.selectFile) popupMenu.addAction(actionSaveToFile) + actionShowExpressionsBuilder = QAction( + self.tr('Use expression...'), self.btnSelect) + actionShowExpressionsBuilder.triggered.connect(self.showExpressionsBuilder) + popupMenu.addAction(actionShowExpressionsBuilder) + if isinstance(self.output, OutputVector) \ and self.alg.provider.supportsNonFileBasedOutput(): actionSaveToMemory = QAction( @@ -100,6 +106,22 @@ class OutputSelectionPanel(BASE, WIDGET): popupMenu.exec_(QCursor.pos()) + def showExpressionsBuilder(self): + dlg = QgsExpressionBuilderDialog(None, self.leText.text(), self, "generic", self.expressionContext()) + dlg.setWindowTitle(self.tr("Expression based output")); + if dlg.exec_() == QDialog.Accepted: + self.leText.setText(dlg.expressionText()) + + def expressionContext(self): + context = QgsExpressionContext() + context.appendScope(QgsExpressionContextUtils.globalScope()) + context.appendScope(QgsExpressionContextUtils.projectScope()) + processingScope = QgsExpressionContextScope() + for param in self.ag.parameters: + processingScope.setVariable("%s_value" % param.name, "") + context.appendScope(processingScope) + return context + def saveToTemporaryFile(self): self.leText.setText('') @@ -202,6 +224,12 @@ class OutputSelectionPanel(BASE, WIDGET): def getValue(self): fileName = unicode(self.leText.text()) + context = self.expressionContext() + exp = QgsExpression(fileName) + if not exp.hasParserError(): + result = exp.evaluate(context) + if not exp.hasEvalError(): + fileName = result if fileName.strip() in ['', self.SAVE_TO_TEMP_FILE]: value = None elif fileName.startswith('memory:'): diff --git a/python/plugins/processing/ui/widgetBaseSelector.ui b/python/plugins/processing/ui/widgetBaseSelector.ui index aeed39bf4db..9a88e4ddd0f 100644 --- a/python/plugins/processing/ui/widgetBaseSelector.ui +++ b/python/plugins/processing/ui/widgetBaseSelector.ui @@ -21,10 +21,23 @@ 0 - + + + + 0 + 0 + + + + + + 0 + 0 + + ... diff --git a/python/plugins/processing/ui/widgetLayerSelector.ui b/python/plugins/processing/ui/widgetLayerSelector.ui index a57afc69a3d..4edcdf73058 100644 --- a/python/plugins/processing/ui/widgetLayerSelector.ui +++ b/python/plugins/processing/ui/widgetLayerSelector.ui @@ -6,8 +6,8 @@ 0 0 - 250 - 23 + 608 + 20 @@ -21,10 +21,23 @@ 0 - + + + + 0 + 0 + + + + + + 0 + 0 + + ... @@ -32,6 +45,12 @@ + + + 0 + 0 + + Iterate over this layer