16.1. Structurer les plugins Python

Voici quelques étapes à suivre afin de créer des extensions:

  1. Idée : Avoir une idée de ce que vous voulez faire avec votre nouveau plugin QGIS. Pourquoi le faites-vous ? Quel problème voulez-vous résoudre ? Existe-t-il déjà un autre plugin pour ce problème ?

  2. Créer des fichiers : certains sont essentiels (voir Fichiers de l’extension)

  3. Écrire le code: écrire le code dans les fichiers appropriés

  4. Tester: Rechargez votre extension pour vérifier si tout est OK

  5. Publier : Publiez votre plugin dans le dépôt QGIS ou créez votre propre dépôt comme un « arsenal » d“« armes SIG » personnelles.

16.1.1. Ecriture d’un plugin

Depuis l’introduction des plugins Python dans QGIS, un certain nombre de plugins sont apparus. L’équipe QGIS maintient un Dépôt officiel des extensions QGIS. Vous pouvez utiliser leur source pour en savoir plus sur la programmation avec PyQGIS ou découvrir si vous dupliquez l’effort de développement.

16.1.1.1. Fichiers de l’extension

Voici la structure du dossier pour notre plugin servant d’exemple

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

Quelle est la signification de ces fichiers:

  • __init__.py = Le point de départ du plugin. Il doit avoir la méthode classFactory() et peut avoir n’importe quel autre code d’initialisation.v

  • mainPlugin.py = Le code de travail principal du plugin. Contient toutes les informations sur les actions du plugin et le code principal.

  • resources.qrc = Le document .xml créé par Qt Designer. Contient les chemins relatifs aux ressources des formulaires.

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

  • form.ui = l’interface conçue sur Qt Designer

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

  • metadata.txt = Contient des informations générales, la version, le nom et quelques autres métadonnées utilisées par le site web des plugins et l’infrastructure des plugins.

Ici est un moyen de créer les fichiers de base (squelette) d’un plugin Python QGIS typique.

Il existe un plugin QGIS appelé Plugin Builder 3 qui crée un modèle de plugin pour QGIS. C’est l’option recommandée, car il produit des sources compatibles 3.x.

Avertissement

Si vous prévoyez de télécharger le plugin dans le Dépôt officiel des extensions QGIS vous devez vérifier que votre plugin suit certaines règles supplémentaires, requises pour le plugin Validation

16.1.2. Contenu de l’extension

Vous trouverez ici des informations et des exemples sur ce qu’il faut ajouter dans chacun des fichiers de la structure décrite ci-dessus.

16.1.2.1. Métadonnées de l’extension

Tout d’abord, le gestionnaire de plugin doit récupérer quelques informations de base sur le plugin, comme son nom, sa description, etc. Le fichier metadata.txt est le bon endroit pour mettre ces informations.

Note

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

Nom des métadonnées

Requis

Notes

name

Vrai

une courte chaîne de caractères contenant le nom du plugin

qgisMinimumVersion

Vrai

notation en pointillés de la version minimale de QGIS

qgisMaximumVersion

Faux

notation en pointillés de la version maximale de QGIS

description

Vrai

texte court qui décrit le plugin, pas d” HTML autorisé

about

Vrai

texte plus long qui décrit le plugin en détail, pas de HTML autorisé

version

Vrai

chaîne courte avec la notation en pointillés de la version

author

Vrai

nom de l’auteur

email

Vrai

e-mail de l’auteur, uniquement affiché sur le site web pour les utilisateurs connectés, mais visible dans le gestionnaire de plugin après l’installation du plugin

changelog

Faux

de type texte, indique les modifications de version. Peut être multiligne, le HTML n’est pas autorisé

experimental

Faux

booléen, True ou False - True si la version de l’extension est expérimentale

deprecated

Faux

booléen, True ou False, s’applique à l’extension entière et pas seulement à la version chargée

les mots-clé

Faux

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

homepage

Faux

une URL valide pointant vers la page d’accueil de votre plugin

repository

Vrai

une URL valide du dépôt du code

tracker

Faux

une URL valide pour le signalement des bugs et demandes

icon

Faux

un nom de fichier ou un chemin d’accès relatif (par rapport au dossier de base du paquet compressé du plugin) d’une image accessible sur le web (PNG, JPEG)

category

Faux

Les valeurs possibles sont Raster, Vector, Database et Web

plugin_dependencies

Faux

Liste des autres plugins à installer, séparés par des virgules de type PIP

server

Faux

booléen, True ou False, détermine si l’extension offre une interface serveur

hasProcessingProvider

Faux

booléen, True ou False, détermine si l’extension fournit des algorithmes de Traitement

Par défaut, les extensions sont placées dans le menu Extensions (nous verrons dans la section suivante comment ajouter une entrée de menu pour votre plugin) mais elles peuvent aussi être placées dans les menus Raster, Vecteur, Base de données et Internet.

Une entrée de métadonnées « catégorie » correspondante existe pour le spécifier, de sorte que le plugin peut être classé en conséquence. Cette entrée de métadonnées sert de conseil aux utilisateurs et leur indique où (dans quel menu) se trouve le plugin. Les valeurs autorisées pour « category » sont : Vecteur, Raster, Base de données ou Web. Par exemple, si votre plugin sera disponible à partir du menu « Raster », ajoutez ceci à metadata.txt.

category=Raster

Note

Si qgisMaximumVersion est vide, il sera automatiquement mis à la version majeure plus .99 lorsqu’il sera téléchargé dans le Dépôt officiel des extensions QGIS.

Un exemple pour ce fichier metadata.txt

; the next section is mandatory

[general]
name=HelloWorld
email=me@example.com
author=Just Me
qgisMinimumVersion=3.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
tracker=http://bugs.itopen.it
repository=http://www.itopen.it/repo
; 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=https://www.itopen.it
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=3.99

; Since QGIS 3.8, a comma separated list of plugins to be installed
; (or upgraded) can be specified.
; The example below will try to install (or upgrade) "MyOtherPlugin" version 1.12
; and any version of "YetAnotherPlugin"
plugin_dependencies=MyOtherPlugin==1.12,YetAnotherPlugin

16.1.2.2. __init__.py

Ce fichier est requis par le système d’importation de Python. De plus, QGIS exige que ce fichier contienne une fonction classFactory(), qui est appelée lorsque le plugin est chargé dans QGIS. Elle reçoit une référence à l’instance de QgisInterface et doit retourner un objet de la classe de votre plugin à partir du mainplugin.py — dans notre cas, il s’appelle TestPlugin (voir ci-dessous). Voici à quoi doit ressembler le fichier __init__.py.

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

# any other initialisation needed

16.1.2.3. mainPlugin.py

C’est là que la magie se produit et c’est à cela que ressemble la magie : (par exemple mainPlugin.py)

from qgis.PyQt.QtGui import *
from qgis.PyQt.QtWidgets import *

# initialize Qt resources from file resources.py
from . 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")
    self.action.triggered.connect(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
    self.iface.mapCanvas().renderComplete.connect(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
    self.iface.mapCanvas().renderComplete.disconnect(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 de plugin qui doivent exister dans le fichier source du plugin principal (par exemple mainPlugin.py) sont :

  • __init__ qui permet l’accès à l’interface de QGIS

  • initGui() appelé lorsque l’extension est chargée

  • unload() appelé lorsque le plugin est déchargé

Dans l’exemple ci-dessus, addPluginToMenu() est utilisé. Cela ajoutera l’action de menu correspondante au menu Extensions. D’autres méthodes existent pour ajouter l’action à un autre menu. Voici une liste de ces méthodes :

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

Il est recommandé d’ajouter votre menu de plugin à l’une de ces méthodes prédéfinies afin de conserver une certaine cohérence dans l’organisation des entrées de plugin. Toutefois, vous pouvez ajouter votre groupe de menus personnalisés directement à la barre de menu, comme le montre l’exemple suivant :

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")
    self.action.triggered.connect(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()

N’oubliez pas de donner à QAction et QMenu objectName un nom spécifique à votre plugin pour qu’il puisse être personnalisé.

16.1.2.4. Fichier de ressources

Vous pouvez voir que dans initGui() nous avons utilisé une icône du fichier de ressources (appelé resources.qrc dans notre cas)

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

Il est bon d’utiliser un préfixe qui n’entrera pas en collision avec d’autres plugins ou d’autres parties de QGIS, sinon vous risquez d’obtenir des ressources dont vous ne voulez pas. Il vous suffit maintenant de générer un fichier Python qui contiendra les ressources. C’est fait avec la commande pyrcc5 :

pyrcc5 -o resources.py resources.qrc

Note

Dans les environnements Windows, tenter d’exécuter la commande pyrcc5 à partir de l’invite de commande ou de Powershell entraînera probablement l’erreur « Windows ne peut pas accéder au périphérique, au chemin ou au fichier spécifié […] ». La solution la plus simple est probablement d’utiliser le shell OSGeo4W, mais si vous êtes à l’aise pour modifier la variable d’environnement PATH ou pour spécifier explicitement le chemin de l’exécutable, vous devriez pouvoir le trouver à l’adresse <Your QGIS Install Directory>\bin\pyrcc5.exe.

Et c’est tout… rien de compliqué :)

Si vous avez tout fait correctement, vous devriez pouvoir trouver et charger votre plugin dans le gestionnaire de plugins et voir un message dans la console lorsque l’icône de la barre d’outils ou l’élément de menu approprié est sélectionné.

Lorsque vous travaillez sur un véritable plugin, il est judicieux d’écrire le plugin dans un autre répertoire (de travail) et de créer un makefile qui générera des fichiers d’interface utilisateur + de ressources et d’installer le plugin dans votre installation QGIS.

16.1.3. Documentation

La documentation du plugin peut être écrite sous forme de fichiers d’aide HTML. Le module qgis.utils fournit une fonction, showPluginHelp() qui ouvrira le navigateur de fichiers d’aide, de la même manière que les autres aides QGIS.

La fonction showPluginHelp() recherche les fichiers d’aide dans le même répertoire que le module appelant. Elle cherchera, à son tour, index-ll_cc.html, index-ll.html, index-en.html, index-en_us.html et index.html, en affichant celui qu’elle trouve en premier. Ici, ll_cc est la locale QGIS. Cela permet d’inclure plusieurs traductions de la documentation avec le plugin.

La fonction showPluginHelp() peut également prendre les paramètres packageName, qui identifie un plugin spécifique pour lequel l’aide sera affichée, filename, qui peut remplacer « index » dans les noms des fichiers recherchés, et section, qui est le nom d’une balise d’ancrage html dans le document sur lequel le navigateur sera positionné.

16.1.4. Traduction

En quelques étapes, vous pouvez configurer l’environnement pour la localisation du plugin de sorte que, selon les paramètres locaux de votre ordinateur, le plugin sera chargé dans différentes langues.

16.1.4.1. Exigences en matière de logiciels

La façon la plus simple de créer et de gérer tous les fichiers de traduction est d’installer Qt Linguist. Dans un environnement GNU/Linux basé sur Debian, vous pouvez l’installer en tapant: :

sudo apt install qttools5-dev-tools

16.1.4.2. Fichiers et répertoire

Lorsque vous créez l’extension, vous devriez trouver le répertoire « i18n » dans le répertoire principal de l’extension.

Tous les fichiers de traduction doivent être dans ce répertoire.

16.1.4.2.1. Fichier .pro

Premièrement, vous devez créer un fichier « .pro ». Il s’agit d’un fichier projet qui peut être géré par Qt Linguist.

Dans ce fichier « .pro », vous devez spécifier tous les fichiers et formulaires que vous voulez traduire. Ce fichier est utilisé pour mettre en place les fichiers et les variables de localisation. Un fichier projet possible, correspondant à la structure de notre exemple d’extension :

FORMS = ../form.ui
SOURCES = ../your_plugin.py
TRANSLATIONS = your_plugin_it.ts

Votre plugin peut suivre une structure plus complexe, et il peut être réparti sur plusieurs fichiers. Si c’est le cas, gardez à l’esprit que pylupdate5, le programme que nous utilisons pour lire le fichier pro et mettre à jour la chaîne traduisible, ne développe pas les caractères génériques, vous devez donc placer chaque fichier explicitement dans le fichier pro. Votre fichier de projet pourrait alors ressembler à quelque chose comme ceci :

FORMS = ../ui/about.ui ../ui/feedback.ui \
        ../ui/main_dialog.ui
SOURCES = ../your_plugin.py ../computation.py \
          ../utils.py

De plus, le fichier your_plugin.py est le fichier qui appelle tous les menus et sous-menus de votre plugin dans la barre d’outils QGIS et vous voulez les traduire tous.

Enfin, avec la variable « TRANSLATIONS », vous pouvez spécifier les langages de traduction que vous souhaitez.

Avertissement

Assurez-vous de nommer le fichier ts comme votre_plugin_ + langue + .ts sinon le chargement de la langue échouera ! Utilisez le raccourci de 2 lettres pour la langue (it pour l’italien, de pour l’allemand, etc…)

16.1.4.2.2. Fichier .ts

Une fois que vous avez créé le .pro, vous êtes prêt à générer le(s) fichier(s) .ts pour la (les) langue(s) de votre plugin.

Lancez un terminal, allez au dossier your_plugin/i18n et saisissez:

pylupdate5 your_plugin.pro

Vous devriez voir le(s) fichier(s) your_plugin_language.ts.

Ouvrez le fichier .ts avec Qt Linguist et commencez à traduire.

16.1.4.2.3. Fichier .qm

Lorsque vous avez fini de traduire votre plugin (si certaines chaînes ne sont pas terminées, la langue source de ces chaînes sera utilisée), vous devez créer le fichier .qm (le fichier .ts compilé qui sera utilisé par QGIS).

Il suffit d’ouvrir un terminal , de se rendre dans le répertoire your_plugin/i18n et de taper

lrelease your_plugin.ts

Vous devriez maintenant voir le(s) fichier(s) your_plugin.qm dans le dossier i18n.

16.1.4.3. Traduction en utilisant un fichier Makefile

Vous pouvez également utiliser le makefile pour extraire des messages du code python et des dialogues Qt, si vous avez créé votre plugin avec Plugin Builder. Au début du Makefile, il y a une variable LOCALES: :

LOCALES = en

Ajoutez l’abréviation de la langue à cette variable, par exemple pour la langue hongroise: :

LOCALES = en hu

Vous pouvez maintenant générer ou mettre à jour le fichier hu.ts (et le fichier en.ts aussi) à partir des sources par: :

make transup

Après cela, vous avez mis à jour le fichier .ts pour toutes les langues définies dans la variable LOCALES. Utilisez Qt Linguist pour traduire les messages du programme. Pour terminer la traduction, les fichiers « .qm » peuvent être créés par le transcompilateur: :

make transcompile

Vous devez distribuer les fichiers « .ts » avec votre plugin.

16.1.4.4. Chargement de l’extension

Pour voir la traduction de votre plugin, ouvrez QGIS, changez la langue (parametre ► Options ► General) et redémarrez QGIS.

Vous devriez voir votre plugin dans la bonne langue.

Avertissement

Si vous changez quelque chose dans votre plugin (nouvelle interface utilisateur, nouveau menu, etc.), vous devez générer à nouveau la version mise à jour des fichiers .ts et .qm, donc exécuter à nouveau la commande ci-dessus.

16.1.5. Conseils et Astuces

16.1.5.1. Rechargeur de plugins

Pendant le développement de votre extension, vous aurez fréquemment besoin de la recharger dans QGIS pour la tester. C’est très facile en utilisant l’extension Plugin Reloader. Vous pouvez le trouver via le gestionnaire d’extensions.

16.1.5.2. Accès aux extensions

Vous pouvez accéder à toutes les classes des extensions installées depuis QGIS en utilisant python, ce qui peut être pratique pour le débogage.

my_plugin = qgis.utils.plugins['My Plugin']

16.1.5.3. Journal des messages

Les extensions ont leur propre onglet dans le panneau Journal des messages (log).

16.1.5.4. Distribuer une extension

QGIS héberge des centaines de plugins dans le dépôt de plugins. Pensez à partager le vôtre ! Cela permettra d’étendre les possibilités de QGIS et les gens pourront apprendre de votre code. Tous les plugins hébergés peuvent être trouvés et installés à partir de QGIS grâce au gestionnaire de plugins.

Informations et prérequis consultables ici: plugins.qgis.org.