Belangrijk
Vertalen is een inspanning van de gemeenschap waaraan u deel kunt nemen. Deze pagina is momenteel voor 100.00% vertaald.
17. Een plug-in voor Processing schrijven
Afhankelijk van het soort plug-in dat u gaat ontwikkelen, zou het misschien een betere optie zijn om de functionaliteit ervan toe te voegen als een algoritme voor Processing (of een set daarvan). Dat zou tot een betere integratie in QGIS leiden, aanvullende functionaliteit (omdat het kan worden uitgevoerd in de componenten van Processing, zoals Modelontwerper of de Processing interface voor batchverwerking), en een snellere ontwikkelingstijd (omdat Processing een groot deel van het werk zal overnemen).
U zou, om deze algoritmes te kunnen distribueren, een nieuwe plug-in moeten maken die ze toevoegt aan de Toolbox van Processing. De plug-in zou een provider voor algoritmes moeten bevatten, die moet worden geregistreerd als de plug-in wordt geïnstantieerd.
17.1. Maken vanaf niets
U kunt de volgende stappen volgen, met behulp van de Plugin Builder, om vanaf nul een plug-in te maken die een provider voor algoritmes bevat:
Installeer de plug-in Plugin Builder
Maak een nieuwe plug-in met de Plugin Builder. Wanneer de Plugin Builder u vraagt naar het te gebruiken sjabloon, selecteer dan “Processing provider”.
De gemaakte plug-in bevat een provider met één enkel algoritme. Zowel het bestand voor de provider als het bestand voor het algoritme zijn volledig voorzien van commentaar en bevatten informatie over hoe de provider aan te passen en aanvullende algoritmes toe te voegen. Verwijs daarnaar voor meer meer informatie.
17.2. Een plug-in bijwerken
U dient nog enige code toe te voegen als u uw bestaande plug-in wilt toevoegen aan Processing.
In uw bestand
metadata.txt
moet u een variabele toevoegen:hasProcessingProvider=yes
In het bestand van Python waar uw plug-in wordt ingesteld met de methode
initGui
, dient u enkele regels als volgt aan te passen:1from qgis.core import QgsApplication 2from .processing_provider.provider import Provider 3 4class YourPluginName: 5 6 def __init__(self): 7 self.provider = None 8 9 def initProcessing(self): 10 self.provider = Provider() 11 QgsApplication.processingRegistry().addProvider(self.provider) 12 13 def initGui(self): 14 self.initProcessing() 15 16 def unload(self): 17 QgsApplication.processingRegistry().removeProvider(self.provider)
U kunt een map
processing_provider
maken met daarin drie bestanden:__init__.py
waar niets in staat. Dit is noodzakelijk om een geldig pakket voor Python te maken.provider.py
dat de provider voor Processing zal maken en uw algoritmes zal laten zien.1from qgis.core import QgsProcessingProvider 2from qgis.PyQt.QtGui import QIcon 3 4from .example_processing_algorithm import ExampleProcessingAlgorithm 5 6class Provider(QgsProcessingProvider): 7 8 """ The provider of our plugin. """ 9 10 def loadAlgorithms(self): 11 """ Load each algorithm into the current provider. """ 12 self.addAlgorithm(ExampleProcessingAlgorithm()) 13 # add additional algorithms here 14 # self.addAlgorithm(MyOtherAlgorithm()) 15 16 def id(self) -> str: 17 """The ID of your plugin, used for identifying the provider. 18 19 This string should be a unique, short, character only string, 20 eg "qgis" or "gdal". This string should not be localised. 21 """ 22 return 'yourplugin' 23 24 def name(self) -> str: 25 """The human friendly name of your plugin in Processing. 26 27 This string should be as short as possible (e.g. "Lastools", not 28 "Lastools version 1.0.1 64-bit") and localised. 29 """ 30 return self.tr('Your plugin') 31 32 def icon(self) -> QIcon: 33 """Should return a QIcon which is used for your provider inside 34 the Processing toolbox. 35 """ 36 return QgsProcessingProvider.icon(self)
example_processing_algorithm.py
wat het voorbeeldbestand voor een algoritme bevat. Kopieer/plak de inhoud van het bestand script template en werk dat naar behoefte bij.
U zou een boom moeten hebben die er uitziet zoals deze:
1└── your_plugin_root_folder 2 ├── __init__.py 3 ├── LICENSE 4 ├── metadata.txt 5 └── processing_provider 6 ├── example_processing_algorithm.py 7 ├── __init__.py 8 └── provider.py
Nu kunt u uw plug-in opnieuw laden in QGIS en u zou uw voorbeeldscript moeten zien in de Toolbox en Modelontwerper van Processing.
17.3. Aangepaste algoritmes voor Processing implementeren
17.3.1. Een aangepast algoritme maken
Hier is een eenvoudig voorbeeld van een aangepast buffer-algoritme:
1from qgis.core import (
2 QgsProcessingAlgorithm,
3 QgsProcessingParameterFeatureSource,
4 QgsProcessingParameterNumber,
5 QgsProcessingParameterFeatureSink,
6 QgsFeatureSink,
7)
8
9class BufferAlgorithm(QgsProcessingAlgorithm):
10
11 INPUT = 'INPUT'
12 DISTANCE = 'DISTANCE'
13 OUTPUT = 'OUTPUT'
14
15 def initAlgorithm(self, config=None):
16 self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, 'Input layer'))
17 self.addParameter(QgsProcessingParameterNumber(self.DISTANCE, 'Buffer distance', defaultValue=100.0))
18 self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, 'Output layer'))
19
20 def processAlgorithm(self, parameters, context, feedback):
21 source = self.parameterAsSource(parameters, self.INPUT, context)
22 distance = self.parameterAsDouble(parameters, self.DISTANCE, context)
23 (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
24 source.fields(), source.wkbType(), source.sourceCrs())
25
26 for f in source.getFeatures():
27 f.setGeometry(f.geometry().buffer(distance, 5))
28 sink.addFeature(f, QgsFeatureSink.FastInsert)
29
30 return {self.OUTPUT: dest_id}
31
32 def name(self):
33 return 'buffer'
34
35 def displayName(self):
36 return 'Buffer Features'
37
38 def group(self):
39 return 'Examples'
40
41 def groupId(self):
42 return 'examples'
43
44 def createInstance(self):
45 return BufferAlgorithm()
17.3.2. Het dialoogvenster van het algoritme aanpassen
Aangepaste dialoogvensters zijn speciaal nuttig bij het werken met geneste of dynamische invoeren, als parameters afhankelijk zijn van externe databronnen, zoals API’s (bijv. dynamisch gevulde keuzelijsten), of als u geavanceerde validatie en aangepast gedrag voor de lay-out nodig hebt, dat niet wordt ondersteund door het standaard dialoogvenster van Processing. Voor het overschrijven van de standaard gebruikersinterface (bijv. voor complexe typen parameters of dynamische logica), subclass QgsProcessingAlgorithmDialogBase
. Voor het renderen van uw aangepaste gebruikersinterface in het standaard dialoogvenster van Processing moet u self.setMainWidget(panel)
aanroepen, waar panel
een QgsPanelWidget
is die uw aangepaste lay-out bevat. Dat zorgt ervoor dat uw interface correct wordt weergegeven en op de juiste wijze interacteert met het framewerk van Processing.
Hier is een voorbeeld dat het beheren van signalen met QTimer integreert voor vertraagde invoer:
1from qgis.PyQt.QtCore import Qt, QT_VERSION_STR, QTimer
2from qgis.core import (
3 QgsProcessingAlgorithm,
4 QgsProcessingContext,
5 QgsProcessingFeedback,
6 Qgis,
7)
8from qgis.PyQt.QtWidgets import QWidget, QVBoxLayout, QLineEdit
9from qgis import gui, processing
10from datetime import datetime
11from typing import Dict, Optional
12from osgeo import gdal
13
14class CustomAlgorithmDialog(gui.QgsProcessingAlgorithmDialogBase):
15 def __init__(
16 self,
17 algorithm: QgsProcessingAlgorithm,
18 parent: Optional[QWidget] = None,
19 title: Optional[str] = None,
20 ):
21 super().__init__(
22 parent,
23 flags=Qt.WindowFlags(),
24 mode=gui.QgsProcessingAlgorithmDialogBase.DialogMode.Single,
25 )
26 self.context = QgsProcessingContext()
27 self.setAlgorithm(algorithm)
28 self.setModal(True)
29 self.setWindowTitle(title or algorithm.displayName())
30
31 self.panel = gui.QgsPanelWidget()
32 layout = self.buildDialog()
33 self.panel.setLayout(layout)
34 self.setMainWidget(self.panel)
35
36 self.cancelButton().clicked.connect(self.reject)
37
38 def buildDialog(self) -> QVBoxLayout:
39 layout = QVBoxLayout()
40
41 self.input = QLineEdit()
42
43 # Set up a debounced signal using QTimer
44 self._update_timer = QTimer(self, singleShot=True)
45 self._update_timer.timeout.connect(self._on_collection_id_ready)
46 self.input.textChanged.connect(self._on_collection_id_changed)
47
48 layout.addWidget(self.input)
49
50 return layout
51
52 def _on_collection_id_changed(self):
53 self._update_timer.start(500) # Debounce input
54
55 def _on_collection_id_ready(self):
56 self.pushInfo("Fetching metadata for collection ID…")
57
58 def getParameters(self) -> Dict:
59 try:
60 return {'DISTANCE': float(self.input.text())}
61 except ValueError:
62 raise ValueError("Invalid buffer distance")
63
64 def processingContext(self):
65 return self.context
66
67 def createFeedback(self):
68 return QgsProcessingFeedback()
69
70 def runAlgorithm(self):
71 context = self.processingContext()
72 feedback = self.createFeedback()
73 params = self.getParameters()
74
75 self.pushDebugInfo(f"QGIS version: {Qgis.QGIS_VERSION}")
76 self.pushDebugInfo(f"QGIS code revision: {Qgis.QGIS_DEV_VERSION}")
77 self.pushDebugInfo(f"Qt version: {QT_VERSION_STR}")
78 self.pushDebugInfo(f"GDAL version: {gdal.VersionInfo('--version')}")
79 self.pushCommandInfo(f"Algorithm started at: {datetime.now().isoformat(timespec='seconds')}")
80 self.pushCommandInfo(f"Algorithm '{self.algorithm().displayName()}' starting…")
81 self.pushCommandInfo("Input parameters:")
82 for k, v in params.items():
83 self.pushCommandInfo(f" {k}: {v}")
84
85 results = processing.run(self.algorithm(), params, context=context, feedback=feedback)
86 self.setResults(results)
87 self.showLog()
Het aangepaste dialoogvenster starten voor een opgegeven algoritme, instantieer eenvoudigweg CustomAlgorithmDialog
met de instantie van uw algoritme en roep exec()
aan:
dlg = CustomAlgorithmDialog(BufferAlgorithm())
dlg.exec()
17.3.3. Qt-signalen beheren
Bij het bouwen van reactieve dialoogvensters, beheer zorgvuldig de verbindingen van signalen. Bovenstaand patroon gebruikt een QTimer om invoer vanuit het tekstveld te vertragen, wat snel herhaalde aanroepen voorkomt. Dat is speciaal nuttig bij het ophalen van metadata of bijwerken van elementen van de gebruikersinterface, gebaseerd op invoer van de gebruiker. Verbindt signalen altijd maar een keer (gewoonlijk in __init__
) en gebruik singleShot=True
om ervoor te zorgen dat het slot slechts eenmaal wordt geactiveerd na een vertraging.