Regarding the question: "do I need to use is_child_algorithm=True
?", you need to use it if the algorithm you are running is being run as a child or sub-algorithm, if you like, inside the processAlgorithm()
method of a class which extends the QgsProcessingAlgorithm
class. Based on your comments, I believe this is what you are doing here, however this fairly important detail is missing from your question.
Because, in the Python console you can simply do something like:
layer2_exploded = processing.run("native:explodelines",
{'INPUT':iface.activeLayer(),
'OUTPUT':'TEMPORARY_OUTPUT'})['OUTPUT']
And the return value of the run()
call stored in the layer2_exploded
variable will be a QgsVectorLayer
object; because is_child_algorithm
defaults to False
, so ownership of the result layer is transferred from the processing context back to the caller.
However, when you are doing processing.run()
calls to child algorithms as steps in the processAlgorithm()
method of a QgsProcessingAlgorithm
subclass, you must set the is_child_algorithm
argument to True
. This will mean that ownership remains with the context and that return value of the run()
call will be a map layer id string reference. This why you are getting the error you describe.
If you need to work with a QgsVectorLayer
object, you can use the method QgsProcessingContext.getMapLayer() method which returns a QgsMapLayer
object from a layer id string. You can see in the source code, that this method calls the QgsProcessingUtils.mapLayerFromString() method, which you could also use.
So an example for your case is:
layer2_exploded_id = processing.run("native:explodelines",
{'INPUT':layer1_line,
'OUTPUT':'TEMPORARY_OUTPUT'},
context=context,
feedback=feedback,
is_child_algorithm=True)['OUTPUT']
vlyr = context.getMapLayer(layer2_exploded_id)# QgsVectorLayer object
Here is a trivial but complete, minimal example of a QgsProcessingAlgorithm
making use of this method. Here we check the feature count of the vector layer returned by context.getMapLayer()
, and if it is over 1000, we create a feature request limited to 1000 features, and add those to the output feature sink.
from qgis.PyQt.QtCore import QCoreApplication, QVariant
from qgis.core import (QgsFeatureSink, QgsFeatureRequest,
QgsProcessing, QgsProcessingAlgorithm, QgsProcessingParameterFeatureSource,
QgsProcessingParameterFeatureSink, QgsProcessingUtils)
import processing
class ExAlgo(QgsProcessingAlgorithm):
INPUT = 'INPUT'
OUTPUT = 'OUTPUT'
def __init__(self):
super().__init__()
def name(self):
return "exalgo"
def tr(self, text):
return QCoreApplication.translate("exalgo", text)
def displayName(self):
return self.tr("Example script")
def group(self):
return self.tr("Examples")
def groupId(self):
return "examples"
def shortHelpString(self):
return self.tr("Example script without logic")
def helpUrl(self):
return "https://qgis.org"
def createInstance(self):
return type(self)()
def initAlgorithm(self, config=None):
self.addParameter(QgsProcessingParameterFeatureSource(
self.INPUT,
self.tr("Input layer"),
[QgsProcessing.TypeVectorLine]))
self.addParameter(QgsProcessingParameterFeatureSink(
self.OUTPUT,
self.tr("Output layer"),
QgsProcessing.TypeVectorLine))
def processAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT, context)
layer1_line = source.materialize(QgsFeatureRequest(), feedback=feedback)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
source.fields(), source.wkbType(), source.sourceCrs())
layer2_exploded_id = processing.run("native:explodelines",
{'INPUT':layer1_line,
'OUTPUT':'TEMPORARY_OUTPUT'},
context=context,
feedback=feedback,
is_child_algorithm=True)['OUTPUT']
vlyr = context.getMapLayer(layer2_exploded_id)
feat_request = QgsFeatureRequest()
if vlyr.featureCount() > 1000:
feat_request.setLimit(1000)
for f in vlyr.getFeatures(feat_request):
sink.addFeature(f, QgsFeatureSink.FastInsert)
return {'self.OUTPUT': dest_id}
The QGIS processing framework is a wonderful and versatile tool, but there is a fairly steep learning curve with it and many gotchas. For example, QGIS Processing Algorithms are run by default in a background thread. Therefore it is not thread-safe to interact with the canvas or interface from inside the processAlgorithm()
method. While it is possible to re-implement the flags()
method and return the FlagNoThreading
flag, I don't recommend doing anything non thread safe with your QgsVectorLayer
object from inside the processAlgorithm()
method. While getFeatures()
is fine, don't do anything which interacts with the interface such as applying a style to the layer object etc. For post processing of any output layers which will be loaded on completion, there is the QgsProcessingLayerPostProcessorInterface class. You can see an example here.
There is also the postProcessAlgorithm method, which should be called from the main thread and can be re-implemented to perform any thread-sensitive cleanup tasks.
I hope all this information does not confuse you too much. Most of this is probably not relevant to you at the moment. I am just trying to make you aware of things to watch out for if you get more in depth with writing processing scripts.
is_child_algorithm=False
, then it shall work