Appel des fonctions PyQGIS à partir d'éditeurs externes (Linux)

8

Est-il possible d'intégrer des éditeurs Python externes (tels que KDevelop) à QGIS, afin qu'il soit possible d'exécuter des fonctions dans qgis.core, qgis.utilsetc. en dehors de la console QGIS Python?

En suivant les directives sur le site Web de QGIS ( http://docs.qgis.org/testing/en/docs/pyqgis_developer_cookbook/intro.html ), j'ai essayé cela, mais il renvoie juste 1 et rien d'autre:

import sys
sys.path.append('/usr/share/qgis/python')
import qgis.core
import qgis.utils

app = qgis.core.QgsApplication([], True)
qgis.core.QgsApplication.initQgis()
qgis.utils.iface.addVectorLayer("testing.shp", "anewlayer", "ogr") 
aLayer = qgis.utils.iface.activeLayer()
print aLayer.name()

Tel que:

$ LD_LIBRARY_PATH=/usr/lib64/qgis/ python qgis-test.py && echo "OK" || echo "Died"
Died

J'utilise openSUSE Tumbleweed, 64 bits.

GreatEmerald
la source

Réponses:

8

J'utilise l'intro suivante pour les applications autonomes:

# the_app.py
import os
import sys

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

def main():
    QgsApplication.setPrefixPath(os.environ['QGIS_PREFIX'], True)
    QgsApplication.initQgis()

    # app specific code
    ...

    QgsApplication.exitQgis()
    sys.exit(result)

if __name__ == '__main__':
    main()

Lorsque l'application n'a pas besoin d'une interface graphique (par exemple, faire du géotraitement), remplacez les points de suspension par quelque chose comme ça:

# app specific code (console)
print 'starting test'
layer1 = QgsVectorLayer('LineString', 'anewlayer', 'memory')
print layer.isValid()
QgsMapLayerRegistry.instance().addMapLayer(layer1, False)
print 'layer added to map layer registry'
aLayer = QgsMapLayerRegistry.instance().mapLayersByName('anewlayer')[0]
print aLayer.name()

Pour travailler avec une ou plusieurs couches, il n'est pas nécessaire de les ajouter au registre de couches de la carte, il suffit de les référencer par leur nom de variable (ici layer1).

Lorsque vous utilisez une interface graphique, par exemple une fenêtre de mappage, il s'agit d'une classe python dérivée de QMainWindow, généralement conçue avec QtDesigner. Les points de suspension doivent être remplacés par du code comme dans l'exemple suivant:

# app specific code (GUI)
app = QApplication(sys.argv)

# create gui window
window = Main()
window.show() 

result = app.exec_()
app.deleteLater()

Pour tester cette approche sans avoir réellement d'interface graphique, essayez cette version de console modifiée:

# app specific code (GUI ready)
app = QApplication(sys.argv)

layer1 = QgsVectorLayer('LineString', 'anewlayer', 'memory')
QgsMapLayerRegistry.instance().addMapLayer(layer1, False)
aLayer = QgsMapLayerRegistry.instance().mapLayersByName('anewlayer')[0]
print aLayer.name()

app.qApp.quit()

result = app.exec_()
app.deleteLater()

C'est presque la même chose que la version console (car il n'y a en fait pas d'objets graphiques), mais considérez ce quit()qui arrête l'application.

Pour démarrer une telle application sur Linux:

#!/bin/sh
export PYTHONPATH="/usr/share/qgis/python"
export LD_LIBRARY_PATH="/usr/lib64/qgis"
export QGIS_PREFIX="/usr"
python the_app.py

Et sur une machine Windows:

SET OSGEO4W_ROOT=D:\OSGeo4W64
SET QGISNAME=qgis
SET QGIS=%OSGEO4W_ROOT%\apps\%QGISNAME%
SET QGIS_PREFIX=%QGIS%

CALL %OSGEO4W_ROOT%\bin\o4w_env.bat

SET PATH=%PATH%;%QGIS%\bin
SET PYTHONPATH=%QGIS%\python;%PYTHONPATH%

python the_app.py

Une interface graphique très basique peut ressembler à ceci:

# hand-made window with simple toolbar
class Ui_DemoWindow(object):
    def setupUi(self, window):
        window.setWindowTitle('Demo')

        self.centralWidget = QWidget(window)
        self.centralWidget.setFixedSize(640, 480)
        window.setCentralWidget(self.centralWidget)
        window.move(0, 0)

        self.layout = QVBoxLayout()    
        self.layout.setContentsMargins(0, 0, 0, 0)    
        self.centralWidget.setLayout(self.layout)

        self.toolBar = QToolBar(window)
        window.addToolBar(Qt.TopToolBarArea, self.toolBar)

        # quit action
        self.actionQuit = QAction('Quit', window)
        self.actionQuit.setShortcut(QKeySequence.Quit)

        # do something, here call function to create some geometries
        self.actionCreateGeom = QAction('Geometry', window)

        # link action to GUI elements
        self.toolBar.addAction(self.actionQuit)
        self.toolBar.addSeparator()
        self.toolBar.addAction(self.actionCreateGeom)


class Main(QMainWindow, Ui_DemoWindow):

    def __init__(self):    
        QMainWindow.__init__(self)
        self.setupUi(self)

        self.canvas = QgsMapCanvas()
        self.layout.addWidget(self.canvas)

        self.connect(self.actionQuit, SIGNAL('triggered()'), self.quit) 
        self.connect(self.actionCreateGeom, SIGNAL('triggered()'), self.some_geoms) 

    # quick and dirty: create layer with some features, add layer
    # to map canvas, and zoom canvas to full view
    def some_geoms(self):
        layer = QgsVectorLayer('LineString', 'anewlayer', 'memory')
        # add some features
        prov = layer.dataProvider()
        feats = []
        feat = QgsFeature()
        feat.setGeometry(QgsGeometry().fromPolyline([QgsPoint(-1,1), QgsPoint(1, -1)]))
        feats.append(feat)
        prov.addFeatures(feats)
        layer.updateExtents()

        QgsMapLayerRegistry.instance().addMapLayer(layer)
        self.canvas.setLayerSet([QgsMapCanvasLayer(layer)])
        self.canvas.zoomToFullExtent()    

    def quit(self):
        qApp.quit() 

Comment son look après avoir appuyé sur le bouton Géométrie :

démo

En commençant par cet exemple simple, vous souhaiterez peut-être implémenter le widget de légende, les boîtes de dialogue de propriétés, les boîtes de dialogue de calque, le comportement de sélection et d'édition, etc.

Detlev
la source
En utilisant votre code tel quel, il ne se bloque plus, mais il en résulte que le script reste simplement inactif pour toujours. Il ne peut même pas être arrêté en envoyant SIGINT, il doit être arrêté avec SIGKILL. Suis-je encore en train de manquer quelque chose?
GreatEmerald
Vous pouvez exécuter une application autonome AVEC ou SANS GUI. Lorsque vous avez une interface graphique, l'interface graphique obtient le contrôle jusqu'à ce qu'elle se termine par un quit (). L'initialisation d'une telle classe GUI remplacerait le code spécifique #app dans mon exemple. Si vous n'avez pas d'interface graphique, vous ne pouvez pas utiliser de méthodes liées à l'interface graphique, comme activeLayer (). Je modifie ma réponse pour inclure un exemple.
Detlev
J'ai essayé cela (copié-collé la première partie et inséré le bloc GUI au lieu de l'éliipsis), et j'ai obtenu cette erreur:Traceback (most recent call last): File "test.py", line 25, in <module> main() File "test.py", line 15, in main window = Main() NameError: global name 'Main' is not defined
GreatEmerald
@GreatEmerald Main () ne devrait être qu'un exemple. Si vous avez créé une interface graphique avec QtDesigner ou à partir de zéro, insérez ce nom de classe à la place. Puisque je ne connais pas votre environnement d'application exact, j'ai montré les principes
Detlev
1
@wondim Veuillez consulter gis.stackexchange.com/questions/40375/…
Detlev