4

I have some shapefiles for which I need to generate an index(int) field.

I tried using this function:

def add_index_to_layer(layer):
    fieldindex = layer.fieldNameIndex("index")
    if fieldindex >= 0:
        print "Layer " + layer.name() + " already has an index field"
        return False
    layer.dataProvider().addAttributes([QgsField("index", QVariant.Int)])
    layer.updateFields() # no idea if this is actually needed?
    fieldindex = layer.fieldNameIndex("index")
    i = 0
    layer.startEditing()
    for field in layer.fields():
        print field.name() # this shows the field "index" indeed exists
    for feature in layer.getFeatures():
        print feature.id(), feature["index"]
        feature.setAttribute("index", i)
        # this shows the value is being correctly set at this point
        print feature.id(), feature["index"] 
        i +=1
    layer.commitChanges()

The function runs just fine, the various print functions also show everything works as expected (values being set correctly).

However, when verifiyng by looking at the attribute table, the index field is created, but it always is empty/NULL.

Does anyone have an idea about what is going on? Are some of the changes pending where I expect them to be commited already? I thought layer.startEditing() and layer.commitChanges() would properly encapsulate feature field changes?

I am working with QGIS 2.18.0, and the script is created as a processing script.

1

Try replacing this line:

feature.setAttribute("index", i)

with this:

layer.changeAttributeValue(feature.id(), layer.fieldNameIndex("index"), i)
  • This has fixed it, indeed. Can you give an explanation as to why, though? – Senshi Nov 28 '16 at 9:46
  • @Senshi - I think both answers in this post gives a brief explanation: How to change the value of an attribute using QgsFeature in PyQGIS? – Joseph Nov 28 '16 at 14:05
  • 1
    Thanks. I guess I just have to take the note describing "for some reason Feature can't do it" as an explanation, then :) . – Senshi Nov 28 '16 at 14:27
0

I had this issue occur in a similar case, however I could not use the layer to "circumvent" it.

I had to duplicate a layer, but add an additional field and field value for the duplicate. I did not want to touch the source layer, or have any "temporary" layers being stored, so I had to edit a feature "on the fly" to add the field&attribute. After a lot of doc-reading, the solution for that is not obvious at all, but I got it:

new_field = QgsField('TEST', QVariant.String) # The new field to add
attributes = feature.attributes() # Save the current field values
attributes.append('TestValue') # Append the value that goes with the new field
fields = feature.fields() # Grab the current fields
fields.append(new_field) # Append our new field
feature.setFields(fields) # Set the new fieldset. This wipes the attributes, but properly resets the internal attribute accessors.
feature.setAttributes(attributes) # Finally, put back our old attributes with the new value added

All this convolution is necessary, because setFields() triggers initAttributes() on the feature by default (can be prevented by setting second parameter to False). However, this is necessary so QGIS "knows" about how many attributes to expect for this feature. As an - in my case unexpected - side-effect, this function clears all existing data of all fields. That makes it necessary to store the existing attributes beforehand, alter them, and only re-apply them afterwards. Then, you can do whatever you want with the vector data (such as write it with a VectorWriter).

More explanation/examples:

new_field = QgsField('TEST', QVariant.String)
fields = feature.fields()
fields.append(new_field)
feature.setFields(fields, False)
feature['TEST'] = 'TestValue'

leads to all existing attributes persisting, the new field to be added, but the value for the new field (TestValue) to not be applied.

new_field = QgsField('TEST', QVariant.String)
fields = feature.fields()
fields.append(new_field)
feature.setFields(fields)
feature['TEST'] = 'TestValue' # Same as feature.setAttribute('TEST', 'TestValue')

leads to all existing attributes being wiped, the new field to be added, and the new value for the new field (TestValue) to be applied as well.

This seems very unintuitive, to the point that I'd consider it a bug ( highly unexpected behavior of feature['TEST'] or feature.setAttribute('TEST', 'TestValue') )

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.