1

I'm trying to create a field in a vector layer and then update the values based on the sum of two fields which are already present in the table, using the Python console (QGIS3.4). Also I'm still learning Python... I originally started to develop this under 2.18, but I've recently had to move to QGIS 3 (which is part of the problem)- In 2.18 the first part of the script used to work i.e. creation of a new field in the table.

The original code:

from qgis.core import *
from qgis.utils import *
from qgis.gui import *
from PyQt4.QtCore import QVariant
import processing
import os

layer_P = QgsProject.instance().mapLayersByName("BXO")[0]

SUM = QgsField('t_branch', QVariant.Int)
layer_P.addAttribute (SUM)
idx = layer_P.fieldNameIndex('t_branch')

layer_P.startEditing()

e = QgsExpression ('CB_Num_Ent' + 'CB_Num_Ext')
e.prepare( 't_branch'() )

for f in layer_P.getFeatures():
    f[idx] = e.evaluate( f )
    layer_P.updateFeature( f )

layer_P.updateField()
layer_P.commitChanges()

I've been looking at the API and seen that fieldNameIndex has been replaced with fields() .lookupField() or fields() .indexFromName() and since I've tried a couple of variations - for example:

layer_P = QgsProject.instance().mapLayersByName("BXO")[0]

SUM = QgsField('t_branch', QVariant.Int)
layer_P.addAttribute (SUM)

idx = layer_P.lookupField('t_branch')

layer_P.startEditing()

e = QgsExpression ('CB_Num_Ent' + 'CB_Num_Ext')
e.prepare( layer_P.fields() )

for f in layer_P.getFeatures():
    f[idx] = e.evaluate( f )
    layer_P.updateFeature( f )

layer_P.updateField()
layer_P.commitChanges()

but for the minute I'm still get the same type of error message ...AttributeError: 'QgsVectorLayer' object has no attribute 'lookupField'

So, firstly I'm trying to find out how to modify the script following the removal of indexFromName. And if that works, hopefully update my new field based on the calculation here :

> e = QgsExpression ('CB_Num_Ent' + 'CB_Num_Ext')

For info the script was adapted from the following : Adding field and calculating expression with PyQGIS

PyQGIS: Multiply Fields and populate new field

How to fill fields with layer name in pyqgis

3

Please try the following code- this worked for me in the python console in QGIS 3.4.

In addition to layer.fieldNameIndex changing to layer.fields().lookupField or layer.fields().indexFromName, there has also been changes to the QgsExpression class. The evaluate() method which took a QgsFeature object as its argument in 2.X has been removed. It now takes a QgsExpressionContext() object. This context has a setFeature() method to which a QgsFeature is passed.

The prepare() method of QgsExpression which took a QgsFields argument in 2.X, now also takes a Context argument. To prepare the expression for evaluation, we need to create a QgsExpressionContextScope object, call setFields, passing the layer fields, then append the scope to the context.

from PyQt5.QtCore import QVariant

layer = QgsProject().instance().mapLayersByName('BXO')[0]
prov = layer.dataProvider()
fld = QgsField('t_branch', QVariant.Int)
prov.addAttributes([fld])
layer.updateFields()
idx = layer.fields().lookupField('t_branch')
layer.startEditing()

e = QgsExpression('CB_Num_Ent + CB_Num_Ext')
c = QgsExpressionContext()
s = QgsExpressionContextScope()
s.setFields(layer.fields())
c.appendScope(s)
e.prepare(c)

for f in layer.getFeatures():
    c.setFeature(f)
    value = e.evaluate(c)
    atts = {idx: value}
    layer.dataProvider().changeAttributeValues({f.id():atts})
layer.commitChanges()

EDIT: I found that if the fields in the expression calculation are coming from a joined layer, there are potential issues with how the expression is parsed. For example, if the join fields are coming from a csv, then the example above worked fine. However, if the join fields were coming from a shapefile, I had to change the line containing the expression from:

e = QgsExpression('Field_name_1 + Field_name_2')

to:

e = QgsExpression('"Field_name_1" + "Field_name_2"')
  • I'm testing the code now - for the minute it creates the field ok but doesn't update the values - perhaps due to the joined fields - I'll keep trying and let you know – lhowarth Jan 24 at 13:40
  • Bummer...does it work on the "native" fields? I tested it summing two integer fields & it was working fine. – Ben W Jan 24 at 13:58
  • Ok, well same problem - works fine on 'native' fields but not on joined fields... no idea why - the script obviously works differently than the field calculater in the attribute table - would be good to know why! – lhowarth Jan 24 at 14:37
  • 1
    That's disappointing. Not sure what's going on there. May be worth asking a new question. – Ben W Jan 24 at 14:49
  • 1
    @lhowarth, I did some testing and experienced a similar issue to you if the join layer was another shapefile (join fields from a csv worked fine). Looks like just an issue with how expressions are parsed. See my edit to my answer above. – Ben W Jan 26 at 4:42
1

You can try:

layer = iface.activeLayer()
fieldlist = ['Field1','Field2','Sum1_2']
field_indexes = [layer.dataProvider().fieldNameIndex(f) for f in fieldlist]

layer.startEditing()
for feature in layer.getFeatures():
    _=layer.changeAttributeValue(feature.id(),field_indexes[-1], sum(feature.attributes()[field_indexes[0]:field_indexes[1]+1]))
layer.commitChanges()
  • Thanks, I thought fieldNameIndex was replaced in QGIS 3 ? That said, it I have tried your script and it does seem to work :) - partly - I nolonger have NULL values in the field but everything equals 0 - which is wrong - so I'm guessing it's to do with the field_indexes ? – lhowarth Jan 24 at 10:59
  • @lhowarth Not sure why you get 0. I tried it on two different shapefiles with success. What is the field type of your fields? – BERA Jan 24 at 11:23
  • @ BERA Field 1 and Field 2 are both integer64 my Sum1_2 field is straight Integer – lhowarth Jan 24 at 11:45
  • 1
    @ BERA - they're all integers so i don't think it'll be that, but I'll try and make my Sum1_2 field an Integer64 and see if that changes the result – lhowarth Jan 24 at 11:49
  • 1
    ok, so your script works if the fields are native to the table (i.e. not produced through a join).... – lhowarth Jan 24 at 12:41

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.