I am using QGIS 3.2.1 Bonn 64 bit on Windows 10. I would like to loop through a list of field names and perform calculations on each field. Only the first loop in the iteration performs any calculations. The rest are skipped.

result_path = r"D:\QGIS\hex_results.geojson"

tracks = QgsProject.instance().mapLayersByName("patrol_tracks")[0]
result = QgsProject.instance().mapLayersByName("hex_results")[0]

t = tracks.getFeatures()
r = result.getFeatures()

r_prov = result.dataProvider()
r_fields = r_prov.fieldNameMap()

months = [x for x in processing.run("qgis:listuniquevalues", {'INPUT':tracks,'FIELDS':"Month_Year",'OUTPUT':'u'})['UNIQUE_VALUES'].split(";")]

for m in months:
    result.startEditing()
    field_index = result.fields().lookupField(m)
    print (m)
    print (field_index)
    a = {field_index : 0}
    for feat in r:
        fid = feat.id()
        r_prov.changeAttributeValues({fid : a})

    result.commitChanges()

The print calls work for every list item but the field attributes are only altered for the first iteration.

Another method I have tried (using the same variable names as above) is:

for m in months:
    processing.run('qgis:fieldcalculator', {'INPUT':result, 'FIELD_NAME':m, 'FIELD_TYPE':1, 'FIELD_LENGTH':10, 'FIELD_PRECISION':0, 'NEW_FIELD': False, 'FORMULA':'0', 'OUTPUT':result_path})

But with the same result - only the first field in the list is calculated. This code will be part of a more complex script that calculates field values based on the intersection of selected features - which is more or less working up until this point.

Any suggestions? I wondered if the dataProvider needs to be refreshed or updated after each iteration but commitChanges(), added later, doesn't seem to have any effect.

up vote 5 down vote accepted

I belive the QgsFeatureIterator is closed after returning all features.

Example:

>>layer = iface.activeLayer()
>>feats = layer.getFeatures()

>>[f['GRID_ID'] for f in feats]
['AP7', 'AP8', 'AP9', 'AP3', 'AP4', 'AP5', 'AP6', 'AP1', 'AP2']

>>[f['GRID_ID'] for f in feats]
[] #No more features are return after used once

>>feats = layer.getFeatures() #Recreate it
>>[f['GRID_ID'] for f in feats]
['AP7', 'AP8', 'AP9', 'AP3', 'AP4', 'AP5', 'AP6', 'AP1', 'AP2']

So in your case recreate it for each iteration:

...
for m in months:
    result.startEditing()
    field_index = result.fields().lookupField(m)
    print (m)
    print (field_index)
    a = {field_index : 0}
    for feat in r:
        fid = feat.id()
        r_prov.changeAttributeValues({fid : a})
    result.commitChanges()
    r = result.getFeatures()
  • 2
    You are absolutely right. The QgsFeatureIterator was empty after the first iteration. It was as simple as adding the last line: r = result.getFeatures() and just for the record it works even after removing the startEditing() and commitChanges() methods. Many thanks. – Matt Needle Sep 20 at 14:07

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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