I have a processing script (QGIS 3.12) that extracts values from an input table using the native:extractbyattribute
algorithm. This produces a temporary output table with the display name 'Extracted (attribute) and I would like to rename this to something more meaningful within the script.
I can get the unique name of the layer with:
extracted = processing.run('native:extractbyattribute', alg_params, context=context, feedback=feedback, is_child_algorithm=True)
layer = QgsProcessingUtils.mapLayerFromString(format(extracted['OUTPUT']), context )
but I can't see how to set the display name programmatically within the script. The layer is of type QgsVectorLayer
. I can get its attributes with layer.dataProvider()
, but this does not include a name attribute.
There are several ways to iterate through the layers from the console, but no information on how to do this through a script.
5 Answers 5
You need to use the QgsProcessingLayerPostProcessorInterface.postProcessLayer
method to rename the layer after it has been added to the project. Using the QgsMapLayer.setName
method has no effect. To use this, define a new class:
class Renamer (QgsProcessingLayerPostProcessorInterface):
def __init__(self, layer_name):
self.name = layer_name
super().__init__()
def postProcessLayer(self, layer, context, feedback):
layer.setName(self.name)
Then in your code, create an instance of the class and attach it to the output layer:
global renamer
renamer = Renamer('DiffBuf')
context.layerToLoadOnCompletionDetails(self.dest_id).setPostProcessor(renamer)
It is essential that you declare the variable holding the class as global
, otherwise this will not work. self.dest_id
in the code is the value of the second return value of parameterAsSink
.
-
1why is global necessary here?ar-siddiqui– ar-siddiqui2022年04月14日 16:50:59 +00:00Commented Apr 14, 2022 at 16:50
-
1By passing renamer to setPostProcessor you are passing an object whose postProcessLayer method will be called at a later time, outside this scope. If it's not declared global, it will not exist outside the scope in which it was defined,and thus you'll get an error. By declaring it global, you give the object global scope so that it will be valid when the method is invoked. If this is not making sense, read about scope of variables in Python.Llaves– Llaves2022年04月15日 03:39:35 +00:00Commented Apr 15, 2022 at 3:39
-
1Got you, I spent hours before I come here and realized I am missing global. QGIS wasn't throwing an error, just not applying styles, which added to the confusion. Working fine now.ar-siddiqui– ar-siddiqui2022年04月15日 13:41:03 +00:00Commented Apr 15, 2022 at 13:41
If I understood correctly the problem, you need to use the QgsMapLayer.setName()
function (documentation here).
So, taking your code, you should be able to set the layer display name with:
layer.setName("LayerDisplayName")
Hope this helps.
The approach that works in my testing context (PS: I have thrown away the QgsProcessingUtils
, not needed or I missed something)
This answer was an addition to solve the part you were stucked
Seems good, but the display name in the layers panel remains stubbornly 'Extracted (attribute)'.
I've used the file world_map.gpkg
always present will all QGIS installation e.g https://twitter.com/geomenke/status/1034902521725083648 Hence, you can test with the same conditions as me by changing the path use in INPUT
key from dictionary alg_params
from qgis.core import QgsProcessingContext, QgsProcessingFeedback, QgsProject
alg_params = { 'FIELD' : 'fid', 'INPUT' : '/usr/share/qgis/resources/data/world_map.gpkg|layername=countries', 'OPERATOR' : 4, 'OUTPUT' : 'TEMPORARY_OUTPUT', 'VALUE' : '1000' }
feedback = QgsProcessingFeedback()
context = QgsProcessingContext()
extracted = processing.run('native:extractbyattribute', alg_params, context=context, feedback=feedback, is_child_algorithm=False) # Changed is_child_algorithm=True to is_child_algorithm=False as it's a standalone example for the demo here
layer = extracted['OUTPUT']
print(layer.name()) # See it's the wrong name
layer.setName('My New Name') # Fix the name
QgsProject.instance().addMapLayer(layer) # Add the layer to the project to see it works
-
extracted['OUTPUT'] returns a string (name of created layer), so layer will be set to this string. Script complains 'layer' has no attribute 'name()'TonyMJ– TonyMJ2020年05月24日 07:23:31 +00:00Commented May 24, 2020 at 7:23
-
extracted['OUTPUT'] gives me
<QgsMapLayer: 'Extracted (attribute)' (memory)>
with the sample I provide, not a stringThomasG77– ThomasG772020年05月24日 07:33:44 +00:00Commented May 24, 2020 at 7:33 -
Are you running this within a processing framework script?TonyMJ– TonyMJ2020年05月24日 09:06:45 +00:00Commented May 24, 2020 at 9:06
-
Not within PyQGIS consoleThomasG77– ThomasG772020年05月24日 15:02:08 +00:00Commented May 24, 2020 at 15:02
-
Your code works well when run from the console, but not when run from within a processing framework script. In this context type(layer) returns <class 'str'> . That's why I need to use layerNameFromString().TonyMJ– TonyMJ2020年05月24日 15:28:57 +00:00Commented May 24, 2020 at 15:28
That kind of works. I tried:
layer = QgsProcessingUtils.mapLayerFromString(format(extracted['OUTPUT']), context )
feedback.pushInfo('\nLayer name is initially {}'. format(layer.name()))
layer.setName('my_layer')
feedback.pushInfo('\nLayer name is now {}'. format(layer.name()))
The first pushInfo call produces: 'The name of the layer is Extracted__attribute__627cdb7b_f503_4a62_b847_6264ef1ece33'
The second produces :'Layer name is now my_layer'
Seems good, but the display name in the layers panel remains stubbornly 'Extracted (attribute)'. So, the display name (which doesn't have to be unique) may not be the same as the layer name (which does).
Best approach for me has been this answer by lejedi76:
extracted = processing.run('native:extractbyattribute', alg_params, context=context, feedback=feedback, is_child_algorithm=True)
layer_details = context.layerToLoadOnCompletionDetails(extracted['OUTPUT'])
layer_details.name = "Meaningful name"