Merge pull request #4715 from nyalldawson/processing_pt31

[processing] Bring back iterating mode for algorithms
This commit is contained in:
Nyall Dawson 2017-06-12 16:32:34 +10:00 committed by GitHub
commit 492ad2db08
20 changed files with 711 additions and 177 deletions

View File

@ -255,6 +255,16 @@ class QgsProcessingAlgorithm
:rtype: bool
%End
virtual QString asPythonCommand( const QVariantMap &parameters, QgsProcessingContext &context ) const;
%Docstring
Returns a Python command string which can be executed to run the algorithm
using the specified ``parameters``.
Algorithms which cannot be run from a Python command should return an empty
string.
:rtype: str
%End
protected:
bool addParameter( QgsProcessingParameterDefinition *parameterDefinition /Transfer/ );

View File

@ -288,6 +288,14 @@ class QgsProcessingParameterDefinition
:rtype: bool
%End
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
%Docstring
Returns a string version of the parameter input ``value``, which is suitable for use as an input
parameter value when running an algorithm directly from a Python command.
The returned value must be correctly escaped - e.g. string values must be wrapped in ' 's.
:rtype: str
%End
protected:
@ -517,6 +525,8 @@ class QgsProcessingParameterBoolean : QgsProcessingParameterDefinition
%End
virtual QString type() const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
};
class QgsProcessingParameterCrs : QgsProcessingParameterDefinition
@ -540,6 +550,8 @@ class QgsProcessingParameterCrs : QgsProcessingParameterDefinition
virtual QString type() const;
virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
};
class QgsProcessingParameterMapLayer : QgsProcessingParameterDefinition
@ -563,6 +575,8 @@ class QgsProcessingParameterMapLayer : QgsProcessingParameterDefinition
virtual QString type() const;
virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
};
class QgsProcessingParameterExtent : QgsProcessingParameterDefinition
@ -586,6 +600,8 @@ class QgsProcessingParameterExtent : QgsProcessingParameterDefinition
virtual QString type() const;
virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
};
@ -693,6 +709,8 @@ class QgsProcessingParameterMatrix : QgsProcessingParameterDefinition
virtual QString type() const;
virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
QStringList headers() const;
%Docstring
@ -763,6 +781,8 @@ class QgsProcessingParameterMultipleLayers : QgsProcessingParameterDefinition
virtual QString type() const;
virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
QgsProcessingParameterDefinition::LayerType layerType() const;
%Docstring
@ -826,6 +846,8 @@ class QgsProcessingParameterNumber : QgsProcessingParameterDefinition
virtual QString type() const;
virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
double minimum() const;
%Docstring
@ -891,6 +913,8 @@ class QgsProcessingParameterRange : QgsProcessingParameterDefinition
virtual QString type() const;
virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
QgsProcessingParameterNumber::Type dataType() const;
%Docstring
@ -928,6 +952,8 @@ class QgsProcessingParameterRasterLayer : QgsProcessingParameterDefinition
virtual QString type() const;
virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
};
@ -954,6 +980,8 @@ class QgsProcessingParameterEnum : QgsProcessingParameterDefinition
virtual QString type() const;
virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
QStringList options() const;
%Docstring
@ -1003,6 +1031,8 @@ class QgsProcessingParameterString : QgsProcessingParameterDefinition
%End
virtual QString type() const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
bool multiLine() const;
%Docstring
@ -1039,6 +1069,8 @@ class QgsProcessingParameterExpression : QgsProcessingParameterDefinition
%End
virtual QString type() const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
QString parentLayerParameter() const;
%Docstring
@ -1109,6 +1141,8 @@ class QgsProcessingParameterTableField : QgsProcessingParameterDefinition
virtual QString type() const;
virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
QString parentLayerParameter() const;
%Docstring
@ -1173,6 +1207,8 @@ class QgsProcessingParameterFeatureSource : QgsProcessingParameterDefinition
virtual QString type() const;
virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
QList< int > dataTypes() const;
%Docstring
@ -1214,6 +1250,8 @@ class QgsProcessingParameterFeatureSink : QgsProcessingParameterDefinition
virtual bool isDestination() const;
virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
QgsProcessingParameterDefinition::LayerType dataType() const;
%Docstring
@ -1260,6 +1298,8 @@ class QgsProcessingParameterRasterOutput : QgsProcessingParameterDefinition
virtual bool isDestination() const;
virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
};
class QgsProcessingParameterFileOutput : QgsProcessingParameterDefinition
@ -1286,6 +1326,8 @@ class QgsProcessingParameterFileOutput : QgsProcessingParameterDefinition
virtual bool isDestination() const;
virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
QString fileFilter() const;
%Docstring

View File

@ -126,6 +126,15 @@ class QgsProcessingUtils
:rtype: QgsRectangle
%End
static QVariant generateIteratingDestination( const QVariant &input, const QVariant &id, QgsProcessingContext &context );
%Docstring
Converts an ``input`` parameter value for use in source iterating mode, where one individual sink
is created per input feature.
The ``id`` parameter represents the unique ID for this output, which is embedded into the resulting
parameter value.
:rtype: QVariant
%End
};

View File

@ -224,9 +224,9 @@ class FieldsCalculatorDialog(BASE, WIDGET):
parameters = self.getParamValues()
if parameters:
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
ProcessingLog.addToLog(self.alg.getAsCommand())
context = dataobjects.createContext()
ProcessingLog.addToLog(self.alg.asPythonCommand(parameters, context))
self.executed, results = execute(self.alg, parameters, context, self.feedback)
if self.executed:
handleAlgorithmResults(self.alg,

View File

@ -301,23 +301,6 @@ class GeoAlgorithm(QgsProcessingAlgorithm):
return out.value
return None
def getAsCommand(self):
"""Returns the command that would run this same algorithm from
the console.
Should return None if the algorithm cannot be run from the
console.
"""
s = 'processing.run("' + self.id() + '",'
for param in self.parameterDefinitions():
s += param.getValueAsCommandLineParameter() + ','
for out in self.outputs:
if not out.flags() & QgsProcessingParameterDefinition.FlagHidden:
s += out.getValueAsCommandLineParameter() + ','
s = s[:-1] + ')'
return s
def tr(self, string, context=''):
if context == '':
context = self.__class__.__name__

View File

@ -139,13 +139,8 @@ class Processing(object):
for (name, value) in list(args[0].items()):
param = alg.parameterDefinition(name)
if param:
# TODO
# and param.setValue(value):
parameters[param.name()] = value
continue
output = alg.getOutputFromName(name)
if output and output.setValue(value):
continue
# fix_print_with_import
print('Error: Wrong parameter value %s for parameter %s.' % (value, name))
QgsMessageLog.logMessage(
@ -249,7 +244,7 @@ class Processing(object):
QApplication.restoreOverrideCursor()
if isinstance(feedback, MessageBarProgress):
feedback.close()
return alg
return results
@staticmethod
def tr(string, context=''):

View File

@ -86,14 +86,6 @@ class Parameter(object):
def __str__(self):
return u'{} <{}>'.format(self.name(), self.__class__.__name__)
def getValueAsCommandLineParameter(self):
"""
Returns the value of this parameter as it should have been
entered in the console if calling an algorithm using the
processing.run() method.
"""
return str(self.value)
def todict(self):
o = deepcopy(self.__dict__)
del o['metadata']
@ -145,9 +137,6 @@ class ParameterCrs(Parameter):
if self.value == 'ProjectCrs':
self.value = QgsProject.instance().crs().authid()
def getValueAsCommandLineParameter(self):
return '"' + str(self.value) + '"'
def getAsScriptCode(self):
param_type = ''
if self.flags() & QgsProcessingParameterDefinition.FlagOptional:
@ -169,17 +158,6 @@ class ParameterCrs(Parameter):
return ParameterCrs(name, descName, None, isOptional)
class ParameterDataObject(Parameter):
def getValueAsCommandLineParameter(self):
if self.value is None:
return str(None)
else:
s = QgsProcessingUtils.normalizeLayerSource(str(self.value))
s = '"%s"' % s
return s
class ParameterExtent(Parameter):
USE_MIN_COVERING_EXTENT = 'USE_MIN_COVERING_EXTENT'
@ -189,12 +167,6 @@ class ParameterExtent(Parameter):
# The value is a string in the form "xmin, xmax, ymin, ymax"
self.skip_crs_check = False
def getValueAsCommandLineParameter(self):
if self.value is not None:
return '"' + str(self.value) + '"'
else:
return str(None)
def getAsScriptCode(self):
param_type = ''
if self.flags() & QgsProcessingParameterDefinition.FlagOptional:
@ -217,9 +189,6 @@ class ParameterPoint(Parameter):
Parameter.__init__(self, name, description, default, optional)
# The value is a string in the form "x, y"
def getValueAsCommandLineParameter(self):
return '"' + str(self.value) + '"'
def getAsScriptCode(self):
param_type = ''
if self.flags() & QgsProcessingParameterDefinition.FlagOptional:
@ -243,9 +212,6 @@ class ParameterFile(Parameter):
self.ext = ext
self.isFolder = parseBool(isFolder)
def getValueAsCommandLineParameter(self):
return '"' + str(self.value) + '"'
def getAsScriptCode(self):
param_type = ''
if self.flags() & QgsProcessingParameterDefinition.FlagOptional:
@ -275,9 +241,6 @@ class ParameterFixedTable(Parameter):
self.numRows = int(numRows)
self.fixedNumOfRows = parseBool(fixedNumOfRows)
def getValueAsCommandLineParameter(self):
return '"' + str(self.value) + '"'
@staticmethod
def tableToString(table):
tablestring = ''
@ -302,7 +265,7 @@ class ParameterFixedTable(Parameter):
return ParameterFixedTable(name, descName, optional=isOptional)
class ParameterMultipleInput(ParameterDataObject):
class ParameterMultipleInput(Parameter):
"""A parameter representing several data objects.
@ -313,7 +276,7 @@ class ParameterMultipleInput(ParameterDataObject):
exported = None
def __init__(self, name='', description='', datatype=-1, optional=False, metadata={}):
ParameterDataObject.__init__(self, name, description, None, optional, metadata=metadata)
Parameter.__init__(self, name, description, None, optional, metadata=metadata)
self.datatype = int(float(datatype))
self.exported = None
self.minNumInputs = 0
@ -552,13 +515,6 @@ class ParameterNumber(Parameter):
return value
def getValueAsCommandLineParameter(self):
if self.value is None:
return str(None)
if isinstance(self.value, str):
return '"%s"' + self.value
return str(self.value)
class ParameterRange(Parameter):
@ -576,14 +532,11 @@ class ParameterRange(Parameter):
else:
self.isInteger = False
def getValueAsCommandLineParameter(self):
return '"' + str(self.value) + '"' if self.value is not None else str(None)
class ParameterRaster(ParameterDataObject):
class ParameterRaster(Parameter):
def __init__(self, name='', description='', optional=False, showSublayersDialog=True):
ParameterDataObject.__init__(self, name, description, None, optional)
Parameter.__init__(self, name, description, None, optional)
self.showSublayersDialog = parseBool(showSublayersDialog)
def getAsScriptCode(self):
@ -663,19 +616,11 @@ class ParameterEvaluationException(Exception):
class ParameterString(Parameter):
NEWLINE = '\n'
ESCAPED_NEWLINE = '\\n'
def __init__(self, name='', description='', default=None, multiline=False,
optional=False, evaluateExpressions=False, metadata={}):
Parameter.__init__(self, name, description, default, optional, metadata)
self.multiline = parseBool(multiline)
def getValueAsCommandLineParameter(self):
return ('"' + str(self.value.replace(ParameterString.NEWLINE,
ParameterString.ESCAPED_NEWLINE)) + '"'
if self.value is not None else str(None))
def getAsScriptCode(self):
param_type = ''
if self.flags() & QgsProcessingParameterDefinition.FlagOptional:
@ -707,18 +652,10 @@ class ParameterString(Parameter):
class ParameterExpression(Parameter):
NEWLINE = '\n'
ESCAPED_NEWLINE = '\\n'
def __init__(self, name='', description='', default=None, optional=False, parent_layer=None):
Parameter.__init__(self, name, description, default, optional)
self.parent_layer = parent_layer
def getValueAsCommandLineParameter(self):
return ('"' + str(self.value.replace(ParameterExpression.NEWLINE,
ParameterExpression.ESCAPED_NEWLINE)) + '"'
if self.value is not None else str(None))
def getAsScriptCode(self):
param_type = ''
if self.flags() & QgsProcessingParameterDefinition.FlagOptional:
@ -740,10 +677,10 @@ class ParameterExpression(Parameter):
return ParameterExpression(name, descName, optional=isOptional)
class ParameterTable(ParameterDataObject):
class ParameterTable(Parameter):
def __init__(self, name='', description='', optional=False):
ParameterDataObject.__init__(self, name, description, None, optional)
Parameter.__init__(self, name, description, None, optional)
self.exported = None
def getSafeExportedTable(self):
@ -807,9 +744,6 @@ class ParameterTableField(Parameter):
self.multiple = multiple
self.datatype = int(datatype)
def getValueAsCommandLineParameter(self):
return '"' + str(self.value) + '"' if self.value is not None else str(None)
def __str__(self):
return self.name() + ' <' + self.__module__.split('.')[-1] + ' from ' \
+ self.parent + '>'
@ -852,11 +786,11 @@ class ParameterTableField(Parameter):
return ParameterTableField(name, descName, parent, datatype, isOptional)
class ParameterVector(ParameterDataObject):
class ParameterVector(Parameter):
def __init__(self, name='', description='', datatype=[-1],
optional=False):
ParameterDataObject.__init__(self, name, description, None, optional)
Parameter.__init__(self, name, description, None, optional)
if isinstance(datatype, int):
datatype = [datatype]
elif isinstance(datatype, str):

View File

@ -228,16 +228,20 @@ class AlgorithmDialog(AlgorithmDialogBase):
start_time = time.time()
if self.iterateParam:
self.buttonCancel.setEnabled(self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel)
if executeIterating(self.alg, parameters, self.iterateParam, context, feedback):
feedback.pushInfo(
self.tr('Execution completed in {0:0.2f} seconds'.format(time.time() - start_time)))
self.buttonCancel.setEnabled(False)
self.finish(parameters, context, feedback)
else:
self.buttonCancel.setEnabled(False)
QApplication.restoreOverrideCursor()
self.resetGUI()
else:
# TODO
#command = self.alg.getAsCommand()
#if command:
# ProcessingLog.addToLog(command)
command = self.alg.asPythonCommand(parameters, context)
if command:
ProcessingLog.addToLog(command)
self.buttonCancel.setEnabled(self.alg.flags() & QgsProcessingAlgorithm.FlagCanCancel)
result = executeAlgorithm(self.alg, parameters, context, feedback)
feedback.pushInfo(self.tr('Execution completed in {0:0.2f} seconds'.format(time.time() - start_time)))

View File

@ -28,14 +28,17 @@ __copyright__ = '(C) 2012, Victor Olaya'
__revision__ = '$Format:%H$'
import sys
from copy import deepcopy
from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (QgsFeature,
QgsVectorFileWriter,
QgsProcessingFeedback,
QgsSettings,
QgsProcessingUtils,
QgsMessageLog)
QgsMessageLog,
QgsProperty,
QgsProcessingParameters,
QgsProcessingOutputLayerDefinition)
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.gui.Postprocessing import handleAlgorithmResults
from processing.tools import dataobjects
@ -69,41 +72,49 @@ def executeIterating(alg, parameters, paramToIter, context, feedback):
# Generate all single-feature layers
settings = QgsSettings()
systemEncoding = settings.value('/UI/encoding', 'System')
layerfile = parameters[paramToIter]
layer = QgsProcessingUtils.mapLayerFromString(layerfile, context, False)
feat = QgsFeature()
filelist = []
outputs = {}
features = QgsProcessingUtils.getFeatures(layer, context)
for feat in features:
output = getTempFilename('shp')
filelist.append(output)
writer = QgsVectorFileWriter(output, systemEncoding,
layer.fields(), layer.wkbType(), layer.crs())
writer.addFeature(feat)
del writer
# store output values to use them later as basenames for all outputs
for out in alg.outputs:
outputs[out.name] = out.value
parameter_definition = alg.parameterDefinition(paramToIter)
if not parameter_definition:
return False
# now run all the algorithms
for i, f in enumerate(filelist):
parameters[paramToIter] = f
for out in alg.outputs:
filename = outputs[out.name]
if filename:
filename = filename[:filename.rfind('.')] + '_' + str(i) \
+ filename[filename.rfind('.'):]
out.value = filename
feedback.setProgressText(tr('Executing iteration {0}/{1}...').format(i, len(filelist)))
feedback.setProgress(i * 100 / len(filelist))
ret, results = execute(alg, parameters, None, feedback)
if ret:
handleAlgorithmResults(alg, context, None, False)
else:
iter_source = QgsProcessingParameters.parameterAsSource(parameter_definition, parameters, context)
sink_list = []
if iter_source.featureCount() == 0:
return False
total = 100.0 / iter_source.featureCount()
for current, feat in enumerate(iter_source.getFeatures()):
if feedback.isCanceled():
return False
sink, sink_id = QgsProcessingUtils.createFeatureSink('memory:', context, iter_source.fields(), iter_source.wkbType(), iter_source.sourceCrs())
sink_list.append(sink_id)
sink.addFeature(feat)
del sink
feedback.setProgress(int(current * total))
# store output values to use them later as basenames for all outputs
outputs = {}
for out in alg.destinationParameterDefinitions():
outputs[out.name()] = parameters[out.name()]
# now run all the algorithms
for i, f in enumerate(sink_list):
if feedback.isCanceled():
return False
parameters[paramToIter] = f
for out in alg.destinationParameterDefinitions():
o = outputs[out.name()]
parameters[out.name()] = QgsProcessingUtils.generateIteratingDestination(o, i, context)
feedback.setProgressText(tr('Executing iteration {0}/{1}...').format(i, len(sink_list)))
feedback.setProgress(i * 100 / len(sink_list))
ret, results = execute(alg, parameters, context, feedback)
if not ret:
return False
handleAlgorithmResults(alg, context, feedback, False)
return True

View File

@ -118,7 +118,7 @@ class ParametersPanel(BASE, WIDGET):
button = QToolButton()
icon = QIcon(os.path.join(pluginPath, 'images', 'iterate.png'))
button.setIcon(icon)
button.setToolTip(self.tr('Iterate over this layer'))
button.setToolTip(self.tr('Iterate over this layer, creating a separate output for every feature in the layer'))
button.setCheckable(True)
layout.addWidget(button)
self.iterateButtons[param.name()] = button

View File

@ -56,6 +56,13 @@ def handleAlgorithmResults(alg, context, feedback=None, showResults=True):
feedback.setProgressText(QCoreApplication.translate('Postprocessing', 'Loading resulting layers'))
i = 0
for l, details in context.layersToLoadOnCompletion().items():
if feedback.isCanceled():
return False
if len(context.layersToLoadOnCompletion()) > 2:
# only show progress feedback if we're loading a bunch of layers
feedback.setProgress(100 * i / float(len(context.layersToLoadOnCompletion())))
try:
layer = QgsProcessingUtils.mapLayerFromString(l, context)
if layer is not None:
@ -98,7 +105,7 @@ def handleAlgorithmResults(alg, context, feedback=None, showResults=True):
# wrongLayers.append(out.description)
# elif isinstance(out, OutputHTML):
# resultsList.addResult(alg.icon(), out.description, out.value)
# i += 1
i += 1
QApplication.restoreOverrideCursor()
if wrongLayers:

View File

@ -49,8 +49,7 @@ from processing.core.parameters import (Parameter,
ParameterTableField,
ParameterBoolean,
ParameterString,
ParameterNumber,
ParameterDataObject)
ParameterNumber)
from processing.gui.Help2Html import getHtmlFromDescriptionsDict
@ -424,13 +423,15 @@ class ModelerAlgorithm(GeoAlgorithm):
value = param.defaultValue()
# We allow unexistent filepaths, since that allows
# algorithms to skip some conversion routines
if not param.checkValueIsAcceptable(value) and not isinstance(param,
ParameterDataObject):
raise GeoAlgorithmExecutionException(
self.tr('Wrong value {0} for {1} {2}', 'ModelerAlgorithm').format(
value, param.__class__.__name__, param.name()
)
)
# TODO
#if not param.checkValueIsAcceptable(value) and not isinstance(param,
# ParameterDataObject):
# raise GeoAlgorithmExecutionException(
# self.tr('Wrong value {0} for {1} {2}', 'ModelerAlgorithm').format(
# value, param.__class__.__name__, param.name()
# )
# )
for out in algInstance.outputs:
if not out.flags() & QgsProcessingParameterDefinition.FlagHidden:
@ -519,9 +520,9 @@ class ModelerAlgorithm(GeoAlgorithm):
feedback.pushDebugInfo(
self.tr('Model processed ok. Executed {0} algorithms total', 'ModelerAlgorithm').format(len(executed)))
def getAsCommand(self):
def asPythonCommand(self, parameters, context):
if self.descriptionFile:
return GeoAlgorithm.getAsCommand(self)
return QgsProcessingAlgorithm.asPythonCommand(self, parameters, context)
else:
return None

View File

@ -57,17 +57,6 @@ start_app()
class ParameterTest(unittest.TestCase):
def testGetValueAsCommandLineParameter(self):
parameter = Parameter('myName', 'myDesc')
parameter.setValue(None)
self.assertEqual(parameter.getValueAsCommandLineParameter(), "None")
parameter.setValue("someValue")
self.assertEqual(parameter.getValueAsCommandLineParameter(), 'someValue')
parameter.setValue(123)
self.assertEqual(parameter.getValueAsCommandLineParameter(), '123')
def testScriptCode(self):
"""Simple check that default constructed object export/import correctly"""
paramClasses = [c for c in list(sys.modules[__name__].__dict__.values())
@ -113,18 +102,6 @@ class ParameterCRSTest(unittest.TestCase):
self.assertTrue(result.optional)
class ParameterDataObjectTest(unittest.TestCase):
def testGetValueAsCommandLineParameter(self):
parameter = ParameterDataObject('myName', 'myDesc')
parameter.setValue(None)
self.assertEqual(parameter.getValueAsCommandLineParameter(), "None")
parameter = ParameterDataObject('myName', 'myDesc')
parameter.setValue("someFile.dat")
self.assertEqual(parameter.getValueAsCommandLineParameter(), '"someFile.dat"')
class ParameterExtentTest(unittest.TestCase):
def testScriptCode(self):
@ -176,11 +153,6 @@ class ParameterSelectionTest(unittest.TestCase):
class ParameterFileTest(unittest.TestCase):
def testGetValueAsCommandLineParameter(self):
parameter = ParameterFile('myName', 'myDesc')
parameter.setValue('myFile.png')
self.assertEqual(parameter.getValueAsCommandLineParameter(), '"myFile.png"')
def testScriptCode(self):
parameter = ParameterFile('myName', 'myDescription')
code = parameter.getAsScriptCode()

View File

@ -185,6 +185,26 @@ bool QgsProcessingAlgorithm::validateInputCrs( const QVariantMap &parameters, Qg
return true;
}
QString QgsProcessingAlgorithm::asPythonCommand( const QVariantMap &parameters, QgsProcessingContext &context ) const
{
QString s = QStringLiteral( "processing.run(\"%1\"," ).arg( id() );
QStringList parts;
Q_FOREACH ( const QgsProcessingParameterDefinition *def, mParameters )
{
if ( def->flags() & QgsProcessingParameterDefinition::FlagHidden )
continue;
if ( !parameters.contains( def->name() ) || !parameters.value( def->name() ).isValid() )
continue;
parts << QStringLiteral( "'%1':%2" ).arg( def->name(), def->valueAsPythonString( parameters.value( def->name() ), context ) );
}
s += QStringLiteral( " {%1})" ).arg( parts.join( ',' ) );
return s;
}
bool QgsProcessingAlgorithm::addParameter( QgsProcessingParameterDefinition *definition )
{
if ( !definition )

View File

@ -253,6 +253,15 @@ class CORE_EXPORT QgsProcessingAlgorithm
virtual bool validateInputCrs( const QVariantMap &parameters,
QgsProcessingContext &context ) const;
/**
* Returns a Python command string which can be executed to run the algorithm
* using the specified \a parameters.
*
* Algorithms which cannot be run from a Python command should return an empty
* string.
*/
virtual QString asPythonCommand( const QVariantMap &parameters, QgsProcessingContext &context ) const;
protected:
/**

View File

@ -585,6 +585,13 @@ QList<QgsMapLayer *> QgsProcessingParameters::parameterAsLayerList( const QgsPro
}
}
}
else if ( val.type() == QVariant::StringList )
{
Q_FOREACH ( const QString &s, val.toStringList() )
{
resultStringList << s;
}
}
else
resultStringList << val.toString();
@ -726,10 +733,25 @@ bool QgsProcessingParameterDefinition::checkValueIsAcceptable( const QVariant &i
return true;
}
QString QgsProcessingParameterDefinition::valueAsPythonString( const QVariant &value, QgsProcessingContext & ) const
{
if ( value.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );
return value.toString().prepend( '\'' ).append( '\'' );
}
QgsProcessingParameterBoolean::QgsProcessingParameterBoolean( const QString &name, const QString &description, const QVariant &defaultValue, bool optional )
: QgsProcessingParameterDefinition( name, description, defaultValue, optional )
{}
QString QgsProcessingParameterBoolean::valueAsPythonString( const QVariant &val, QgsProcessingContext & ) const
{
if ( val.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( val.value< QgsProperty >().asExpression() );
return val.toBool() ? QStringLiteral( "True" ) : QStringLiteral( "False" );
}
QgsProcessingParameterCrs::QgsProcessingParameterCrs( const QString &name, const QString &description, const QVariant &defaultValue, bool optional )
: QgsProcessingParameterDefinition( name, description, defaultValue, optional )
{
@ -752,6 +774,20 @@ bool QgsProcessingParameterCrs::checkValueIsAcceptable( const QVariant &input, Q
return true;
}
QString QgsProcessingParameterCrs::valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const
{
if ( value.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );
QVariantMap p;
p.insert( name(), value );
QgsMapLayer *layer = QgsProcessingParameters::parameterAsLayer( this, p, context );
if ( layer )
return QgsProcessingUtils::normalizeLayerSource( layer->source() ).prepend( '\'' ).append( '\'' );
return QgsProcessingParameterDefinition::valueAsPythonString( value, context );
}
QgsProcessingParameterMapLayer::QgsProcessingParameterMapLayer( const QString &name, const QString &description, const QVariant &defaultValue, bool optional )
: QgsProcessingParameterDefinition( name, description, defaultValue, optional )
{
@ -789,6 +825,17 @@ bool QgsProcessingParameterMapLayer::checkValueIsAcceptable( const QVariant &inp
return false;
}
QString QgsProcessingParameterMapLayer::valueAsPythonString( const QVariant &val, QgsProcessingContext &context ) const
{
if ( val.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( val.value< QgsProperty >().asExpression() );
QVariantMap p;
p.insert( name(), val );
QgsMapLayer *layer = QgsProcessingParameters::parameterAsLayer( this, p, context );
return layer ? QgsProcessingUtils::normalizeLayerSource( layer->source() ).prepend( '\'' ).append( '\'' ) : QString();
}
QgsProcessingParameterExtent::QgsProcessingParameterExtent( const QString &name, const QString &description, const QVariant &defaultValue, bool optional )
: QgsProcessingParameterDefinition( name, description, defaultValue, optional )
{
@ -836,6 +883,20 @@ bool QgsProcessingParameterExtent::checkValueIsAcceptable( const QVariant &input
return false;
}
QString QgsProcessingParameterExtent::valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const
{
if ( value.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );
QVariantMap p;
p.insert( name(), value );
QgsMapLayer *layer = QgsProcessingParameters::parameterAsLayer( this, p, context );
if ( layer )
return QgsProcessingUtils::normalizeLayerSource( layer->source() ).prepend( '\'' ).append( '\'' );
return QgsProcessingParameterDefinition::valueAsPythonString( value, context );
}
QgsProcessingParameterPoint::QgsProcessingParameterPoint( const QString &name, const QString &description, const QVariant &defaultValue, bool optional )
: QgsProcessingParameterDefinition( name, description, defaultValue, optional )
{
@ -943,6 +1004,36 @@ bool QgsProcessingParameterMatrix::checkValueIsAcceptable( const QVariant &input
return false;
}
QString QgsProcessingParameterMatrix::valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const
{
if ( value.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );
QVariantMap p;
p.insert( name(), value );
QVariantList list = QgsProcessingParameters::parameterAsMatrix( this, p, context );
QStringList parts;
Q_FOREACH ( const QVariant &v, list )
{
if ( v.type() == QVariant::List )
{
QStringList parts2;
Q_FOREACH ( const QVariant &v2, v.toList() )
{
parts2 << v2.toString();
}
parts << parts2.join( ',' ).prepend( '[' ).append( ']' );
}
else
{
parts << v.toString();
}
}
return parts.join( ',' ).prepend( '[' ).append( ']' );
}
QStringList QgsProcessingParameterMatrix::headers() const
{
return mHeaders;
@ -1045,6 +1136,27 @@ bool QgsProcessingParameterMultipleLayers::checkValueIsAcceptable( const QVarian
return false;
}
QString QgsProcessingParameterMultipleLayers::valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const
{
if ( value.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );
QVariantMap p;
p.insert( name(), value );
QList<QgsMapLayer *> list = QgsProcessingParameters::parameterAsLayerList( this, p, context );
if ( !list.isEmpty() )
{
QStringList parts;
Q_FOREACH ( const QgsMapLayer *layer, list )
{
parts << QgsProcessingUtils::normalizeLayerSource( layer->source() ).prepend( '\'' ).append( '\'' );
}
return parts.join( ',' ).prepend( '[' ).append( ']' );
}
return QgsProcessingParameterDefinition::valueAsPythonString( value, context );
}
QgsProcessingParameterDefinition::LayerType QgsProcessingParameterMultipleLayers::layerType() const
{
return mLayerType;
@ -1096,6 +1208,14 @@ bool QgsProcessingParameterNumber::checkValueIsAcceptable( const QVariant &input
return true;
}
QString QgsProcessingParameterNumber::valueAsPythonString( const QVariant &value, QgsProcessingContext & ) const
{
if ( value.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );
return value.toString();
}
double QgsProcessingParameterNumber::minimum() const
{
return mMin;
@ -1173,6 +1293,23 @@ bool QgsProcessingParameterRange::checkValueIsAcceptable( const QVariant &input,
return false;
}
QString QgsProcessingParameterRange::valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const
{
if ( value.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );
QVariantMap p;
p.insert( name(), value );
QList< double > parts = QgsProcessingParameters::parameterAsRange( this, p, context );
QStringList stringParts;
Q_FOREACH ( double v, parts )
{
stringParts << QString::number( v );
}
return stringParts.join( ',' ).prepend( '[' ).append( ']' );
}
QgsProcessingParameterNumber::Type QgsProcessingParameterRange::dataType() const
{
return mDataType;
@ -1218,6 +1355,17 @@ bool QgsProcessingParameterRasterLayer::checkValueIsAcceptable( const QVariant &
return false;
}
QString QgsProcessingParameterRasterLayer::valueAsPythonString( const QVariant &val, QgsProcessingContext &context ) const
{
if ( val.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( val.value< QgsProperty >().asExpression() );
QVariantMap p;
p.insert( name(), val );
QgsRasterLayer *layer = QgsProcessingParameters::parameterAsRasterLayer( this, p, context );
return layer ? QgsProcessingUtils::normalizeLayerSource( layer->source() ).prepend( '\'' ).append( '\'' ) : QString();
}
QgsProcessingParameterEnum::QgsProcessingParameterEnum( const QString &name, const QString &description, const QStringList &options, bool allowMultiple, const QVariant &defaultValue, bool optional )
: QgsProcessingParameterDefinition( name, description, defaultValue, optional )
, mOptions( options )
@ -1282,6 +1430,32 @@ bool QgsProcessingParameterEnum::checkValueIsAcceptable( const QVariant &input,
return false;
}
QString QgsProcessingParameterEnum::valueAsPythonString( const QVariant &value, QgsProcessingContext & ) const
{
if ( value.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );
if ( value.type() == QVariant::List )
{
QStringList parts;
Q_FOREACH ( const QVariant &val, value.toList() )
{
parts << QString::number( static_cast< int >( val.toDouble() ) );
}
return parts.join( ',' ).prepend( '[' ).append( ']' );
}
else if ( value.type() == QVariant::String )
{
QStringList parts = value.toString().split( ',' );
if ( parts.count() > 1 )
{
return parts.join( ',' ).prepend( '[' ).append( ']' );
}
}
return QString::number( static_cast< int >( value.toDouble() ) );
}
QStringList QgsProcessingParameterEnum::options() const
{
return mOptions;
@ -1309,6 +1483,16 @@ QgsProcessingParameterString::QgsProcessingParameterString( const QString &name,
}
QString QgsProcessingParameterString::valueAsPythonString( const QVariant &value, QgsProcessingContext & ) const
{
if ( value.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );
QString s = value.toString();
s.replace( '\n', QStringLiteral( "\\n" ) );
return s.prepend( '\'' ).append( '\'' );
}
bool QgsProcessingParameterString::multiLine() const
{
return mMultiLine;
@ -1326,6 +1510,16 @@ QgsProcessingParameterExpression::QgsProcessingParameterExpression( const QStrin
}
QString QgsProcessingParameterExpression::valueAsPythonString( const QVariant &value, QgsProcessingContext & ) const
{
if ( value.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );
QString s = value.toString();
s.replace( '\n', QStringLiteral( "\\n" ) );
return s.prepend( '\'' ).append( '\'' );
}
QString QgsProcessingParameterExpression::parentLayerParameter() const
{
return mParentLayerParameter;
@ -1383,6 +1577,33 @@ bool QgsProcessingParameterTableField::checkValueIsAcceptable( const QVariant &i
return true;
}
QString QgsProcessingParameterTableField::valueAsPythonString( const QVariant &value, QgsProcessingContext & ) const
{
if ( value.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );
if ( value.type() == QVariant::List )
{
QStringList parts;
Q_FOREACH ( const QVariant &val, value.toList() )
{
parts << val.toString().prepend( '\'' ).append( '\'' );
}
return parts.join( ',' ).prepend( '[' ).append( ']' );
}
else if ( value.type() == QVariant::StringList )
{
QStringList parts;
Q_FOREACH ( QString s, value.toStringList() )
{
parts << s.prepend( '\'' ).append( '\'' );
}
return parts.join( ',' ).prepend( '[' ).append( ']' );
}
return value.toString().prepend( '\'' ).append( '\'' );
}
QString QgsProcessingParameterTableField::parentLayerParameter() const
{
return mParentLayerParameter;
@ -1457,6 +1678,29 @@ bool QgsProcessingParameterFeatureSource::checkValueIsAcceptable( const QVariant
return false;
}
QString QgsProcessingParameterFeatureSource::valueAsPythonString( const QVariant &value, QgsProcessingContext & ) const
{
if ( value.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );
if ( value.canConvert<QgsProcessingFeatureSourceDefinition>() )
{
QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( value );
if ( fromVar.source.propertyType() == QgsProperty::StaticProperty )
{
return QStringLiteral( "QgsProcessingFeatureSourceDefinition('%1', %2)" ).arg( fromVar.source.staticValue().toString(),
fromVar.selectedFeaturesOnly ? QStringLiteral( "True" ) : QStringLiteral( "False" ) );
}
else
{
return QStringLiteral( "QgsProcessingFeatureSourceDefinition(QgsProperty.fromExpression('%1'), %2)" ).arg( fromVar.source.asExpression(),
fromVar.selectedFeaturesOnly ? QStringLiteral( "True" ) : QStringLiteral( "False" ) );
}
}
return value.toString().prepend( '\'' ).append( '\'' );
}
QList< int > QgsProcessingParameterFeatureSource::dataTypes() const
{
return mDataTypes;
@ -1501,6 +1745,27 @@ bool QgsProcessingParameterFeatureSink::checkValueIsAcceptable( const QVariant &
return true;
}
QString QgsProcessingParameterFeatureSink::valueAsPythonString( const QVariant &value, QgsProcessingContext & ) const
{
if ( value.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );
if ( value.canConvert<QgsProcessingOutputLayerDefinition>() )
{
QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( value );
if ( fromVar.sink.propertyType() == QgsProperty::StaticProperty )
{
return QStringLiteral( "QgsProcessingOutputLayerDefinition('%1')" ).arg( fromVar.sink.staticValue().toString() );
}
else
{
return QStringLiteral( "QgsProcessingOutputLayerDefinition(QgsProperty.fromExpression('%1'))" ).arg( fromVar.sink.asExpression() );
}
}
return value.toString().prepend( '\'' ).append( '\'' );
}
QgsProcessingParameterDefinition::LayerType QgsProcessingParameterFeatureSink::dataType() const
{
return mDataType;
@ -1560,6 +1825,27 @@ bool QgsProcessingParameterRasterOutput::checkValueIsAcceptable( const QVariant
return true;
}
QString QgsProcessingParameterRasterOutput::valueAsPythonString( const QVariant &value, QgsProcessingContext & ) const
{
if ( value.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );
if ( value.canConvert<QgsProcessingOutputLayerDefinition>() )
{
QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( value );
if ( fromVar.sink.propertyType() == QgsProperty::StaticProperty )
{
return QStringLiteral( "QgsProcessingOutputLayerDefinition('%1')" ).arg( fromVar.sink.staticValue().toString() );
}
else
{
return QStringLiteral( "QgsProcessingOutputLayerDefinition(QgsProperty.fromExpression('%1'))" ).arg( fromVar.sink.asExpression() );
}
}
return value.toString().prepend( '\'' ).append( '\'' );
}
QgsProcessingParameterFileOutput::QgsProcessingParameterFileOutput( const QString &name, const QString &description, const QString &fileFilter, const QVariant &defaultValue, bool optional )
: QgsProcessingParameterDefinition( name, description, defaultValue, optional )
@ -1596,6 +1882,27 @@ bool QgsProcessingParameterFileOutput::checkValueIsAcceptable( const QVariant &i
return true;
}
QString QgsProcessingParameterFileOutput::valueAsPythonString( const QVariant &value, QgsProcessingContext & ) const
{
if ( value.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );
if ( value.canConvert<QgsProcessingOutputLayerDefinition>() )
{
QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( value );
if ( fromVar.sink.propertyType() == QgsProperty::StaticProperty )
{
return QStringLiteral( "QgsProcessingOutputLayerDefinition('%1')" ).arg( fromVar.sink.staticValue().toString() );
}
else
{
return QStringLiteral( "QgsProcessingOutputLayerDefinition(QgsProperty.fromExpression('%1'))" ).arg( fromVar.sink.asExpression() );
}
}
return value.toString().prepend( '\'' ).append( '\'' );
}
QString QgsProcessingParameterFileOutput::fileFilter() const
{
return mFileFilter;

View File

@ -319,6 +319,13 @@ class CORE_EXPORT QgsProcessingParameterDefinition
*/
virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const;
/**
* Returns a string version of the parameter input \a value, which is suitable for use as an input
* parameter value when running an algorithm directly from a Python command.
* The returned value must be correctly escaped - e.g. string values must be wrapped in ' 's.
*/
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
protected:
//! Parameter name
@ -530,6 +537,7 @@ class CORE_EXPORT QgsProcessingParameterBoolean : public QgsProcessingParameterD
bool optional = false );
QString type() const override { return QStringLiteral( "boolean" ); }
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
};
/**
@ -550,6 +558,7 @@ class CORE_EXPORT QgsProcessingParameterCrs : public QgsProcessingParameterDefin
QString type() const override { return QStringLiteral( "crs" ); }
bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override;
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
};
/**
@ -570,6 +579,7 @@ class CORE_EXPORT QgsProcessingParameterMapLayer : public QgsProcessingParameter
QString type() const override { return QStringLiteral( "layer" ); }
bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override;
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
};
/**
@ -590,6 +600,7 @@ class CORE_EXPORT QgsProcessingParameterExtent : public QgsProcessingParameterDe
QString type() const override { return QStringLiteral( "extent" ); }
bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override;
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
};
@ -691,6 +702,7 @@ class CORE_EXPORT QgsProcessingParameterMatrix : public QgsProcessingParameterDe
QString type() const override { return QStringLiteral( "matrix" ); }
bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override;
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
/**
* Returns a list of column headers (if set).
@ -761,6 +773,7 @@ class CORE_EXPORT QgsProcessingParameterMultipleLayers : public QgsProcessingPar
QString type() const override { return QStringLiteral( "multilayer" ); }
bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override;
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
/**
* Returns the layer type for layers acceptable by the parameter.
@ -825,6 +838,7 @@ class CORE_EXPORT QgsProcessingParameterNumber : public QgsProcessingParameterDe
QString type() const override { return QStringLiteral( "number" ); }
bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override;
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
/**
* Returns the minimum value acceptable by the parameter.
@ -889,6 +903,7 @@ class CORE_EXPORT QgsProcessingParameterRange : public QgsProcessingParameterDef
QString type() const override { return QStringLiteral( "range" ); }
bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override;
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
/**
* Returns the acceptable data type for the range.
@ -925,6 +940,7 @@ class CORE_EXPORT QgsProcessingParameterRasterLayer : public QgsProcessingParame
QString type() const override { return QStringLiteral( "raster" ); }
bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override;
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
};
@ -948,6 +964,7 @@ class CORE_EXPORT QgsProcessingParameterEnum : public QgsProcessingParameterDefi
QString type() const override { return QStringLiteral( "enum" ); }
bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override;
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
/**
* Returns the list of acceptable options for the parameter.
@ -998,6 +1015,7 @@ class CORE_EXPORT QgsProcessingParameterString : public QgsProcessingParameterDe
bool optional = false );
QString type() const override { return QStringLiteral( "string" ); }
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
/**
* Returns true if the parameter allows multiline strings.
@ -1035,6 +1053,7 @@ class CORE_EXPORT QgsProcessingParameterExpression : public QgsProcessingParamet
bool optional = false );
QString type() const override { return QStringLiteral( "expression" ); }
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
/**
* Returns the name of the parent layer parameter, or an empty string if this is not set.
@ -1104,6 +1123,7 @@ class CORE_EXPORT QgsProcessingParameterTableField : public QgsProcessingParamet
QString type() const override { return QStringLiteral( "field" ); }
bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override;
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
/**
* Returns the name of the parent layer parameter, or an empty string if this is not set.
@ -1168,6 +1188,7 @@ class CORE_EXPORT QgsProcessingParameterFeatureSource : public QgsProcessingPara
QString type() const override { return QStringLiteral( "source" ); }
bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override;
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
/**
* Returns the geometry types for sources acceptable by the parameter.
@ -1209,6 +1230,7 @@ class CORE_EXPORT QgsProcessingParameterFeatureSink : public QgsProcessingParame
QString type() const override { return QStringLiteral( "sink" ); }
bool isDestination() const override { return true; }
bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override;
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
/**
* Returns the layer type for sinks associated with the parameter.
@ -1253,6 +1275,7 @@ class CORE_EXPORT QgsProcessingParameterRasterOutput : public QgsProcessingParam
QString type() const override { return QStringLiteral( "rasterOut" ); }
bool isDestination() const override { return true; }
bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override;
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
};
/**
@ -1276,6 +1299,7 @@ class CORE_EXPORT QgsProcessingParameterFileOutput : public QgsProcessingParamet
QString type() const override { return QStringLiteral( "fileOut" ); }
bool isDestination() const override { return true; }
bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override;
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
/**
* Returns the file filter string for files compatible with this output.

View File

@ -23,6 +23,7 @@
#include "qgsvectorlayerexporter.h"
#include "qgsvectorfilewriter.h"
#include "qgsmemoryproviderutils.h"
#include "qgsprocessingparameters.h"
QList<QgsRasterLayer *> QgsProcessingUtils::compatibleRasterLayers( QgsProject *project, bool sort )
{
@ -365,6 +366,40 @@ QgsRectangle QgsProcessingUtils::combineLayerExtents( const QList<QgsMapLayer *>
return extent;
}
QVariant QgsProcessingUtils::generateIteratingDestination( const QVariant &input, const QVariant &id, QgsProcessingContext &context )
{
if ( !input.isValid() )
return QStringLiteral( "memory:%1" ).arg( id.toString() );
if ( input.canConvert<QgsProcessingOutputLayerDefinition>() )
{
QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( input );
QVariant newSink = generateIteratingDestination( fromVar.sink, id, context );
fromVar.sink = QgsProperty::fromValue( newSink );
return fromVar;
}
else if ( input.canConvert<QgsProperty>() )
{
QString res = input.value< QgsProperty>().valueAsString( context.expressionContext() );
return generateIteratingDestination( res, id, context );
}
else
{
QString res = input.toString();
if ( res.startsWith( QStringLiteral( "memory:" ) ) )
{
return res + '_' + id.toString();
}
else
{
// assume a filename type output for now
// TODO - uris?
int lastIndex = res.lastIndexOf( '.' );
return res.left( lastIndex ) + '_' + id.toString() + res.mid( lastIndex );
}
}
}
//
// QgsProcessingFeatureSource

View File

@ -161,6 +161,14 @@ class CORE_EXPORT QgsProcessingUtils
*/
static QgsRectangle combineLayerExtents( const QList< QgsMapLayer *> layers, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() );
/**
* Converts an \a input parameter value for use in source iterating mode, where one individual sink
* is created per input feature.
* The \a id parameter represents the unique ID for this output, which is embedded into the resulting
* parameter value.
*/
static QVariant generateIteratingDestination( const QVariant &input, const QVariant &id, QgsProcessingContext &context );
private:
static bool canUseLayer( const QgsRasterLayer *layer );

View File

@ -187,6 +187,31 @@ class DummyAlgorithm : public QgsProcessingAlgorithm
QVERIFY( !validateInputCrs( parameters, context ) );
}
void runAsPythonCommandChecks()
{
addParameter( new QgsProcessingParameterString( "p1" ) );
addParameter( new QgsProcessingParameterString( "p2" ) );
QgsProcessingParameterString *hidden = new QgsProcessingParameterString( "p3" );
hidden->setFlags( QgsProcessingParameterDefinition::FlagHidden );
addParameter( hidden );
QVariantMap params;
QgsProcessingContext context;
QCOMPARE( asPythonCommand( params, context ), QStringLiteral( "processing.run(\"test\", {})" ) );
params.insert( "p1", "a" );
QCOMPARE( asPythonCommand( params, context ), QStringLiteral( "processing.run(\"test\", {'p1':'a'})" ) );
params.insert( "p2", QVariant() );
// not set, should be no change
QCOMPARE( asPythonCommand( params, context ), QStringLiteral( "processing.run(\"test\", {'p1':'a'})" ) );
params.insert( "p2", "b" );
QCOMPARE( asPythonCommand( params, context ), QStringLiteral( "processing.run(\"test\", {'p1':'a','p2':'b'})" ) );
// hidden, shouldn't be shown
params.insert( "p3", "b" );
QCOMPARE( asPythonCommand( params, context ), QStringLiteral( "processing.run(\"test\", {'p1':'a','p2':'b'})" ) );
}
};
//dummy provider for testing
@ -290,6 +315,8 @@ class TestQgsProcessing: public QObject
void processingFeatureSink();
void algorithmScope();
void validateInputCrs();
void generateIteratingDestination();
void asPythonCommand();
private:
@ -1317,6 +1344,12 @@ void TestQgsProcessing::parameterBoolean()
params.remove( "non_optional_default_false" );
QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), false );
QCOMPARE( def->valueAsPythonString( false, context ), QStringLiteral( "False" ) );
QCOMPARE( def->valueAsPythonString( true, context ), QStringLiteral( "True" ) );
QCOMPARE( def->valueAsPythonString( "false", context ), QStringLiteral( "False" ) );
QCOMPARE( def->valueAsPythonString( "true", context ), QStringLiteral( "True" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
def.reset( new QgsProcessingParameterBoolean( "optional_default_true", QString(), true, true ) );
QVERIFY( def->checkValueIsAcceptable( false ) );
@ -1444,6 +1477,12 @@ void TestQgsProcessing::parameterCrs()
params.insert( "non_optional", QString( "i'm not a crs, and nothing you can do will make me one" ) );
QVERIFY( !QgsProcessingParameters::parameterAsCrs( def.get(), params, context ).isValid() );
QCOMPARE( def->valueAsPythonString( "EPSG:12003", context ), QStringLiteral( "'EPSG:12003'" ) );
QCOMPARE( def->valueAsPythonString( "ProjectCrs", context ), QStringLiteral( "'ProjectCrs'" ) );
QCOMPARE( def->valueAsPythonString( raster1, context ), QString( "'" ) + testDataDir + QStringLiteral( "tenbytenraster.asc'" ) );
QCOMPARE( def->valueAsPythonString( r1->id(), context ), QString( "'" ) + testDataDir + QStringLiteral( "tenbytenraster.asc'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
// optional
def.reset( new QgsProcessingParameterCrs( "optional", QString(), QString( "EPSG:3113" ), true ) );
params.insert( "optional", QVariant() );
@ -1514,6 +1553,10 @@ void TestQgsProcessing::parameterLayer()
params.insert( "non_optional", QVariant::fromValue( v1 ) );
QCOMPARE( QgsProcessingParameters::parameterAsLayer( def.get(), params, context ), v1 );
QCOMPARE( def->valueAsPythonString( raster1, context ), QString( "'" ) + testDataDir + QStringLiteral( "tenbytenraster.asc'" ) );
QCOMPARE( def->valueAsPythonString( r1->id(), context ), QString( "'" ) + testDataDir + QStringLiteral( "tenbytenraster.asc'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( r1 ), context ), QString( "'" ) + testDataDir + QStringLiteral( "tenbytenraster.asc'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
// optional
def.reset( new QgsProcessingParameterMapLayer( "optional", QString(), v1->id(), true ) );
@ -1592,6 +1635,12 @@ void TestQgsProcessing::parameterExtent()
params.insert( "non_optional", QString( "i'm not a crs, and nothing you can do will make me one" ) );
QVERIFY( QgsProcessingParameters::parameterAsExtent( def.get(), params, context ).isNull() );
QCOMPARE( def->valueAsPythonString( "1,2,3,4", context ), QStringLiteral( "'1,2,3,4'" ) );
QCOMPARE( def->valueAsPythonString( r1->id(), context ), QString( "'" ) + testDataDir + QStringLiteral( "tenbytenraster.asc'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( r1 ), context ), QString( "'" ) + testDataDir + QStringLiteral( "tenbytenraster.asc'" ) );
QCOMPARE( def->valueAsPythonString( raster2, context ), QString( "'" ) + testDataDir + QStringLiteral( "landsat.tif'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
// optional
def.reset( new QgsProcessingParameterExtent( "optional", QString(), QString( "5,6,7,8" ), true ) );
QVERIFY( def->checkValueIsAcceptable( false ) );
@ -1637,6 +1686,8 @@ void TestQgsProcessing::parameterPoint()
QCOMPARE( point.x(), 0.0 );
QCOMPARE( point.y(), 0.0 );
QCOMPARE( def->valueAsPythonString( "1,2", context ), QStringLiteral( "'1,2'" ) );
// optional
def.reset( new QgsProcessingParameterPoint( "optional", QString(), QString( "5.1,6.2" ), true ) );
QVERIFY( def->checkValueIsAcceptable( "1.1,2" ) );
@ -1676,6 +1727,9 @@ void TestQgsProcessing::parameterFile()
QVERIFY( def->checkValueIsAcceptable( "bricks.BMP" ) );
QVERIFY( !def->checkValueIsAcceptable( "bricks.pcx" ) );
QCOMPARE( def->valueAsPythonString( "bricks.bmp", context ), QStringLiteral( "'bricks.bmp'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
// optional
def.reset( new QgsProcessingParameterFile( "optional", QString(), QgsProcessingParameterFile::File, QString(), QString( "gef.bmp" ), true ) );
QVERIFY( def->checkValueIsAcceptable( false ) );
@ -1715,6 +1769,12 @@ void TestQgsProcessing::parameterMatrix()
params.insert( "non_optional", QString( "4,5,6" ) );
QCOMPARE( QgsProcessingParameters::parameterAsMatrix( def.get(), params, context ), QVariantList() << 4 << 5 << 6 );
QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "[5]" ) );
QCOMPARE( def->valueAsPythonString( QVariantList() << 1 << 2 << 3, context ), QStringLiteral( "[1,2,3]" ) );
QCOMPARE( def->valueAsPythonString( QVariantList() << ( QVariantList() << 1 << 2 << 3 ) << ( QVariantList() << 1 << 2 << 3 ), context ), QStringLiteral( "[1,2,3,1,2,3]" ) );
QCOMPARE( def->valueAsPythonString( "1,2,3", context ), QStringLiteral( "[1,2,3]" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
// optional
def.reset( new QgsProcessingParameterMatrix( "optional", QString(), 3, false, QStringList(), QVariantList() << 4 << 5 << 6, true ) );
QVERIFY( def->checkValueIsAcceptable( 5 ) );
@ -1819,6 +1879,13 @@ void TestQgsProcessing::parameterLayerList()
QVERIFY( !def->checkValueIsAcceptable( QStringList() << "layer12312312" << "layerB" ) );
QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "layer12312312" << "layerB" ) );
QCOMPARE( def->valueAsPythonString( "layer12312312", context ), QStringLiteral( "'layer12312312'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( r1 ), context ), QStringLiteral( "['" ) + testDataDir + QStringLiteral( "tenbytenraster.asc']" ) );
QCOMPARE( def->valueAsPythonString( r1->id(), context ), QStringLiteral( "['" ) + testDataDir + QStringLiteral( "tenbytenraster.asc']" ) );
QCOMPARE( def->valueAsPythonString( QStringList() << r1->id() << raster2, context ), QStringLiteral( "['" ) + testDataDir + QStringLiteral( "tenbytenraster.asc','" ) + testDataDir + QStringLiteral( "landsat.tif']" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
// optional with one default layer
def.reset( new QgsProcessingParameterMultipleLayers( "optional", QString(), QgsProcessingParameterDefinition::TypeAny, v1->id(), true ) );
QVERIFY( !def->checkValueIsAcceptable( false ) );
@ -1910,6 +1977,10 @@ void TestQgsProcessing::parameterNumber()
QVERIFY( def->checkValueIsAcceptable( 15 ) );
QVERIFY( def->checkValueIsAcceptable( "11.1" ) );
QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "5" ) );
QCOMPARE( def->valueAsPythonString( QStringLiteral( "1.1" ), context ), QStringLiteral( "1.1" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
// optional
def.reset( new QgsProcessingParameterNumber( "optional", QString(), QgsProcessingParameterNumber::Double, 5.4, true ) );
QVERIFY( def->checkValueIsAcceptable( 5 ) );
@ -1976,6 +2047,10 @@ void TestQgsProcessing::parameterRange()
params.insert( "non_optional", QVariantList() << 1.1 );
range = QgsProcessingParameters::parameterAsRange( def.get(), params, context );
QCOMPARE( def->valueAsPythonString( "1.1,2", context ), QStringLiteral( "[1.1,2]" ) );
QCOMPARE( def->valueAsPythonString( QVariantList() << 1.1 << 2, context ), QStringLiteral( "[1.1,2]" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
// optional
def.reset( new QgsProcessingParameterRange( "optional", QString(), QgsProcessingParameterNumber::Double, QString( "5.4,7.4" ), true ) );
QVERIFY( def->checkValueIsAcceptable( "1.1,2" ) );
@ -2048,6 +2123,11 @@ void TestQgsProcessing::parameterRasterLayer()
params.insert( "non_optional", QString( "i'm not a layer, and nothing you can do will make me one" ) );
QVERIFY( !QgsProcessingParameters::parameterAsRasterLayer( def.get(), params, context ) );
QCOMPARE( def->valueAsPythonString( raster1, context ), QString( "'" ) + testDataDir + QStringLiteral( "tenbytenraster.asc'" ) );
QCOMPARE( def->valueAsPythonString( r1->id(), context ), QString( "'" ) + testDataDir + QStringLiteral( "tenbytenraster.asc'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( r1 ), context ), QString( "'" ) + testDataDir + QStringLiteral( "tenbytenraster.asc'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
// optional
def.reset( new QgsProcessingParameterRasterLayer( "optional", QString(), r1->id(), true ) );
QCOMPARE( QgsProcessingParameters::parameterAsRasterLayer( def.get(), params, context )->id(), r1->id() );
@ -2113,6 +2193,10 @@ void TestQgsProcessing::parameterEnum()
iNumber = QgsProcessingParameters::parameterAsEnum( def.get(), params, context );
QCOMPARE( iNumber, 2 );
QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "5" ) );
QCOMPARE( def->valueAsPythonString( QStringLiteral( "1.1" ), context ), QStringLiteral( "1" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
// multiple
def.reset( new QgsProcessingParameterEnum( "non_optional", QString(), QStringList() << "A" << "B" << "C", true, 5, false ) );
QVERIFY( !def->checkValueIsAcceptable( false ) );
@ -2136,6 +2220,9 @@ void TestQgsProcessing::parameterEnum()
iNumbers = QgsProcessingParameters::parameterAsEnums( def.get(), params, context );
QCOMPARE( iNumbers, QList<int>() << 0 << 2 );
QCOMPARE( def->valueAsPythonString( QVariantList() << 1 << 2, context ), QStringLiteral( "[1,2]" ) );
QCOMPARE( def->valueAsPythonString( QStringLiteral( "1,2" ), context ), QStringLiteral( "[1,2]" ) );
// optional
def.reset( new QgsProcessingParameterEnum( "optional", QString(), QStringList() << "a" << "b", false, 5, true ) );
QVERIFY( def->checkValueIsAcceptable( 1 ) );
@ -2196,6 +2283,12 @@ void TestQgsProcessing::parameterString()
params.insert( "non_optional", QString( "abcdef" ) );
QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString( "abcdef" ) );
QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "'5'" ) );
QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc\ndef" ), context ), QStringLiteral( "'abc\\ndef'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
// optional
def.reset( new QgsProcessingParameterString( "optional", QString(), QString( "default" ), false, true ) );
QVERIFY( def->checkValueIsAcceptable( 1 ) );
@ -2223,6 +2316,12 @@ void TestQgsProcessing::parameterExpression()
params.insert( "non_optional", QString( "abcdef" ) );
QCOMPARE( QgsProcessingParameters::parameterAsExpression( def.get(), params, context ), QString( "abcdef" ) );
QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "'5'" ) );
QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc\ndef" ), context ), QStringLiteral( "'abc\\ndef'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
// optional
def.reset( new QgsProcessingParameterExpression( "optional", QString(), QString( "default" ), QString(), true ) );
QVERIFY( def->checkValueIsAcceptable( 1 ) );
@ -2259,6 +2358,9 @@ void TestQgsProcessing::parameterField()
QStringList fields = QgsProcessingParameters::parameterAsFields( def.get(), params, context );
QCOMPARE( fields, QStringList() << "a" );
QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
// multiple
def.reset( new QgsProcessingParameterTableField( "non_optional", QString(), QString(), QString(), QgsProcessingParameterTableField::Any, true, false ) );
QVERIFY( def->checkValueIsAcceptable( 1 ) );
@ -2275,6 +2377,9 @@ void TestQgsProcessing::parameterField()
fields = QgsProcessingParameters::parameterAsFields( def.get(), params, context );
QCOMPARE( fields, QStringList() << "a" << "b" );
QCOMPARE( def->valueAsPythonString( QStringList() << "a" << "b", context ), QStringLiteral( "['a','b']" ) );
QCOMPARE( def->valueAsPythonString( QStringList() << "a" << "b", context ), QStringLiteral( "['a','b']" ) );
// optional
def.reset( new QgsProcessingParameterTableField( "optional", QString(), QString( "def" ), QString(), QgsProcessingParameterTableField::Any, false, true ) );
QVERIFY( def->checkValueIsAcceptable( 1 ) );
@ -2369,6 +2474,12 @@ void TestQgsProcessing::parameterFeatureSource()
params.insert( "non_optional", QString( "i'm not a layer, and nothing you can do will make me one" ) );
QVERIFY( !QgsProcessingParameters::parameterAsVectorLayer( def.get(), params, context ) );
QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( "abc" ) ), context ), QStringLiteral( "QgsProcessingFeatureSourceDefinition('abc', False)" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "abc" ), true ) ), context ), QStringLiteral( "QgsProcessingFeatureSourceDefinition('abc', True)" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ) ) ), context ), QStringLiteral( "QgsProcessingFeatureSourceDefinition(QgsProperty.fromExpression('\"abc\" || \"def\"'), False)" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
// optional
def.reset( new QgsProcessingParameterFeatureSource( "optional", QString(), QList< int >() << QgsProcessingParameterDefinition::TypeVectorAny, v1->id(), true ) );
params.insert( "optional", QVariant() );
@ -2409,6 +2520,12 @@ void TestQgsProcessing::parameterFeatureSink()
QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp", &context ) );
QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( "abc" ) ), context ), QStringLiteral( "QgsProcessingOutputLayerDefinition('abc')" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context ), QStringLiteral( "QgsProcessingOutputLayerDefinition('abc')" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ) ) ), context ), QStringLiteral( "QgsProcessingOutputLayerDefinition(QgsProperty.fromExpression('\"abc\" || \"def\"'))" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
// optional
def.reset( new QgsProcessingParameterFeatureSink( "optional", QString(), QgsProcessingParameterDefinition::TypeVectorAny, QString(), true ) );
QVERIFY( !def->checkValueIsAcceptable( false ) );
@ -2460,6 +2577,12 @@ void TestQgsProcessing::parameterRasterOut()
params.insert( "non_optional", QgsProcessingOutputLayerDefinition( "test.tif" ) );
QCOMPARE( QgsProcessingParameters::parameterAsRasterOutputLayer( def.get(), params, context ), QStringLiteral( "test.tif" ) );
QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( "abc" ) ), context ), QStringLiteral( "QgsProcessingOutputLayerDefinition('abc')" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context ), QStringLiteral( "QgsProcessingOutputLayerDefinition('abc')" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ) ) ), context ), QStringLiteral( "QgsProcessingOutputLayerDefinition(QgsProperty.fromExpression('\"abc\" || \"def\"'))" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
// optional
def.reset( new QgsProcessingParameterRasterOutput( "optional", QString(), QString( "default.tif" ), true ) );
QVERIFY( !def->checkValueIsAcceptable( false ) );
@ -2508,6 +2631,13 @@ void TestQgsProcessing::parameterFileOut()
params.insert( "non_optional", QgsProcessingOutputLayerDefinition( "test.txt" ) );
QCOMPARE( QgsProcessingParameters::parameterAsFileOutput( def.get(), params, context ), QStringLiteral( "test.txt" ) );
QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( "abc" ) ), context ), QStringLiteral( "QgsProcessingOutputLayerDefinition('abc')" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context ), QStringLiteral( "QgsProcessingOutputLayerDefinition('abc')" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ) ) ), context ), QStringLiteral( "QgsProcessingOutputLayerDefinition(QgsProperty.fromExpression('\"abc\" || \"def\"'))" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
// optional
def.reset( new QgsProcessingParameterFileOutput( "optional", QString(), QString(), QString( "default.txt" ), true ) );
QVERIFY( !def->checkValueIsAcceptable( false ) );
@ -2714,5 +2844,38 @@ void TestQgsProcessing::validateInputCrs()
alg.runValidateInputCrsChecks();
}
void TestQgsProcessing::generateIteratingDestination()
{
QgsProcessingContext context;
QCOMPARE( QgsProcessingUtils::generateIteratingDestination( "memory:x", 1, context ).toString(), QStringLiteral( "memory:x_1" ) );
QCOMPARE( QgsProcessingUtils::generateIteratingDestination( "memory:x", 2, context ).toString(), QStringLiteral( "memory:x_2" ) );
QCOMPARE( QgsProcessingUtils::generateIteratingDestination( "ape.shp", 1, context ).toString(), QStringLiteral( "ape_1.shp" ) );
QCOMPARE( QgsProcessingUtils::generateIteratingDestination( "ape.shp", 2, context ).toString(), QStringLiteral( "ape_2.shp" ) );
QCOMPARE( QgsProcessingUtils::generateIteratingDestination( "/home/bif.o/ape.shp", 2, context ).toString(), QStringLiteral( "/home/bif.o/ape_2.shp" ) );
QgsProject p;
QgsProcessingOutputLayerDefinition def;
def.sink = QgsProperty::fromValue( "ape.shp" );
def.destinationProject = &p;
QVariant res = QgsProcessingUtils::generateIteratingDestination( def, 2, context );
QVERIFY( res.canConvert<QgsProcessingOutputLayerDefinition>() );
QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( res );
QCOMPARE( fromVar.sink.staticValue().toString(), QStringLiteral( "ape_2.shp" ) );
QCOMPARE( fromVar.destinationProject, &p );
def.sink = QgsProperty::fromExpression( "'ape' || '.shp'" );
res = QgsProcessingUtils::generateIteratingDestination( def, 2, context );
QVERIFY( res.canConvert<QgsProcessingOutputLayerDefinition>() );
fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( res );
QCOMPARE( fromVar.sink.staticValue().toString(), QStringLiteral( "ape_2.shp" ) );
QCOMPARE( fromVar.destinationProject, &p );
}
void TestQgsProcessing::asPythonCommand()
{
DummyAlgorithm alg( "test" );
alg.runAsPythonCommandChecks();
}
QGSTEST_MAIN( TestQgsProcessing )
#include "testqgsprocessing.moc"