From 8cc9a50a521bf3879700bb7b9a5ba32dc767bf74 Mon Sep 17 00:00:00 2001 From: volaya Date: Mon, 5 Sep 2016 12:59:21 +0200 Subject: [PATCH] [processing] moved script syntax from script algorithm class to parameters classes --- python/plugins/processing/core/parameters.py | 211 +++++++++++++++++- .../processing/script/ScriptAlgorithm.py | 139 +----------- 2 files changed, 206 insertions(+), 144 deletions(-) diff --git a/python/plugins/processing/core/parameters.py b/python/plugins/processing/core/parameters.py index a243a99118e..b276411a573 100644 --- a/python/plugins/processing/core/parameters.py +++ b/python/plugins/processing/core/parameters.py @@ -30,6 +30,7 @@ __revision__ = '$Format:%H$' import sys import os +import inspect from qgis.PyQt.QtCore import QCoreApplication from qgis.core import QgsRasterLayer, QgsVectorLayer @@ -37,19 +38,23 @@ from processing.tools.vector import resolveFieldIndex, features from processing.tools.system import isWindows from processing.tools import dataobjects - -def getParameterFromString(s): - tokens = s.split("|") - params = [t if str(t) != str(None) else None for t in tokens[1:]] - clazz = getattr(sys.modules[__name__], tokens[0]) - return clazz(*params) - - def parseBool(s): if s is None or s == str(None).lower(): return None return str(s).lower() == str(True).lower() +def _splitParameterOptions(line): + tokens = line.split('=', 1) + if tokens[1].lower().strip().startswith('optional'): + isOptional = True + definition = tokens[1].strip()[len('optional') + 1:] + else: + isOptional = False + definition = tokens[1] + return isOptional, tokens[0], definition + +def _createDescriptiveName(s): + return s.replace('_', ' ') class Parameter(object): @@ -128,8 +133,7 @@ class Parameter(object): if context == '': context = 'Parameter' return QCoreApplication.translate(context, string) - - + class ParameterBoolean(Parameter): default_metadata = { @@ -158,6 +162,20 @@ class ParameterBoolean(Parameter): param_type += 'optional ' param_type += 'boolean ' return '##' + self.name + '=' + param_type + str(self.default) + + @classmethod + def fromScriptCode(self, line): + isOptional, name, definition = _splitParameterOptions(line) + if definition.startswith("boolean"): + descName = _createDescriptiveName(name) + default = definition.strip()[len('boolean') + 1:] + if default: + param = ParameterBoolean(name, descName, default) + else: + param = ParameterBoolean(name, descName) + param.optional = isOptional + return param + class ParameterCrs(Parameter): @@ -194,6 +212,16 @@ class ParameterCrs(Parameter): param_type += 'crs ' return '##' + self.name + '=' + param_type + str(self.default) + @classmethod + def fromScriptCode(self, line): + isOptional, name, definition = _splitParameterOptions(line) + if definition.startswith("crs"): + descName = _createDescriptiveName(name) + default = definition.strip()[len('crs') + 1:] + if default: + return ParameterCrs(name, descName, default, isOptional) + else: + return ParameterCrs(name, descName, None, isOptional) class ParameterDataObject(Parameter): @@ -243,6 +271,14 @@ class ParameterExtent(Parameter): param_type += 'optional ' param_type += 'extent' return '##' + self.name + '=' + param_type + + @classmethod + def fromScriptCode(self, line): + isOptional, name, definition = _splitParameterOptions(line) + if definition.startswith("extent"): + descName = _createDescriptiveName(name) + default = definition.strip()[len('extent') + 1:] or None + return ParameterExtent(name, descName, default, isOptional) class ParameterPoint(Parameter): @@ -279,6 +315,14 @@ class ParameterPoint(Parameter): param_type += 'point' return '##' + self.name + '=' + param_type + @classmethod + def fromScriptCode(self, line): + isOptional, name, definition = _splitParameterOptions(line) + if definition.startswith("point"): + descName = _createDescriptiveName(name) + default = definition.strip()[len('point') + 1:] or None + return ParameterPoint(name, descName, default, isOptional) + class ParameterFile(Parameter): @@ -318,6 +362,13 @@ class ParameterFile(Parameter): param_type += 'file' return '##' + self.name + '=' + param_type + @classmethod + def fromScriptCode(self, line): + isOptional, name, definition = _splitParameterOptions(line) + if definition.startswith("file") or definition.startswith("folder"): + descName = _createDescriptiveName(name) + return ParameterFile(name, descName, definition.startswith("folder"), isOptional) + class ParameterFixedTable(Parameter): @@ -356,6 +407,14 @@ class ParameterFixedTable(Parameter): tablestring = tablestring[:-1] return tablestring + @classmethod + def fromScriptCode(self, line): + isOptional, name, definition = _splitParameterOptions(line) + if definition.startswith("point"): + descName = _createDescriptiveName(name) + default = definition.strip()[len('point') + 1:] or None + return ParameterPoint(name, descName, default, isOptional) + class ParameterMultipleInput(ParameterDataObject): @@ -542,6 +601,17 @@ class ParameterMultipleInput(ParameterDataObject): param_type += 'multiple vector' return '##' + self.name + '=' + param_type + @classmethod + def fromScriptCode(self, line): + isOptional, name, definition = _splitParameterOptions(line) + descName = _createDescriptiveName(name) + if definition.lower().strip() == 'multiple raster': + return ParameterMultipleInput(name, descName, + dataobjects.TYPE_RASTER, isOptional) + elif definition.lower().strip() == 'multiple vector': + return ParameterMultipleInput(name, definition, + dataobjects.TYPE_VECTOR_ANY, isOptional) + class ParameterNumber(Parameter): @@ -599,6 +669,13 @@ class ParameterNumber(Parameter): param_type += 'number' return '##' + self.name + '=' + param_type + str(self.default) + @classmethod + def fromScriptCode(self, line): + isOptional, name, definition = _splitParameterOptions(line) + descName = _createDescriptiveName(name) + if definition.lower().strip().startswith('number'): + default = definition.strip()[len('number') + 1:] or None + return ParameterNumber(name, descName, default=default, optional=isOptional) class ParameterRange(Parameter): @@ -701,6 +778,13 @@ class ParameterRaster(ParameterDataObject): param_type += 'raster' return '##' + self.name + '=' + param_type + @classmethod + 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) class ParameterSelection(Parameter): @@ -744,7 +828,18 @@ class ParameterSelection(Parameter): except: return False - + @classmethod + def fromScriptCode(self, line): + isOptional, name, definition = _splitParameterOptions(line) + descName = _createDescriptiveName(name) + if definition.lower().strip().startswith('selectionfromfile'): + options = definition.strip()[len('selectionfromfile '):].split(';') + return ParameterSelection(name, descName, options, isSource=True, optional=isOptional) + elif definition.lower().strip().startswith('selection'): + options = definition.strip()[len('selection '):].split(';') + return ParameterSelection(name, descName, options, optional=isOptional) + + class ParameterString(Parameter): NEWLINE = '\n' @@ -781,6 +876,22 @@ class ParameterString(Parameter): param_type += 'string' return '##' + self.name + '=' + param_type + self.default + @classmethod + def fromScriptCode(self, line): + isOptional, name, definition = _splitParameterOptions(line) + descName = _createDescriptiveName(name) + if definition.lower().strip().startswith('string'): + default = definition.strip()[len('string') + 1:] + if default: + return ParameterString(name, descName, default, optional=isOptional) + else: + return ParameterString(name, descName, optional=isOptional) + elif definition.lower().strip().startswith('longstring'): + default = definition.strip()[len('longstring') + 1:] + if default: + return ParameterString(name, descName, default, multiline=True, optional=isOptional) + else: + return ParameterString(name, descName, multiline=True, optional=isOptional) class ParameterTable(ParameterDataObject): @@ -853,6 +964,13 @@ class ParameterTable(ParameterDataObject): param_type += 'table' return '##' + self.name + '=' + param_type + @classmethod + def fromScriptCode(self, line): + isOptional, name, definition = _splitParameterOptions(line) + descName = _createDescriptiveName(name) + if definition.lower().strip().startswith('table'): + return ParameterTable(name, descName, isOptional) + class ParameterTableField(Parameter): @@ -908,6 +1026,24 @@ class ParameterTableField(Parameter): return '##' + self.name + '=' + param_type + self.parent + @classmethod + def fromScriptCode(self, line): + isOptional, name, definition = _splitParameterOptions(line) + descName = _createDescriptiveName(name) + if definition.lower().strip().startswith('field'): + if definition.lower().strip().startswith('field number'): + parent = definition.strip()[len('field number') + 1:] + datatype = ParameterTableField.DATA_TYPE_NUMBER + elif definition.lower().strip().startswith('field string'): + parent = definition.strip()[len('field string') + 1:] + datatype = ParameterTableField.DATA_TYPE_STRING + else: + parent = definition.strip()[len('field') + 1:] + datatype = ParameterTableField.DATA_TYPE_ANY + + return ParameterTableField(name, descName, parent, datatype, isOptional) + + class ParameterTableMultipleField(Parameter): """A parameter representing several table fields. @@ -974,6 +1110,23 @@ class ParameterTableMultipleField(Parameter): param_type += 'multiple field ' return '##' + self.name + '=' + param_type + self.parent + @classmethod + def fromScriptCode(self, line): + isOptional, name, definition = _splitParameterOptions(line) + if definition.lower().strip().startswith('multiple field'): + descName = _createDescriptiveName(name) + if definition.lower().strip().startswith('multiple field number'): + field = definition.strip()[len('multiple field number') + 1:] + datatype = ParameterTableMultipleField.DATA_TYPE_NUMBER + elif definition.lower().strip().startswith('multiple field string'): + field = definition.strip()[len('multiple field string') + 1:] + datatype = ParameterTableMultipleField.DATA_TYPE_STRING + else: + field = definition.strip()[len('multiple field') + 1:] + datatype = ParameterTableMultipleField.DATA_TYPE_ANY + + return ParameterTableMultipleField(name, descName, field, datatype, isOptional) + class ParameterVector(ParameterDataObject): @@ -1050,6 +1203,22 @@ class ParameterVector(ParameterDataObject): param_type += 'vector' return '##' + self.name + '=' + param_type + @classmethod + def fromScriptCode(self, line): + isOptional, name, definition = _splitParameterOptions(line) + descName = _createDescriptiveName(name) + if definition.lower().strip() == 'vector': + return ParameterVector(name, descName, + [dataobjects.TYPE_VECTOR_ANY], isOptional) + elif definition.lower().strip() == 'vector point': + return ParameterVector(name, descName, + [dataobjects.TYPE_VECTOR_POINT], isOptional) + elif definition.lower().strip() == 'vector line': + return ParameterVector(name, descName, + [dataobjects.TYPE_VECTOR_LINE], isOptional) + elif definition.lower().strip() == 'vector polygon': + return ParameterVector(name, descName, + [dataobjects.TYPE_VECTOR_POLYGON], isOptional) class ParameterGeometryPredicate(Parameter): @@ -1089,3 +1258,23 @@ class ParameterGeometryPredicate(Parameter): else: self.value = value return True + + +paramClasses = [c for c in sys.modules[__name__].__dict__.values() if inspect.isclass(c) and issubclass(c, Parameter)] + +def getParameterFromString(s): + print s + #Try the parameter definitions used in description files + if '|' in s: + tokens = s.split("|") + params = [t if unicode(t) != unicode(None) else None for t in tokens[1:]] + clazz = getattr(sys.modules[__name__], tokens[0]) + return clazz(*params) + else: #try script syntax + for paramClass in paramClasses: + try: + param = paramClass.fromScriptCode(s) + if param is not None: + return param + except AttributeError: + pass diff --git a/python/plugins/processing/script/ScriptAlgorithm.py b/python/plugins/processing/script/ScriptAlgorithm.py index de552a1b448..95ad656122c 100644 --- a/python/plugins/processing/script/ScriptAlgorithm.py +++ b/python/plugins/processing/script/ScriptAlgorithm.py @@ -146,12 +146,6 @@ class ScriptAlgorithm(GeoAlgorithm): out = None line = line.replace('#', '') - # If the line is in the format of the text description files for - # normal algorithms, then process it using parameter and output - # factories - if '|' in line: - self.processDescriptionParameterLine(line) - return if line == "nomodeler": self.showInModeler = False return @@ -170,15 +164,8 @@ class ScriptAlgorithm(GeoAlgorithm): if tokens[1].lower().strip().startswith('output'): outToken = tokens[1].strip()[len('output') + 1:] out = self.processOutputParameterToken(outToken) - - elif tokens[1].lower().strip().startswith('optional'): - optToken = tokens[1].strip()[len('optional') + 1:] - param = self.processInputParameterToken(optToken, tokens[0]) - if param: - param.optional = True - else: - param = self.processInputParameterToken(tokens[1], tokens[0]) + param = getParameterFromString(line) if param is not None: self.addParameter(param) @@ -191,125 +178,11 @@ class ScriptAlgorithm(GeoAlgorithm): self.tr('Could not load script: %s.\n' 'Problem with line "%s"', 'ScriptAlgorithm') % (self.descriptionFile or '', line)) - def processInputParameterToken(self, token, name): - param = None - - descName = self.createDescriptiveName(name) - - if token.lower().strip() == 'raster': - param = ParameterRaster(name, descName, False) - elif token.lower().strip() == 'vector': - param = ParameterVector(name, descName, - [dataobjects.TYPE_VECTOR_ANY]) - elif token.lower().strip() == 'vector point': - param = ParameterVector(name, descName, - [dataobjects.TYPE_VECTOR_POINT]) - elif token.lower().strip() == 'vector line': - param = ParameterVector(name, descName, - [dataobjects.TYPE_VECTOR_LINE]) - elif token.lower().strip() == 'vector polygon': - param = ParameterVector(name, descName, - [dataobjects.TYPE_VECTOR_POLYGON]) - elif token.lower().strip() == 'table': - param = ParameterTable(name, descName, False) - elif token.lower().strip() == 'multiple raster': - param = ParameterMultipleInput(name, descName, - dataobjects.TYPE_RASTER) - param.optional = False - elif token.lower().strip() == 'multiple vector': - param = ParameterMultipleInput(name, descName, - dataobjects.TYPE_VECTOR_ANY) - param.optional = False - elif token.lower().strip().startswith('selectionfromfile'): - options = token.strip()[len('selectionfromfile '):].split(';') - param = ParameterSelection(name, descName, options, isSource=True) - elif token.lower().strip().startswith('selection'): - options = token.strip()[len('selection '):].split(';') - param = ParameterSelection(name, descName, options) - elif token.lower().strip().startswith('boolean'): - default = token.strip()[len('boolean') + 1:] - if default: - param = ParameterBoolean(name, descName, default) - else: - param = ParameterBoolean(name, descName) - elif token.lower().strip() == 'extent': - param = ParameterExtent(name, descName) - elif token.lower().strip() == 'point': - param = ParameterPoint(name, descName) - elif token.lower().strip() == 'file': - param = ParameterFile(name, descName, False) - elif token.lower().strip() == 'folder': - param = ParameterFile(name, descName, True) - elif token.lower().strip().startswith('number'): - default = token.strip()[len('number') + 1:] - if default: - param = ParameterNumber(name, descName, default=default) - else: - param = ParameterNumber(name, descName) - elif token.lower().strip().startswith('field'): - if token.lower().strip().startswith('field number'): - field = token.strip()[len('field number') + 1:] - datatype = ParameterTableField.DATA_TYPE_NUMBER - elif token.lower().strip().startswith('field string'): - field = token.strip()[len('field string') + 1:] - datatype = ParameterTableField.DATA_TYPE_STRING - else: - field = token.strip()[len('field') + 1:] - datatype = ParameterTableField.DATA_TYPE_ANY - found = False - for p in self.parameters: - if p.name == field: - found = True - break - if found: - param = ParameterTableField( - name=name, - description=descName, - parent=field, - datatype=datatype - ) - elif token.lower().strip().startswith('multiple field'): - if token.lower().strip().startswith('multiple field number'): - field = token.strip()[len('multiple field number') + 1:] - datatype = ParameterTableMultipleField.DATA_TYPE_NUMBER - elif token.lower().strip().startswith('multiple field string'): - field = token.strip()[len('multiple field string') + 1:] - datatype = ParameterTableMultipleField.DATA_TYPE_STRING - else: - field = token.strip()[len('multiple field') + 1:] - datatype = ParameterTableMultipleField.DATA_TYPE_ANY - found = False - for p in self.parameters: - if p.name == field: - found = True - break - if found: - param = ParameterTableMultipleField( - name=name, - description=descName, - parent=field, - datatype=datatype - ) - elif token.lower().strip().startswith('string'): - default = token.strip()[len('string') + 1:] - if default: - param = ParameterString(name, descName, default) - else: - param = ParameterString(name, descName) - elif token.lower().strip().startswith('longstring'): - default = token.strip()[len('longstring') + 1:] - if default: - param = ParameterString(name, descName, default, multiline=True) - else: - param = ParameterString(name, descName, multiline=True) - elif token.lower().strip().startswith('crs'): - default = token.strip()[len('crs') + 1:] - if default: - param = ParameterCrs(name, descName, default) - else: - param = ParameterCrs(name, descName) - - return param + def processInputParameterLine(self, line): + for paramClass in paramClasses: + param = paramClass.fromScriptCode(line) + if param is not None: + return param def processOutputParameterToken(self, token): out = None