7

I'm writing a script that requires the attributes of different layers to work, and I would like to know how to call a layer that is not the currently active one and get its features. For example, I have three layers, named "Poles", "Lines" and "Loads", and I need to get the all the features of the layers with layer.getFeatures() and save those features in a list for each layer. For only one layer we usually do this:

If my active layer is "Poles", I write:

    layer = iface.activeLayer()
    poles = layer.getFeatures()

And then I can do anything with "poles", but how can I do this with the other layers that are not active? If I wanted to do the same with the layer "Lines", I would have to activate it "manually" by clicking on it, and repeating that code.

I have tried with this method: Getting layer by name in PyQGIS?

And this one: How to get a layer that is not active with PyQGIS

But I always get this error message, because I'm not activating the layer, only creating a list of layer names.

Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'list' object has no attribute 'getFeatures'

How can I activate a layer by its name to get the features in it?

13

TL;DR

To get all features of a layer by the layer name you do not need to activate it. Just use

name = 'counties'
layer = QgsProject.instance().mapLayersByName( name )[0]
poles = layer.getFeatures()
for pole in poles:
    if is_north(pole):
        print('it is the north pole')

Active Layer

First of all, you do not need to care about the active layer. The active layer is the layer which is currently selected in the layer tree and therefore is nice for two things

  1. letting the user select a layer to operate on
  2. selecting a layer for quickly testing code from the python console

The second one is handy when you are developing, the first one is what all the maptools are based on: the currently selected layer (the active one) is modified, selected, deleted...

Methods to access layers

However, you seem to want to access layers not based on user input but based on something else.

Identifying layers

All layers have

  • An id that needs to be unique
  • A name that is not necessarily (but often) unique and can be changed by the user
  • A reference (also often referred to as pointer but in python the two terms are interchangeable) that is unique and stable for the lifetime of a layer during the application run time and can be assigned to a variable (e.g. vlayer is a reference to the currently active layer in your example).
  • Properties like the URI (table name etc.)

If you already have a reference to the layer (because you created/added it in your plugin) just use that one.

If you don't, think about what property you want to use to get access to the layer. Most likely something that is stable (the layer id is very stable for the lifetime of a project but always is different for different projects), the name is user adjustable, the table may depend on the data source.

Accessing layers

There is the map layer registry which lets you access layers. It manages references to all layers. This used to be called QgsMapLayerRegistry, but that is no longer available in QGIS 3, so QgsProject can be used instead.

registry = QgsProject.instance()

If you have the layer id go with

layer = registry.mapLayer( id )

If you have the layer name

layer = registry.mapLayersByName( name )[0] # The method returns a list since it can be ambiguous, 0 picks the first entry in the list

If you have something else you have to write your own code:

for lyr in registry.mapLayers().values():
    if lyr.dataProvider().dataSourceUri().table() == 'mytable':
        layer = lyr
        break

Or a shorthand that does the same and makes you feel more hacker'ish but will make the people who will have to maintain the code later on cost some extra time to decipher:

layer = (l for l in registry.mapLayers().values() if l.dataProvider().dataSourceUri().table() == 'mytable').next()
  • 1
    Thank you for your answer @Mathias. I've tried using the first method that you mention, but when I try to execute layer = QgsMapLayerRegistry.instance().mapLayerByName( name )[0] in the Python console I get this error message: File "<input>", line 1, in <module> AttributeError: 'QgsMapLayerRegistry' object has no attribute 'mapLayerByName'. What could be the cause of this? – PabloQG Oct 7 '15 at 4:43
  • 1
    Already solved it!, All the problem was only a missing s in mapLayerByName; The method is called mapLayersByName. Again, thank you very much for the detailed answer. – PabloQG Oct 7 '15 at 5:02
  • Minor comment to help those as new as me... in "mapLayersByName( name )" when you replace the name with the name of the layer you need to put this in speech marks. So for example "mapLayersByName('mytable') – Rostranimin Jun 16 '17 at 13:32
  • @Rostranimin I extended it slightly to include this – Matthias Kuhn Jun 17 '17 at 15:59
3

It's not completely by name but, you can also use QgsMapCanvas objects. Next code:

mapcanvas = iface.mapCanvas()

n = mapcanvas.layerCount()

layers = [mapcanvas.layer(i) for i in range(n)]

layers_names = [ layer.name() for layer in layers ]

#truly names of vector layers
print "layers_names = ", layers_names

poles = [ pole for pole in layers[0].getFeatures() ] 

#testing only one feature
if poles[0].geometry().type() == QGis.Point:
    print "It's a point layer"

lines = [ line for line in layers[1].getFeatures() ]

#testing only one feature
if lines[0].geometry().type() == QGis.Line:
    print "It's a line layer"

it was used with these vector layers at next image:

enter image description here

When the code was run at the Python Console the result was:

layers_names =  [u'random_points', u'line']
It's a point layer
It's a line layer

If you invert the order of layers at the Map Legend there is only printed this list:

layers_names =  [u'line', u'random_points']

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.