Viktigt

Översättning är en gemenskapsinsats du kan gå med i. Den här sidan är för närvarande översatt till 100.00%.

23.9. Skriva nya Processing-algoritmer som Python-skript

Det finns två alternativ för att skriva Processing-algoritmer med hjälp av Python.

I QGIS kan du använda Create new script i menyn Scripts högst upp i verktygslådan Processing Toolbox för att öppna Processing Script Editor där du kan skriva din kod. För att förenkla uppgiften kan du börja med en skriptmall genom att använda Create new script from template från samma meny. Detta öppnar en mall som utökar QgsProcessingAlgorithm.

Om du sparar skriptet i mappen scripts (standardplatsen) med tillägget .py blir algoritmen tillgänglig i verktygslådan Processing Toolbox.

23.9.1. Förlängning av QgsProcessingAlgorithm

Följande kod

  1. tar ett vektorlager som indata

  2. räknar antalet funktioner

  3. gör en buffertoperation

  4. skapar ett rasterlager från resultatet av buffertoperationen

  5. returnerar buffertlager, rasterlager och antal funktioner

  1from qgis.PyQt.QtCore import QCoreApplication
  2from qgis.core import (QgsProcessing,
  3                       QgsProcessingAlgorithm,
  4                       QgsProcessingException,
  5                       QgsProcessingOutputNumber,
  6                       QgsProcessingParameterDistance,
  7                       QgsProcessingParameterFeatureSource,
  8                       QgsProcessingParameterVectorDestination,
  9                       QgsProcessingParameterRasterDestination)
 10from qgis import processing
 11
 12
 13class ExampleProcessingAlgorithm(QgsProcessingAlgorithm):
 14    """
 15    This is an example algorithm that takes a vector layer,
 16    creates some new layers and returns some results.
 17    """
 18
 19    def tr(self, string):
 20        """
 21        Returns a translatable string with the self.tr() function.
 22        """
 23        return QCoreApplication.translate('Processing', string)
 24
 25    def createInstance(self):
 26        # Must return a new copy of your algorithm.
 27        return ExampleProcessingAlgorithm()
 28
 29    def name(self):
 30        """
 31        Returns the unique algorithm name.
 32        """
 33        return 'bufferrasterextend'
 34
 35    def displayName(self):
 36        """
 37        Returns the translated algorithm name.
 38        """
 39        return self.tr('Buffer and export to raster (extend)')
 40
 41    def group(self):
 42        """
 43        Returns the name of the group this algorithm belongs to.
 44        """
 45        return self.tr('Example scripts')
 46
 47    def groupId(self):
 48        """
 49        Returns the unique ID of the group this algorithm belongs
 50        to.
 51        """
 52        return 'examplescripts'
 53
 54    def shortHelpString(self):
 55        """
 56        Returns a localised short help string for the algorithm.
 57        """
 58        return self.tr('Example algorithm short description')
 59
 60    def initAlgorithm(self, config=None):
 61        """
 62        Here we define the inputs and outputs of the algorithm.
 63        """
 64        # 'INPUT' is the recommended name for the main input
 65        # parameter.
 66        self.addParameter(
 67            QgsProcessingParameterFeatureSource(
 68                'INPUT',
 69                self.tr('Input vector layer'),
 70                types=[QgsProcessing.TypeVectorAnyGeometry]
 71            )
 72        )
 73        self.addParameter(
 74            QgsProcessingParameterVectorDestination(
 75                'BUFFER_OUTPUT',
 76                self.tr('Buffer output'),
 77            )
 78        )
 79        # 'OUTPUT' is the recommended name for the main output
 80        # parameter.
 81        self.addParameter(
 82            QgsProcessingParameterRasterDestination(
 83                'OUTPUT',
 84                self.tr('Raster output')
 85            )
 86        )
 87        self.addParameter(
 88            QgsProcessingParameterDistance(
 89                'BUFFERDIST',
 90                self.tr('BUFFERDIST'),
 91                defaultValue = 1.0,
 92                # Make distance units match the INPUT layer units:
 93                parentParameterName='INPUT'
 94            )
 95        )
 96        self.addParameter(
 97            QgsProcessingParameterDistance(
 98                'CELLSIZE',
 99                self.tr('CELLSIZE'),
100                defaultValue = 10.0,
101                parentParameterName='INPUT'
102            )
103        )
104        self.addOutput(
105            QgsProcessingOutputNumber(
106                'NUMBEROFFEATURES',
107                self.tr('Number of features processed')
108            )
109        )
110
111    def processAlgorithm(self, parameters, context, feedback):
112        """
113        Here is where the processing itself takes place.
114        """
115        # First, we get the count of features from the INPUT layer.
116        # This layer is defined as a QgsProcessingParameterFeatureSource
117        # parameter, so it is retrieved by calling
118        # self.parameterAsSource.
119        input_featuresource = self.parameterAsSource(parameters,
120                                                     'INPUT',
121                                                     context)
122        numfeatures = input_featuresource.featureCount()
123
124        # Retrieve the buffer distance and raster cell size numeric
125        # values. Since these are numeric values, they are retrieved
126        # using self.parameterAsDouble.
127        bufferdist = self.parameterAsDouble(parameters, 'BUFFERDIST',
128                                            context)
129        rastercellsize = self.parameterAsDouble(parameters, 'CELLSIZE',
130                                                context)
131        if feedback.isCanceled():
132            return {}
133        buffer_result = processing.run(
134            'native:buffer',
135            {
136                # Here we pass on the original parameter values of INPUT
137                # and BUFFER_OUTPUT to the buffer algorithm.
138                'INPUT': parameters['INPUT'],
139                'OUTPUT': parameters['BUFFER_OUTPUT'],
140                'DISTANCE': bufferdist,
141                'SEGMENTS': 10,
142                'DISSOLVE': True,
143                'END_CAP_STYLE': 0,
144                'JOIN_STYLE': 0,
145                'MITER_LIMIT': 10
146            },
147            # Because the buffer algorithm is being run as a step in
148            # another larger algorithm, the is_child_algorithm option
149            # should be set to True
150            is_child_algorithm=True,
151            #
152            # It's important to pass on the context and feedback objects to
153            # child algorithms, so that they can properly give feedback to
154            # users and handle cancelation requests.
155            context=context,
156            feedback=feedback)
157
158        # Check for cancelation
159        if feedback.isCanceled():
160            return {}
161
162        # Run the separate rasterization algorithm using the buffer result
163        # as an input.
164        rasterized_result = processing.run(
165            'qgis:rasterize',
166            {
167                # Here we pass the 'OUTPUT' value from the buffer's result
168                # dictionary off to the rasterize child algorithm.
169                'LAYER': buffer_result['OUTPUT'],
170                'EXTENT': buffer_result['OUTPUT'],
171                'MAP_UNITS_PER_PIXEL': rastercellsize,
172                # Use the original parameter value.
173                'OUTPUT': parameters['OUTPUT']
174            },
175            is_child_algorithm=True,
176            context=context,
177            feedback=feedback)
178
179        if feedback.isCanceled():
180            return {}
181
182        # Return the results
183        return {'OUTPUT': rasterized_result['OUTPUT'],
184                'BUFFER_OUTPUT': buffer_result['OUTPUT'],
185                'NUMBEROFFEATURES': numfeatures}

Standardfunktioner för bearbetningsalgoritm:

  • createInstance (obligatorisk)

    Måste returnera en ny kopia av din algoritm. Om du ändrar namnet på klassen, se till att du också uppdaterar det värde som returneras här för att matcha!

  • namn (obligatoriskt)

    Returnerar det unika algoritmnamnet som används för att identifiera algoritmen.

  • displayName (obligatoriskt)

    Returnerar det översatta algoritmnamnet.

  • group

    Returnerar namnet på den grupp som den här algoritmen tillhör.

  • groupId

    Returnerar det unika ID:t för den grupp som den här algoritmen tillhör.

  • shortHelpString

    Returnerar en lokaliserad kort hjälpsträng för algoritmen.

  • initAlgorithm (obligatorisk)

    Här definierar vi algoritmens in- och utgångar.

    INPUT och OUTPUT är rekommenderade namn för huvudparametrarna för inmatning respektive utmatning.

    Om en parameter är beroende av en annan parameter används parentParameterName för att ange detta förhållande (kan vara fältet/bandet för ett lager eller avståndsenheterna för ett lager).

  • processAlgorithm (obligatorisk)

    Det är här som bearbetningen sker.

    Parametrar hämtas med hjälp av specialfunktioner, t.ex. parameterAsSource och parameterAsDouble.

    processing.run kan användas för att köra andra bearbetningsalgoritmer från en bearbetningsalgoritm. Den första parametern är namnet på algoritmen, den andra är en ordbok med parametrarna för algoritmen. is_child_algorithm sätts normalt till True när en algoritm körs från en annan algoritm. context och feedback informerar algoritmen om den miljö den ska köras i och kanalen för att kommunicera med användaren (fånga upp begäran om avbrytande, rapportera framsteg, ge textuell feedback). När (föräldra)algoritmens parametrar används som parametrar för ”underordnade” algoritmer, skall de ursprungliga parametervärdena användas (t.ex. parameters['OUTPUT']).

    Det är god praxis att kontrollera feedbackobjektet för annullering så mycket som det rimligen är möjligt! Detta gör det möjligt att avbryta på ett smidigt sätt, istället för att tvinga användarna att vänta på att en oönskad behandling ska ske.

    Algoritmen ska returnera värden för alla utdataparametrar som den har definierat som en ordbok. I det här fallet är det buffert- och rastrerade utdatalager samt antalet bearbetade funktioner. Ordbokens nycklar måste matcha de ursprungliga parameter-/utdatanamnen.

23.9.2. @alg-dekoratorn

Med hjälp av @alg-dekoratorn kan du skapa dina egna algoritmer genom att skriva Python-koden och lägga till några extra rader för att tillhandahålla ytterligare information som behövs för att göra den till en riktig Processing-algoritm. Detta förenklar skapandet av algoritmer och specifikationen av in- och utgångar.

En viktig begränsning med dekoratormetoden är att algoritmer som skapas på detta sätt alltid kommer att läggas till i användarens Processing Scripts-provider - det är inte möjligt att lägga till dessa algoritmer i en anpassad provider, t.ex. för användning i plugins.

Följande kod använder @alg-dekoratorn för att

  1. använda ett vektorlager som indata

  2. räkna antalet funktioner

  3. göra en buffertoperation

  4. skapa ett rasterlager från resultatet av buffertoperationen

  5. returnerar buffertlager, rasterlager och antal funktioner

 1from qgis import processing
 2from qgis.processing import alg
 3from qgis.core import QgsProject
 4
 5@alg(name='bufferrasteralg', label='Buffer and export to raster (alg)',
 6     group='examplescripts', group_label='Example scripts')
 7# 'INPUT' is the recommended name for the main input parameter
 8@alg.input(type=alg.SOURCE, name='INPUT', label='Input vector layer')
 9# 'OUTPUT' is the recommended name for the main output parameter
10@alg.input(type=alg.RASTER_LAYER_DEST, name='OUTPUT',
11           label='Raster output')
12@alg.input(type=alg.VECTOR_LAYER_DEST, name='BUFFER_OUTPUT',
13           label='Buffer output')
14@alg.input(type=alg.DISTANCE, name='BUFFERDIST', label='BUFFER DISTANCE',
15           default=1.0)
16@alg.input(type=alg.DISTANCE, name='CELLSIZE', label='RASTER CELL SIZE',
17           default=10.0)
18@alg.output(type=alg.NUMBER, name='NUMBEROFFEATURES',
19            label='Number of features processed')
20
21def bufferrasteralg(instance, parameters, context, feedback, inputs):
22    """
23    Description of the algorithm.
24    (If there is no comment here, you will get an error)
25    """
26    input_featuresource = instance.parameterAsSource(parameters,
27                                                     'INPUT', context)
28    numfeatures = input_featuresource.featureCount()
29    bufferdist = instance.parameterAsDouble(parameters, 'BUFFERDIST',
30                                            context)
31    rastercellsize = instance.parameterAsDouble(parameters, 'CELLSIZE',
32                                                context)
33    if feedback.isCanceled():
34        return {}
35    buffer_result = processing.run('native:buffer',
36                               {'INPUT': parameters['INPUT'],
37                                'OUTPUT': parameters['BUFFER_OUTPUT'],
38                                'DISTANCE': bufferdist,
39                                'SEGMENTS': 10,
40                                'DISSOLVE': True,
41                                'END_CAP_STYLE': 0,
42                                'JOIN_STYLE': 0,
43                                'MITER_LIMIT': 10
44                                },
45                               is_child_algorithm=True,
46                               context=context,
47                               feedback=feedback)
48    if feedback.isCanceled():
49        return {}
50    rasterized_result = processing.run('qgis:rasterize',
51                               {'LAYER': buffer_result['OUTPUT'],
52                                'EXTENT': buffer_result['OUTPUT'],
53                                'MAP_UNITS_PER_PIXEL': rastercellsize,
54                                'OUTPUT': parameters['OUTPUT']
55                               },
56                               is_child_algorithm=True, context=context,
57                               feedback=feedback)
58    if feedback.isCanceled():
59        return {}
60    return {'OUTPUT': rasterized_result['OUTPUT'],
61            'BUFFER_OUTPUT': buffer_result['OUTPUT'],
62            'NUMBEROFFEATURES': numfeatures}

Som du kan se handlar det om två algoritmer (’native:buffer’ och ’qgis:rasterize’). Den sista (’qgis:rasterize’) skapar ett rasterlager från det buffertlager som genererades av den första (’native:buffer’).

Den del av koden där denna bearbetning sker är inte svår att förstå om du har läst föregående kapitel. De första raderna kräver dock lite extra förklaring. De innehåller den information som behövs för att omvandla din kod till en algoritm som kan köras från någon av GUI-komponenterna, t.ex. verktygslådan eller modelldesignern.

Dessa rader är alla anrop till dekoratorfunktionerna @alg som hjälper till att förenkla kodningen av algoritmen.

  • Dekoratorn @alg används för att definiera algoritmens namn och plats i verktygslådan.

  • Dekoratorn @alg.input används för att definiera algoritmens ingångar.

  • Dekoratorn @alg.output används för att definiera algoritmens utdata.

För befintliga parametrar och deras motsvarighet, läs Inmatnings- och utmatningstyper för bearbetningsalgoritmer.

23.9.3. Utmatning av algoritm för överlämning

När du deklarerar en utdata som representerar ett lager (raster eller vektor) kommer algoritmen att försöka lägga till det i QGIS när den är klar.

  • Utmatning av rasterskikt: QgsProcessingParameterRasterDestination / alg.RASTER_LAYER_DEST.

  • Vektorskiktets utgång: QgsProcessingParameterVectorDestination / alg.VECTOR_LAYER_DEST.

Så även om metoden processing.run() inte lägger till de lager som skapas i användarens aktuella projekt, kommer de två utdatalagren (buffert och rasterbuffert) att laddas, eftersom de sparas till de destinationer som användaren anger (eller till tillfälliga destinationer om användaren inte anger några destinationer).

Om ett lager skapas som resultat av en algoritm bör det deklareras som sådant. Annars kommer du inte att kunna använda algoritmen på rätt sätt i modelleraren, eftersom det som deklareras inte kommer att stämma överens med vad algoritmen verkligen skapar.

Du kan returnera strängar, tal och annat genom att ange dem i resultatordlistan (som visas för ”NUMBEROFFEATURES”), men de ska alltid uttryckligen definieras som utdata från din algoritm. Vi uppmuntrar algoritmer att ge ut så många användbara värden som möjligt, eftersom dessa kan vara värdefulla för användning i senare algoritmer när din algoritm används som en del av en modell.

23.9.4. Kommunicera med användaren

Om din algoritm tar lång tid att bearbeta är det en bra idé att informera användaren om framstegen. Du kan använda feedback (QgsProcessingFeedback) för detta.

Förloppstexten och förloppsfältet kan uppdateras med två metoder: setProgressText(text) och setProgress(percent).

Du kan ge mer information genom att använda pushCommandInfo(text), pushDebugInfo(text), pushInfo(text) och reportError(text).

Om ditt skript har ett problem är det korrekta sättet att hantera det att skapa ett QgsProcessingException. Du kan skicka ett meddelande som ett argument till konstruktören för undantaget. Processing tar hand om hanteringen och kommunikationen med användaren, beroende på varifrån algoritmen körs (verktygslåda, modellerare, Python-konsol, …)

23.9.5. Dokumentera dina skript

Du kan dokumentera dina skript genom att överbelasta metoderna helpString() och helpUrl() i QgsProcessingAlgorithm.

23.9.6. Flaggor

Du kan åsidosätta metoden flags() i QgsProcessingAlgorithm för att berätta mer för QGIS om din algoritm. Du kan t.ex. tala om för QGIS att skriptet ska döljas för modelleraren, att det kan avbrytas, att det inte är trådsäkert och mycket mer.

Tips

Som standard kör Processing algoritmer i en separat tråd för att hålla QGIS responsivt medan bearbetningsuppgiften körs. Om din algoritm kraschar regelbundet använder du förmodligen API-anrop som inte är säkra att göra i en bakgrundstråd. Försök att returnera flaggan QgsProcessingAlgorithm.FlagNoThreading från din algoritms flags()-metod för att tvinga Processing att köra din algoritm i huvudtråden istället.

23.9.7. Bästa praxis för att skriva skriptalgoritmer

Här är en snabb sammanfattning av idéer att tänka på när du skapar dina skriptalgoritmer och, särskilt, om du vill dela dem med andra QGIS-användare. Genom att följa dessa enkla regler säkerställs enhetlighet mellan de olika bearbetningselementen, t.ex. verktygslådan, modelleraren eller gränssnittet för batchbearbetning.

  • Ladda inte resulterande lager. Låt Processing hantera dina resultat och ladda dina lager om det behövs.

  • Deklarera alltid de utdata som din algoritm skapar.

  • Visa inte meddelanderutor eller använd något GUI-element från skriptet. Om du vill kommunicera med användaren använder du metoderna i feedback-objektet (QgsProcessingFeedback) eller kastar en QgsProcessingException.

Det finns redan många bearbetningsalgoritmer tillgängliga i QGIS. Du kan hitta kod på QGIS repo.