Python plug-ins ontwikkelen

Het is mogelijk plug-ins te maken in de programmeertaal Python. In vergelijking met de klassieke plug-ins die zijn geschreven in C++ zouden deze eenvoudiger te schrijven, te begrijpen, te onderhouden en te verdelen zijn vanwege de dynamische natuur van de taal Python.

Plug-ins in Python worden samen met plug-ins in C++ vermeld in Beheer en installeer plug-ins in QGIS. Er wordt naar gezocht in deze paden:

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

De thuismap (hierboven vermeld als ~) op Windows is gewoonlijk iets als C:\Documents and Settings\(gebruiker) (op Windows XP of eerder) of C:\Users\(gebruiker). Omdat QGIS Python 2.7 gebruikt, moeten submappen van deze paden een bestand __init__.py bevatten om te worden beschouwd als Python pakketten die kunnen worden geïmporteerd als plug-ins.

Notitie

Bij het instellen van QGIS_PLUGINPATH naar een bestaand pad voor een map, kunt u dit pad toevoegen aan de lijst met paden die wordt gebruikt voor het zoeken naar plug-ins.

Stappen:

  1. Idee: Weet wat u met uw nieuwe plug-in voor QGIS wilt gaan doen. Waarom doet u dat? Welk probleem wilt u oplossen? Is er al een andere plug-in voor dat probleem?

  2. Bestanden maken: Maak de hieronder beschreven bestanden. Een beginpunt (__init__.py). Vul Metadata van de plug-in (metadata.txt) in. Een hoofdgedeelte voor een plug-in in Python (mainplugin.py). Een formulier in QT-Designer (form.ui), met zijn resources.qrc.

  3. Code schrijven: Schrijf de code in mainplugin.py

  4. Testen: Sluit en heropen QGIS en importeer uw plug-in opnieuw. Controleer of alles OK is.

  5. Publiceren: Publiceer uw plug-in in de opslagplaats van QGIS of maak uw eigen opslagplaats als een “arsenaal” van persoonlijke “wapens voor GIS”.

Een plug-in schrijven

Sinds de introductie van plug-ins in Python in QGIS, zijn een anatal Plug-ins verschenen - op Plugin Repositories wiki page kunt u er enkele van vinden, u kunt hun bronnen gebruiken om meer te leren over het programmeren met PyQGIS of uitzoeken of u de inspanningen voor de ontwikkeling niet dupliceert. Het team van QGIS onderhoudt ook een Officiële Python plug-in opslagplaats. Klaar om een plug-in te maken, maar geen idee wat te doen? Python Plugin Ideas wiki page vermeldt wensen van de gemeenschap!

Plug-inbestanden

Hier is de mappenstructuur van uw voorbeeld-plug-in

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*

Wat is de betekenis van de bestanden:

  • __init__.py = Het beginpunt van de plug-in. Het moet de methode classFactory() hebben en mag elke andere code voor initialisatie hebben.

  • mainPlugin.py = De belangrijkste werkende code van de plug-in. Bevat alle informatie over de acties van de plug-in en de hoofdcode.

  • resources.qrc = Het door Qt Designer gemaakte .xml-document. Bevat relatieve paden naar de bronnen van de formulieren.

  • resources.py = De vertaling van het bestand .qrc, hierboven beschreven, naar Python.

  • form.ui = De GUI, gemaakt door Qt Designer.

  • form.py = De vertaling van de form.ui, hierboven beschreven, naar 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.

Hier staat een online geautomatiseerde manier voor het maken van de basisbestanden (skelet) van een typische plug-in voor Python in QGIS.

Er is ook een plug-in voor QGIS, genaamd Plugin Builder die een sjabloon voor een plug-in maakt uit QGIS en geen internetverbinding vereist. Dit is de aanbevolen optie, omdat het compatibele bronnen produceert voor 2.0.

Waarschuwing

Als u van plan bent de plug-in te uploaden naar Officiële Python plug-in opslagplaats moet u controleren of uw plug-in enkele aanvullende regels volgt, vereist voor plug-in Validatie

Inhoud van de plug-in

Hier vindt u informatie en voorbeelden over wat in elk van de bestanden moet worden toegevoegd in de hierboven beschreven bestandsstructuur.

Metadata van de plug-in

Als eerste moet beheer en installeer plug-ins enige basisinformatie ophalen over de plug-in, zoals de naam, omschrijving etc. ervan. Bestand metadata.txt is de juiste plaats om deze informatie te vermelden.

Belangrijk

Alle metadata moet inde codering UTF-8 zijn.

Naam van de metadata

Vereist

Opmerkingen

name

Ja

een korte string die de naam van de plug-in bevat

qgisMinimumVersion

Ja

gestippelde notatie van de minimale versie van QGIS

qgisMaximumVersion

Nee

gestippelde notatie van de maximale versie van QGIS

description

Ja

korte tekst die de plug-in beschrijft, geen HTML toegestaan

about

Nee

langere tekst die de plug-in tot in detail beschrijft, geen HTML toegestaan

version

Ja

korte string met de versie in gestippelde notatie

author

Ja

author name
email

Ja

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

Nee

string, mag meerdere regels zijn, geen HTML toegestaan

experimental

Nee

Booleaanse vlag, True of False

deprecated

Nee

Booleaanse vlag, True of False, is van toepassing op de gehele plug-in en niet alleen op de geüploade versie

tags

Nee

kommagescheiden lijst, spaties zijn binnen de individuele tags toegestaan

homepage

Nee

een geldige URL die verwijst naar de startpagina voor uw plug-in

repository

Nee

een geldige URL voor de opslagplaats van de broncode

tracker

Nee

een geldige URL voor tickets en probleemrapporten

icon

Nee

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

Nee

één van Raster, Vector, Database of Web

Standaard worden plug-ins geplaatst in het menu Plug-ins (we zullen in het volgende gedeelte zien hoe een menuitem voor uw plug-in toe te voegen) maar zij kunnen ook worden geplaatst in de menu’s Raster, Vector, Database en Web.

Een overeenkomend item voor de metadata “category” bestaat om dat te specificeren, zodat de plug-in overeenkomstig kan worden geclassificeerd. Dit item voor de metadata wordt gebruikt als tip voor de gebruikers en vertelt ze waar (in welk menu) de plug-in kan worden gevonden. Toegestane waarden voor “category” zijn: Vector, Raster, Database of Web. Als u bijvoorbeeld wilt dat uw plug-in bereikbaar is in het menu Raster, voeg dat dan toe aan metadata.txt

category=Raster

Notitie

Als qgisMaximumVersion leeg is, zal het automatisch worden ingesteld op de hoofdversie plus .99 indien geüpload naar de Officiële Python plug-in opslagplaats.

Een voorbeeld voor dit 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

Dit bestand wordt vereist door het systeem voor importeren van Python. Ook vereist QGIS dat dit bestand een functie classFactory() bevat, die wordt aangeroepen als de plug-in wordt geladen in QGIS. Het ontvangt een verwijzing naar de instance van QgisInterface en moet een instance teruggeven van de klasse van uw plug-in uit mainplugin.py — in ons geval is dat genaamd TestPlugin (zie hieronder). Zo zou __init__.py er uit moeten zien

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

## any other initialisation needed

mainPlugin.py

Dit is waar de magie gebeurt en dit is hoe de magie eruit ziet: (biijv. 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!"

De enige functies voor plug-ins die moeten bestaan in het hoofd-bronbestand (bijv. mainPlugin.py) zijn:

  • __init__ –> wat toegang geeft tot de interface van QGIS

  • initGui() –> aangeroepen wanneer de plug-in wordt geladen

  • unload() –> aangeroepen wanneer de plug-in wordt ontladen

U kunt zien dat in het voorbeeld hierboven addPluginToMenu() is gebruikt. Dit zal de overeenkomstige menuactie toevoegen aan het menu Plug-ins. Alternatieve methoden bestaan om de actie aan een ander menu toe te wijzen. Hier is een lijst met die methoden:

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

Alle hebben dezelfde syntaxis als de methode addPluginToMenu().

Toevoegen van het menu van uw plug-in aan een van de voorgedefinieerde methoden wordt aanbevolen om consistentie te behouden in hoe items voor plug-ins zijn georganiseerd. U kunt echter uw aangepaste groepen voor het menu direct aan de Menubalk toevoegen, zoals het volgende voorbeeld demonstreert:

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()

Vergeet niet om QAction en QMenu objectName in te stellen op een naam die specifiek is voor uw plug-in zodat hij kan worden aangepast.

Bronbestand

U kunt zien dat we in initGui() een pictogram hebben gebruikt uit het bronbestand (in ons geval resources.qrc aangeroepen)

<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

En dat is alles... niets gecompliceerds :)

Als u alles juist heeft gedaan zou u in staat moeten zijn uw plug-in op te zoeken en te laden vanuit Beheer en installeer plug-ins en een bericht in de console te zien wanneer het pictogram op de werkbalk of het menuitem is geselecteerd.

Bij het werken aan een echte plug-in is het verstandig om de plug-in in een andere (werk-)map te schrijven en een makefile te maken dat de UI + bronbestanden zal genereren en de plug-in zal installeren in uw installatie van QGIS.

Documentatie

De documentatie voor de plug-in mag worden geschreven als helpbestanden in HTML. De module qgis.utils verschaft een functie, showPluginHelp() dat de browser voor Helpbestanden zal openen, op dezelfde manier als andere help voor 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.

De functie showPluginHelp() kan ook de parameters packageName , welke een specifieke plug-in specificeert waarvoor de Helpbestanden zullen worden weergegeven, filename, wat “index” mag vervangen in de namen van de gezochte bestanden, en section, wat de naam is van een tag voor een HTML-anker in het document waar de browser zal worden gepositioneerd, aannemen.