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