diff --git a/python/plugins/processing/algs/qgis/FieldsCalculator.py b/python/plugins/processing/algs/qgis/FieldsCalculator.py index cbc6f988009..d97df4332d9 100644 --- a/python/plugins/processing/algs/qgis/FieldsCalculator.py +++ b/python/plugins/processing/algs/qgis/FieldsCalculator.py @@ -89,11 +89,6 @@ class FieldsCalculator(GeoAlgorithm): output = self.getOutputFromName(self.OUTPUT_LAYER) - if output.value == '': - ext = output.getDefaultFileExtension(self) - output.value = system.getTempFilenameInTempFolder( - output.name + '.' + ext) - fields = layer.fields() if newField: fields.append(QgsField(fieldName, fieldType, '', width, precision)) diff --git a/python/plugins/processing/core/GeoAlgorithm.py b/python/plugins/processing/core/GeoAlgorithm.py index bb10aa110c5..9162331fd0f 100644 --- a/python/plugins/processing/core/GeoAlgorithm.py +++ b/python/plugins/processing/core/GeoAlgorithm.py @@ -197,10 +197,8 @@ class GeoAlgorithm(object): self.model = model try: self.setOutputCRS() - self.resolveTemporaryOutputs() - self.resolveDataObjects() + self.resolveOutputs() self.evaluateParameterValues() - self.checkOutputFileExtensions() self.runPreExecutionScript(progress) self.processAlgorithm(progress) progress.setPercentage(100) @@ -328,51 +326,24 @@ class GeoAlgorithm(object): return name return 'GTiff' - def checkOutputFileExtensions(self): - """Checks if the values of outputs are correct and have one of - the supported output extensions. - - If not, it adds the first one of the supported extensions, which - is assumed to be the default one. - """ - for out in self.outputs: - if not out.hidden and out.value is not None: - if not os.path.isabs(out.value): - continue - if isinstance(out, OutputRaster): - exts = dataobjects.getSupportedOutputRasterLayerExtensions() - elif isinstance(out, OutputVector): - exts = dataobjects.getSupportedOutputVectorLayerExtensions() - elif isinstance(out, OutputTable): - exts = dataobjects.getSupportedOutputTableExtensions() - elif isinstance(out, OutputHTML): - exts = ['html', 'htm'] - else: - continue - idx = out.value.rfind('.') - if idx == -1: - out.value = out.value + '.' + exts[0] - else: - ext = out.value[idx + 1:] - if ext not in exts + ['dbf']: - out.value = out.value + '.' + exts[0] - - def evaluateParameterValues(self): for param in self.parameters: try: param.evaluate(self) except ValueError, e: + traceback.print_exc() raise GeoAlgorithmExecutionException(str(e)) - def resolveTemporaryOutputs(self): + def resolveOutputs(self): """Sets temporary outputs (output.value = None) with a - temporary file instead. + temporary file instead. Resolves expressions as well. """ - for out in self.outputs: - if not out.hidden and out.value is None: - setTempOutput(out, self) - + try: + for out in self.outputs: + out.resolveValue(self) + except ValueError, e: + raise GeoAlgorithmExecutionException(str(e)) + def setOutputCRS(self): layers = dataobjects.getAllLayers() for param in self.parameters: diff --git a/python/plugins/processing/core/outputs.py b/python/plugins/processing/core/outputs.py index 0e1a69049ef..b3e5d13371c 100644 --- a/python/plugins/processing/core/outputs.py +++ b/python/plugins/processing/core/outputs.py @@ -20,7 +20,6 @@ from builtins import str from builtins import range from builtins import object - __author__ = 'Victor Olaya' __date__ = 'August 2012' __copyright__ = '(C) 2012, Victor Olaya' @@ -29,15 +28,27 @@ __copyright__ = '(C) 2012, Victor Olaya' __revision__ = '$Format:%H$' +import os import sys from qgis.PyQt.QtCore import QCoreApplication, QSettings from processing.core.ProcessingConfig import ProcessingConfig -from processing.tools.system import isWindows, getTempFilenameInTempFolder +from processing.tools.system import isWindows, getTempFilenameInTempFolder, getTempDirInTempFolder from processing.tools.vector import VectorWriter, TableWriter from processing.tools import dataobjects +from qgis.core import QgsExpressionContext, QgsExpressionContextUtils, QgsExpression, QgsExpressionContextScope + +def _expressionContext(alg): + context = QgsExpressionContext() + context.appendScope(QgsExpressionContextUtils.globalScope()) + context.appendScope(QgsExpressionContextUtils.projectScope()) + processingScope = QgsExpressionContextScope() + for param in alg.parameters: + processingScope.setVariable('%s_value' % param.name, '') + context.appendScope(processingScope) + return context class Output(object): @@ -82,6 +93,44 @@ class Output(object): return True except: return False + + def _resolveTemporary(self, alg): + ext = self.getDefaultFileExtension() + return getTempFilenameInTempFolder(self.name + '.' + ext) + + def _supportedExtensions(self): + return [] + + def resolveValue(self, alg): + if not self.hidden and not bool(self.value): + self.value = self._resolveTemporary(alg) + else: + exp = QgsExpression(self.value) + if exp.hasParserError(): + raise ValueError(self.tr("Error in output expression: ") + exp.parserErrorString()) + self.value = exp.evaluate(_expressionContext(alg)) + if exp.hasEvalError(): + raise ValueError("Error evaluating output expression: " + exp.evalErrorString()) + + + + print self.value + if ":" not in self.value: + if not os.path.isabs(self.value): + self.value = os.path.join(ProcessingConfig.getSetting(ProcessingConfig.OUTPUT_FOLDER), + self.value) + supported = self._supportedExtensions() + if supported: + idx = self.value.rfind('.') + if idx == -1: + self.value = self.value + '.' + self.getDefaultFileExtension() + else: + ext = self.value[idx + 1:] + if ext not in supported: + self.value = self.value + '.' + self.getDefaultFileExtension() + + def expressionContext(self, alg): + return _expressionContext(alg) def typeName(self): return self.__class__.__name__.replace('Output', '').lower() @@ -93,7 +142,9 @@ class Output(object): class OutputDirectory(Output): - directory = True + + def resolveValue(self, alg): + self.value = getTempDirInTempFolder() class OutputExtent(Output): @@ -118,10 +169,7 @@ class OutputExtent(Output): class OutputCrs(Output): def __init__(self, name='', description=''): - self.name = name - self.description = description - self.value = None - self.hidden = True + Output.__init__(self, name, description, True) class OutputFile(Output): @@ -136,7 +184,7 @@ class OutputFile(Output): else: return self.tr('%s files(*.%s)', 'OutputFile') % (self.ext, self.ext) - def getDefaultFileExtension(self, alg): + def getDefaultFileExtension(self): return self.ext or 'file' @@ -145,17 +193,14 @@ class OutputHTML(Output): def getFileFilter(self, alg): return self.tr('HTML files(*.html)', 'OutputHTML') - def getDefaultFileExtension(self, alg): + def getDefaultFileExtension(self): return 'html' class OutputNumber(Output): def __init__(self, name='', description=''): - self.name = name - self.description = description - self.value = None - self.hidden = True + Output.__init__(self, name, description, True) class OutputRaster(Output): @@ -168,11 +213,8 @@ class OutputRaster(Output): exts[i] = self.tr('%s files (*.%s)', 'OutputVector') % (exts[i].upper(), exts[i].lower()) return ';;'.join(exts) - def getDefaultFileExtension(self, alg): - supported = alg.provider.getSupportedOutputRasterLayerExtensions() - default = ProcessingConfig.getSetting(ProcessingConfig.DEFAULT_OUTPUT_RASTER_LAYER_EXT) - ext = default if default in supported else supported[0] - return ext + def getDefaultFileExtension(self): + return ProcessingConfig.getSetting(ProcessingConfig.DEFAULT_OUTPUT_RASTER_LAYER_EXT) def getCompatibleFileName(self, alg): """ @@ -189,18 +231,17 @@ class OutputRaster(Output): return self.value else: if self.compatible is None: - self.compatible = getTempFilenameInTempFolder( - self.name + '.' + self.getDefaultFileExtension(alg)) + supported = alg.provider.getSupportedOutputRasterLayerExtensions() + default = ProcessingConfig.getSetting(ProcessingConfig.DEFAULT_OUTPUT_RASTER_LAYER_EXT) + ext = default if default in supported else supported[0] + self.compatible = getTempFilenameInTempFolder(self.name + '.' + ext) return self.compatible class OutputString(Output): def __init__(self, name='', description=''): - self.name = name - self.description = description - self.value = None - self.hidden = True + Output.__init__(self, name, description, True) class OutputTable(Output): @@ -209,13 +250,13 @@ class OutputTable(Output): compatible = None def getFileFilter(self, alg): - exts = ['csv'] + exts = ['dbf'] for i in range(len(exts)): exts[i] = exts[i].upper() + ' files(*.' + exts[i].lower() + ')' return ';;'.join(exts) - def getDefaultFileExtension(self, alg): - return alg.provider.getSupportedOutputTableExtensions()[0] + def getDefaultFileExtension(self): + return "dbf" def getCompatibleFileName(self, alg): """Returns a filename that is compatible with the algorithm @@ -233,7 +274,7 @@ class OutputTable(Output): else: if self.compatible is None: self.compatible = getTempFilenameInTempFolder( - self.name + '.' + self.getDefaultFileExtension(alg)) + self.name + '.' + alg.provider.getSupportedOutputTableExtensions()[0]) return self.compatible def getTableWriter(self, fields): @@ -286,14 +327,13 @@ class OutputVector(Output): exts[i] = self.tr('%s files (*.%s)', 'OutputVector') % (exts[i].upper(), exts[i].lower()) return ';;'.join(exts) - def getDefaultFileExtension(self, alg): - supported = alg.provider.getSupportedOutputVectorLayerExtensions() + def getDefaultFileExtension(self): if self.hasGeometry(): default = ProcessingConfig.getSetting(ProcessingConfig.DEFAULT_OUTPUT_VECTOR_LAYER_EXT) else: default = 'dbf' - ext = default if default in supported else supported[0] - return ext + return default + def getCompatibleFileName(self, alg): """Returns a filename that is compatible with the algorithm @@ -309,8 +349,10 @@ class OutputVector(Output): return self.value else: if self.compatible is None: - self.compatible = getTempFilenameInTempFolder( - self.name + '.' + self.getDefaultFileExtension(alg)) + default = self.getDefaultFileExtension() + supported = alg.provider.getSupportedOutputVectorLayerExtensions() + ext = default if default in supported else supported[0] + self.compatible = getTempFilenameInTempFolder(self.name + '.' + ext) return self.compatible def getVectorWriter(self, fields, geomType, crs, options=None): @@ -345,7 +387,13 @@ class OutputVector(Output): def dataType(self): return dataobjects.vectorDataType(self) - + def _resolveTemporary(self, alg): + if alg.provider.supportsNonFileBasedOutput(): + return "memory:" + else: + ext = self.getDefaultFileExtension() + return getTempFilenameInTempFolder(self.name + '.' + ext) + def getOutputFromString(s): try: diff --git a/python/plugins/processing/core/parameters.py b/python/plugins/processing/core/parameters.py index 2028dc27854..bbb0ef45fc7 100644 --- a/python/plugins/processing/core/parameters.py +++ b/python/plugins/processing/core/parameters.py @@ -102,6 +102,17 @@ def _expressionContext(): context.appendScope(processingScope) return context +def _resolveLayers(value): + layers = dataobjects.getAllLayers() + if value: + inputlayers = value.split(';') + for i, inputlayer in enumerate(inputlayers): + for layer in layers: + if layer.name() == inputlayer: + inputlayers[i] = layer.source() + break + return ";".join(inputlayers) + class Parameter: """ @@ -312,6 +323,9 @@ class ParameterDataObject(Parameter): s = dataobjects.normalizeLayerSource(str(self.value)) s = '"%s"' % s return s + + def evaluate(self, alg): + self.value = _resolveLayers(self.value) class ParameterExtent(Parameter): @@ -771,6 +785,9 @@ class ParameterMultipleInput(ParameterDataObject): return ParameterMultipleInput(name, definition, dataobjects.TYPE_VECTOR_ANY, isOptional) + def evaluate(self, alg): + self.value = _resolveLayers(self.value) + class ParameterNumber(Parameter): @@ -861,7 +878,7 @@ class ParameterNumber(Parameter): return result def evaluate(self, alg): - if isinstance(self.value, basestring): + if isinstance(self.value, basestring) and bool(self.value): self.value = self._evaluate(self.value) def _layerVariables(self, element, alg=None): @@ -1024,7 +1041,6 @@ class ParameterRaster(ParameterDataObject): def fromScriptCode(self, line): isOptional, name, definition = _splitParameterOptions(line) descName = _createDescriptiveName(name) - print isOptional, name, definition if definition.lower().strip().startswith('raster'): return ParameterRaster(name, descName, optional=isOptional) @@ -1111,7 +1127,7 @@ class ParameterString(Parameter): self.evaluateExpressions = parseBool(evaluateExpressions) def setValue(self, obj): - if obj is None: + if not bool(obj): if not self.optional: return False self.value = None @@ -1153,13 +1169,14 @@ class ParameterString(Parameter): 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 + if isinstance(self.value, basestring) and bool(self.value) and self.evaluateExpressions: + 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() diff --git a/python/plugins/processing/gui/OutputSelectionPanel.py b/python/plugins/processing/gui/OutputSelectionPanel.py index 58830956fba..a18dcfe20a9 100644 --- a/python/plugins/processing/gui/OutputSelectionPanel.py +++ b/python/plugins/processing/gui/OutputSelectionPanel.py @@ -83,14 +83,14 @@ class OutputSelectionPanel(BASE, WIDGET): if isinstance(self.output, OutputVector) \ and self.alg.provider.supportsNonFileBasedOutput(): - # use memory layers for temporary files if supported - actionSaveToTempFile = QAction( + # use memory layers for temporary layers if supported + actionSaveToTemp = QAction( self.tr('Create temporary layer'), self.btnSelect) else: - actionSaveToTempFile = QAction( + actionSaveToTemp = QAction( self.tr('Save to a temporary file'), self.btnSelect) - actionSaveToTempFile.triggered.connect(self.saveToTemporaryFile) - popupMenu.addAction(actionSaveToTempFile) + actionSaveToTemp.triggered.connect(self.saveToTemporary) + popupMenu.addAction(actionSaveToTemp) actionSaveToFile = QAction( self.tr('Save to file...'), self.btnSelect) @@ -121,22 +121,13 @@ class OutputSelectionPanel(BASE, WIDGET): popupMenu.exec_(QCursor.pos()) def showExpressionsBuilder(self): - dlg = QgsExpressionBuilderDialog(None, self.leText.text(), self, 'generic', self.expressionContext()) + dlg = QgsExpressionBuilderDialog(None, self.leText.text(), self, 'generic', + self.output.expressionContext(self.alg)) 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.alg.parameters: - processingScope.setVariable('%s_value' % param.name, '') - context.appendScope(processingScope) - return context - - def saveToTemporaryFile(self): + def saveToTemporary(self): self.leText.setText('') def saveToPostGIS(self): @@ -237,32 +228,4 @@ class OutputSelectionPanel(BASE, WIDGET): self.leText.setText(dirName) def getValue(self): - fileName = str(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.startswith("[") and fileName.endswith("]"): - fileName = fileName[1:-1] - if fileName.strip() in ['', self.SAVE_TO_TEMP_FILE, self.SAVE_TO_TEMP_LAYER]: - if isinstance(self.output, OutputVector) \ - and self.alg.provider.supportsNonFileBasedOutput(): - # use memory layers for temporary files if supported - value = 'memory:' - else: - value = None - elif fileName.startswith('memory:'): - value = fileName - elif fileName.startswith('postgis:'): - value = fileName - elif fileName.startswith('spatialite:'): - value = fileName - elif not os.path.isabs(fileName): - value = ProcessingConfig.getSetting( - ProcessingConfig.OUTPUT_FOLDER) + os.sep + fileName - else: - value = fileName - - return value + return self.leText.text()