Importante
La traducción es un esfuerzo comunitario puede unirse. Esta página está actualmente traducida en |progreso de traducción|.
17. Escribir nuevos complementos de procesamiento
Dependiendo del tipo de complemento que vaya a desarrollar, podría ser una mejor opción agregar su funcionalidad como un algoritmo de procesamiento (o un conjunto de ellos). Eso proporcionaría una mejor integración dentro de QGIS, funcionalidad adicional (ya que se puede ejecutar en los componentes de Processing, como el modelador o la interfaz de procesamiento por lotes), y un tiempo de desarrollo más rápido (ya que Processing tomará una gran parte del trabajo).
Para distribuir esos algoritmos, debe crear un nuevo complemento que los agregue a Caja de Herramientas de Procesos. El complemento debe contener un proveedor de algoritmos, que debe registrarse cuando se crea una instancia del complemento.
17.1. Creando desde cero
Para crear un complemento desde cero que contenga un proveedor de algoritmos, puede seguir estos pasos utilizando el Generador de complementos:
Instala el complemento Plugin Builder
Crea un nuevo complemento, usando el Plugin Builder. En el cuadro de diálogo del Plugin Builder, selecciona «Processing provider».
El complemento creado contiene un proveedor con un solo algoritmo. Tanto el archivo del proveedor como el archivo del algoritmo están completamente comentados y contienen información sobre cómo modificar el proveedor y agregar algoritmos adicionales. Consúltelos para obtener más información.
17.2. Actuializar un complemento
Si quiere añadir su complemento existente a Procesos, necesitará añadir algo de código.
En su archivo
metadata.txt
, necesitará añadir una variable:hasProcessingProvider=yes
En el archivo Python donde tu complemento está instalado con el método
initGui
, necesitas adaptar algunas líneas como esta: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)
Puedes crear una carpeta
processing_provider
con tres archivos en ella:__init__.py
sin nada en él. Esto es necesario para crear un paquete Python válido.provider.py
que creará el proveedor de procesamiento y expondrá sus algoritmos.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
el cuál contiene el archivo de ejemplo de algoritmo. Copiar/pegar el contenido de la fuente archivo de plantilla de script y actualizalo de acuerdo a tus necesidades.
Debería tener un árbol similar a este:
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
Ahora puede volver a cargar su complemento en QGIS y debería ver su script de ejemplo en la caja de herramientas de procesamiento y el modelador.
17.3. Implementación de algoritmos de procesamiento personalizados
17.3.1. Creación de un algoritmo personalizado
Aquí hay un ejemplo sencillo de un algoritmo de búfer personalizado:
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. Personalización del cuadro de diálogo del algoritmo
Los cuadros de diálogo personalizados son especialmente útiles cuando se trabaja con entradas anidadas o dinámicas, cuando los parámetros dependen de fuentes de datos externas como API (por ejemplo, menús desplegables rellenados dinámicamente) o cuando se necesita una validación avanzada y un comportamiento de diseño personalizado que no es compatible con el cuadro de diálogo de procesamiento predeterminado. Para anular la interfaz de usuario predeterminada (por ejemplo, para tipos de parámetros complejos o lógica dinámica), cree una subclase de QgsProcessingAlgorithmDialogBase
. Para renderizar su interfaz de usuario personalizada en la ventana del cuadro de diálogo Procesamiento estándar, debe llamar a self.setMainWidget(panel)
, donde panel
es un QgsPanelWidget
que contiene su diseño personalizado. Esto garantiza que su interfaz se muestre correctamente e interactúe adecuadamente con el marco de trabajo de Procesamiento.
Aquí hay un ejemplo que integra la gestión de señales utilizando QTimer para entradas sin rebote:
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()
Para iniciar el cuadro de diálogo personalizado para un algoritmo determinado, simplemente instancie CustomAlgorithmDialog
con su instancia de algoritmo y llame a exec()
:
dlg = CustomAlgorithmDialog(BufferAlgorithm())
dlg.exec()
17.3.3. Gestión de señales Qt
Al crear diálogos reactivos, gestione cuidadosamente las conexiones de señales. El patrón anterior utiliza un QTimer para eliminar el rebote de la entrada del campo de texto, lo que evita llamadas repetidas rápidas. Esto resulta especialmente útil al recuperar metadatos o actualizar elementos de la interfaz de usuario basados en la entrada del usuario. Conecte siempre las señales una vez (normalmente en __init__
) y utilice singleShot=True
para garantizar que la ranura solo se active una vez tras un retraso.