mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-15 00:02:52 -04:00
[processing] evaluate parameters before executing algorithm
This allows a better use of expressions
This commit is contained in:
parent
01f380863f
commit
e353d226a8
@ -199,7 +199,7 @@ class GeoAlgorithm(object):
|
||||
self.setOutputCRS()
|
||||
self.resolveTemporaryOutputs()
|
||||
self.resolveDataObjects()
|
||||
self.resolveMinCoveringExtent()
|
||||
self.evaluateParameterValues()
|
||||
self.checkOutputFileExtensions()
|
||||
self.runPreExecutionScript(progress)
|
||||
self.processAlgorithm(progress)
|
||||
@ -207,7 +207,7 @@ class GeoAlgorithm(object):
|
||||
self.convertUnsupportedFormats(progress)
|
||||
self.runPostExecutionScript(progress)
|
||||
except GeoAlgorithmExecutionException as gaee:
|
||||
lines = [self.tr('Uncaught error while executing algorithm')]
|
||||
lines = [self.tr('Error while executing algorithm')]
|
||||
lines.append(traceback.format_exc())
|
||||
ProcessingLog.addToLog(ProcessingLog.LOG_ERROR, gaee.msg)
|
||||
raise GeoAlgorithmExecutionException(gaee.msg, lines, gaee)
|
||||
@ -340,11 +340,9 @@ class GeoAlgorithm(object):
|
||||
if not os.path.isabs(out.value):
|
||||
continue
|
||||
if isinstance(out, OutputRaster):
|
||||
exts = \
|
||||
dataobjects.getSupportedOutputRasterLayerExtensions()
|
||||
exts = dataobjects.getSupportedOutputRasterLayerExtensions()
|
||||
elif isinstance(out, OutputVector):
|
||||
exts = \
|
||||
dataobjects.getSupportedOutputVectorLayerExtensions()
|
||||
exts = dataobjects.getSupportedOutputVectorLayerExtensions()
|
||||
elif isinstance(out, OutputTable):
|
||||
exts = dataobjects.getSupportedOutputTableExtensions()
|
||||
elif isinstance(out, OutputHTML):
|
||||
@ -360,60 +358,12 @@ class GeoAlgorithm(object):
|
||||
out.value = out.value + '.' + exts[0]
|
||||
|
||||
|
||||
def canUseAutoExtent(self):
|
||||
def evaluateParameterValues(self):
|
||||
for param in self.parameters:
|
||||
if isinstance(param, (ParameterRaster, ParameterVector)):
|
||||
return True
|
||||
if isinstance(param, ParameterMultipleInput):
|
||||
return True
|
||||
return False
|
||||
|
||||
def resolveMinCoveringExtent(self):
|
||||
for param in self.parameters:
|
||||
if isinstance(param, ParameterExtent):
|
||||
if param.value is None:
|
||||
param.value = self.getMinCoveringExtent()
|
||||
|
||||
def getMinCoveringExtent(self):
|
||||
first = True
|
||||
found = False
|
||||
for param in self.parameters:
|
||||
if param.value:
|
||||
if isinstance(param, (ParameterRaster, ParameterVector)):
|
||||
if isinstance(param.value, (QgsRasterLayer,
|
||||
QgsVectorLayer)):
|
||||
layer = param.value
|
||||
else:
|
||||
layer = dataobjects.getObject(param.value)
|
||||
if layer:
|
||||
found = True
|
||||
self.addToRegion(layer, first)
|
||||
first = False
|
||||
elif isinstance(param, ParameterMultipleInput):
|
||||
layers = param.value.split(';')
|
||||
for layername in layers:
|
||||
layer = dataobjects.getObject(layername)
|
||||
if layer:
|
||||
found = True
|
||||
self.addToRegion(layer, first)
|
||||
first = False
|
||||
if found:
|
||||
return '{},{},{},{}'.format(
|
||||
self.xmin, self.xmax, self.ymin, self.ymax)
|
||||
else:
|
||||
return None
|
||||
|
||||
def addToRegion(self, layer, first):
|
||||
if first:
|
||||
self.xmin = layer.extent().xMinimum()
|
||||
self.xmax = layer.extent().xMaximum()
|
||||
self.ymin = layer.extent().yMinimum()
|
||||
self.ymax = layer.extent().yMaximum()
|
||||
else:
|
||||
self.xmin = min(self.xmin, layer.extent().xMinimum())
|
||||
self.xmax = max(self.xmax, layer.extent().xMaximum())
|
||||
self.ymin = min(self.ymin, layer.extent().yMinimum())
|
||||
self.ymax = max(self.ymax, layer.extent().yMaximum())
|
||||
try:
|
||||
param.evaluate(self)
|
||||
except ValueError, e:
|
||||
raise GeoAlgorithmExecutionException(str(e))
|
||||
|
||||
def resolveTemporaryOutputs(self):
|
||||
"""Sets temporary outputs (output.value = None) with a
|
||||
|
@ -33,11 +33,12 @@ import os
|
||||
from inspect import isclass
|
||||
from copy import deepcopy
|
||||
|
||||
|
||||
from qgis.utils import iface
|
||||
from qgis.PyQt.QtCore import QCoreApplication
|
||||
from qgis.core import QgsRasterLayer, QgsVectorLayer, QgsMapLayer, QgsCoordinateReferenceSystem
|
||||
from qgis.core import (QgsRasterLayer, QgsVectorLayer, QgsMapLayer, QgsCoordinateReferenceSystem,
|
||||
QgsExpressionContext, QgsExpressionContextUtils, QgsExpression, QgsExpressionContextScope)
|
||||
|
||||
from processing.tools.vector import resolveFieldIndex, features
|
||||
from processing.tools.system import isWindows
|
||||
from processing.tools import dataobjects
|
||||
|
||||
def parseBool(s):
|
||||
@ -58,7 +59,47 @@ def _splitParameterOptions(line):
|
||||
def _createDescriptiveName(s):
|
||||
return s.replace('_', ' ')
|
||||
|
||||
class Parameter(object):
|
||||
def _expressionContext():
|
||||
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
|
||||
|
||||
class Parameter:
|
||||
|
||||
"""
|
||||
Base class for all parameters that a geoalgorithm might
|
||||
@ -148,6 +189,9 @@ class Parameter(object):
|
||||
wrapper = wrapper(self, dialog, row, col)
|
||||
# or a wrapper instance
|
||||
return wrapper
|
||||
|
||||
def evaluate(self, alg):
|
||||
pass
|
||||
|
||||
class ParameterBoolean(Parameter):
|
||||
|
||||
@ -330,6 +374,51 @@ class ParameterExtent(Parameter):
|
||||
default = definition.strip()[len('extent') + 1:] or None
|
||||
return ParameterExtent(name, descName, default, isOptional)
|
||||
|
||||
def evaluate(self, alg):
|
||||
if self.optional and not bool(self.value):
|
||||
self.value = self.getMinCoveringExtent()
|
||||
|
||||
def getMinCoveringExtent(self, alg):
|
||||
first = True
|
||||
found = False
|
||||
for param in alg.parameters:
|
||||
if param.value:
|
||||
if isinstance(param, (ParameterRaster, ParameterVector)):
|
||||
if isinstance(param.value, (QgsRasterLayer,
|
||||
QgsVectorLayer)):
|
||||
layer = param.value
|
||||
else:
|
||||
layer = dataobjects.getObject(param.value)
|
||||
if layer:
|
||||
found = True
|
||||
self.addToRegion(layer, first)
|
||||
first = False
|
||||
elif isinstance(param, ParameterMultipleInput):
|
||||
layers = param.value.split(';')
|
||||
for layername in layers:
|
||||
layer = dataobjects.getObject(layername)
|
||||
if layer:
|
||||
found = True
|
||||
self.addToRegion(layer, first)
|
||||
first = False
|
||||
if found:
|
||||
return '{},{},{},{}'.format(
|
||||
self.xmin, self.xmax, self.ymin, self.ymax)
|
||||
else:
|
||||
return None
|
||||
|
||||
def addToRegion(self, layer, first):
|
||||
if first:
|
||||
self.xmin = layer.extent().xMinimum()
|
||||
self.xmax = layer.extent().xMaximum()
|
||||
self.ymin = layer.extent().yMinimum()
|
||||
self.ymax = layer.extent().yMaximum()
|
||||
else:
|
||||
self.xmin = min(self.xmin, layer.extent().xMinimum())
|
||||
self.xmax = max(self.xmax, layer.extent().xMaximum())
|
||||
self.ymin = min(self.ymin, layer.extent().yMinimum())
|
||||
self.ymax = max(self.ymax, layer.extent().yMaximum())
|
||||
|
||||
|
||||
class ParameterPoint(Parameter):
|
||||
|
||||
@ -715,21 +804,30 @@ class ParameterNumber(Parameter):
|
||||
self.value = None
|
||||
return True
|
||||
|
||||
try:
|
||||
if float(n) - int(float(n)) == 0:
|
||||
value = int(float(n))
|
||||
else:
|
||||
value = float(n)
|
||||
if self.min is not None:
|
||||
if value < self.min:
|
||||
return False
|
||||
if self.max is not None:
|
||||
if value > self.max:
|
||||
return False
|
||||
self.value = value
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
if isinstance(n, basestring):
|
||||
try:
|
||||
v = self._evaluate(n)
|
||||
float(v)
|
||||
self.value = n
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
else:
|
||||
try:
|
||||
if float(n) - int(float(n)) == 0:
|
||||
value = int(float(n))
|
||||
else:
|
||||
value = float(n)
|
||||
if self.min is not None:
|
||||
if value < self.min:
|
||||
return False
|
||||
if self.max is not None:
|
||||
if value > self.max:
|
||||
return False
|
||||
self.value = value
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def getAsScriptCode(self):
|
||||
param_type = ''
|
||||
@ -745,6 +843,31 @@ class ParameterNumber(Parameter):
|
||||
if definition.lower().strip().startswith('number'):
|
||||
default = definition.strip()[len('number') + 1:] or None
|
||||
return ParameterNumber(name, descName, default=default, optional=isOptional)
|
||||
|
||||
def _evaluate(self, v):
|
||||
exp = QgsExpression(v)
|
||||
if exp.hasParserError():
|
||||
raise ValueError(self.tr("Error in parameter expression: ") + exp.parserErrorString())
|
||||
result = exp.evaluate(_expressionContext())
|
||||
if exp.hasEvalError():
|
||||
raise ValueError("Error evaluating parameter expression: " + exp.evalErrorString())
|
||||
return result
|
||||
|
||||
def evaluate(self, alg):
|
||||
if isinstance(self.value, basestring):
|
||||
self.value = self._evaluate(self.value)
|
||||
|
||||
def expressionContext(self):
|
||||
return _expressionContext()
|
||||
|
||||
def getValueAsCommandLineParameter(self):
|
||||
if self.value is None:
|
||||
return str(None)
|
||||
if isinstance(self.value, basestring):
|
||||
return '"%s"' % self.value
|
||||
return str(self.value)
|
||||
|
||||
|
||||
|
||||
class ParameterRange(Parameter):
|
||||
|
||||
@ -919,6 +1042,13 @@ class ParameterSelection(Parameter):
|
||||
return ParameterSelection(name, descName, options, optional=isOptional)
|
||||
|
||||
|
||||
class ParameterEvaluationException(Exception):
|
||||
|
||||
def __init__(self, param, msg):
|
||||
Exception.__init__(msg)
|
||||
self.param = param
|
||||
|
||||
|
||||
class ParameterString(Parameter):
|
||||
|
||||
default_metadata = {
|
||||
@ -976,6 +1106,18 @@ class ParameterString(Parameter):
|
||||
return ParameterString(name, descName, default, multiline=True, optional=isOptional)
|
||||
else:
|
||||
return ParameterString(name, descName, multiline=True, optional=isOptional)
|
||||
|
||||
def evaluate(self, alg):
|
||||
exp = QgsExpression(self.value)
|
||||
if exp.hasParserError():
|
||||
raise ValueError(self.tr("Error in parameter expression: ") + exp.parserErrorString())
|
||||
result = exp.evaluate(_expressionContext())
|
||||
if exp.hasEvalError():
|
||||
raise ValueError("Error evaluating parameter expression: " + exp.evalErrorString())
|
||||
self.value = result
|
||||
|
||||
def expressionContext(self):
|
||||
return _expressionContext()
|
||||
|
||||
class ParameterTable(ParameterDataObject):
|
||||
|
||||
|
@ -31,7 +31,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, QgsExpressionContext, QgsExpressionContextUtils, QgsExpression
|
||||
from qgis.core import QgsMapLayerRegistry
|
||||
|
||||
from processing.core.ProcessingLog import ProcessingLog
|
||||
from processing.core.ProcessingConfig import ProcessingConfig
|
||||
@ -62,8 +62,6 @@ from processing.core.outputs import OutputRaster
|
||||
from processing.core.outputs import OutputVector
|
||||
from processing.core.outputs import OutputTable
|
||||
|
||||
from processing.tools import dataobjects
|
||||
|
||||
|
||||
class AlgorithmDialog(AlgorithmDialogBase):
|
||||
|
||||
@ -114,18 +112,6 @@ 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, wrapper, alg=None):
|
||||
return param.setValue(wrapper.value())
|
||||
|
||||
|
@ -1,153 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
***************************************************************************
|
||||
NumberInputDialog.py
|
||||
---------------------
|
||||
Date : August 2012
|
||||
Copyright : (C) 2012 by Victor Olaya
|
||||
Email : volayaf at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************
|
||||
"""
|
||||
from builtins import str
|
||||
from builtins import range
|
||||
|
||||
__author__ = 'Victor Olaya'
|
||||
__date__ = 'August 2012'
|
||||
__copyright__ = '(C) 2012, Victor Olaya'
|
||||
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
import os
|
||||
|
||||
from qgis.PyQt import uic
|
||||
from qgis.PyQt.QtWidgets import QDialog, QTreeWidgetItem, QMessageBox
|
||||
from qgis.core import QgsRasterLayer
|
||||
|
||||
from qgis.utils import iface
|
||||
from processing.tools import dataobjects
|
||||
|
||||
pluginPath = os.path.split(os.path.dirname(__file__))[0]
|
||||
WIDGET, BASE = uic.loadUiType(
|
||||
os.path.join(pluginPath, 'ui', 'DlgNumberInput.ui'))
|
||||
|
||||
|
||||
class NumberInputDialog(BASE, WIDGET):
|
||||
|
||||
def __init__(self, isInteger):
|
||||
super(NumberInputDialog, self).__init__(None)
|
||||
self.setupUi(self)
|
||||
|
||||
if hasattr(self.leFormula, 'setPlaceholderText'):
|
||||
self.leFormula.setPlaceholderText(
|
||||
self.tr('[Enter your formula here]'))
|
||||
|
||||
self.treeValues.doubleClicked.connect(self.addValue)
|
||||
|
||||
self.value = None
|
||||
self.isInteger = isInteger
|
||||
|
||||
if not self.isInteger:
|
||||
self.lblWarning.hide()
|
||||
|
||||
self.fillTree()
|
||||
|
||||
def fillTree(self):
|
||||
layersItem = QTreeWidgetItem()
|
||||
layersItem.setText(0, self.tr('Values from data layers extents'))
|
||||
self.treeValues.addTopLevelItem(layersItem)
|
||||
layers = dataobjects.getAllLayers()
|
||||
for layer in layers:
|
||||
layerItem = QTreeWidgetItem()
|
||||
layerItem.setText(0, str(layer.name()))
|
||||
layerItem.addChild(TreeValueItem(self.tr('Min X'),
|
||||
layer.extent().xMinimum()))
|
||||
layerItem.addChild(TreeValueItem(self.tr('Max X'),
|
||||
layer.extent().xMaximum()))
|
||||
layerItem.addChild(TreeValueItem(self.tr('Min Y'),
|
||||
layer.extent().yMinimum()))
|
||||
layerItem.addChild(TreeValueItem(self.tr('Max Y'),
|
||||
layer.extent().yMaximum()))
|
||||
if isinstance(layer, QgsRasterLayer):
|
||||
cellsize = (layer.extent().xMaximum()
|
||||
- layer.extent().xMinimum()) / layer.width()
|
||||
layerItem.addChild(TreeValueItem(self.tr('Cellsize'),
|
||||
cellsize))
|
||||
layersItem.addChild(layerItem)
|
||||
|
||||
layersItem = QTreeWidgetItem()
|
||||
layersItem.setText(0, self.tr('Values from raster layers statistics'))
|
||||
self.treeValues.addTopLevelItem(layersItem)
|
||||
layers = dataobjects.getRasterLayers()
|
||||
for layer in layers:
|
||||
for i in range(layer.bandCount()):
|
||||
stats = layer.dataProvider().bandStatistics(i + 1)
|
||||
layerItem = QTreeWidgetItem()
|
||||
layerItem.setText(0, str(layer.name()))
|
||||
layerItem.addChild(TreeValueItem(self.tr('Mean'), stats.mean))
|
||||
layerItem.addChild(TreeValueItem(self.tr('Std. deviation'),
|
||||
stats.stdDev))
|
||||
layerItem.addChild(TreeValueItem(self.tr('Max value'),
|
||||
stats.maximumValue))
|
||||
layerItem.addChild(TreeValueItem(self.tr('Min value'),
|
||||
stats.minimumValue))
|
||||
layersItem.addChild(layerItem)
|
||||
|
||||
canvasItem = QTreeWidgetItem()
|
||||
canvasItem.setText(0, self.tr('Values from QGIS map canvas'))
|
||||
self.treeValues.addTopLevelItem(canvasItem)
|
||||
extent = iface.mapCanvas().extent()
|
||||
extentItem = QTreeWidgetItem()
|
||||
extentItem.setText(0, self.tr('Current extent'))
|
||||
extentItem.addChild(TreeValueItem(self.tr('Min X'), extent.xMinimum()))
|
||||
extentItem.addChild(TreeValueItem(self.tr('Max X'), extent.xMaximum()))
|
||||
extentItem.addChild(TreeValueItem(self.tr('Min Y'), extent.yMinimum()))
|
||||
extentItem.addChild(TreeValueItem(self.tr('Max Y'), extent.yMaximum()))
|
||||
canvasItem.addChild(extentItem)
|
||||
|
||||
extent = iface.mapCanvas().fullExtent()
|
||||
extentItem = QTreeWidgetItem()
|
||||
extentItem.setText(0,
|
||||
self.tr('Full extent of all layers in map canvas'))
|
||||
extentItem.addChild(TreeValueItem(self.tr('Min X'), extent.xMinimum()))
|
||||
extentItem.addChild(TreeValueItem(self.tr('Max X'), extent.xMaximum()))
|
||||
extentItem.addChild(TreeValueItem(self.tr('Min Y'), extent.yMinimum()))
|
||||
extentItem.addChild(TreeValueItem(self.tr('Max Y'), extent.yMaximum()))
|
||||
canvasItem.addChild(extentItem)
|
||||
|
||||
def addValue(self):
|
||||
item = self.treeValues.currentItem()
|
||||
if isinstance(item, TreeValueItem):
|
||||
formula = self.leFormula.text() + ' ' + str(item.value)
|
||||
self.leFormula.setText(formula.strip())
|
||||
|
||||
def accept(self):
|
||||
try:
|
||||
self.value = float(eval(str(self.leFormula.text())))
|
||||
if self.isInteger:
|
||||
self.value = int(round(self.value))
|
||||
QDialog.accept(self)
|
||||
except:
|
||||
QMessageBox.critical(self, self.tr('Wrong expression'),
|
||||
self.tr('The expression entered is not correct'))
|
||||
|
||||
def reject(self):
|
||||
self.value = None
|
||||
QDialog.reject(self)
|
||||
|
||||
|
||||
class TreeValueItem(QTreeWidgetItem):
|
||||
|
||||
def __init__(self, name, value):
|
||||
QTreeWidgetItem.__init__(self)
|
||||
self.value = value
|
||||
self.setText(0, name + ': ' + str(value))
|
@ -32,131 +32,44 @@ from qgis.PyQt import uic
|
||||
from qgis.PyQt.QtCore import pyqtSignal
|
||||
from qgis.PyQt.QtWidgets import QDialog
|
||||
|
||||
from math import log10, floor
|
||||
from qgis.core import (QgsDataSourceUri,
|
||||
QgsCredentials,
|
||||
QgsExpressionContext,
|
||||
QgsExpressionContextUtils,
|
||||
QgsExpression,
|
||||
QgsRasterLayer,
|
||||
QgsExpressionContextScope)
|
||||
QgsRasterLayer)
|
||||
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(
|
||||
os.path.join(pluginPath, 'ui', 'widgetNumberSelector.ui'))
|
||||
os.path.join(pluginPath, 'ui', 'widgetBaseSelector.ui'))
|
||||
|
||||
|
||||
class NumberInputPanel(BASE, WIDGET):
|
||||
|
||||
hasChanged = pyqtSignal()
|
||||
|
||||
def __init__(self, number, minimum, maximum, isInteger):
|
||||
def __init__(self, param):
|
||||
super(NumberInputPanel, self).__init__(None)
|
||||
self.setupUi(self)
|
||||
|
||||
self.spnValue.setExpressionsEnabled(True)
|
||||
self.isInteger = isInteger
|
||||
if self.isInteger:
|
||||
self.spnValue.setDecimals(0)
|
||||
else:
|
||||
# Guess reasonable step value
|
||||
if (maximum == 0 or maximum) and (minimum == 0 or minimum):
|
||||
self.spnValue.setSingleStep(self.calculateStep(minimum, maximum))
|
||||
self.param = param
|
||||
self.text = param.default
|
||||
|
||||
if maximum == 0 or maximum:
|
||||
self.spnValue.setMaximum(maximum)
|
||||
else:
|
||||
self.spnValue.setMaximum(99999999)
|
||||
if minimum == 0 or minimum:
|
||||
self.spnValue.setMinimum(minimum)
|
||||
else:
|
||||
self.spnValue.setMinimum(-99999999)
|
||||
|
||||
# Set default value
|
||||
if number == 0 or number:
|
||||
self.spnValue.setValue(float(number))
|
||||
self.spnValue.setClearValue(float(number))
|
||||
elif minimum == 0 or minimum:
|
||||
self.spnValue.setValue(float(minimum))
|
||||
self.spnValue.setClearValue(float(minimum))
|
||||
else:
|
||||
self.spnValue.setValue(0)
|
||||
self.spnValue.setClearValue(0)
|
||||
|
||||
self.btnCalc.setFixedHeight(self.spnValue.height())
|
||||
|
||||
self.btnCalc.clicked.connect(self.showExpressionsBuilder)
|
||||
|
||||
self.spnValue.valueChanged.connect(lambda: self.hasChanged.emit())
|
||||
self.btnSelect.clicked.connect(self.showExpressionsBuilder)
|
||||
self.leText.textChanged.connect(lambda: self.hasChanged.emit())
|
||||
|
||||
def showExpressionsBuilder(self):
|
||||
context = self.expressionContext()
|
||||
dlg = QgsExpressionBuilderDialog(None, self.spnValue.text(), self, 'generic', context)
|
||||
context = self.param.expressionContext()
|
||||
dlg = QgsExpressionBuilderDialog(None, self.leText.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
|
||||
self.setValue(dlg.expressionText())
|
||||
|
||||
|
||||
def getValue(self):
|
||||
return self.spnValue.value()
|
||||
return self.leText.text()
|
||||
|
||||
def setValue(self, value):
|
||||
self.spnValue.setValue(value)
|
||||
|
||||
def calculateStep(self, minimum, maximum):
|
||||
valueRange = maximum - minimum
|
||||
if valueRange <= 1.0:
|
||||
step = valueRange / 10.0
|
||||
# round to 1 significant figure
|
||||
return round(step, -int(floor(log10(step))))
|
||||
else:
|
||||
return 1.0
|
||||
self.leText.setText(unicode(value))
|
||||
|
74
python/plugins/processing/gui/StringInputPanel.py
Normal file
74
python/plugins/processing/gui/StringInputPanel.py
Normal file
@ -0,0 +1,74 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
***************************************************************************
|
||||
NumberInputPanel.py
|
||||
---------------------
|
||||
Date : August 2016
|
||||
Copyright : (C) 2016 by Victor Olaya
|
||||
Email : volayaf at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************
|
||||
"""
|
||||
|
||||
__author__ = 'Victor Olaya'
|
||||
__date__ = 'August 2016'
|
||||
__copyright__ = '(C) 2016, Victor Olaya'
|
||||
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
import os
|
||||
|
||||
from qgis.PyQt import uic
|
||||
from qgis.PyQt.QtCore import pyqtSignal
|
||||
from qgis.PyQt.QtWidgets import QDialog
|
||||
|
||||
from qgis.core import (QgsDataSourceUri,
|
||||
QgsCredentials,
|
||||
QgsExpression,
|
||||
QgsRasterLayer)
|
||||
from qgis.gui import QgsEncodingFileDialog, QgsExpressionBuilderDialog
|
||||
from qgis.utils import iface
|
||||
|
||||
pluginPath = os.path.split(os.path.dirname(__file__))[0]
|
||||
WIDGET, BASE = uic.loadUiType(
|
||||
os.path.join(pluginPath, 'ui', 'widgetBaseSelector.ui'))
|
||||
|
||||
|
||||
class StringInputPanel(BASE, WIDGET):
|
||||
|
||||
hasChanged = pyqtSignal()
|
||||
|
||||
def __init__(self, param):
|
||||
super(StringInputPanel, self).__init__(None)
|
||||
self.setupUi(self)
|
||||
|
||||
self.param = param
|
||||
self.text = param.default
|
||||
|
||||
self.btnSelect.clicked.connect(self.showExpressionsBuilder)
|
||||
self.leText.textChanged.connect(lambda: self.hasChanged.emit())
|
||||
|
||||
def showExpressionsBuilder(self):
|
||||
context = self.param.expressionContext()
|
||||
dlg = QgsExpressionBuilderDialog(None, self.leText.text(), self, 'generic', context)
|
||||
dlg.setWindowTitle(self.tr('Expression based input'))
|
||||
if dlg.exec_() == QDialog.Accepted:
|
||||
exp = QgsExpression(dlg.expressionText())
|
||||
if not exp.hasParserError():
|
||||
self.setValue(dlg.expressionText())
|
||||
|
||||
|
||||
def getValue(self):
|
||||
return self.leText.text()
|
||||
|
||||
def setValue(self, value):
|
||||
self.leText.setText(unicode(value))
|
@ -50,6 +50,7 @@ from processing.gui.MultipleInputPanel import MultipleInputPanel
|
||||
from processing.gui.BatchInputSelectionPanel import BatchInputSelectionPanel
|
||||
from processing.gui.FixedTablePanel import FixedTablePanel
|
||||
from processing.gui.ExtentSelectionPanel import ExtentSelectionPanel
|
||||
from processing.gui.StringInputPanel import StringInputPanel
|
||||
|
||||
|
||||
DIALOG_STANDARD = 'standard'
|
||||
@ -442,8 +443,7 @@ class NumberWidgetWrapper(WidgetWrapper):
|
||||
|
||||
def createWidget(self):
|
||||
if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
|
||||
return NumberInputPanel(self.param.default, self.param.min, self.param.max,
|
||||
self.param.isInteger)
|
||||
return NumberInputPanel(self.param)
|
||||
else:
|
||||
widget = QComboBox()
|
||||
widget.setEditable(True)
|
||||
@ -605,9 +605,9 @@ class StringWidgetWrapper(WidgetWrapper):
|
||||
if self.param.default:
|
||||
widget.setPlainText(self.param.default)
|
||||
else:
|
||||
widget = QLineEdit()
|
||||
widget = StringInputPanel(self.param)
|
||||
if self.param.default:
|
||||
widget.setText(self.param.default)
|
||||
widget.setValue(self.param.default)
|
||||
elif self.dialogType == DIALOG_BATCH:
|
||||
widget = QLineEdit()
|
||||
if self.param.default:
|
||||
@ -642,22 +642,10 @@ class StringWidgetWrapper(WidgetWrapper):
|
||||
if self.param.multiline:
|
||||
text = self.widget.toPlainText()
|
||||
else:
|
||||
text = self.widget.text()
|
||||
|
||||
if self.param.evaluateExpressions:
|
||||
try:
|
||||
text = self.evaluateExpression(text)
|
||||
except:
|
||||
pass
|
||||
text = self.widget.getValue()
|
||||
return text
|
||||
if self.dialogType == DIALOG_BATCH:
|
||||
text = self.widget.text()
|
||||
if self.param.evaluateExpressions:
|
||||
try:
|
||||
text = self.evaluateExpression(text)
|
||||
except:
|
||||
pass
|
||||
return text
|
||||
else:
|
||||
if self.param.multiline:
|
||||
value = self.widget.getValue()
|
||||
|
@ -1,58 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>251</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QgsDoubleSpinBox" name="spnValue">
|
||||
<property name="decimals">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>-99999999.999999001622200</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>99999999.999999001622200</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="btnCalc">
|
||||
<property name="toolTip">
|
||||
<string>Open number input dialog</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QgsDoubleSpinBox</class>
|
||||
<extends>QDoubleSpinBox</extends>
|
||||
<header>qgis.gui</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
Loading…
x
Reference in New Issue
Block a user