PyQGIS – Changing Attributes of Newly Created Features in Python

editingfeaturesfields-attributespyqgis

  • I have a layer with two attributes: "text" (string) and "int" (integer).
  • I used drag'n'drop form design to have a form with just the "text" attribute in it.
  • I added Python Init Code:
from functools import partial

from PyQt5.QtWidgets import QPushButton

def on_ok(layer, feature):
    field_idx = layer.fields().indexOf("int")
    attribute_changed = layer.changeAttributeValue(feature.id(), field_idx, 12345)
    if not attribute_changed:
        raise Exception("Attribute value could not be changed!")

def my_form_open(dialog, layer, feature):
    ok_button = dialog.findChild(QPushButton)
    ok_button.clicked.connect(partial(on_ok, layer, feature))

So the form does not show an input widget for the "int" field. But when pressing OK, the code should change the edited feature's "int" value to 12345.

This works if I edit existing features.

But if I create a new feature, the "int" value will be empty/NULL instead of 12345.
Any pointers on how to properly update a new feature's attribute fields, that exist on a layer, but are not shown the user with the QGIS' widgets in the attribute form?

I have already tried

attribute_changed = feature.setAttribute(field_idx, 12345)

and

feature[attribute_name] = 12345

with no success either.

In my real scenario the value would be dynamic and defined elsewhere, the 12345 is just an example.

Best Answer

If you add a print statement in on_ok to check the feature.id() you will probably find that it is wildly different to what you expect.

I was able to get the correct feature id by querying the editBuffer of the layer for the most recently added feature. While editing, features receive an incrementally decreasing negative feature id, so the most recent is the lowest value.

from PyQt5.QtWidgets import QMessageBox, QPushButton
from functools import partial

def on_ok(layer, feature):
    field_idx = layer.fields().indexOf("int")
    
    print(feature.id()) # <--- not what you expect

    edit_buffer = layer.editBuffer()
    added_feats = edit_buffer.addedFeatures()

    # get the id of the most recently added feature in the edit buffer
    last_added_feat_id = min(list(added_feats.keys()))
    
    attribute_changed = layer.changeAttributeValue(last_added_feat_id, field_idx, 12345)
    if not attribute_changed:
        raise Exception("Attribute value could not be changed!")

def my_form_open(dialog, layer, feature):
    ok_button = dialog.findChild(QPushButton)
    ok_button.clicked.connect(partial(on_ok, layer, feature))
Related Question