3 Créer une extension (Plugin)
3.1 Plugins utiles
Pour nous faciliter la tâche, nous allons utiliser deux plugins dédiés au développement de plugins QGIS:
Vérifier leur présence dans les plugins installés de QGIS:
Si ils sont absents, installez-les via le gestionnaire d’extension.
3.2 Créer un plugin simple
Nous allons procéder aux étapes suivantes :
- Créer un plugin à partir d’un modèle
- Modifier l’interface
- Modifier le code Python
Le plugin présenté ici retournera les coordonnées du point cliqué à la souris. Il s’agit d’un exemple adapté de celui présenté dans le The PyQGIS Programmer’s Guide.
3.2.1 Créer une base de départ
Lancez Plugin Builder, une première fenêtre va s’ouvrir. Saisissez les informations suivantes, tous les champs sont obligatoires.
Validez, une deuxième fenêtre s’ouvre pour saisir des informations complémentaires sur le plugin. Là aussi, la saisie est obligatoire.
Ensuite nous devons choisir le modèle. Nous allons commencer avec le modèle Tool button with dialog qui est assez simple mais que nous compléxifierons.
Plusieurs modèles sont disponibles:
- Tool button with dialog
- Tool button with dock widgets
- Processing Provider
Saisissez le texte que vous voulez voir apparaître dans le menu.
Un menu déroulant propose de choisir dans quel menu le plugin sera intégré. Il est possible de choisir parmi les menus suivants:
- Plugins
- Database
- Raster
- Vector
- Web
Laissez Plugins proposé par défaut.
Cliquez sur Next, le Plugin Builder propose alors les éléments qu’il est possible d’inclure dans le plugin.
- Internationalization (support pour les traductions)
- Help (Génération de l’aide avec l’outil Sphinx)
- Unit tests (tests unitaires)
- Helper scripts (scripts d’aide durant le développement)
- Makefile (GNU Makefile pour compiler le plugin)
- pb_tool (outil Python en ligne de commande pour compiler, déployer et packager le plugin)
Laissez tout coché par défaut.
L’étape suivante propose de renseigner les liens pour la publication du plugin. Laissez les valeurs par défaut. Il s’agit de lien vers des dépôts GitHub ou Gitlab où les développeurs pourront auditer le code et proposer des améliorations ou reporter des problèmes.
Ensuite le Plugin Builder demande le chemin vers un répertoire de travail pour y déposer l’architecture du futur plugin.
Ce dossier doit être différent du répertoire d’installation des plugins de QGIS.
Enfin la dernière fenêtre récapitule les informations importantes et rappelle les prochaines étapes:
Your plugin OuSuisJe was created in:
/media/work/developpements/plugins/ousuisje
Your QGIS plugin directory is located at:
/home/niko/.local/share/QGIS/QGIS3/profiles/default/python/plugins
Prochaines étapes:
- Compiler
resources.py
avec pyrcc5 (cette étape n’està faire qu’une seule fois, à la création du plugin) - Eventuellement, tester les sources générées avec
make test
(ou avec un IDE) - Copier le répertoire complet dans le dossier contenant les plugins
- tester le plugin en l’activant dans le plugin manager de QGIS
- Modifier le plugin en éditant
ousuisje.py
- Éventuellement remplacer l’icone par une icone personnalisée
- Modifier l’interface utilisateur en ouvrant
ousuisje_dialog_base.ui
dans Qt Designer
Le dossier contenant notre plugin contient un certain nombre de fichiers et dossiers:
- help: dossier contenant les fichiers permettant de générer l’aide
- i18n : internationalisation (traductions)
- icon.png : icone
- __init__.py : fichier python initialisant le plugin
- Makefile : fichier de configuration pour la compilation
- metadata.txt : métadonnées
- ousuisje_dialog_base.ui : fichier d’interface graphique
- ousuisje_dialog.py : fichier Python d’interaction avec l’interface graphique
- ousuisje.py : implémentation du plugin
- pb_tool.cfg : configuration de l’outil pb_tool
- plugin_upload.py : fichier Python pour le chargement du plugin dans QGIS
- pylintrc : règles de mise en forme automatique du code
- README.html : page web informative
- README.txt : document texte informatif
- resources.qrc : resources Qt
- scripts : dossier contenant les scripts du plugin
- test : tests unitaires
Le fichier resources.py
est créé par pyrcc5
à partir des ressources.
pyrcc5
est un outil en ligne de commande.
Sous Windows, pyrcc5
est fourni avec le shell OSGeo4W.
Néanmoins le recours à un fichier .bat
peut être utile pour charger
les bons chemins vers les binaires.
Dans un fichier compile.bat
dans votre dossier de développement,
copiez-collez les lignes suivantes:
@echo off
call "C:\Program Files\QGIS 3.16\bin\o4w_env.bat"
call "C:\Program Files\QGIS 3.16\bin\qt5_env.bat"
call "C:\Program Files\QGIS 3.16\bin\py3_env.bat"
@echo on
pyrcc5 -o resources.py resources.qrc
source: GIS stackexchange
Vérifiez bien que les chemins vers les exécutables correspondent à votre
installation.
Pour lancer la compilation, double-cliquez sur compile.bat
.
Sous Linux, entrez la commande suivante dans un terminal:
pyrcc5 -o resources_rc.py resources.qrc
Un fichier ressources_rc.py devrait être créé.
Pour tester votre plugin, copiez le répertoire du plugin dans le répertoire des plugins de QGIS.
Fermez et relancez QGIS pour lui faire charger le plugin.
En effet, au démarrage de QGIS, celui-ci scanne le répertoire des plugins.
Il tente ensuite de charger tous les plugins présents en les initialisant avec
un appel du __init__.py
.
Si le plugin charge sans erreur, l’interface graphique est chargée et le plugin
est affiché dans la liste des plugins actifs.
En cas d’erreurs, un message est affiché dans QGIS.
Une fois QGIS relancé, allez dans le gestionnaire de plugins et activez le plugin
Où suis-je ?
.
L’icone du plugin est maintenant affichée dans la barre d’outils plugin. Cliquez dessus pour lancer le plugin.
Une fenêtre avec une interface simple apparait.
L’interface ne propose que deux boutons:
Ok
Cancel
Les deux boutons ferment la fenêtre mais envoient des signaux différents qui seront récupérés par QGIS.
Nous avons un plugin fonctionnel mais pas très utile. Modifions l’interface pour préparer la suite.
3.2.2 Modification de l’interface
Pour notre besoin (afficher des coordonnées), la fenêtre du plugin est trop grande. De plus, il nous faut y ajouter des éléments en modifiant le fichier d’interface (.ui) et le fichier d’implémentation (ousuisje.py).
Commençons par l’interface. Nous allons utiliser pour cela l’outil Qt Designer qui est installé avec QGIS.
Pour notre plugin, nous allons rester sur une interface simple:
- une étiquette expliquant le résultat
- une boite texte affichant les coordonnées du point cliqué
- un bouton pour fermer la fenêtre
Ouvrez Qt Designer et ouvrez le fichier ousuisje_dialog_base.ui
avec.
Nous devons:
- Supprimer le groupe de bouton Ok/Cancel
- Ajouter un widget
Label
par cliquer-déposer - En changer le texte et la mise en forme
- Ajouter un widget
Line Edit
que nous placerons dessous l’étiquette - Un bouton
Push Button
en dessous - Double-cliquez sur le bouton et inscrivez “Fermer”
- Redimensionnez la fenêtre pour enlever l’espace inutile
Nous avons notre nouvelle interface. Avant de fermer Qt Designer, il nous faut encore définir l’action du bouton Fermer. Pour cela nous devons définir le signal et le slot.
Dans le menu Edition, cliquez sur Éditer signaux/slots
.
Cliquez sur le bouton Fermer, maintenez appuyer et déplacer le curseur vers une
partie vide de l’interface.
Relachez, la boite de dialogue de configuration des connexions doit apparaitre.
Dans le panneau gauche, cliquez sur pressed()
.
Dans le panneau droit, cliquez sur reject()
.
Cliquez sur Ok
pour valider la connexion.
Côté code, un bouton suivant son type pourra avoir plusieurs attributs (coché/non coché, accessible ou non, etc.), il transmet aussi un signal (pressé, relaché, cliqué) qui sera exploité par le code Python.
Il faut que le signal défini dans l’interface et celui attendu dans le code Python coïncide.
Plus d’informations sur les boutons dans la documentation de PyQt.
Sauvegardez les changements sur le fichier ousuisje_dialog_base.ui
.
Nous avons à présent une interface ainsi qu’un bouton de fermeture qui fonctionne, nous pouvons passer au code Python qui va intéragir avec notre interface.
3.2.3 Implémentation du code Python
L’initialisation du plugin se fait par le fichier ousuisje.py
qui contient la méthode __init__()
.
Commençons par celui-ci, ouvrez-le avec une éditeur de texte.
3.2.3.1 ousuisje.py
Jetons un œil à ce fichier.
Il importe un certain nombre de module et défini la classe ousuisje
qui contient la méthode __init__()
.
from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QAction
# Initialize Qt resources from file resources.py
from .resources import *
# Import the code for the dialog
from .ousuisje_dialog import OuSuisJeDialog
import os.path
class OuSuisJe:
"""QGIS Plugin Implementation."""
def __init__(self, iface):
"""Constructor.
:param iface: An interface instance that will be passed to this class
which provides the hook by which you can manipulate the QGIS
application at run time.
:type iface: QgsInterface
"""
# Save reference to the QGIS interface
self.iface = iface
# initialize plugin directory
self.plugin_dir = os.path.dirname(__file__)
[...]
Nous devons ajouter les lignes de code définissant notre plugin comme un map tool. Ainsi l’outil Où suis-je ?, quand cliqué, deviendra l’outil de carte actif et manipulera les clics sur la carte. Il restera actif tant qu’un autre outil de carte (zoom, pan, etc) n’est pas sélectionné.
Ajouter l’import suivant: from qgis.gui import QgsMapToolEmitPoint
Cette classe implémente un outil de carte qui émet un point contenant des coordonnées.
Nous devons aussi modifier la méthode init pour récupérer le canvas courant et le lier à notre outil d’émission de point.
## Récupération du canvas courant
self.canvas = self.iface.mapCanvas()
## Création d'un outil émettant un point au clic
self.point_tool = QgsMapToolEmitPoint(self.canvas)
Pour le moment, notre fichier Python ressemble à cela:
from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QAction
from qgis.gui import QgsMapToolEmitPoint
# Initialize Qt resources from file resources.py
from .resources import *
# Import the code for the dialog
from .ousuisje_dialog import OuSuisJeDialog
import os.path
class OuSuisJe:
"""QGIS Plugin Implementation."""
def __init__(self, iface):
"""Constructor.
:param iface: An interface instance that will be passed to this class
which provides the hook by which you can manipulate the QGIS
application at run time.
:type iface: QgsInterface
"""
# Save reference to the QGIS interface
self.iface = iface
# initialize plugin directory
self.plugin_dir = os.path.dirname(__file__)
# Récupération du canvas courant
self.canvas = self.iface.mapCanvas()
# Création de l'outil émettant un point au clic
self.point_tool = QgsMapToolEmitPoint(self.canvas)
Nous devons aussi récupérer un signal quand le canvas est cliqué. Modifiez la méthode initGui comme suit:
def initGui(self):
"""Create the menu entries and toolbar icons inside the QGIS GUI."""
= ':/plugins/ousuisje/icon.png'
icon_path self.add_action(
icon_path,=self.tr(u'Où suis-je ?'),
text=self.run,
callback=self.iface.mainWindow())
parent
# will be set False in run()
self.first_start = True
# connecte le signal que le canvas a été cliqué
self.point_tool.canvasClicked.connect(self.display_point)
Ensuite, modifions la méthode Run pour que Où suis-je devienne l’outil de carte. Pour cela, videz-la de son contenu et insérez les lignes suivantes.
def run(self):
"""Run method that performs all the real work"""
if self.first_start == True:
self.first_start = False
# Creer la boite de dialogue
self.dlg = OuSuisJeDialog()
# connecte le signal que le canvas a été cliqué
self.point_tool.canvasClicked.connect(self.display_point)
# Défini ousuisje comme étant l'outil de carte actif
self.canvas.setMapTool(self.point_tool)
Nous avons maintenant besoin qu’une action soit réalisée au moment du clic et que les coordonnées du point soit calculée. Ajouter la méthode display_point à la fin de la classe.
def display_point(self, point, button):
# Affiche les coordonnées du clic
self.dlg.hide() # cache la boite de dialogue
= "{}, {}".format(point.x(), point.y()) ## formatage des coordonnées
coords self.dlg.lineEdit.setText(coords) # affichage des coordonnées
self.dlg.show() # affiche de nouveau la boite de dialogue
Vous remarquez qu’il est fait référence à dlg. Il s’agit de la boite de dialogue. Pour récupérer sa référence, ajouter cette ligne après les lignes de traduction.
# Création de la boite de dialogue (après les traductions)
self.dlg = OuSuisJeDialog()
Copiez le plugin dans le répertoire de QGIS et rechargez-le avec Plugin Reloader. Puis cliquez n’importe où sur la carte.
3.3 Exercices
3.3.1 Plugin Où-suis-je
?
- Changer l’icone pour une icone mieux adaptée (pensez à recompiler les ressources)
- Afficher les coordonnées avec seulement 3 décimales
- Créez un bouton qui copie les coordonnées dans le presse-papier quand il est cliqué. (Voir classe QClipboard dans la documentation de Qt).
3.3.2 Plugin Proximité
Transformer proximite.py
en plugin, fonctionnalités attendues:
- icone personnalisée
- charger une couche de point d’intérêt
- charger une couche de superposition
- saisir une distance
X
- trouver les éléments de la couche de superposition à distance
X
des points d’intérêts de l’autre couche - la couche de résultat doit être chargée dans le projet à l’issue du calcul
- sélectionner un chemin vers un fichier de sauvegarde (si vide, la couche sera conservée en mémoire)
- gérer les erreurs de saisie de la distance (conversion
,
-> ‘.’, génération d’un message si caractère non valide)
Pour l’interface, le Qt Designer fournit avec QGIS offre des outils dédiés à ce dernier, pensez à les explorer !
3.3.3 Plugin Lieux propices
- Créer un plugin
Lieux propices
à partir des consignes et du code de l’exercice 2.2.8