Guía para desarrollar plugins de QGIS

Roberer

Por Roberto Jiménez

Geospatial & GIS Analyst

Índice


Los plugins o complementos son extensiones que extienden la funcionalidad de QGIS más allá de las posibilidades que trae por defecto. De hecho, una de las cuestiones más importantes acerca de QGIS y su concepción como Sistema de Información Geográfica libre es precisamente que cualquiera con los conocimientos apropiados contribuya a aumentar sus funcionalidades a través de estos plugins.

QGIS cuenta con un completo repositorio de plugins al cual podemos acceder directamente desde el propio programa de escritorio, realizar búsquedas e instalarlos en un momento, así como compartir los que hayamos credo nosotros.


Repositorio Plugins QGIS

A diferencia de las herramientas de geoproceso, los plugins otorgan una mayor libertad para personalizar la ventana gráfica de la herramienta o la estructura de los algoritmos que queremos que se ejecuten. Hay de todo tipo: algunos funcionan prácticamente como herramientas de geoproceso, pero otros añaden maravillosas funcionalidades como la de mostrar nuestro lienzo de QGIS en GoogleEarth o tener a mano un catálogo de mapas base.

Para crear nuestros plugins necesitaremos tener listo lo siguiente:

– Instalación de QGIS con OSGeo

La instalación de QGIS a través del paquete de OSGeo (OSGeo4W para Windows) es necesaria para la creación de plugins puesto que incluye la consola Shell necesaria para compilar el plugin y QtDesigner.

Podéis descargarlo desde aquí

– IDE Python

Para programar en Python es recomendable en muchas ocasiones usar una IDE como Pycharm o VSCode para trabajar cómodamente, aunque también puede hacerse a través del editor de texto de la consola de Python de QGIS.

Plugin Builder 3

Este plugin nos permite crear automáticamente toda la estructura de archivos básica necesaria para construir el plugin. Bastará con instalarlo a través del administrador de complementos de QGIS e iniciarlo desde el propio QGIS para comenzar a crear el plugin.

– Plugin Reloader

Con este plugin podremos actualizar nuestro plugin para ver en tiempo real los cambios que hagamos durante su creación y probar si las características que implementamos dan los resultados esperados.

– QtDesigner

Este programa servirá para construir la salida gráfica de nuestro plugin a través de un lienzo en el que podremos colocar distintos objetos y enlazarlos con los objetos de nuestro código de Python.

QtDesigner puede descargarse o venir integrado con la instalación de QGIS a través del paquete de OSGeo4W. ¿No parece de QGIS verdad? Ocurre que QGIS está desarrollado en base a Qt, un framework que provee herramientas de desarrollo que también es utilizado por otras aplicaciones informáticas, y entre estas herramientas se encuentra QtDesigner.

Pasos a seguir

Vamos a crear un plugin que realice un Multiparte a partes sencillas a todas las capas vectoriales que se encuentren en una carpeta, dado que la herramienta de procesamiento que proporciona QGIS solo permite aplicarlo a una capa cada vez que se ejecuta.


Multipartes QGIS

Este es un procedimiento básico en el control de calidad de datos geográficos y se le conoce en inglés como Exploding Multipart Polygons. A nuestro nuevo plugin lo llamaremos MultiExploding.

Para entender más a fondo lo que expongo a continuación recomiendo echarle un vistazo a los otros posts que tengo sobre Python y QGIS:

1 – Crear la estructura base del plugin con Plugin Builder 3

El primer paso será instalar Plugin Builder 3 en QGIS, activarlo e iniciarlo. Una vez hecho esto, se abrirá la siguiente ventana:


Plugin Builder QGIS

Debemos rellenar todos los datos que pide. Como podéis observar, se sugieren ejemplos para introducir correctamente los nombres de la nueva clase de Python que se va a crear, el módulo etc.


Plugin Builder QGIS

El siguiente paso será rellanar el About, que es la descripción detallada de lo que hace el plugin que aparecerá en el repositorio oficial en caso de que lo compartamos.


Plugin Builder QGIS

A continuación tendremos que escoger una plantilla para nuestro plugin, el menú en el que aparecerá el plugin cuando lo añadamos a QGIS y un texto descriptivo que aparecerá en dicho menú:


Plugin Builder QGIS

El siguiente paso es para pedir que se creen archivos adicionales que pueden ser de utilidad. Por defecto se encuentran marcados:


Plugin Builder QGIS

Por último, podemos añadir distintos enlaces para compartir el repositorio de código del plugin, la página web o las etiquetas que mejor describan nuestro plugin para que sea encontrado fácilmente. Además, podemos marcar el plugin como experimental para advertir que no está completo y/o puede generar resultados no deseados:


Plugin Builder QGIS

Una vez hecho todo esto, guardaremos el plugin en la ruta que queramos. En el directorio escogido se creará una nueva carpeta con el nombre del plugin en la que se encontrarán todos los archivos del plugin:


Plugin Builder QGIS

Si todo ha salido bien, se mostrará el siguiente mensaje:


Plugin Builder QGIS

2 – Compilar manualmente el nuevo plugin

Es muy probable que al crear el plugin haya saltado el siguiente error:


Error compilar plugin QGIS

Significa que no pudo compilarse (en resumen, traducirse al lenguaje del PC para que funcione) y que tendremos que hacerlo manualmente, pero es muy sencillo. Debemos abrir la terminal – Shell – de OSGEO e introducir los siguientes comandos:

			cd C:\ruta_carpeta_plugin
			call qt5_env.bat
			py3_env.bat
			pyrcc5 –o resources.py resources.qrc
			

Si todo ha salido bien, el proceso completo sería el siguiente:


Compilar plugin QGIS

La estructura de archivos dentro del plugin quedaría de la siguiente manera:


Estructura archivos plugin QGIS

3 – Instalación del nuevo plugin en QGIS

Con el plugin ya compilado, tan solo tendremos que copiarlo a la carpeta de plugins de QGIS y, si tenemos QGIS abierto, reiniciarlo.

En Windows, la carpeta de plugins de QGIS suele encontrarse en

C:\Users\Usuario\AppData\Roaming\QGIS\QGIS3\profiles\default\python\plugins

Una vez abierto de nuevo QGIS, recordad activar el plugin desde el administrador de complementos.

4 – Diseñar la interfaz

Para diseñar la interfaz tendremos que abrir QtDesigner y abrir con él un archivo que encontraremos dentro de la carpeta del plugin llamado nombreplugin_dialog_base.ui (en mi caso Multi_Exploding_dialog_base.ui) que es el que contiene los datos relativos a la interfaz de usuario del plugin.


Qt Designer QGIS

La ventana que se abre dentro de Qt Designer será el lienzo en el que montaremos la interfaz de usuario de nuestro plugin, es decir, los botones para seleccionar la carpeta de entrada, los parámetros con los que queremos que se ejecute la herramienta, el nombre de la carpeta de salida…

La plantilla que hayamos seleccionado en la creación del plugin con Plugin Builder 3 determinará el aspecto que presentará nuestra interfaz. Al haber seleccionado Tool button with dialog obtendremos la clásica ventana con los botones de aceptar para ejecutar la herramienta y cancelar para cerrarla.

Lo siguiente que tendremos que hacer será arrastrar los widgets que necesitemos desde las cajas de la izquierda a nuestro lienzo. Para nuestro plugin necesitaremos:


Plugin QGIS UI

Podemos cambiar tanto la posición de los widgets como su tamaño o la etiqueta que muestran:


Qt designer UI

En la parte derecha de Qt Designer nos aparecerán las propiedades de cada uno de estos widgets y la posibilidad de modificarlas (algunas de ellas). Las más básicas que necesitamos conocer para programar el plugin son:


Qt designer QGIS

En la documentación de las clases de los widgets que hemos incorporado podremos encontrar los métodos que nos permitirán jugar con ellos:

Una vez guardamos los cambios, si vamos a QGIS y recargamos el plugin usando Plugin Reloader (1) al volver a hacer click en nuestro plugin (2) se deberían ver los cambios hechos en la interfaz:


QGIS plugin reloader

5 – Programar el script

El último paso consiste en programar el script para darle funcionalidad a la interfaz y hacer que se ejecute el algoritmo de Multiparte a partes sencillas para todas las entidades de todos los archivos vectoriales de la carpeta de entrada, y que sus resultados se almacenen en la carpeta con el nombre indicado (que se localizará dentro de la carpeta de entrada)

Para ello, tendremos que abrir con un IDE (VSCode en las capturas) el archivo nombreplugin.py que encontraremos en la carpeta del plugin. En mi caso, dicho archivo es Multi_Exploding.py.


QGIS Plugin Python code

Al abrirlo veremos que la estructura básica del script se encuentra ya creada gracias a Model Builder 3. La estructura es muy similar a la que vemos en las herramientas de geoproceso:

  1. Codificación del sistema y descripción del plugin
  2. Importación de módulos
  3. Creación de la nueva clase MultiExploding

La mayor diferencia respecto a las herramientas de geoproceso va a estar en cómo definiremos la nueva clase. En esta ocasión no usaremos la herencia para basar nuestro plugin en otra clase, y tampoco tendremos que definir los parámetros justo al comienzo de la clase.

Nos encontraremos los siguientes métodos ya definidos para nuestra nueva clase:

def __init__(self, iface)Constructor de clase
def tr(self, message)Traductor de la interfaz
def add_action(self, iconpath…) Configuración del icono en la barra de herramientas de QGIS
def initGui(self)Integra el plugin en la interfaz de usuario de QGIS
def unload(self)Configura el proceso de eliminación del plugin de la interfaz de QGIS
def run(self)Código que se ejecutará una vez se abra el plugin

Por lo general, no será necesario editar ninguno de estos métodos salvo run, que es el que ejecutará las funciones que implementemos al script.

Lo que sí tendremos que hacer es crear un nuevo método para dar funcionalidad a la interfaz de nuestro plugin, como veremos en el siguiente punto.

Self es el nombre que habitualmente se le da al objeto que representa a la propia clase. Iface es el nombre obligatorio para referenciar la interfaz de QGIS. Ambos quedaron definidos por Plugin Builder 3 en el método __init__ al comienzo de la clase.

5.1 – Programar el Push Button y el Line Edit para obtener la ruta de entrada

Lo primero que debemos hacer es definir un nuevo método llamado select_input_folder para nuestra clase (self) por el cual, cuando se invoque, se acceda al sistema de archivos del PC y se copie nuestro Line Edit (nombrado por defecto lineEdit) la ruta de la carpeta seleccionada:

			def select_input_folder(self):
					inputfolder = QFileDialog.getExistingDirectory(self.dlg,"Choose input directory", 
							"",QFileDialog.DontResolveSymlinks)
					self.dlg.lineEdit.setText(inputfolder)
			

Dicho método lo hemos colocado justo después del método unload que ya venía definido. Lo que hace es guardar en el objeto inputfolder la ruta seleccionada y lo inserta con .setText() en el Line Edit.

Para que al pulsar el botón accedamos al sistema de archivos deberemos añadir la línea resaltada en la sentencia if que hay al comienzo del bloque run:

			def run(self):
					"""Run method that performs all the real work"""

					# Create the dialog with elements (after translation) and keep reference
					# Only create GUI ONCE in callback, so that it will only load when the plugin is started
					if self.first_start == True:
						self.first_start = False
						self.dlg = MultiExplodingDialog()
						self.dlg.pushButton.clicked.connect(self.select_input_folder)
			

Dentro de esta sentencia if añadiremos todas aquellas conexiones de que queremos que se establezcan al abrir el plugin dentro de QGIS. En este caso, lo que hacemos es indicar con los métodos .clicked.connect() que cuando hagamos click a nuestro botón de nombre pushButton se ejecute el método .select_input_folder que hemos definido anteriormente.

El último paso para que funcione será importar la clase QFileDialog al comienzo de nuestro script para poder navegar por el sistema de archivos:

			from qgis.PyQt.QtWidgets import QAction, QFileDialog
			

Guardamos los cambios, recargamos el plugin con Plugin Reloader y probamos si funciona:


Load layer QGIS plugin

5.2 – Limpiar los Line Edit

Tanto el primer Line Edit (lineEdit) como el segundo (lineEdit_2) por defecto almacenarán el texto que contengan aunque cerremos el plugin y volvamos a abrirlo. Para evitar esto, añadiremos las siguientes líneas dentro de run (después de la sentencia if que ya modificamos previamente) para que ambos Line Edits se limpien al iniciar el plugin:

			self.dlg.lineEdit.setText('')
			self.dlg.lineEdit_2.setText('')
			

Es tan sencillo como aplicar el método .setText() a los Line Edits y darles un texto vacío.

5.3 – Programar la ejecución del algoritmo

Lo único que nos quedaría es indicar qué ocurre cuando pulsemos el botón de ACEPTAR de nuestro plugin. Básicamente, se tomará el texto de nuestros Line Edits como variables de entrada y salida del algoritmo Multiparte a partes sencillas que queremos aplicar a todos los shapefiles de la carpeta seleccionada.

Lo primero será importar los siguientes módulos:

			from qgis.core import QgsVectorLayer, QgsField
			from qgis.utils import iface
			import processing
			import os
			import shutil
			

A continuación, nos iremos a la sentencia if result que se encuentra al final del script…

			if result:
						# Do something useful here - delete the line containing pass and
						# substitute with your code.
						pass
			

… y tal como se indica sustituiremos su contenido por todo aquello que queramos que suceda al pulsar ACEPTAR (o ejecutar) Lo tenéis a detallado a continuación:

					if result:

						## objetos con las rutas de entrada y salida a partir de los Line Edits
						entrada = self.dlg.lineEdit.text() + '/'
						nombre_salida = self.dlg.lineEdit_2.text()
						salida = entrada + nombre_salida + '/'

						## filtrar los ficheros de la carpeta de entrada 
						listaFicheros = os.listdir(entrada)
						ext = '.shp'
						listaShp = []

						for i in listaFicheros:
							if os.path.splitext(i)[1] == ext:
								listaShp.append(i)

						## comprobar si existe la capa de salida
						if os.path.exists(salida):
							shutil.rmtree(salida)
							os.mkdir(salida)
						else:
							os.mkdir(salida)

						## por cada capa de nuestra lista de capas
						for f in listaShp:

							## obtener su nombre sin la extension
							nombre = os.path.splitext(f)[0]

							## cargar el archivo como capa vectorial
							capa = QgsVectorLayer(entrada + f, nombre, 'ogr')

							## definir la ruta de salida completa, incluyendo el nombre y la extension
							output = salida + nombre + '_exploded' + '.shp'

							## generar una barra de progreso cuando se ejecute el plugin
							iface.messageBar().pushInfo(u'Exploding features', output)

							## definir en un diccionario los parametros del algoritmo
							params = {'INPUT': capa, 'OUTPUT': output}

							## ejecutar el algoritmo
							processing.run('qgis:multiparttosingleparts', params)

							## nuevo campo id autoincrementable
							capa2 = QgsVectorLayer(output,nombre,'ogr')
							capa2.startEditing()

							capa2.dataProvider().addAttributes([QgsField('newID', QVariant.Int)])
							capa2.updateFields()
							
							entidades = capa2.getFeatures()
							contador = 0

							for entidad in entidades:
								entidad['newID'] = contador
								capa2.updateFeature(entidad)
								contador += 1
								
							capa2.commitChanges()

							## cargar la capa resultante al proyecto
							iface.addVectorLayer(output, nombre + '_exploded', 'ogr')
						
			

Resultados

Pondremos a prueba el plugin aplicándolo a dos capas que se encuentran en una carpeta de mi escritorio. Cada una contiene 3 polígonos distintos pero dos de ellos pertenecen a la misma entidad, es decir, son entidades multiparte:


capas test QGIS

Si abrimos sus atributos comprobamos que, efectivamente, cada una tiene solo 2 entidades y además solo cuentan con un único campo id:


capas QGIS prueba

A continuación abrimos el nuevo plugin y seleccionamos la carpeta en la que se encuentran estas dos capas. Además, indicamos que la carpeta en la que se guardarán los resultados (que se ubicará dentro de la carpeta de entrada) se llamará resultados:


multi exploding plugin QGIS

Vemos que la carpeta de resultados se creó correctamente y las capas creadas se añadieron automáticamente a QGIS:


resultados QGIS

Si a continuación abrimos la tabla de atributos, vemos que se creó correctamente el nuevo campo de identificación newID necesario para identificar de forma única cada entidad, puesto que al dividirse las nuevas entidades conservan los atributos de la entidad original:


tabla atributos QGIS

Gracias a este nuevo campo podemos editar la simbología de las capas nuevas para que cada una esté representada por un color distinto:


simbología QGIS

Tenéis al plugin al completo en GitHub.