Thursday 16 November 2017

pyqgis - Creating and updating a field from calculation


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



Answer




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"')

No comments:

Post a Comment

arcpy - Changing output name when exporting data driven pages to JPG?

Is there a way to save the output JPG, changing the output file name to the page name, instead of page number? I mean changing the script fo...