Skip to content
Permalink
Browse files

[processing] Default to allowing background execution of algorithms

Since the underlying issues with the Python bindings are now fixed,
in most cases we can safely default to allowing an algorithm to
run in a background thread!!

So now we make this the default, and require individual algorithms
which are NOT thread safe to declare this. This includes algorithms
which directly manipulate the current project or layers (such as
setting layer styles), alter the selections in layers, or which
rely on 3rd party libraries (for now, SAGA and GRASS algorithms
are marked as not thread safe... TODO - someone more familiar with
these libraries can investigate and remove the flag if appropriate).

Also models are marked as non-thread safe. TODO: only flag an
individual model as thread-unsafe if any of its child algorithms
report this flag.
  • Loading branch information
nyalldawson committed Jan 29, 2018
1 parent 070e137 commit a05d941e4e50bc912756833e2c6091fb1196ad94
Showing with 89 additions and 288 deletions.
  1. +2 −0 python/core/processing/models/qgsprocessingmodelalgorithm.sip.in
  2. +1 −1 python/core/processing/qgsprocessingalgorithm.sip.in
  3. +4 −0 python/plugins/processing/algs/grass7/Grass7Algorithm.py
  4. +4 −0 python/plugins/processing/algs/qgis/CreateAttributeIndex.py
  5. +4 −0 python/plugins/processing/algs/qgis/DefineProjection.py
  6. +4 −0 python/plugins/processing/algs/qgis/EliminateSelection.py
  7. +4 −0 python/plugins/processing/algs/qgis/ExecuteSQL.py
  8. +4 −0 python/plugins/processing/algs/qgis/ImportIntoSpatialite.py
  9. +4 −0 python/plugins/processing/algs/qgis/RandomSelection.py
  10. +4 −0 python/plugins/processing/algs/qgis/RandomSelectionWithinSubsets.py
  11. +4 −0 python/plugins/processing/algs/qgis/SelectByAttribute.py
  12. +4 −0 python/plugins/processing/algs/qgis/SelectByExpression.py
  13. +5 −1 python/plugins/processing/algs/qgis/SetRasterStyle.py
  14. +5 −1 python/plugins/processing/algs/qgis/SetVectorStyle.py
  15. +4 −0 python/plugins/processing/algs/qgis/SpatialIndex.py
  16. +4 −0 python/plugins/processing/algs/qgis/SpatialiteExecuteSQL.py
  17. +5 −1 python/plugins/processing/algs/qgis/TruncateTable.py
  18. +5 −0 python/plugins/processing/algs/saga/SagaAlgorithm.py
  19. +1 −1 python/plugins/processing/gui/AlgorithmDialog.py
  20. +0 −5 src/3d/processing/qgsalgorithmtessellate.cpp
  21. +0 −1 src/3d/processing/qgsalgorithmtessellate.h
  22. +0 −5 src/analysis/processing/qgsalgorithmaddincrementalfield.cpp
  23. +0 −1 src/analysis/processing/qgsalgorithmaddincrementalfield.h
  24. +0 −5 src/analysis/processing/qgsalgorithmassignprojection.cpp
  25. +0 −1 src/analysis/processing/qgsalgorithmassignprojection.h
  26. +0 −5 src/analysis/processing/qgsalgorithmboundary.cpp
  27. +0 −1 src/analysis/processing/qgsalgorithmboundary.h
  28. +0 −5 src/analysis/processing/qgsalgorithmboundingbox.cpp
  29. +0 −1 src/analysis/processing/qgsalgorithmboundingbox.h
  30. +0 −5 src/analysis/processing/qgsalgorithmbuffer.cpp
  31. +0 −1 src/analysis/processing/qgsalgorithmbuffer.h
  32. +0 −5 src/analysis/processing/qgsalgorithmcentroid.cpp
  33. +0 −1 src/analysis/processing/qgsalgorithmcentroid.h
  34. +0 −5 src/analysis/processing/qgsalgorithmclip.cpp
  35. +0 −1 src/analysis/processing/qgsalgorithmclip.h
  36. +0 −5 src/analysis/processing/qgsalgorithmconvexhull.cpp
  37. +0 −1 src/analysis/processing/qgsalgorithmconvexhull.h
  38. +0 −5 src/analysis/processing/qgsalgorithmdissolve.cpp
  39. +0 −1 src/analysis/processing/qgsalgorithmdissolve.h
  40. +0 −5 src/analysis/processing/qgsalgorithmdropgeometry.cpp
  41. +0 −1 src/analysis/processing/qgsalgorithmdropgeometry.h
  42. +0 −5 src/analysis/processing/qgsalgorithmdropmzvalues.cpp
  43. +0 −1 src/analysis/processing/qgsalgorithmdropmzvalues.h
  44. +0 −5 src/analysis/processing/qgsalgorithmextenttolayer.cpp
  45. +0 −1 src/analysis/processing/qgsalgorithmextenttolayer.h
  46. +0 −5 src/analysis/processing/qgsalgorithmextractbyattribute.cpp
  47. +0 −1 src/analysis/processing/qgsalgorithmextractbyattribute.h
  48. +0 −5 src/analysis/processing/qgsalgorithmextractbyexpression.cpp
  49. +0 −1 src/analysis/processing/qgsalgorithmextractbyexpression.h
  50. +0 −6 src/analysis/processing/qgsalgorithmextractbyextent.cpp
  51. +0 −1 src/analysis/processing/qgsalgorithmextractbyextent.h
  52. +5 −5 src/analysis/processing/qgsalgorithmextractbylocation.cpp
  53. +1 −1 src/analysis/processing/qgsalgorithmextractbylocation.h
  54. +0 −5 src/analysis/processing/qgsalgorithmextractnodes.cpp
  55. +0 −1 src/analysis/processing/qgsalgorithmextractnodes.h
  56. +0 −5 src/analysis/processing/qgsalgorithmfiledownloader.cpp
  57. +0 −1 src/analysis/processing/qgsalgorithmfiledownloader.h
  58. +0 −5 src/analysis/processing/qgsalgorithmfixgeometries.cpp
  59. +0 −1 src/analysis/processing/qgsalgorithmfixgeometries.h
  60. +0 −5 src/analysis/processing/qgsalgorithmjoinbyattribute.cpp
  61. +0 −1 src/analysis/processing/qgsalgorithmjoinbyattribute.h
  62. +0 −5 src/analysis/processing/qgsalgorithmjoinwithlines.cpp
  63. +0 −1 src/analysis/processing/qgsalgorithmjoinwithlines.h
  64. +0 −5 src/analysis/processing/qgsalgorithmlineintersection.cpp
  65. +0 −1 src/analysis/processing/qgsalgorithmlineintersection.h
  66. +0 −5 src/analysis/processing/qgsalgorithmloadlayer.cpp
  67. +0 −1 src/analysis/processing/qgsalgorithmloadlayer.h
  68. +0 −5 src/analysis/processing/qgsalgorithmmeancoordinates.cpp
  69. +0 −1 src/analysis/processing/qgsalgorithmmeancoordinates.h
  70. +0 −5 src/analysis/processing/qgsalgorithmmergelines.cpp
  71. +0 −1 src/analysis/processing/qgsalgorithmmergelines.h
  72. +0 −5 src/analysis/processing/qgsalgorithmmergevector.cpp
  73. +0 −1 src/analysis/processing/qgsalgorithmmergevector.h
  74. +0 −5 src/analysis/processing/qgsalgorithmminimumenclosingcircle.cpp
  75. +0 −1 src/analysis/processing/qgsalgorithmminimumenclosingcircle.h
  76. +0 −5 src/analysis/processing/qgsalgorithmmultiparttosinglepart.cpp
  77. +0 −1 src/analysis/processing/qgsalgorithmmultiparttosinglepart.h
  78. +0 −5 src/analysis/processing/qgsalgorithmorderbyexpression.cpp
  79. +0 −1 src/analysis/processing/qgsalgorithmorderbyexpression.h
  80. +0 −5 src/analysis/processing/qgsalgorithmorientedminimumboundingbox.cpp
  81. +0 −1 src/analysis/processing/qgsalgorithmorientedminimumboundingbox.h
  82. +0 −5 src/analysis/processing/qgsalgorithmpackage.cpp
  83. +0 −1 src/analysis/processing/qgsalgorithmpackage.h
  84. +0 −5 src/analysis/processing/qgsalgorithmpromotetomultipart.cpp
  85. +0 −1 src/analysis/processing/qgsalgorithmpromotetomultipart.h
  86. +0 −5 src/analysis/processing/qgsalgorithmrasterlayeruniquevalues.cpp
  87. +0 −1 src/analysis/processing/qgsalgorithmrasterlayeruniquevalues.h
  88. +0 −5 src/analysis/processing/qgsalgorithmremoveduplicatenodes.cpp
  89. +0 −1 src/analysis/processing/qgsalgorithmremoveduplicatenodes.h
  90. +0 −5 src/analysis/processing/qgsalgorithmremovenullgeometry.cpp
  91. +0 −1 src/analysis/processing/qgsalgorithmremovenullgeometry.h
  92. +0 −5 src/analysis/processing/qgsalgorithmrenamelayer.cpp
  93. +0 −1 src/analysis/processing/qgsalgorithmrenamelayer.h
  94. +1 −1 src/analysis/processing/qgsalgorithmsaveselectedfeatures.cpp
  95. +0 −5 src/analysis/processing/qgsalgorithmsimplify.cpp
  96. +0 −1 src/analysis/processing/qgsalgorithmsimplify.h
  97. +0 −5 src/analysis/processing/qgsalgorithmsmooth.cpp
  98. +0 −1 src/analysis/processing/qgsalgorithmsmooth.h
  99. +0 −5 src/analysis/processing/qgsalgorithmsnaptogrid.cpp
  100. +0 −1 src/analysis/processing/qgsalgorithmsnaptogrid.h
  101. +0 −5 src/analysis/processing/qgsalgorithmsplitwithlines.cpp
  102. +0 −1 src/analysis/processing/qgsalgorithmsplitwithlines.h
  103. +1 −1 src/analysis/processing/qgsalgorithmstringconcatenation.cpp
  104. +0 −6 src/analysis/processing/qgsalgorithmsubdivide.cpp
  105. +0 −1 src/analysis/processing/qgsalgorithmsubdivide.h
  106. +0 −5 src/analysis/processing/qgsalgorithmtransect.cpp
  107. +0 −1 src/analysis/processing/qgsalgorithmtransect.h
  108. +0 −5 src/analysis/processing/qgsalgorithmtransform.cpp
  109. +0 −1 src/analysis/processing/qgsalgorithmtransform.h
  110. +0 −5 src/analysis/processing/qgsalgorithmtranslate.cpp
  111. +0 −1 src/analysis/processing/qgsalgorithmtranslate.h
  112. +0 −6 src/analysis/processing/qgsalgorithmuniquevalueindex.cpp
  113. +0 −1 src/analysis/processing/qgsalgorithmuniquevalueindex.h
  114. +6 −0 src/core/processing/models/qgsprocessingmodelalgorithm.cpp
  115. +1 −0 src/core/processing/models/qgsprocessingmodelalgorithm.h
  116. +1 −1 src/core/processing/qgsprocessingalgorithm.h
  117. +1 −1 src/gui/processing/qgsprocessingalgorithmdialogbase.cpp
@@ -47,6 +47,8 @@ Constructor for QgsProcessingModelAlgorithm.

virtual QString helpUrl() const;

virtual Flags flags() const;


virtual bool canExecute( QString *errorMessage /Out/ = 0 ) const;

@@ -44,7 +44,7 @@ Abstract base class for processing algorithms.
FlagSupportsBatch,
FlagCanCancel,
FlagRequiresMatchingCrs,
FlagCanRunInBackground,
FlagNoThreading,
FlagDeprecated,
};
typedef QFlags<QgsProcessingAlgorithm::Flag> Flags;
@@ -151,6 +151,10 @@ def icon(self):
def svgIconPath(self):
return QgsApplication.iconPath("providerGrass.svg")

def flags(self):
# TODO - maybe it's safe to background thread this?
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def tr(self, string, context=''):
if context == '':
context = self.__class__.__name__
@@ -27,6 +27,7 @@

from qgis.core import (QgsVectorDataProvider,
QgsFields,
QgsProcessingAlgorithm,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterField,
QgsProcessingOutputVectorLayer)
@@ -56,6 +57,9 @@ def initAlgorithm(self, config=None):
self.tr('Attribute to index'), None, self.INPUT))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Indexed layer')))

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def name(self):
return 'createattributeindex'

@@ -29,6 +29,7 @@
import re

from qgis.core import (QgsProcessing,
QgsProcessingAlgorithm,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterCrs,
QgsProcessingOutputVectorLayer)
@@ -65,6 +66,9 @@ def name(self):
def displayName(self):
return self.tr('Define current projection')

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def processAlgorithm(self, parameters, context, feedback):
layer = self.parameterAsVectorLayer(parameters, self.INPUT, context)
crs = self.parameterAsCrs(parameters, self.CRS, context)
@@ -33,6 +33,7 @@
QgsFeature,
QgsFeatureSink,
QgsGeometry,
QgsProcessingAlgorithm,
QgsProcessingException,
QgsProcessingUtils,
QgsProcessingParameterVectorLayer,
@@ -67,6 +68,9 @@ def groupId(self):
def __init__(self):
super().__init__()

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def initAlgorithm(self, config=None):
self.modes = [self.tr('Largest Area'),
self.tr('Smallest Area'),
@@ -28,6 +28,7 @@
from qgis.core import (QgsVirtualLayerDefinition,
QgsVectorLayer,
QgsWkbTypes,
QgsProcessingAlgorithm,
QgsProcessingParameterMultipleLayers,
QgsProcessingParameterString,
QgsProcessingParameterEnum,
@@ -62,6 +63,9 @@ def groupId(self):
def __init__(self):
super().__init__()

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def initAlgorithm(self, config=None):
self.addParameter(QgsProcessingParameterMultipleLayers(name=self.INPUT_DATASOURCES,
description=self.tr('Additional input datasources (called input1, .., inputN in the query)'),
@@ -27,6 +27,7 @@

from qgis.core import (QgsDataSourceUri,
QgsFeatureSink,
QgsProcessingAlgorithm,
QgsVectorLayerExporter,
QgsProcessingException,
QgsProcessingParameterFeatureSource,
@@ -76,6 +77,9 @@ def initAlgorithm(self, config=None):
self.addParameter(QgsProcessingParameterBoolean(self.DROP_STRING_LENGTH, self.tr('Drop length constraints on character fields'), False))
self.addParameter(QgsProcessingParameterBoolean(self.FORCE_SINGLEPART, self.tr('Create single-part geometries instead of multi-part'), False))

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def name(self):
return 'importintospatialite'

@@ -32,6 +32,7 @@
from qgis.core import (QgsFeatureSink,
QgsProcessingException,
QgsProcessingUtils,
QgsProcessingAlgorithm,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterEnum,
QgsProcessingParameterNumber,
@@ -59,6 +60,9 @@ def group(self):
def groupId(self):
return 'vectorselection'

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def __init__(self):
super().__init__()

@@ -33,6 +33,7 @@
from qgis.core import (QgsFeatureRequest,
QgsProcessingException,
QgsProcessingUtils,
QgsProcessingAlgorithm,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterEnum,
QgsProcessingParameterField,
@@ -65,6 +66,9 @@ def groupId(self):
def __init__(self):
super().__init__()

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def initAlgorithm(self, config=None):
self.methods = [self.tr('Number of selected features'),
self.tr('Percentage of selected features')]
@@ -28,6 +28,7 @@
from qgis.PyQt.QtCore import QVariant
from qgis.core import (QgsExpression,
QgsProcessingException,
QgsProcessingAlgorithm,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterField,
QgsProcessingParameterEnum,
@@ -71,6 +72,9 @@ def groupId(self):
def __init__(self):
super().__init__()

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def initAlgorithm(self, config=None):
self.i18n_operators = ['=',
'!=',
@@ -26,6 +26,7 @@

from qgis.core import (QgsExpression,
QgsVectorLayer,
QgsProcessingAlgorithm,
QgsProcessingException,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterExpression,
@@ -50,6 +51,9 @@ def groupId(self):
def __init__(self):
super().__init__()

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def initAlgorithm(self, config=None):
self.methods = [self.tr('creating new selection'),
self.tr('adding to current selection'),
@@ -29,7 +29,8 @@

from qgis.PyQt.QtXml import QDomDocument

from qgis.core import (QgsProcessingParameterRasterLayer,
from qgis.core import (QgsProcessingAlgorithm,
QgsProcessingParameterRasterLayer,
QgsProcessingParameterFile,
QgsProcessingOutputRasterLayer)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
@@ -50,6 +51,9 @@ def groupId(self):
def __init__(self):
super().__init__()

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def initAlgorithm(self, config=None):
self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT,
self.tr('Raster layer')))
@@ -25,7 +25,8 @@

__revision__ = '$Format:%H$'

from qgis.core import (QgsProcessingParameterFile,
from qgis.core import (QgsProcessingAlgorithm,
QgsProcessingParameterFile,
QgsProcessingParameterVectorLayer,
QgsProcessingOutputVectorLayer)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
@@ -46,6 +47,9 @@ def groupId(self):
def __init__(self):
super().__init__()

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def initAlgorithm(self, config=None):
self.addParameter(QgsProcessingParameterVectorLayer(self.INPUT,
self.tr('Vector layer')))
@@ -28,6 +28,7 @@
import os

from qgis.core import (QgsVectorDataProvider,
QgsProcessingAlgorithm,
QgsProcessingParameterVectorLayer,
QgsProcessingOutputVectorLayer,
QgsProcessing)
@@ -57,6 +58,9 @@ def initAlgorithm(self, config=None):
[QgsProcessing.TypeVectorPolygon, QgsProcessing.TypeVectorPoint, QgsProcessing.TypeVectorLine]))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Indexed layer')))

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def name(self):
return 'createspatialindex'

@@ -26,6 +26,7 @@
__revision__ = '$Format:%H$'

from qgis.core import (QgsDataSourceUri,
QgsProcessingAlgorithm,
QgsProcessingException,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterString)
@@ -58,6 +59,9 @@ def name(self):
def displayName(self):
return self.tr('SpatiaLite execute SQL')

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def processAlgorithm(self, parameters, context, feedback):
database = self.parameterAsVectorLayer(parameters, self.DATABASE, context)
databaseuri = database.dataProvider().dataSourceUri()
@@ -25,7 +25,8 @@

__revision__ = '$Format:%H$'

from qgis.core import (QgsProcessingParameterVectorLayer,
from qgis.core import (QgsProcessingAlgorithm,
QgsProcessingParameterVectorLayer,
QgsProcessingOutputVectorLayer,
QgsProcessingException)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
@@ -53,6 +54,9 @@ def initAlgorithm(self, config=None):
self.tr('Input Layer')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Truncated layer')))

def flags(self):
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def name(self):
return 'truncatetable'

@@ -33,6 +33,7 @@
QgsProcessingException,
QgsMessageLog,
QgsProcessing,
QgsProcessingAlgorithm,
QgsProcessingParameterRasterLayer,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterBoolean,
@@ -99,6 +100,10 @@ def groupId(self):
def shortHelpString(self):
return shortHelp.get(self.id(), None)

def flags(self):
# TODO - maybe it's safe to background thread this?
return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def defineCharacteristicsFromFile(self):
with open(self.description_file) as lines:
line = lines.readline().strip('\n').strip()
@@ -251,7 +251,7 @@ def on_complete(ok, results):

self.finish(ok, results, context, feedback)

if self.algorithm().flags() & QgsProcessingAlgorithm.FlagCanRunInBackground:
if not (self.algorithm().flags() & QgsProcessingAlgorithm.FlagNoThreading):
# Make sure the Log tab is visible before executing the algorithm
self.showLog()

@@ -31,11 +31,6 @@ QString QgsTessellateAlgorithm::displayName() const
return QObject::tr( "Tessellate" );
}

QgsProcessingAlgorithm::Flags QgsTessellateAlgorithm::flags() const
{
return QgsProcessingFeatureBasedAlgorithm::flags() | QgsProcessingAlgorithm::FlagCanRunInBackground;
}

QStringList QgsTessellateAlgorithm::tags() const
{
return QObject::tr( "3d,triangle" ).split( ',' );
@@ -36,7 +36,6 @@ class QgsTessellateAlgorithm : public QgsProcessingFeatureBasedAlgorithm
QgsTessellateAlgorithm() = default;
QString name() const override;
QString displayName() const override;
Flags flags() const override;
QStringList tags() const override;
QString group() const override;
QString shortHelpString() const override;
@@ -19,11 +19,6 @@

///@cond PRIVATE

QgsProcessingAlgorithm::Flags QgsAddIncrementalFieldAlgorithm::flags() const
{
return QgsProcessingFeatureBasedAlgorithm::flags() | QgsProcessingAlgorithm::FlagCanRunInBackground;
}

QString QgsAddIncrementalFieldAlgorithm::name() const
{
return QStringLiteral( "addautoincrementalfield" );
@@ -34,7 +34,6 @@ class QgsAddIncrementalFieldAlgorithm : public QgsProcessingFeatureBasedAlgorith
public:

QgsAddIncrementalFieldAlgorithm() = default;
Flags flags() const override;
QString name() const override;
QString displayName() const override;
QStringList tags() const override;
@@ -19,11 +19,6 @@

///@cond PRIVATE

QgsProcessingAlgorithm::Flags QgsAssignProjectionAlgorithm::flags() const
{
return QgsProcessingFeatureBasedAlgorithm::flags() | QgsProcessingAlgorithm::FlagCanRunInBackground;
}

QString QgsAssignProjectionAlgorithm::name() const
{
return QStringLiteral( "assignprojection" );
@@ -34,7 +34,6 @@ class QgsAssignProjectionAlgorithm : public QgsProcessingFeatureBasedAlgorithm
public:

QgsAssignProjectionAlgorithm() = default;
Flags flags() const override;
QString name() const override;
QString displayName() const override;
QStringList tags() const override;
@@ -19,11 +19,6 @@

///@cond PRIVATE

QgsProcessingAlgorithm::Flags QgsBoundaryAlgorithm::flags() const
{
return QgsProcessingFeatureBasedAlgorithm::flags() | QgsProcessingAlgorithm::FlagCanRunInBackground;
}

QString QgsBoundaryAlgorithm::name() const
{
return QStringLiteral( "boundary" );
@@ -34,7 +34,6 @@ class QgsBoundaryAlgorithm : public QgsProcessingFeatureBasedAlgorithm
public:

QgsBoundaryAlgorithm() = default;
Flags flags() const override;
QString name() const override;
QString displayName() const override;
QStringList tags() const override;

0 comments on commit a05d941

Please sign in to comment.
You can’t perform that action at this time.