Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Algorithms don't have to be split to prepare/process/postProcess
Since it's safe to evaluate parameters in background threads
now, it's usually going to be ok to evaluate everything in
the processAlgorithm step.

This keeps the algorithm code as simple as possible, and will
make porting faster.

Note that the prepare/postProcess virtual methods still exist
and can be used when an algorithm MUST do setup/cleanup work
in the main thread.
  • Loading branch information
nyalldawson committed Jul 6, 2017
1 parent f39b7a0 commit 8a84e13
Show file tree
Hide file tree
Showing 46 changed files with 817 additions and 1,353 deletions.
10 changes: 5 additions & 5 deletions python/core/processing/qgsprocessingalgorithm.sip
Expand Up @@ -259,7 +259,7 @@ class QgsProcessingAlgorithm
:rtype: bool
%End

bool runPrepared( QgsProcessingContext &context, QgsProcessingFeedback *feedback );
QVariantMap runPrepared( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback );
%Docstring
Runs the algorithm, which has been prepared by an earlier call to prepare().
This method is safe to call from any thread. Returns true if the algorithm was successfully executed.
Expand All @@ -272,7 +272,7 @@ class QgsProcessingAlgorithm
This method modifies the algorithm instance, so it is not safe to call
on algorithms directly retrieved from QgsProcessingRegistry and QgsProcessingProvider. Instead, a copy
of the algorithm should be created with clone() and prepare()/runPrepared() called on the copy.
:rtype: bool
:rtype: QVariantMap
%End

QVariantMap postProcess( QgsProcessingContext &context, QgsProcessingFeedback *feedback );
Expand Down Expand Up @@ -374,7 +374,7 @@ class QgsProcessingAlgorithm
:rtype: bool
%End

virtual bool processAlgorithm( QgsProcessingContext &context, QgsProcessingFeedback *feedback ) = 0 /VirtualErrorHandler=processing_exception_handler/;
virtual QVariantMap processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) = 0 /VirtualErrorHandler=processing_exception_handler/;
%Docstring
Runs the algorithm using the specified ``parameters``. Algorithms should implement
their custom processing logic here.
Expand All @@ -389,10 +389,10 @@ class QgsProcessingAlgorithm
values such as statistical calculations.
.. seealso:: prepareAlgorithm()
.. seealso:: postProcessAlgorithm()
:rtype: bool
:rtype: QVariantMap
%End

virtual QVariantMap postProcessAlgorithm( QgsProcessingContext &context, QgsProcessingFeedback *feedback ) = 0 /VirtualErrorHandler=processing_exception_handler/;
virtual QVariantMap postProcessAlgorithm( QgsProcessingContext &context, QgsProcessingFeedback *feedback ) /VirtualErrorHandler=processing_exception_handler/;
%Docstring
Allows the algorithm to perform any required cleanup tasks. The returned variant map
includes the results evaluated by the algorithm. These may be output layer references, or calculated
Expand Down
6 changes: 1 addition & 5 deletions python/core/processing/qgsprocessingmodelalgorithm.sip
Expand Up @@ -835,11 +835,7 @@ Copies are protected to avoid slicing

protected:

virtual bool prepareAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback );
virtual bool processAlgorithm( QgsProcessingContext &context, QgsProcessingFeedback *feedback );

virtual QVariantMap postProcessAlgorithm( QgsProcessingContext &context, QgsProcessingFeedback *feedback );
virtual QVariantMap processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback );


};
Expand Down
43 changes: 15 additions & 28 deletions python/plugins/processing/algs/qgis/AddTableField.py
Expand Up @@ -74,38 +74,28 @@ def __init__(self):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT_LAYER, self.tr('Added')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT_LAYER, self.tr('Added')))

self.source = None
self.fieldType = None
self.fieldLength = None
self.fieldName = None
self.fieldPrecision = None
self.sink = None
self.dest_id = None

def name(self):
return 'addfieldtoattributestable'

def displayName(self):
return self.tr('Add field to attributes table')

def prepareAlgorithm(self, parameters, context, feedback):
self.source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)
def processAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)

self.fieldType = self.parameterAsEnum(parameters, self.FIELD_TYPE, context)
self.fieldName = self.parameterAsString(parameters, self.FIELD_NAME, context)
self.fieldLength = self.parameterAsInt(parameters, self.FIELD_LENGTH, context)
self.fieldPrecision = self.parameterAsInt(parameters, self.FIELD_PRECISION, context)
fieldType = self.parameterAsEnum(parameters, self.FIELD_TYPE, context)
fieldName = self.parameterAsString(parameters, self.FIELD_NAME, context)
fieldLength = self.parameterAsInt(parameters, self.FIELD_LENGTH, context)
fieldPrecision = self.parameterAsInt(parameters, self.FIELD_PRECISION, context)

fields = self.source.fields()
fields.append(QgsField(self.fieldName, self.TYPES[self.fieldType], '',
self.fieldLength, self.fieldPrecision))
(self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LAYER, context,
fields, self.source.wkbType(), self.source.sourceCrs())
return True
fields = source.fields()
fields.append(QgsField(fieldName, self.TYPES[fieldType], '',
fieldLength, fieldPrecision))
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LAYER, context,
fields, source.wkbType(), source.sourceCrs())

def processAlgorithm(self, context, feedback):
features = self.source.getFeatures()
total = 100.0 / self.source.featureCount() if self.source.featureCount() else 0
features = source.getFeatures()
total = 100.0 / source.featureCount() if source.featureCount() else 0

for current, input_feature in enumerate(features):
if feedback.isCanceled():
Expand All @@ -116,10 +106,7 @@ def processAlgorithm(self, context, feedback):
attributes.append(None)
output_feature.setAttributes(attributes)

self.sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total))

return True

def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT_LAYER: self.dest_id}
return {self.OUTPUT_LAYER: dest_id}
26 changes: 9 additions & 17 deletions python/plugins/processing/algs/qgis/Aspect.py
Expand Up @@ -68,30 +68,22 @@ def __init__(self):
self.addParameter(QgsProcessingParameterRasterOutput(self.OUTPUT, self.tr('Aspect')))
self.addOutput(QgsProcessingOutputRasterLayer(self.OUTPUT, self.tr('Aspect')))

self.inputFile = None
self.outputFile = None
self.outputFormat = None
self.zFactor = None

def name(self):
return 'aspect'

def displayName(self):
return self.tr('Aspect')

def prepareAlgorithm(self, parameters, context, feedback):
self.inputFile = exportRasterLayer(self.parameterAsRasterLayer(parameters, self.INPUT, context))
self.zFactor = self.parameterAsDouble(parameters, self.Z_FACTOR, context)
def processAlgorithm(self, parameters, context, feedback):
inputFile = exportRasterLayer(self.parameterAsRasterLayer(parameters, self.INPUT, context))
zFactor = self.parameterAsDouble(parameters, self.Z_FACTOR, context)

self.outputFile = self.parameterAsRasterOutputLayer(parameters, self.OUTPUT, context)
outputFile = self.parameterAsRasterOutputLayer(parameters, self.OUTPUT, context)

self.outputFormat = raster.formatShortNameFromFileName(self.outputFile)
return True
outputFormat = raster.formatShortNameFromFileName(outputFile)

def processAlgorithm(self, context, feedback):
aspect = QgsAspectFilter(self.inputFile, self.outputFile, self.outputFormat)
aspect.setZFactor(self.zFactor)
return aspect.processRaster(feedback) == 0
aspect = QgsAspectFilter(inputFile, outputFile, outputFormat)
aspect.setZFactor(zFactor)
aspect.processRaster(feedback)

def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.outputFile}
return {self.OUTPUT: outputFile}
26 changes: 9 additions & 17 deletions python/plugins/processing/algs/qgis/AutoincrementalField.py
Expand Up @@ -51,10 +51,6 @@ def __init__(self):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Incremented')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Incremented')))

self.source = None
self.sink = None
self.dest_id = None

def group(self):
return self.tr('Vector table tools')

Expand All @@ -64,18 +60,16 @@ def name(self):
def displayName(self):
return self.tr('Add autoincremental field')

def prepareAlgorithm(self, parameters, context, feedback):
self.source = self.parameterAsSource(parameters, self.INPUT, context)
fields = self.source.fields()
def processAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT, context)
fields = source.fields()
fields.append(QgsField('AUTO', QVariant.Int))

(self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, self.source.wkbType(), self.source.sourceCrs())
return True
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, source.wkbType(), source.sourceCrs())

def processAlgorithm(self, context, feedback):
features = self.source.getFeatures()
total = 100.0 / self.source.featureCount() if self.source.featureCount() else 0
features = source.getFeatures()
total = 100.0 / source.featureCount() if source.featureCount() else 0
for current, input_feature in enumerate(features):
if feedback.isCanceled():
break
Expand All @@ -85,9 +79,7 @@ def processAlgorithm(self, context, feedback):
attributes.append(current)
output_feature.setAttributes(attributes)

self.sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total))
return True

def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.dest_id}
return {self.OUTPUT: dest_id}
48 changes: 19 additions & 29 deletions python/plugins/processing/algs/qgis/BasicStatistics.py
Expand Up @@ -119,52 +119,42 @@ def __init__(self):
self.addOutput(QgsProcessingOutputNumber(self.THIRDQUARTILE, self.tr('Third quartile')))
self.addOutput(QgsProcessingOutputNumber(self.IQR, self.tr('Interquartile Range (IQR)')))

self.source = None
self.field = None
self.field_name = None
self.output_file = None
self.results = {}

def name(self):
return 'basicstatisticsforfields'

def displayName(self):
return self.tr('Basic statistics for fields')

def prepareAlgorithm(self, parameters, context, feedback):
self.source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)
self.field_name = self.parameterAsString(parameters, self.FIELD_NAME, context)
self.field = self.source.fields().at(self.source.fields().lookupField(self.field_name))
def processAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)
field_name = self.parameterAsString(parameters, self.FIELD_NAME, context)
field = source.fields().at(source.fields().lookupField(field_name))

self.output_file = self.parameterAsFileOutput(parameters, self.OUTPUT_HTML_FILE, context)
return True
output_file = self.parameterAsFileOutput(parameters, self.OUTPUT_HTML_FILE, context)

def processAlgorithm(self, context, feedback):
request = QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry).setSubsetOfAttributes([self.field_name], self.source.fields())
features = self.source.getFeatures(request)
count = self.source.featureCount()
request = QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry).setSubsetOfAttributes([field_name], source.fields())
features = source.getFeatures(request)
count = source.featureCount()

data = []
data.append(self.tr('Analyzed field: {}').format(self.field_name))
data.append(self.tr('Analyzed field: {}').format(field_name))
results = {}

if self.field.isNumeric():
d, self.results = self.calcNumericStats(features, feedback, self.field, count)
if field.isNumeric():
d, results = self.calcNumericStats(features, feedback, field, count)
data.extend(d)
elif self.field.type() in (QVariant.Date, QVariant.Time, QVariant.DateTime):
d, self.results = self.calcDateTimeStats(features, feedback, self.field, count)
elif field.type() in (QVariant.Date, QVariant.Time, QVariant.DateTime):
d, results = self.calcDateTimeStats(features, feedback, field, count)
data.extend(d)
else:
d, self.results = self.calcStringStats(features, feedback, self.field, count)
d, results = self.calcStringStats(features, feedback, field, count)
data.extend(d)

if self.output_file:
self.createHTML(self.output_file, data)
self.results[self.OUTPUT_HTML_FILE] = self.output_file

return True
if output_file:
self.createHTML(output_file, data)
results[self.OUTPUT_HTML_FILE] = output_file

def postProcessAlgorithm(self, context, feedback):
return self.results
return results

def calcNumericStats(self, features, feedback, field, count):
total = 100.0 / count if count else 0
Expand Down
27 changes: 9 additions & 18 deletions python/plugins/processing/algs/qgis/Boundary.py
Expand Up @@ -58,10 +58,6 @@ def __init__(self):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT_LAYER, self.tr('Boundary')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT_LAYER, self.tr("Boundaries")))

self.source = None
self.sink = None
self.dest_id = None

def icon(self):
return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'convex_hull.png'))

Expand All @@ -74,11 +70,10 @@ def name(self):
def displayName(self):
return self.tr('Boundary')

def prepareAlgorithm(self, parameters, context, feedback):
self.source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)
def processAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)

input_wkb = self.source.wkbType()
output_wkb = None
input_wkb = source.wkbType()
if QgsWkbTypes.geometryType(input_wkb) == QgsWkbTypes.LineGeometry:
output_wkb = QgsWkbTypes.MultiPoint
elif QgsWkbTypes.geometryType(input_wkb) == QgsWkbTypes.PolygonGeometry:
Expand All @@ -88,13 +83,11 @@ def prepareAlgorithm(self, parameters, context, feedback):
if QgsWkbTypes.hasM(input_wkb):
output_wkb = QgsWkbTypes.addM(output_wkb)

(self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LAYER, context,
self.source.fields(), output_wkb, self.source.sourceCrs())
return True
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LAYER, context,
source.fields(), output_wkb, source.sourceCrs())

def processAlgorithm(self, context, feedback):
features = self.source.getFeatures()
total = 100.0 / self.source.featureCount() if self.source.featureCount() else 0
features = source.getFeatures()
total = 100.0 / source.featureCount() if source.featureCount() else 0

for current, input_feature in enumerate(features):
if feedback.isCanceled():
Expand All @@ -109,9 +102,7 @@ def processAlgorithm(self, context, feedback):

output_feature.setGeometry(output_geometry)

self.sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total))
return True

def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT_LAYER: self.dest_id}
return {self.OUTPUT_LAYER: dest_id}
24 changes: 8 additions & 16 deletions python/plugins/processing/algs/qgis/BoundingBox.py
Expand Up @@ -66,26 +66,20 @@ def __init__(self):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT_LAYER, self.tr('Bounds'), QgsProcessingParameterDefinition.TypeVectorPolygon))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT_LAYER, self.tr("Bounds")))

self.source = None
self.sink = None
self.dest_id = None

def name(self):
return 'boundingboxes'

def displayName(self):
return self.tr('Bounding boxes')

def prepareAlgorithm(self, parameters, context, feedback):
self.source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)
def processAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)

(self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LAYER, context,
self.source.fields(), QgsWkbTypes.Polygon, self.source.sourceCrs())
return True
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LAYER, context,
source.fields(), QgsWkbTypes.Polygon, source.sourceCrs())

def processAlgorithm(self, context, feedback):
features = self.source.getFeatures()
total = 100.0 / self.source.featureCount() if self.source.featureCount() else 0
features = source.getFeatures()
total = 100.0 / source.featureCount() if source.featureCount() else 0

for current, input_feature in enumerate(features):
if feedback.isCanceled():
Expand All @@ -100,9 +94,7 @@ def processAlgorithm(self, context, feedback):

output_feature.setGeometry(output_geometry)

self.sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total))
return True

def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT_LAYER: self.dest_id}
return {self.OUTPUT_LAYER: dest_id}

0 comments on commit 8a84e13

Please sign in to comment.