Desarrollo de Plugins Python

Es posible crear complementos en lenguaje de programación Python. En comparación con complementos clásicos escritos en C++, éstas deberían ser más fácil de escribir, comprender, mantener y distribuir debido a la naturaleza dinámica del lenguaje Python.

Los complementos de Python están listados con complementos C++ en el administrador de complementos de QGIS. Se buscaron en estas rutas:

  • UNIX/Mac: ~/.qgis/python/plugins and (qgis_prefix)/share/qgis/python/plugins
  • Windows: ~/.qgis/python/plugins and (qgis_prefix)/python/plugins

El directorio principal (denotado por el anterior ~) en Windows es generalmente algo como C:\Documents and Settings\(user). Desde que QGIS esta utilizando Python 2.7, los subdirectorios de esta ruta deben contener un archivo __init__.py para ser considerados paquetes de Python que pueden ser importados como complementos.

Nota

Al establecer QGIS_PLUGINPATH a una ruta de directorio existente, se puede añadir la ruta a la lista de rutas donde se han buscado complementos.

Pasos:

  1. Idea: ¿Tiene una idea acerca de lo que quiere hacer con su nuevo complemento de QGIS. ¿Por qué lo hace? ¿Qué problema desea resolver? ¿Existe ya otro complemento para ese problema?

  2. Crear archivos: Crear los archivos descritos a continuación. Un punto inicial (__init__.py). Rellene el Metadato del complemento (metadata.txt) Un cuerpo de complemento python principal (mainplugin.py). Una forma en QT-Designer (form.ui), con su resources.qrc.

  3. Escribir código: Escribir el código dentro del mainplugin.py

  4. Prueba: Cerrar y abrir QGIS e importar el su complemento de nuevo. Comprobar si todo está bien.

  5. Publicar: Publica su complemento en el repositorio de QGIS o hacer su propio repositorio como un “arsenal” de “armas SIG” personales.

Escribir un complemento

Desde la introducción de los complementos de Python en QGIS, una serie de complementos han aparecido - en Plugin Repositories wiki page se pueden encontrar algunos de ellos, puede utilizar su fuente para aprender más acerca de la programación con PyQGIS o averiguar si no está duplicando el esfuerzo de desarrollo. El equipo de QGIS también mantiene un Official python plugin repository. ¿Listo para crear un complemento pero ni idea de qué hacer? Python Plugin Ideas wiki page listas de deseos de la comunidad!

Archivos de complementos

Aquí está la estructura de directorios de nuestro ejemplo de complemento

PYTHON_PLUGINS_PATH/
  MyPlugin/
    __init__.py    --> *required*
    mainPlugin.py  --> *required*
    metadata.txt   --> *required*
    resources.qrc  --> *likely useful*
    resources.py   --> *compiled version, likely useful*
    form.ui        --> *likely useful*
    form.py        --> *compiled version, likely useful*

Cuál es el significado de los archivos:

  • __init__.py = El punto de partida del complemento. Se tiene que tener el método classFactory() y puede tener cualquier otro código de inicialización.

  • mainPlugin.py = El código principal de trabajo del complemento. Contiene toda la información acerca de las acciones del complemento y el código principal.

  • resources.qrc = El documento .xml creado por Qt Designer. Contiene rutas relativas a los recursos de las formas.

  • resources.py = La traducción del archivo .qrc descrito anteriormente para Python.

  • form.ui = La GUI creada por Qt Designer.

  • form.py = La traducción de la form.ui descrito anteriormente para Python.

  • metadata.txt = Required for QGIS >= 1.8.0. Containts general info, version, name and some other metadata used by plugins website and plugin infrastructure. Since QGIS 2.0 the metadata from __init__.py are not accepted anymore and the metadata.txt is required.

Aquí es una manera automatizada en línea de crear los archivos básicos (esqueleto) de un complemento típico de QGIS Python.

También hay un complemento QGIS llamado Plugin Builder que crea la plantilla del complemento desde QGIS y no requiere conexión a Internet. Esta es la opción recomendada, ya que produce 2.0 fuentes compatibles.

Advertencia

Si su plan para actualizar el complemento del Official python plugin repository se debe validar que su complemento siga algunas reglas adicionales, necesarias para complementos Validation

Contenido del complemento

Aquí se puede encontrar información y ejemplos sobre lo que se debe añadir en cada uno de los archivos de la estructura de archivos descrito anteriormente.

Metadato del complemento

En primer lugar, Administrador de complementos necesita recuperar cierta información básica sobre el complemento tales como su nombre, descripción, etc. El archivo metadata.txt es el lugar adecuado para colocar esta información.

Importante

Todos los metadatos deben estar en codificación UTF-8.

Nombre del metadato

Necesario

Notas

nombre

Verdadero

una cadena corta contiene el nombre del complemento

qgisMinimumVersion

Verdadero

notación de la versión mínima de QGIS

qgisMaximumVersion

Falso

notación de la versión máxima de QGIS

descripción

Verdadero

texto corto que describe el complemento, no se permite HTML

acerca de

Falso

texto más largo que describe el complemento en detalles, no se permite HTML

versión

Verdadero

cadena corta con la notación versión punteado

autor

Verdadero

nombre del autor

correo electrónico

Verdadero

email of the author, will not be shown on the web site

registro de cambios

Falso

cadena, puede ser multilínea, no se permite HTML

experimental

Falso

bandera booleana, True o False

obsoleto

Falso

bandera booleana, True or False, se aplica a todo complemento y no solo a la versión actualizada

etiquetas

Falso

lista separada por comas, se permiten espacios dentro de las etiquetas individuales

página principal

Falso

una URL válida que apunte a la página principal de su complemento

repositorio

Falso

una URL válida para el repositorio del código fuente

rastreador

Falso

una URL válida para las entradas e informes de errores

icono

Falso

a file name or a relative path (relative to the base folder of the plugin’s compressed package)

categoría

Falso

uno de Ráster, Vector, Base de Datos y Web

Por defecto, los complementos se colocan en el menú Complementos (veremos en la siguiente sección sobre cómo añadir una entrada de menú para su complemento), pero también pueden ser colocados en los menús Raster, Vector, Database and Web.

Una entrada de metadato correspondiente “categoría” para especificar que existe, por lo que el complemento se puede clasificar en consecuencia. Esta entrada de metadatos se utiliza como consejo para los usuarios y les dice dónde (en qué menú) el complemento se puede encontrar. Los valores permitidos para “categoría” son: vector, ráster, base de datos o web. Por ejemplo, si su complemento estará disponible desde el menú Ráster, añadir esto a metadata.txt

category=Raster

Nota

Si qgisMaximumVersion está vacía, se ajustará automáticamente a la versión principal .99 cuando se actualizan a el Official python plugin repository.

Un ejemplo para este metadata.txt

; the next section is mandatory

[general]
name=HelloWorld
[email protected]
author=Just Me
qgisMinimumVersion=2.0
description=This is an example plugin for greeting the world.
    Multiline is allowed:
    lines starting with spaces belong to the same
    field, in this case to the "description" field.
    HTML formatting is not allowed.
about=This paragraph can contain a detailed description
    of the plugin. Multiline is allowed, HTML is not.
version=version 1.2
; end of mandatory metadata

; start of optional metadata
category=Raster
changelog=The changelog lists the plugin versions
    and their changes as in the example below:
    1.0 - First stable release
    0.9 - All features implemented
    0.8 - First testing release

; Tags are in comma separated value format, spaces are allowed within the
; tag name.
; Tags should be in English language. Please also check for existing tags and
; synonyms before creating a new one.
tags=wkt,raster,hello world

; these metadata can be empty, they will eventually become mandatory.
homepage=http://www.itopen.it
tracker=http://bugs.itopen.it
repository=http://www.itopen.it/repo
icon=icon.png

; experimental flag (applies to the single version)
experimental=True

; deprecated flag (applies to the whole plugin and not only to the uploaded version)
deprecated=False

; if empty, it will be automatically set to major version + .99
qgisMaximumVersion=2.0

__init__.py

El archivo es necesario por el sistema de importación de Python. También, QGIS requiere que este archivo contenga una función classFactory(), que se llama cuando el complemento se carga a QGIS. Recibe referencia a instancia de QgisInterface y debe volver la instancia de la clase de su complemento desde el mainplugin.py — en nuestro caso se llama TestPlugin (ver más abajo). Esta es como __init__.py debe ser.

def classFactory(iface):
  from mainPlugin import TestPlugin
  return TestPlugin(iface)

## any other initialisation needed

mainPlugin.py

Aquí es donde sucede la magia y así es como la magia se ve: (por ejemplo mainPlugin.py)

from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.core import *

# initialize Qt resources from file resources.py
import resources

class TestPlugin:

  def __init__(self, iface):
    # save reference to the QGIS interface
    self.iface = iface

  def initGui(self):
    # create action that will start plugin configuration
    self.action = QAction(QIcon(":/plugins/testplug/icon.png"), "Test plugin", self.iface.mainWindow())
    self.action.setObjectName("testAction")
    self.action.setWhatsThis("Configuration for test plugin")
    self.action.setStatusTip("This is status tip")
    QObject.connect(self.action, SIGNAL("triggered()"), self.run)

    # add toolbar button and menu item
    self.iface.addToolBarIcon(self.action)
    self.iface.addPluginToMenu("&Test plugins", self.action)

    # connect to signal renderComplete which is emitted when canvas
    # rendering is done
    QObject.connect(self.iface.mapCanvas(), SIGNAL("renderComplete(QPainter *)"), self.renderTest)

  def unload(self):
    # remove the plugin menu item and icon
    self.iface.removePluginMenu("&Test plugins", self.action)
    self.iface.removeToolBarIcon(self.action)

    # disconnect form signal of the canvas
    QObject.disconnect(self.iface.mapCanvas(), SIGNAL("renderComplete(QPainter *)"), self.renderTest)

  def run(self):
    # create and show a configuration dialog or something similar
    print "TestPlugin: run called!"

  def renderTest(self, painter):
    # use painter for drawing to map canvas
    print "TestPlugin: renderTest called!"

Las únicas funciones de complemento que deben existir en el archivo principal fuente (por ejemplo mainPlugin.py) son:

  • __init__ –> que da acceso a la interfaz de QGIS

  • initGui() –> se llama cuando se carga el complemento

  • unload() –> se llama cuando se descarga el complemento

Se puede ver que en el ejemplo anterior, el addPluginToMenu() se utiliza. Esto añadirá la acción del menú correspondiente al menú Complementos. Métodos alternativos existen para añadir la acción a diferentes menús. Aquí esta la lista de esos métodos:

  • addPluginToRasterMenu()
  • addPluginToVectorMenu()
  • addPluginToDatabaseMenu()
  • addPluginToWebMenu()

Todos ellos tienen la misma sintaxis como el método addPluginToMenu()

Añadir el menú de su complemento a uno de aquellos métodos predefinidos se recomienda mantener la coherencia en la forma en que se organizan las entradas de complementos. Sin embargo, puede agregar a su grupo de menú personalizado directamente a la barra de menú, como el siguiente ejemplo demuestra:

def initGui(self):
    self.menu = QMenu(self.iface.mainWindow())
    self.menu.setObjectName("testMenu")
    self.menu.setTitle("MyMenu")

    self.action = QAction(QIcon(":/plugins/testplug/icon.png"), "Test plugin", self.iface.mainWindow())
    self.action.setObjectName("testAction")
    self.action.setWhatsThis("Configuration for test plugin")
    self.action.setStatusTip("This is status tip")
    QObject.connect(self.action, SIGNAL("triggered()"), self.run)
    self.menu.addAction(self.action)

    menuBar = self.iface.mainWindow().menuBar()
    menuBar.insertMenu(self.iface.firstRightStandardMenu().menuAction(), self.menu)

def unload(self):
    self.menu.deleteLater()

No olvide establecer QAction y QMenu objectName a un nombre especifico a su complemento para que pueda ser personalizado.

Archivo de recurso

Se puede ver que en initGui() hemos utilizado un icono desde el archivo fuente (llamado resources.qrc en nuestro caso)

<RCC>
  <qresource prefix="/plugins/testplug" >
     <file>icon.png</file>
  </qresource>
</RCC>

It is good to use a prefix that will not collide with other plugins or any parts of QGIS, otherwise you might get resources you did not want. Now you just need to generate a Python file that will contain the resources. It’s done with pyrcc4 command

pyrcc4 -o resources.py resources.qrc

y eso es todo... nada complicado :)

Si ha hecho todo correctamente debe ser capaz de encontrar y cargar sus complementos en el administrador y ver un mensaje en consola cuando el icono en la barra de herramientas o el elemento del menú apropiado es seleccionado.

Cuando se trabaja en un complemento real es aconsejable escribirlo en otro directorio (de trabajo) y crear un makefile que generará los archivos de interfaz de usuario + recursos e instalar el complemento a la instalación de QGIS.

Documentación

La documentación para el complemento puede estar escrita como archivos de ayuda HTML. El módulo qgis.utils proporciona una función, showPluginHelp() que abrirá el explorador de archivos de ayuda, de la misma manera como otra ayuda QGIS.

The showPluginHelp`() function looks for help files in the same directory as the calling module. It will look for, in turn, index-ll_cc.html, index-ll.html, index-en.html, index-en_us.html and index.html, displaying whichever it finds first. Here ll_cc is the QGIS locale. This allows multiple translations of the documentation to be included with the plugin.

La función showPluginHelp() también puede tener parámetros de nombre de paquete, que identifica un complemento específico para el que se mostrará la ayuda, nombre de archivo, que puede sustituir “índice” en los nombres de los archivos que se buscan, y la sección, que es el nombre de una etiqueta de anclaje html en el documento sobre el que se colocará el navegador.