Outdated version of the documentation. Find the latest one here.

Développer des Extensions Python

Il est possible de créer des extensions dans le langage de programmation Python. Comparé aux extensions classiques développées en C++, celles-ci devraient être plus faciles à écrire, comprendre, maintenir et distribuer du fait du caractère dynamique du langage python.

Dans le gestionnaire d’extensions de QGIS, les extensions Python et C++ sont listées ensemble. Elles sont récupérées depuis les dossiers:

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

  • Windows: ~/.qgis/python/plugins et (qgis_prefix)/python/plugins

Le dossier Home (noté ci-dessus par ~) sous Windows est habituellement du genre C:\Documents and Settings\(user). Les sous-dossiers à l’intérieur de ces chemins sont considérés comme des paquets Python pouvant être importés dans QGIS comme extensions.

Étapes:

  1. Idée: Avoir une idée de ce que vous souhaitez faire avec votre nouvelle extension. Pourquoi le faites-vous? Quel problème souhaitez-vous résoudre? N’y a-t-il pas déjà une autre extension pour ce problème?

  2. Créer des fichiers: Créer les fichiers décrits plus loin. Un point de départ (__init.py__). Remplissez les Métadonnées de l’extension (metadata.txt). Un corps principal de l’extension (plugin.py). Un formulaire sous QT-Designer (form.ui), et ses resources.qrc.

  3. Écrire le code: Écrire le code à l’intérieur du fichier plugin.py

  4. Test: Fermez et ré-ouvrez QGIS et importez à nouveau votre extension. Vérifiez si tout est OK.

  5. Publier: Publiez votre extension dans le dépôt QGIS ou créez votre propre dépôt tel un “arsenal” pour vos “armes SIG” personnelles.

Écriture d’une extension

Depuis l’introduction des extensions python dans QGIS, un certain nombre d’extensions est apparu - sur le wiki du Dépôt des Extensions vous trouverez certaines d’entre elles, vous pouvez utiliser leur source pour en savoir plus sur la programmation avec PyQGIS ou pour savoir si vous ne dupliquez pas des efforts de développement. L’équipe QGIS maintient également un Dépôt officiel des extensions Python. Prêt à créer une extension, mais aucune idée de quoi faire ? Le wiki des Idées d’extensions Python liste les souhaits de la communauté !

Fichiers de l’extension

Voici la structure du dossier de l’extension en exemple:

PYTHON_PLUGINS_PATH/
  testplug/
    __init__.py
    plugin.py
    metadata.txt
    resources.qrc
    resources.py
    form.ui
    form.py

A quoi correspondent ces fichiers?

  • __init__.py = Le point de départ de l’extension. Il est normalement vide.

  • plugin.py = Le code principal de l’extension. Contient toutes les informations sur les actions de l’extension et le code principal.

  • resources.qrc = Le document .xml créé par QT-Designer. Contient les chemins relatifs vers les ressources des formulaires.

  • resources.py = La traduction en Python du fichier resources.qrc décrit ci-dessus.

  • form.ui = L’interface graphique créée par QT-Designer.

  • form.py = La traduction en Python du fichier form.ui décrit ci-dessus.

  • metadata.txt = Requis pour QGIS >= 1.8.0. Contient les informations générales, la version, le nom et d’autres métadonnées utilisées par le site des extensions et l’infrastructure de l’extension. Les métadonnées dans le fichier metadata.txt sont à privilégier aux méthodes dans le fichier __init__.py. Si le fichier texte est présent, il est utilisé pour récupérer les valeurs. A partir de QGIS 2.0, les métadonnées du fichier __init__.py ne seront plus acceptées et le fichier metadata.txt sera requis.

Vous trouverez ici et deux façons automatisées pour créer les fichiers de base (le squelette) d’une classique extension Python sous QGIS.

Il existe également une extension appelée Plugin Builder qui crée un modèle d’extension et ne requiert pas de connexion internet. C’est l’option qui vous est recommandée car elle produit des sources compatibles avec QGIS 2.0.

Warning

Si vous projetez de déposer l’extension sur le Dépôt officiel des extensions Python, vous devez vérifier que votre extension respecte certaines règles supplémentaires, requises pour sa Validation

Contenu de l’extension

Ici vous pouvez trouver des informations et des exemples sur ce qu’il faut ajouter dans chacun des fichiers de la structure de fichiers décrite ci-dessus.

Métadonnées de l’extension

Tout d’abord, le gestionnaire d’extensions a besoin de récupérer des informations de base sur l’extension par exemple son nom, sa description, etc. Le fichier metadata.txt est le bon endroit où mettre cette information.

Important

Toutes les métadonnées doivent être encodées en UTF-8.

Nom de la métadonnée

Requis

Notes

nom

Vrai

texte court contenant le nom de l’extension

qgisMinimumVersion

Vrai

dotted notation of minimum QGIS version
qgisMaximumVersion

Faux

dotted notation of maximum QGIS version
description

Vrai

un texte plus long décrivant l’extension, pas de HTML autorisé

version

Vrai

short string with the version dotted notation

auteur

Vrai

nom de l’auteur

e-mail

Vrai

e-mail de l’auteur, n’apparaîtra pas sur le site web

changelog

Faux

texte, peut être multi-lignes, pas de HTML autorisé

expérimental

Faux

indicateur booléen, Vrai ou Faux

obsolète

Faux

indicateur booléen, Vrai or Faux, s’applique à l’extension entière et pas simplement à la version chargée

tags

Faux

liste séparée par une virgule, les espaces sont autorisés à l’intérieur des balises individuelles

page d’accueil

Faux

une URL valide pointant vers la page d’accueil de l’extension

dépôt

Faux

une URL valide pour le dépôt du code source

tracker

Faux

une URL valide pour les billets et rapports de bugs

icône

Faux

un nom de fichier ou un chemin relatif (par rapport au dossier racine de l’extension, fichiers compressés)

catégorie

Faux

soit Raster, Vecteur, Base de Données ou Web

Par défaut, les extensions sont placées dans le menu Extension (nous verrons dans la section suivante comment ajouter une entrée de menu pour votre extension) mais ils peuvent aussi être placés dans le menu Raster, Vecteur, Base de donnée ou Web. Une entrée “catégorie” existe dans les métadonnées afin de spécifier cela, pour que l’extension soit classée en conséquence. Cette entrée de métadonnées est utilisée comme astuce pour les utilisateurs et leur dit où (dans quel menu) l’extension peut être trouvée. Les valeurs autorisées pour “catégorie” sont : Vecteur, Raster, Base de données, Web et Couches. Par exemple, si votre extension sera disponible dans le menu Raster, ajoutez ceci à metadata.txt:

category=Raster

Note

If qgisMaximumVersion is empty, it will be automatically set to the major version plus .99 when uploaded to the Dépôt officiel des extensions Python.

Un exemple de fichier metadata.txt:

; the next section is mandatory

[general]
name=HelloWorld
[email protected]
author=Just Me
qgisMinimumVersion=2.0
description=This is a plugin for greeting the
    (going multiline) world
version=version 1.2
; end of mandatory metadata

; start of optional metadata
category=Raster
changelog=this is a very
    very
    very
    very
    very
    very long multiline changelog

; tags are in comma separated value format, spaces are allowed
tags=wkt,raster,hello world

; these metadata can be empty
homepage=http://www.itopen.it
tracker=http://bugs.itopen.it
repository=http://www.itopen.it/repo
icon=icon.png

; experimental flag
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

plugin.py

One thing worth mentioning is classFactory() function which is called when the plugin gets loaded to QGIS. It receives reference to instance of QgisInterface and must return instance of your plugin - in our case it’s called TestPlugin. This is how should this class look like (e.g. testplugin.py):

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

# initialize Qt resources from file resouces.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.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!"

Les seules fonctions incontournables dans une fonction sont initGui() et unload(). Ces fonctions sont appelées lorsque l’extension est chargée ou déchargée.

You can see that in the above example, the addPluginMenu() is used. This will add the corresponding menu action to the Plugins menu. Alternative methods exist to add the action to a different menu. Here is a list of those methods:

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

Tous ont la même syntaxe que la méthode addPluginToMenu().

Ajouter votre extension dans un des menus prédéfinis est une méthode recommandée pour conserver la cohérence de l’organisation des entrées d’extensions. Toutefois, vous pouvez ajouter votre propre groupe de menus directement à la barre de menus, comme le montre l’exemple suivant:

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

    self.action = QAction(QIcon(":/plugins/testplug/icon.png"), "Test plugin", \
      self.iface.mainWindow())
    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()

Resource File

Vous pouvez voir que dans initGui(), nous avons utilisé une icône depuis le fichier ressource (appelé resources.qrc dans notre cas):

<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

And that’s all... nothing complicated :) If you’ve done everything correctly you should be able to find and load your plugin in the plugin manager and see a message in console when toolbar icon or appropriate menu item is selected.

When working on a real plugin it’s wise to write the plugin in another (working) directory and create a makefile which will generate UI + resource files and install the plugin to your QGIS installation.

Documentation

La documentation sur l’extension peut être écrite sous forme de fichiers d’aide HTML. Le module qgis.utils fournit une fonction, showPluginHelp(), qui ouvrira le fichier d’aide dans un navigateur, de la même manière que pour l’aide de QGIS.

La fonction showPluginHelp`() recherche les fichiers d’aide dans le même dossier que le module d’appel. elle recherchera, dans l’ordre, index-ll_cc.html, index-ll.html, index-en.html, index-en_us.html et index.html, affichant celui qu’elle trouve en premier. Ici, ll_cc est pour la locale de QGIS. Ceci permet d’inclure des traductions multiples dans la documentation de l’extension.

The showPluginHelp() function can also take parameters packageName, which identifies a specific plugin for which the help will be displayed, filename, which can replace “index” in the names of files being searched, and section, which is the name of an html anchor tag in the document on which the browser will be positioned.