8

I am currently trying to convert a python script from QGIS 2.18 to QGIS 3. Therefore, I am trying to integrate my python script into the template from @underdark: see here.

I am currently stepping from one error into another so I seek help as I was not able to find any other question on Stack Exchange or any documentation.

Here is some part of my script so far:

def processAlgorithm(self, parameters, context, feedback):
    inEdges = self.parameterAsVectorLayer(parameters, self.INPUT_VECTOR_LAYER_EDGES, context)
    inNodes = self.parameterAsVectorLayer(parameters, self.INPUT_VECTOR_LAYER_NODES, context)
    outMerged = self.parameterAsOutputLayer(parameters, self.OUTPUT_VECTOR_LAYER_MERGED, context)

    #DO SOME CALCULATION
    
    # get field indices for inputlayer
    idxEdgeId = inEdges.fieldNameIndex(ID)
    idxPriority = inEdges.fieldNameIndex(PRIORITY)
    idxLength  = inEdges.fieldNameIndex(LENGTH)
    idxTime    = inEdges.fieldNameIndex(TIME)
    idxFrom  = inEdges.fieldNameIndex(FROM)
    idxTo  = inEdges.fieldNameIndex(TO)

    idx_id = inNodes.fieldNameIndex(NODEID)
    idx_x = inNodes.fieldNameIndex(COORD_X)
    idx_y = inNodes.fieldNameIndex(COORD_Y)

    # Set the provider to accept the data source
    outdp = outMerged.dataProvider()

    # define attributes and add them to the vector layer
    outdp.addAttributes([QgsField(EDGEID, QVariant.String),
                        QgsField(NODE1, QVariant.String),
                        QgsField(NODE2, QVariant.String),
                        QgsField(PRIORITY, QVariant.Int),
                        QgsField(VTO, QVariant.Double),
                        QgsField(VFROM, QVariant.Double)])
    outMerged.updateFields()

    # get field indices for outputlayer
    outEdgeId = outdp.fieldNameIndex(EDGEID)
    outNode1 = outdp.fieldNameIndex(NODE1)
    outNode2 = outdp.fieldNameIndex(NODE2)
    outPriority = outdp.fieldNameIndex(PRIORITY)
    outVTo = outdp.fieldNameIndex(VTO)
    outVFrom = outdp.fieldNameIndex(VFROM)

    listOfEdgeIds = []
    count = 0
    flag = 0

    (...)

For your information: I have two input vector layers (one point, and one line layer) and out of that, I want to create one outputlayer (which should be a line-vector layer as well).

But currently I have trouble with the fieldNameIndex(QString) - method in the first section of the code. I have already tried to call the method with the dataProvider() of the input layer, but that hasn't worked out. I get error:

Traceback (most recent call last): File "C:\Users\tlichten\AppData\Roaming\QGIS\QGIS3\profiles\default\processing\scripts\merging.py", line 89, in processAlgorithm idxEdgeId = inEdges.fieldNameIndex(ID) AttributeError: 'NoneType' object has no attribute 'fieldNameIndex'

Although I have to input vector layers, I cannot select any of them if I want to start the script. The execute script window looks like this:

enter image description here

In QGIS2.18, there would be two drop-down menus in which to choose between the input layers.

I have adapted the initAlgorithm() to the following:

    def initAlgorithm(self, config=None):
    self.addParameter(QgsProcessingParameterVectorLayer(
        self.INPUT_VECTOR_LAYER_EDGES, 
        self.tr('Edges'),
        [QgsProcessing.TypeVectorAnyGeometry]))
    self.addParameter(QgsProcessingParameterRasterLayer(
        self.INPUT_VECTOR_LAYER_NODES,
        self.tr('Nodes')
        [QgsProcessing.TypeVectorAnyGeometry]))
    self.addParameter(QgsProcessingParameterRasterDestination(
        self.OUTPUT_VECTOR_LAYER_MERGED,
        self.tr("Output Vector Layer Merged"),
        [QgsProcessing.TypeVectorAnyGeometry]))

But I can still not select any input layers if I execute the script.

6
  • I have shortened the code segment. The error message I receive when trying to execute the script is as follows: Traceback (most recent call last): File "C:\Users\tlichten\AppData\Roaming\QGIS\QGIS3\profiles\default\processing\scripts\merging.py", line 89, in processAlgorithm idxEdgeId = inEdges.fieldNameIndex(ID) AttributeError: 'NoneType' object has no attribute 'fieldNameIndex'
    – applebrown
    Nov 26, 2018 at 13:11
  • I have edited my question. Please see above. How do I select the layer in the input field. If I run the script from the toolbox I cannot select any inputlayer as it was with QGIS 2.18. I can just press start...
    – applebrown
    Nov 26, 2018 at 13:45
  • Your code snippet is different than the sourced example you reference. That one uses a class which gets the parameters.
    – artwork21
    Nov 26, 2018 at 14:03
  • in initAlgorithm() function
    – artwork21
    Nov 26, 2018 at 14:41
  • 1
    You can't mix "Raster" and "Vector" in one parameter definition (your INPUT_VECTOR_LAYER_NODES name suggest it should be a vector not a raster) . Do you need Raster input or output? For output you should call self.addOutput.
    – barteksch
    Nov 26, 2018 at 15:12

3 Answers 3

12

That method has been deprecated, use this instead to get the field index:

fields = inEdges.fields()
idxEdgeId = fields.indexFromName('fieldName')
5
  • thank you for your answer, I have tried your solution, but it looks like the fields() method is not known. This is my error now: Traceback (most recent call last): File "C:\Users\tlichten\AppData\Roaming\QGIS\QGIS3\profiles\default\processing\scripts\merging.py", line 89, in processAlgorithm fields = inEdges.fields() AttributeError: 'NoneType' object has no attribute 'fields'
    – applebrown
    Nov 26, 2018 at 13:30
  • How/where is the processAlgorithm() being called? The vector layer "inEdges" is not valid.
    – artwork21
    Nov 26, 2018 at 13:38
  • I have edited the question. Please have a look! Thank you so much
    – applebrown
    Nov 26, 2018 at 14:24
  • is the newFeature.setAttribute() method deprecated as well?? How can I set an attribute of a new feature for the output layer?
    – applebrown
    Nov 26, 2018 at 15:40
  • 1
    That method should still work. The referenced blog you noted in your question shows how to do that. Also, that question is different from this question so you should post a new question about that.
    – artwork21
    Nov 26, 2018 at 17:09
6

fieldNameIndex function was removed in QGIS3:

Removed fieldNameIndex(), use fields().lookupField() or fields().indexFromName() instead

You can convert your code as follows:

inEdges = self.parameterAsVectorLayer(parameters, self.INPUT_VECTOR_LAYER_EDGES, context)
inEdgesFields = inEdges.fields()

idxEdgeId = inEdgesFields.indexFromName(ID)

For more details see https://qgis.org/api/api_break.html


Exception you've got, suggest that you have problem with parameters, make sure you add parameters with proper key in initAlgorithm function (in QGIS2 it was defineCharacteristics):

def initAlgorithm(self, config=None):
    self.addParameter(QgsProcessingParameterVectorLayer(self.INPUT_VECTOR_LAYER_EDGES, self.tr('Edges'), types=[QgsProcessing.TypeVector]))
7
  • thank you for your answer, I have tried your solution, but it looks like the fields() method is not known. This is my error now: Traceback (most recent call last): File "C:\Users\tlichten\AppData\Roaming\QGIS\QGIS3\profiles\default\processing\scripts\merging.py", line 90, in processAlgorithm inEdgesFields = inEdges.fields() AttributeError: 'NoneType' object has no attribute 'fields'
    – applebrown
    Nov 26, 2018 at 13:31
  • Make sure the parameters are properly added in initAlgorithm(self, config=None) function and you select valid layer in the input field
    – barteksch
    Nov 26, 2018 at 13:39
  • the initAlgorithm method should work properly. But how do I select the layer in the input field. If I run the script from the toolbox I cannot select any inputlayer as it was with QGIS 2.18. I can just press start...
    – applebrown
    Nov 26, 2018 at 13:42
  • I have edited my question. Please have a look for better understanding :)
    – applebrown
    Nov 26, 2018 at 13:46
  • See my edited answer
    – barteksch
    Nov 26, 2018 at 13:48
1

Alternatively there is also a possibility using the indexOf(fieldName: str) method.

layer = iface.activeLayer()

fields = [f.name() for f in layer.fields()]

for field in fields:
    print(f'Field with a name "{field}" has the {layer.fields().indexOf(field)} index.')

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.