Skip to Content

QGIS : lancer des scripts Python ou des commandes Shell depuis la Console Python ou avec Script Runner (sans créer d'extension)

Niveau Intermédiaire
Logiciels utilisés Quantum GIS (QGIS)
divers modules Python (au choix)
Plateforme Windows | Mac | Linux | FreeBSD

pythonQue faire, si comme moi, vous n'avez pas le courage de créer une extension chaque fois que vous voulez effectuer un traitement spécifique à l'aide d'un script Python ?

Bien évidemment, en utilisant la Console Python. Si vous connaissez bien Python, les avantages sont évidents comme nous allons le voir.

La Console Python

La Console Python permet de tout faire dans QGIS, même en utilisant PyQt pour ceux qui le désirent vraiment (voir osgeo-org.1560.n6.nabble.com/question-to-possible-enhancements-td4122157.html#a4122159) :

Mais comment lancer un script externe ou une commande Shell depuis cette console qui est elle-même un script Python ? Indépendamment de QGIS, il existe plusieurs solutions en Python pour exécuter cette tâche :

Avec la commande execfile

Dans la Console Python :

>>> execfile("test.py")

Le script va bien être exécuté, mais si je veux passer des arguments et récupérer des résultats, la solution est de passer par sys.argv :

>>> import sys
>>> sys.argv=[mes_arguments]
>>> execfile("test.py") # pour autant que test.py traite le(s) sys.argv

Avec execfile(), le script est exécuté par l'interpréteur courant dans le même environnement. De ce fait, toute variable définie dans test.py sera aussi disponible dans la Console Python après exécution du script.

Par exemple, soit le simple script suivant qui définit la variable salut :

test.py

  1. import sys
  2. salut = "Bonjour ",sys.argv

Le résultat dans la console :

Il est aussi possible de lancer n'importe quel exécutable, mais la communication s'avère plus délicate. Il vaut mieux passer par le module subprocess.

avec le module subprocess

Il permet, à l'inverse d'execfile, de lancer des processus parallèles (avec des exécutables différents, dans un environnement différent) avec des paramètres et de récupérer facilement les résultats. Ces processus peuvent être des applications tierces, des commandes Shell ou des scripts Ruby, Python, etc.

Ce module est préconisé pour remplacer les diverses commandes os.popen docs.python.org/dev/library/subprocess.html#replacing-os-popen-os-popen2-os-popen3. Pour bien comprendre ce qu'il fait, voir jimmyg.org/blog/2009/working-with-python-subprocess.html ou blog.doughellmann.com/2007/07/pymotw-subprocess.html. Il fonctionne suivant les principes des pipes Unix (fr.wikipedia.org/wiki/Tube_%28shell%29)

>>> import subprocess
>>> mesParams = .....
>>> proc = subprocess.Popen(['python','test.py','mesParams'], stdout=subprocess.PIPE)
>>> # ou sys.executable appelle l'exécutable Python, quelque soit le système d'exploitation
>>> proc = subprocess.Popen([sys.executable,'test.py','myParams'], stdout=subprocess.PIPE)
>>> # exploitation des résultats
>>> sortie = proc.communicate()[0] # ou proc.stdout.read()

Puisque la commande fait appel à un exécutable précis, il est possible de lancer des scripts avec diverses versions de Python (python3 est mon exécutable de la version 3.x de Python alors que QGIS utilise l'exécutable python qui correspond sur mon Mac à Python 2.6) et de récupérer les résultats:

>>> a = subprocess.Popen(['python3', 'test2.py'],stdout=subprocess.PIPE)

Il est aussi possible de lancer n'importe quel exécutable en lui passant des paramètres et en récupérant les résultats (avec des problèmes avec QGIS sur les systèmes Windows). Illustrons son usage avec une commande inutile (gdalinfo) puisque GDAL est intégré à QGIS :

J'utilise cette technique pour communiquer avec les commandes de GMT ou de R (exécution d'un script avec la commande Rscript, plutôt que d'utiliser les extensions utilisant R pour QGIS qui, honnêtement, ne fonctionnent pas très bien), entre autres choses.

>>> b = subprocess.Popen(['Rscript','monscript.R','mesParams'], stdout=subprocess.PIPE)

Soit le script trivial en R suivant :

a <- 10 / (3 + 2)
print(a)

Dans la console :

Il faut noter que je n'ai pas réussi à faire fonctionner l'autre commande pour appeler un exécutable, subprocess.call (alors qu'il n'y a aucun problème dans le Shell normal).

Il y aurait aussi la solution PyRO (Python Remote Object) qui permet de faire communiquer « en direct » deux processus Python qui s'exécutent en même temps, mais mes premiers résultats avec la Console Python ne sont pas concluants.

 

Avec la commande import(module)

Pour importer un script comme un module Python qui renvoie des résultats, il faut:

  1. que le script ait des classes ou des fonctions qui renvoient des résultats après leur avoir passé des paramètres;
  2. ajouter le répertoire où se situe le script Python au PYTHONPATH (avec le module sys) ou changer de répertoire de travail avec os.chdir;
  3. importer le script comme un module;
  4. exécuter une des classes ou une des fonctions du script.
>>> import sys
>>> sys.path.append("/Users/Shared/")
>>> import test
>>> mesParams = .....
>>> # et exécution d'une des classes/fonctions du module
>>> final = test.mafonction(mesParams)

ou

>>> import os
>>> os.chdir("/Users/Shared/")
>>> import test

 

Sur Windows, où l'installation du Python de QGIS est indépendante de celle éventuellement installée de manière standard, la commande sys.path.append permet d'utiliser les modules de l'installation standard, pour autant que les versions de Python soient les mêmes. Sinon, il est toujours possible d'importer un module qui utilise le module subprocess. En effet, soit le script suivant qui utilise subprocess pour appeler le script R précédent :

sortieR.py

  1. import subprocess
  2. def result():
  3. a = subprocess.Popen(['Rscript','/Users/martinlaloux/test.R'], stdout=subprocess.PIPE)
  4. sortie = a.stdout.read()
  5. return sortie
  6. if __name__ == '__main__':
  7. result()

Le résultat dans la Console Python sera :

Je peux ainsi lancer les scripts « création automatique d'un fichier style à partir d'un fichier texte délimité » et « création automatique d'un fichier style à partir d'un fichier shapefile » (www.portailsig.org/content/creation-automatique-d-un-fichier-style-partir-d-un-fichier-texte-delimite), directement depuis la console, avec une petite modification pour que la fonction accepte le fichier à traiter comme argument :

>>> import creastyle2
>>> # création d'un fichier style à partir d'un fichier shapefile
>>> creastyle2.traitement("test.shp")

 

Avec la commande import(module) et les modules PyQGIS de la console

Jusqu'à présent, nous n'avons utilisé que du Python « standard » sans nécessairement faire appel aux modules de PyQGIS. Gary Sherman a récemment montré comment l'utilisation de l'objet iface (qgis.utils.iface)  permet de créer des scripts externes qui peuvent interagir complètement avec QGIS (spatialgalaxy.net/2012/01/27/qgis-running-scripts-in-the-python-console/)

L'exemple qu'il donne est un script (traduit ici) qui permet de charger et d'afficher dans QGIS tous les fichiers shapefiles d'un répertoire donné.

loader.py (traduction de l'original de Gary Sherman)

  1. #!/usr/bin/env Python
  2. # encoding: utf-8
  3. """Charge tous les fichiers shapefile d'un répertoire et les affiche dans QGIS
  4. Ce script (loader.py) fonctionne dans la console Python.
  5. Exécution:
  6. from loader import Loader
  7. ldr = Loader(qgis.utils.iface)
  8. ldr.load_shapefiles('/mon/chemin/du/répertoire/des_shapefiles')
  9.  
  10. """
  11. from glob import glob
  12. from os import path
  13.  
  14. class Loader:
  15. def __init__(self, iface):
  16. """Initialisation en utilisant qgis.utils.iface
  17. """
  18. self.iface = iface
  19.  
  20. def load_shapefiles(self, shp_path):
  21. """Charge tous les fichiers shapefiles trouvés dans le répertoire"""
  22. print u"Ajout des fichiers shapefiles du répertoire %s" % path.join(shp_path, "*.shp")
  23. shps = glob(path.join(shp_path, "*.shp"))
  24. for shp in shps:
  25. (shpdir, shpfile) = path.split(shp)
  26. self.iface.addVectorLayer(shp, shpfile, 'ogr' )

Et donc, depuis la console  :

 

Plus loin avec l'extension Script Runner

Poursuivant son travail, Gary Sherman a ensuite automatisé le processus en créant une extension qui permet de lancer automatiquement ces scripts, Script Runner (spatialgalaxy.net/2012/01/29/script-runner-a-plugin-to-run-python-scripts-in-qgis/), disponible depuis le menu Extension/Installateur d'extensions python.

La seule différence avec l'exemple précédent est qu'il est nécessaire d'ajouter une fonction permettant à l'extension d'exécuter le script : run_script

def run_script(iface):
       ldr = Loader(iface)
       ldr.load_shapefiles('/Users/Shared/Dropbox/transfert')

En revanche, pas moyen de passer des paramètres, ils doivent être intégrés dans le script.

 

Conclusions

Il y a donc une « vie » Python sans nécessairement devoir créer des extensions. Pour vos traitements courants, si vous connaissez bien Python, la simple Console Python suffit.

Comme vous pouvez le constater sur la capture d'écran de Script Runner, je modifie progressivement mes divers scripts pour être compatibles avec l'extension (même ceux qui n'interfèrent pas directement avec QGIS comme le traitement de fichiers shapefiles avec Shapely ou Fiona, mais  je peux ensuite les charger automatiquement dans QGIS). 

Exemple de résultat du traitement réalisé avec un script Python à partir de Script Runner avec le module matplotlib

Sur les listes de QGIS, un site a déjà été proposé pour partager ces scripts : plugins.qgis.org/snippets/

Tous les traitements ont été effectués sur Mac OS X  avec QGIS versions 1.7.3, 1.8 dev et 1.9 dev. et Python 2.6 (et 3.2).

Site officiel : execfile
Site officiel : module subprocess
Site officiel : GMT ("Generic MappingTool")
Site officiel : Rscript
Site officiel : module PyRO


Creative Commons License
licence Creative Commons Paternité-Pas d'Utilisation Commerciale-Partage des Conditions Initiales à l'Identique Commerciale 2.0 France

Commentaires

erreur dernier script

Traceback (most recent call last):
File "", line 1, in
File "C:/OSGeo4W/apps/qgis/./python\qgis\utils.py", line 309, in _import
mod = _builtin_import(name, globals, locals, fromlist, level)
ImportError: No module named loader

comment faire pour le résoudre merci

QGIS, ScriptRunner, matplotlib & linéaments

Une question posée à votre intention, concernant la représentation des directions de linéaments par rose diagrams, a été posée ici :
http://gis.stackexchange.com/questions/24260/how-to-add-direction-and-di...
Merci beaucoup pour votre aide.

Paramètre

A noter qu'il est maintenant possible (je ne sais pas depuis quand) de passer des paramètres, une interface s'ouvrer permettant de les saisir, c'est extrèmement pratique.

voir http://spatialgalaxy.net/2013/03/18/new-version-of-the-qgis-script-runne... et surtout l'aide de script runner https://github.com/g-sherman/Script-Runner/blob/master/doc/index.rst : passing argument

Poster un nouveau commentaire

Le contenu de ce champ sera maintenu privé et ne sera pas affiché publiquement.