Previously, we already covered how to run Processing tools as well as how to chain Processing tools to create more complex workflows. This time, we will go one step further and create a Processing script that can be called from the Processing toolbox.

You will find different approaches for writing Processing scripts when you look for examples online. This is the case because QGIS >= 3.6 supports a simpler script style which I’ve written about in “Easy Processing scripts comeback in QGIS 3.6”. The following example works in QGIS 3.4 and above.

When we are done, our Processing script will be able to take a vector layer as input, perform a buffer operation on this layer, and return the buffered output.

Select Create New Script from the Processing toolbox to get started:

To create a new Processing script, we need to implement our own QgsProcessingAlgorithm. This means that we have to implement our own custom class (called ExampleAlgo here) that is based on QgsProcessingAlgorithm. The  __init__ function is the so-called constructor of a class. It defines how new objects of this class are created. In this case, we define that ExampleAlgo objects should be created in the same way as QgsProcessingAlgorithm objects by calling its constructor using super().__init__():

from qgis.core import QgsProcessingAlgorithm
 
class ExampleAlgo(QgsProcessingAlgorithm):
    INPUT = 'INPUT'
    OUTPUT = 'OUTPUT'

    def __init__(self):
        super().__init__()

This is just the start. Besides the constructor, our algorithm has to provide the following mandatory functions:

  • nameReturns the unique algorithm name, used for identifying the algorithm.
  • displayName: Returns the algorithm name that will be displayed in the Processing toolbox.
  • createInstance: Must return a new copy of your algorithm. 
  • initAlgorithm: Here we define the inputs and outputs of the algorithm. INPUT and OUTPUT are recommended names for the main input and main output parameters, respectively. If a parameter depends on another parameter, parentParameterName is used to specify this relationship (could be the field / band of a layer or the distance units of a layer).
  • processAlgorithm: This is where the processing takes place. Parameters are retrieved using special purpose functions, for instance parameterAsSource and parameterAsDouble.

The first three functions are not very exciting. We need to pick a name and display name for the algorithm. The createInstance function always looks same:

    def name(self):
        return "exalgo_processing_run"

    def displayName(self):
        return "Example Processing.run() script"

    def createInstance(self):
        return type(self)()

Next, we need to define the inputs and outputs of our algorithm by adding two corresponding parameters to our algorithm: QgsProcessingParameterFeatureSource is the input and QgsProcessingParameterVectorDestination is the output. Note that this requires that we import these parameters at the beginning of our script:

from qgis.core import (QgsProcessing, QgsProcessingAlgorithm, 
                       QgsProcessingParameterFeatureSource, 
                       QgsProcessingParameterVectorDestination)

The strings ‘Input layer’ and ‘Output layer’ are the labels that will be shown in the automatically generated user interface dialog. (If you look at other more complex Processing script examples, such as the script in the official user manual, you’ll find that these strings can be translated for different QGIS language settings by adding a tr() function.)

    def initAlgorithm(self, config=None):
        self.addParameter(
            QgsProcessingParameterFeatureSource(
                self.INPUT,
                'Input layer',
                [QgsProcessing.TypeVectorAnyGeometry]
            )
        )

        self.addParameter(
            QgsProcessingParameterVectorDestination (
                self.OUTPUT,
                'Output layer'
            )
        )

Now we can finally implement the actual data processing code in the processAlgorithm() function. This is based on what we already covered in Chaining Processing tools. However, note that because the buffer algorithm is being run as a step in our larger algorithm, the is_child_algorithm option should be set to True:

    def processAlgorithm(self, parameters, context, feedback):
        outputFile = self.parameterAsOutputLayer(parameters, self.OUTPUT, context)

        buffered_layer = processing.run("native:buffer", 
            {
                'INPUT': parameters['INPUT'],
                'DISTANCE': 1000000,
                'SEGMENTS': 5,
                'END_CAP_STYLE': 0,
                'JOIN_STYLE': 0,
                'MITER_LIMIT': 2,
                'DISSOLVE': False,
                'OUTPUT': outputFile
            }, 
            is_child_algorithm=True,
            context=context, 
            feedback=feedback
        )['OUTPUT']
    
        return {self.OUTPUT : buffered_layer}

If we put it all together, the final script looks like this:

from qgis.core import (QgsProcessing, QgsProcessingAlgorithm, 
                       QgsProcessingParameterFeatureSource, 
                       QgsProcessingParameterVectorDestination)
import processing 
                      
class ExampleAlgo(QgsProcessingAlgorithm):
    INPUT = 'INPUT'
    OUTPUT = 'OUTPUT'

    def __init__(self):
        super().__init__()

    def name(self):
        return "exalgo_processing_run"

    def displayName(self):
        return "Example Processing.run() script"

    def createInstance(self):
        return type(self)()
  
    def initAlgorithm(self, config=None):
        self.addParameter(
            QgsProcessingParameterFeatureSource(
                self.INPUT,
                'Input layer',
                [QgsProcessing.TypeVectorAnyGeometry]
            )
        )

        self.addParameter(
            QgsProcessingParameterVectorDestination (
                self.OUTPUT,
                'Output layer'
            )
        )
        
    def processAlgorithm(self, parameters, context, feedback):
        outputFile = self.parameterAsOutputLayer(parameters, self.OUTPUT, context)

        buffered_layer = processing.run("native:buffer", 
            {
                'INPUT': parameters['INPUT'],
                'DISTANCE': 1000000,
                'SEGMENTS': 5,
                'END_CAP_STYLE': 0,
                'JOIN_STYLE': 0,
                'MITER_LIMIT': 2,
                'DISSOLVE': False,
                'OUTPUT': outputFile
            }, 
            is_child_algorithm=True,
            context=context, 
            feedback=feedback
        )['OUTPUT']
    
        return {self.OUTPUT : buffered_layer}

This is a minimal working example! The official user manual provides a much more extensive script and lots of background information. For example, you might want to group your scripts to form groups of similar algorithms. There’s an optional group() function to achieve this.

These are the basics of creating a Processing script that can be called from the Processing toolbox.


PyQGIS 101 is a work in progress. I’d appreciate any feedback, particularly from beginners!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: