1

In a simple processing script I am trying to use Regex to search and then replace any found values with a new value. It seems to run fine but doesn't save the changes.

If I keep .startEditing() I get

Current thread 0x0000332c (most recent call first):
Windows fatal exception: access violation
Thread 0x0000332c (most recent call first):`

Is it possible to save time by only saving the changes once all the edits have been made?

The whole code is

from qgis import processing
from qgis.processing import alg
from qgis.core import QgsProject
import operator,re

@alg(name='searchupdatealg', label='Search and update (alg)',
     group='mgm', group_label='Example scripts')
# 'INPUT' is the recommended name for the main input parameter
@alg.input(type=alg.SOURCE, name='INPUT', label='Input vector')
@alg.input(type=alg.FIELD, name='Attrib', label='Attribute',
           parentLayerParameterName='INPUT')
@alg.input(type=alg.STRING, name='RegexS', label='Regex String')
@alg.input(type=alg.EXPRESSION, name='OutputS', label='Output String')
# 'OUTPUT' is the recommended name for the main output parameter
@alg.input(type=alg.VECTOR_LAYER_DEST, name='OUTPUT',
           label='Vector output')
           

def searchupdatealg(instance, parameters, context, feedback, inputs):
    """
    Description of the algorithm.
    (If there is no comment here, you will get an error)
    """
    in_layer = instance.parameterAsSource(parameters, 'INPUT', context)
    regex_s = instance.parameterAsString(parameters,'RegexS', context)
    numfeatures = in_layer.featureCount()
    feats = [f for f in in_layer.getFeatures()]
    attrib_n = instance.parameterAsString(parameters,'Attrib', context)
    out_val = instance.parameterAsExpression(parameters,'OutputS', context)
    for feat in feats:
        in_val = feat[attrib_n]
        if len(re.findall(regex_s, in_val))>0:
            #print (in_val, out_val)
            UpdateRecord(in_layer,feat,attrib_n,in_val,out_val)
    if feedback.isCanceled():
        return {}
    return {'NUMBEROFFEATURES': numfeatures, 'String': in_val}
    
def UpdateRecord(in_layer,feat,fieldName,in_val,out_val):
    print(in_layer)
    layerf = QgsProject().instance().mapLayersByName(in_layer.sourceName())[0]
    FieldIndex = feat.fields().indexFromName(fieldName)
    layerf.startEditing() #causes QGIS Access violation
    print(feat.id(),FieldIndex,in_val,out_val)
    layerf.changeAttributeValue(feat.id(), FieldIndex, out_val)
    layerf.commitChanges()

1 Answer 1

0

I found that the start/commit changes need to be in the main function rather than for each change. So

from qgis import processing
from qgis.processing import alg
from qgis.core import QgsProject
import operator,re

@alg(name='searchupdatealg', label='Search and update (alg)',
     group='mgm', group_label='Custom Scripts')
# 'INPUT' is the recommended name for the main input parameter
@alg.input(type=alg.SOURCE, name='INPUT', label='Input vector')
@alg.input(type=alg.FIELD, name='In_Attrib', label='Attribute to search',
           parentLayerParameterName='INPUT')
@alg.input(type=alg.STRING, name='RegexS', label='Regex String')
@alg.input(type=alg.FIELD, name='Out_Attrib', label='Attribute to change',
           parentLayerParameterName='INPUT')
@alg.input(type=alg.EXPRESSION, name='OutputS', label='Output expression')
# 'OUTPUT' is the recommended name for the main output parameter
@alg.input(type=alg.VECTOR_LAYER_DEST, name='OUTPUT',
           label='Vector output')
           

def searchupdatealg(instance, parameters, context, feedback, inputs):
    """
    Search using Regex and output an expression based value
    """
    in_layer = instance.parameterAsSource(parameters, 'INPUT', context)
    regex_s = instance.parameterAsString(parameters,'RegexS', context)
    numfeatures = in_layer.featureCount()
    feats = [f for f in in_layer.getFeatures()]
    in_attrib = instance.parameterAsString(parameters,'In_Attrib', context)
    out_attrib = instance.parameterAsString(parameters,'Out_Attrib', context)
    out_val = instance.parameterAsExpression(parameters,'OutputS', context)
    layerf = QgsProject().instance().mapLayersByName(in_layer.sourceName())[0]
    i=0
    layerf.startEditing()
    for feat in feats:
        in_val = feat[in_attrib]
        if len(re.findall(regex_s, in_val))>0:
            UpdateRecord(layerf,feat,out_attrib,in_val,out_val)
            i+=1
    layerf.commitChanges()
    if feedback.isCanceled():
        return {}
    return {'NUMBEROFFEATURES': numfeatures, 'Number changed': i}
    
def UpdateRecord(layerf,feat,out_attrib,in_val,out_val):
    out_attrib_index = feat.fields().indexFromName(out_attrib)
    print(feat.id(),out_attrib_index,in_val,str(eval(out_val))) #testing only. Comment out in production
    layerf.changeAttributeValue(feat.id(), out_attrib_index, str(eval(out_val)))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.