4

When I declare an algorithm parameter as

@alg.input(type=alg.INT, name='MINUTES', label='Minuten')

this parameter is set to 0 each time the algorithm is opened. Is it somehow possible to declare that the parameter should keep it's value between runs, a) in the same QGIS session, b) between QGIS sessions?

In classic algorithm scripts without decorators this could be done in initAlgorithm().

2
  • 1
    You could could set it as a project variable, see this answer., or store it in QgsSettings(). Commented Jun 6 at 22:48
  • 1
    Or you could just open the tool from the processing history with all previously set parameters populated. Commented Jun 9 at 2:21

1 Answer 1

5

That's a good question, which has also been on my mind for the last few days (see Create coverage layer for atlas).

As @Matt has already mentioned, we can save the parameters in QgsSettings variables. But we always have to update the inputs property of the AlgWrapper instance to keep the new default values.

Here is the code of my example algorithm algRectAlongLines (places rectangles along lines):

"""
***************************************************************************
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
***************************************************************************
*   Created: 19-06-2025 by Christoph Candido ([email protected])   *
*                                                                         *
* This algorithm creates a polygon layer with rectangles along the lines  *
* of the "Input layer". It demonstrates the use of the @alg decorator and *
* shows how to store the input parameters for later use.                  *
*                                                                         *
* Due to the use of the @alg decorator, this algorithm is only supported  *
* by QGIS >= 3.6.                                                         *
*                                                                         *
***************************************************************************
"""
from qgis.PyQt.QtCore import QVariant
from qgis import processing
from qgis.processing import alg
from qgis.core import (QgsProcessing, 
                       QgsGeometry, 
                       QgsFeature, 
                       QgsWkbTypes, 
                       QgsFeatureSink, 
                       QgsFields, 
                       QgsField, 
                       QgsPointXY, 
                       QgsSettings, 
                       QgsApplication,
                       QgsProcessingAlgorithm)


@alg(name="algRectAlongLines", label="Generate rectangles along lines", group="", group_label="")
@alg.input(type=alg.SOURCE, name="INPUT", label="Select Line layer", types= [QgsProcessing.TypeVectorLine])
@alg.input(type=alg.NUMBER, name="WIDTH", label="Enter Rectangle Width", default=QgsSettings().value("algRectAlongLines/WIDTH"))
@alg.input(type=alg.NUMBER, name="HEIGHT", label="Enter Rectangle Height", default=QgsSettings().value("algRectAlongLines/HEIGHT"))
@alg.input(type=alg.NUMBER, name="OVERLAP", label="Enter Rectangle Overlap [%]", default=QgsSettings().value("algRectAlongLines/OVERLAP"))
@alg.input(type=alg.NUMBER, name="TOLERANCE", label="Enter Tolerance for Line Simplification", default=QgsSettings().value("algRectAlongLines/TOLERANCE",0))
@alg.input(type=alg.BOOL, name="ALIGN", label="Align Rectangles with Line Features", default=QgsSettings().value("algRectAlongLines/ALIGN",True))
@alg.input(type=alg.SINK, name="OUTPUT", label="Output layer")
def algRectAlongLines(self, parameters, context, feedback, inputs):
    """
    This algorithm creates a polygon layer with rectangles along the lines of the "Input layer".
    
    We can adjust the width and height of the rectangles, define an overlap factor and set a tolerance value if we want to simplify the line geometries.
    With the "Align Rectangles with Line Features" check box, we can align all rectangles to the line geometries.
    
    Each rectangle created contains the ID of the source line feature, the rotation angle and a sort value.
    
    In addition, the algorithm saves the input parameters in global QGIS variables for repeated use.
    """
    
    def setDefaultValue(dict, param, val):
        s = QgsSettings()
        s.setValue("algRectAlongLines/"+param, val)
        dict[param].setGuiDefaultValueOverride(val)
        return dict[param]
        
    
    width = self.parameterAsDouble(parameters, 'WIDTH', context)
    height = self.parameterAsDouble(parameters, 'HEIGHT', context)
    overlap = self.parameterAsDouble(parameters, 'OVERLAP', context)
    if overlap > 0:
        overlap = overlap / 100.0
    
    tolerance = self.parameterAsDouble(parameters, 'TOLERANCE', context)
    align = self.parameterAsBool(parameters, 'ALIGN', context)

    sortOrder = None
    i = 1
    
    # refresh the inputs property of the AlgWrapper instance to update the default parameters
    inp = self.inputs
    newInputs = { 'WIDTH': setDefaultValue(inp, 'WIDTH', width),
                  'HEIGHT': setDefaultValue(inp, 'HEIGHT', height),
                  'OVERLAP': setDefaultValue(inp, 'OVERLAP', overlap * 100.0),
                  'TOLERANCE': setDefaultValue(inp, 'TOLERANCE', tolerance),
                  'ALIGN': setDefaultValue(inp, 'ALIGN', align) }
    inp.update(newInputs)
   
    
    source = self.parameterAsSource(parameters, 'INPUT', context)

    fields = QgsFields()
    fields.append(QgsField('src_fid', QVariant.Int))
    fields.append(QgsField('angle', QVariant.Double))
    fields.append(QgsField('sort_order', QVariant.Int))
  

    (sink, dest_id) = self.parameterAsSink(parameters, 'OUTPUT',
                context, fields, QgsWkbTypes.Polygon , source.sourceCrs())

    features = source.getFeatures()
    for feature in features:
        if feature.hasGeometry():
            geom = feature.geometry().extendLine(width*0.1,width*0.1)
            if tolerance != 0:
                geom = geom.simplify(tolerance)
            curs = 0
            numpages = geom.length()/width
            if numpages > 1:
                step = 1.0/numpages
                stepnudge = (1.0-overlap) * step
            else:
                step = 1
                stepnudge = 1
       
            currangle = 0
            while curs < 1:
                startpoint = geom.interpolate(curs*geom.length())
                endpoint = geom.interpolate((curs+step)*geom.length())
                if not endpoint:
                    endpoint = geom.interpolate(geom.length())

                x_start = startpoint.asPoint().x()
                y_start = startpoint.asPoint().y()
                x_end = endpoint.asPoint().x()
                y_end = endpoint.asPoint().y()
                x_mid = (x_start + x_end)/2
                y_mid = (y_start + y_end)/2
          
                w2 = width/2.0
                h2 = height/2.0
                minx = -w2
                miny = -h2
                maxx = w2
                maxy = h2
                poly = QgsGeometry().fromWkt(f'POLYGON(({minx} {miny}, {minx} {maxy},{maxx} {maxy}, {maxx} {miny}, {minx} {miny}))')
            
                feat = QgsFeature()
                feat.setFields(fields)
            
                if align:
                    azimuth = startpoint.asPoint().azimuth(endpoint.asPoint())
                    currangle = (startpoint.asPoint().azimuth(endpoint.asPoint())+270)%360
                    poly.rotate(currangle, QgsPointXY(0,0))
                    feat['angle'] = currangle
                else:
                    feat['angle'] = 0
                
                #poly.translate(-width*overlap,0)
                poly.translate(x_mid, y_mid)
                poly.asPolygon()
                curs = curs + stepnudge
            
                feat['src_fid'] = feature.id()
                feat['sort_order'] = i
                feat.setGeometry(poly)
                sink.addFeature(feat, QgsFeatureSink.FastInsert) 
                i += 1
    
    results = {}
    results['OUTPUT'] = dest_id
    return results

5
  • 1
    That's cool and I will try this. I will edit the question and wait a few days before accepting your answer, to encourage "a better solution". Commented Jun 8 at 15:19
  • @Redoute I‘m also interested in a better solution. And I’m pretty sure one of the experts will find it :-) Commented Jun 8 at 16:08
  • @Redoute I found a possible solution (see code updates), where we can update the input parameters with the AlgWrapper inputs property. Commented Jun 9 at 9:31
  • 1
    I can confirm that your solution is working smoothly - except your algorithm needs a doc string to work. I wish I could say the same for my code, which crashes QGIS :( Commented Jun 25 at 18:59
  • @Redoute you are absolutely right with the docstring, but the main purpose was to give you an example for your question ... pimped my code :-) Commented Jun 26 at 6:09

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.