3

In QGIS 3.10, i want to calculate multiple fields at once on more than 10 similar layers. I now i can do it manually, but I'm looking for an automatic way.

I need something like this:

"nomrue"  should be filled with "potable_nomrue" And

"debut"  should be filled with "potable_debut" And

"fin"  should be filled with "potable_fin" And

"seqsegment"  should be filled with "potable_seqsegment" And

"idnoeuddebut"  should be filled with "potable_idnoeuddebut" And

"idnoeudfin"  should be filled with "potable_idnoeudfin" And

"vocation"  should be filled with "potable_vocation" And

And so on... And i need to repeat the operation on multiple layers.

I found some Python code, but i don't understand how to use Python...

Ihave:

lyr = iface.activeLayer()

with edit(lyr):
    for f in lyr.getFeatures():
            f['nomrue'] = f['potable_nomrue']
            f['debut'] = f['potable_debut']
            f['fin'] = f['potable_fin']
            f['seqsegment'] = f['potable_seqsegment']
            f['idnoeuddebut'] = f['potable_idnoeuddebut']
            f['idnoeudfin'] = f['potable_idnoeudfin']
            f['vocation'] = f['potable_vocation']
            f['anneerehab'] = f['potable_anneerehab']
        lyr.updateFeature(f)

But there's an error... What's wrong?

9
  • Each layer have the 25 fields? Can you add a screenshot showing the attribute table of one? By "Field 6" should be filled with "z_Field6" you mean field 6 should be filled with the value of z_Field6?
    – BERA
    Nov 30, 2020 at 15:55
  • Without exemplary data we can't properly help you out. Also, your pseudo-code is ambigious.
    – Erik
    Nov 30, 2020 at 15:55
  • Yeah I mean, the "field 6" should be filled with the value of " z_Field6" Nov 30, 2020 at 16:20
  • 1
    If there is an error, please always give details to that error. What does it say, etc... In your case its an syntax error: for lyr = iface.activeLayer() must be lyr = iface.activeLayer(). But there is also a false indentation on your last line and i ist not defined.
    – MrXsquared
    Nov 30, 2020 at 17:42
  • 1
    And f['nomrue'.format(i)] is lacking where to place the i (for example 'nomrue{}'.format(i) for nomrue1).
    – BERA
    Nov 30, 2020 at 17:58

3 Answers 3

4

You should use python for this.

Add all layers to the map you want to calculate (and no others! And backup your data first) then:

for lyr in QgsProject.instance().mapLayers().values(): #For each layer added to the map
    with edit(lyr): #Start editing
        for f in lyr.getFeatures(): #For each feature/row in the attribute table
            for i in range(1,26): #For each number from 1 to 25
                f['Field {0}'.format(i)] = f['z_Field{0}'.format(i)] #Set value, for example Field 1 = z_Field1
            lyr.updateFeature(f) #Update feature with new value

It is very important that the fields are named exactly like you say and that each layer have the 25 fields.

enter image description here

If you just want to update one layer you can specify it like this:

lyr = QgsProject.instance().mapLayersByName('New scratch layer')[0] #Change New scratch layer to the name of your layer

with edit(lyr):
    for f in lyr.getFeatures():
        for i in range(1,26):
            f['Field {0}'.format(i)] = f['z_Field{0}'.format(i)]
        lyr.updateFeature(f)
4
  • I'll try RN. Thanks for the screenshot, It help me understand how to use the console. Nov 30, 2020 at 16:34
  • Nice! The "only complicated" line is f['Field {0}'.format(i)] = f['z_Field{0}'.format(i)] which is just a way of creating the strings Field1 and z_Field1 etc. See: w3schools.com/python/ref_string_format.asp
    – BERA
    Nov 30, 2020 at 16:35
  • Is "for lyr in QgsProject.instance().mapLayers().values(): #For each layer added to the map" can be modified to target a specified layer in the project? Nov 30, 2020 at 16:37
  • Yes see update. (You can also click/highlight the layer in tree and use lyr = iface.activeLayer())
    – BERA
    Nov 30, 2020 at 16:39
3

Yes, you are near now. Try this:

lyr = iface.activeLayer()
with edit(lyr):
    for f in lyr.getFeatures():
        f['nomrue'] = f['potable_nomrue']
        f['debut'] = f['potable_debut']
        f['fin'] = f['potable_fin']
        f['seqsegment'] = f['potable_seqsegment']
        f['idnoeuddebut'] = f['potable_idnoeuddebut']
        f['idnoeudfin'] = f['potable_idnoeudfin']
        f['vocation'] = f['potable_vocation']
        f['anneerehab'] = f['potable_anneerehab']
        lyr.updateFeature(f)

As said in comments you had a wrong indentation on your last line. But for readability and norms I changed indentation on lines 4 to 11.

For further explanation:

Traceback (most recent call last): File "C:\PROGRA~1\QGIS3~1.14\apps\Python37\lib\code.py", line 90, in runcode exec(code, self.locals) File "", line 1, in File "", line 12 lyr.updateFeature(f) ^ IndentationError: unindent does not match any outer indentation level

means that you have an error in line 12, not line 1. line 12 lyr.updateFeature(f) indicates where this error happened and IndentationError: unindent does not match any outer indentation level clearly states that you have an indentation error. See this answer on SO for more info about that.

1
  • 1
    A BIG thank you to you And @BERA !!! Works!!! Nov 30, 2020 at 19:43
1

You can not do this via Field Calculator automatically because you can only update one field at a time. But you can use this python code snipped:

import re # needed to remove whitespace
layers = QgsProject.instance().mapLayers().values() # get all layers in project
#currentlayer = iface.activeLayer() # Select the layer to edit
for layer in layers: # loop through all layers in project
    currentlayer = layer # set the current edited layer
    currentlayer.startEditing() # Edit the layer
    dict = {} # Create empty dictionary
    fieldindexcounter = 0
    for field in currentlayer.fields(): # loop through fieldnames of layer
        dict[fieldindexcounter] = field.name() # add fieldname to dictionary
        fieldindexcounter += 1
    for feature in currentlayer.getFeatures(): # loop through features of layer
        for key_target, value_target in dict.items(): # loop through fieldnames
            for key_source, value_source in dict.items(): # loop through fieldnames
                fieldname_source = re.sub(r'^.*?_', '_', value_source)[1:] # remove all characters before '_' and the underscore itself
                fieldname_target = value_target # no edit needed
                if fieldname_source == fieldname_target: # If the names then match change value of target
                    currentlayer.changeAttributeValue(feature.id(), key_target, feature.attributes()[key_source]) # fill target attribute with source value
    currentlayer.commitChanges() # save changes to this current layer

It will loop throuh all layers you have added to QGIS and copy all values from 'xxxx_yyyy' to 'yyyy'. To be clearer: it copies the attribute value of a field to another field whichs fieldname matches if you remove all chars before an underscore of the fieldname. Better look at the code and its comments, dont know how to explain this part better :)

To use it go to "Plugins" and click on "Python console". Just copy paste this code and press Enter.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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