I want to change the symbology of a Vector Output Layer that is Returned after running a Processing Tool. I am building a processing plugin. I am trying to make the effects reflect in the displayed output. I want the changes effected on the layer property after running each Processing Tool.
I have copied the relevant section of my code below:
def processAlgorithm(self, parameters, context, model_feedback):
# Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the
# overall progress through the model
feedback = QgsProcessingMultiStepFeedback(2, model_feedback)
results = {}
outputs = {}
# Fix Line Geometries
alg_params = {'INPUT': parameters['VectorLineLayer'], 'OUTPUT': parameters['FixedLines']}
outputs['FixGeometries1'] = processing.run('native:fixgeometries', alg_params, context=context, feedback=feedback, is_child_algorithm=True) #1
layer_A = QgsVectorLayer(outputs['CreateGrid']['OUTPUT'], 'layer_A')
renderer = layer_A.renderer()
symbol = QgsLineSymbol.createSimple({'line_color': '191,191,191,255', 'line_width': '0.20', 'line_style': 'dash'})
layer_A.renderer().setSymbol(symbol)
layer_A.triggerRepaint() # show the change
iface.layerTreeView().refreshLayerSymbology(layer_A.id()) # I want these changes to be effected in the output displayed.
feedback.setCurrentStep(1)
if feedback.isCanceled():
return {}
# Using Field calculator to Add an ID
alg_params = {
'FIELD_LENGTH': 10,
'FIELD_NAME': 'ID',
'FIELD_PRECISION': 0,
'FIELD_TYPE': 1,
'FORMULA': '$id',
'INPUT': layer_A,
'OUTPUT': parameters['LineID']
}
outputs['AddID'] = processing.run('native:fieldcalculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True) #2
layer_B = QgsVectorLayer(outputs['AddID']['OUTPUT'], 'layer_B')
renderer = layer_B.renderer()
symbol = QgsLineSymbol.createSimple({'line_color': '191,191,191,255', 'line_width': '0.20', 'line_style': 'dash'})
layer_B.renderer().setSymbol(symbol)
layer_B.triggerRepaint() # show the change
iface.layerTreeView().refreshLayerSymbology(layer_B.id()) # I want these changes to be effected in the output displayed.
results['LineID'] = layer_B
return results
When I run the routine above, I get this error message: AttributeError: 'NoneType' object has no attribute 'setSymbol'. What can I do differently?
Best Answer
The recommended way to do this is via the QgsProcessingLayerPostProcessorInterface class. There are a few reasons for this. The first is that 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 theflags()
method and return theFlagNoThreading
flag, using a post-processor class makes sense because for an algorithm output, a proper destination parameter such as aQgsProcessingParameterFeatureSink
should be used. This gives the user the option to save to a temporary layer or a file location and also adds the checkbox to 'Open output file after running algorithm', and this can be handled cleanly by checking ifcontext.willLoadLayerOnCompletion()
, then accessing thecontext.layerToLoadOnCompletionDetails()
and setting the post processor to the returnedQgsProcessingContext::LayerDetails
object.Below is an example script which should show you how to do this. You can test it by adding it to the Processing toolbox, and adapting for your needs.
Result:
Acknowledgements:
I originally got the post processor example from a GitHub gist of QGIS core developer, Nyall Dawson. However this has now been deleted so I cannot provide a link.
The processing script template is adapted from here.