3

I have an existing Attribute Table. I add new fields with PyQGIS but I would like to set this fields at the second and third position and, not at the end. Because by default, the new field is added at the last field position.

Example: I have fields A, B, C, D, E, F, G and I want to add the x and y at second and third position: A, x, y, B, C, D, E, F, G

Here is my pyqgis script to create new field:

for champ in champs :
    provider.addAttributes([QgsField(champ, QVariant.String)])
    inlayer.updateFields()
    indice = inlayer.fieldNameIndex(champ)

I know I can do it with refactor fields from processing but I would like to do it with PyQGIS.

  • 1
    I can't write a full answer because it's on hold, but: layer.fields() to get the fields, then fields.append (QgsField, OriginProvider, index). Use the third parameter to specify where you want the new field. Remember to use layer.setFields() and .updateFields() afterwards to properly assign&initialize the layer fields! – Senshi Apr 20 '17 at 6:23
  • Thanks for your answer, now polygeo has unhold my question. You can now answer to it. So this function will not replace the column but move on the right the other columns? – jlSta Apr 20 '17 at 6:44
3

Refactor Fields is actually the only way you can do this. Surprisingly, there is simply no way to reorder fields on a data level. You can change the presented order of fields in the qgis attribute table, but that is not stored in the data.

To actually manipulate the field order in the data, you have to recreate the dataset. This is bothersome and will be slow-ish, because not only do you need to manipulate the fields, but you will also have to rearrange the attributes of every single feature to match them.

Depending on your use case, it's probably gonna be easiest to simply call the existing refactor field algorithm from your pyqgis script, an example for that is given in the official docs

In your case, you would first add the attributes as you do already, then run refactorfields.

##input=vector
##General Tools=group
##Test=name

from qgis.core import *
from PyQt4.QtCore import QVariant

layer = processing.getObject(input)
layer.dataProvider().addAttributes([QgsField('x', QVariant.Integer),QgsField('y', QVariant.Long)])

result = processing.runalg('qgis:refactorfields', 
                                                  layer,
                                                  [{
                                                  'type': QVariant.String, 
                                                  'length': 20, 
                                                  'expression': 'A', 
                                                  'name': u'A', 
                                                  'precision': 0
                                                  },
                                                  {'type': QVariant.Integer, 
                                                  'length': 7, 
                                                  'expression': 'x', 
                                                  'name': u'x', 
                                                  'precision': 4
                                                  },
                                                  {'type': QVariant.Long, 
                                                  'length': 7, 
                                                  'expression': 'y', 
                                                  'name': u'y', 
                                                  'precision': 4
                                                  },
                                                  {'type': QVariant.String, 
                                                  'length': 20, 
                                                  'expression': 'B', 
                                                  'name': u'c', 
                                                  'precision': 0
                                                  }
                                                  # Expand field mapping as needed
                                                  ],
                                                  None)

output = QgsVectorLayer(result['OUTPUT_LAYER'],
                    'new name',
                    'ogr')

QgsMapLayerRegistry.instance().addMapLayer(output)

To further explain the field mapping parameters:

  • type | Any QVariant type. Note that these come from Qt4, not QGIS, and a lot of Qt4 types are not supported by QGis. This has been cause of concern for a while, and hopefully will be changed in Qgis 3. Just use the "basic" types that make sense in Qgis ;) .
  • length | The length of the field (characters or digits)
  • expression | Anything you'd put in the field calculator expression, you can use here. This includes dynamic variables such as @row_number
  • name | The name the field should have
  • precision | the number of digits after the decimal separator. Note that these are subtracted from length, so a field with length 7 and precision 4 can only reach 999.9999

The third parameter of the runalg is set to None, so the new layer is created as a temp layer (result['OUTPUT_LAYER']), but you can change that easily.

Alternatively, you can look at the source and steal the parts that are relevant for you if you need more optimization and running refactorfields would be overkill.

EDIT: Note that this is not an issue unique to QGIS, but a common issue (ArcGIS has it as well). This is because the big GIS players are based on the data specifications done eons ago for shapefiles and other geo formats.

  • in my case, the original attribute table varies with more or less fields. So i 'd do a string concat to create de parameters of refactof field. Great answer. Thks – jlSta Apr 21 '17 at 6:02
  • Tks, it's working. I did a for loop to populate the dict and i used the list tools and index to reorganise. – jlSta May 4 '17 at 9:13

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.