Llamar a funciones PyQGIS desde editores externos (Linux)

8

¿Es posible integrar editores externos de Python (como KDevelop) con QGIS, para que sea posible ejecutar funciones qgis.core, qgis.utilsetc. , fuera de la consola de QGIS Python?

Siguiendo las pautas en el sitio web de QGIS ( http://docs.qgis.org/testing/en/docs/pyqgis_developer_cookbook/intro.html ), probé esto, pero solo devuelve 1 y nada más:

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()

Como:

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

Estoy ejecutando openSUSE Tumbleweed, de 64 bits.

Gran esmeralda
fuente

Respuestas:

8

Uso la siguiente introducción para aplicaciones independientes:

# 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()

Cuando la aplicación no necesita una GUI (por ejemplo, hacer un poco de geoprocesamiento), reemplace los puntos suspensivos con algo así:

# 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()

Para trabajar con una o más capas, no es necesario agregarlas al registro de capas del mapa, solo haga referencia a ellas por su nombre de variable (aquí layer1).

Cuando utiliza una GUI, por ejemplo, una ventana de mapeador, esta sería una clase de Python derivada de QMainWindow, típicamente diseñada con QtDesigner. Los puntos suspensivos tienen que ser reemplazados con código como en el siguiente ejemplo:

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

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

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

Para probar este enfoque sin tener una GUI, prueba esta versión de consola modificada:

# 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()

Es casi lo mismo que la versión de la consola (ya que en realidad no hay objetos gráficos), pero tenga en cuenta lo quit()que detiene la aplicación.

Para iniciar dicha aplicación en Linux:

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

Y en una máquina con 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

Una GUI muy básica puede verse así:

# 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() 

Cómo se ve después de presionar el botón Geometría :

manifestación

Comenzando con este ejemplo simple, es posible que desee implementar el widget de leyenda, los cuadros de diálogo de propiedades, los cuadros de diálogo de capa, el comportamiento de selección y edición, etc.

Detlev
fuente
Al usar su código tal cual, ya no se bloquea, pero da como resultado que el script simplemente permanezca inactivo allí para siempre. Ni siquiera se puede detener enviando SIGINT, se debe detener con SIGKILL. ¿Todavía me falta algo?
GreatEmerald
Puede ejecutar una aplicación independiente CON o SIN GUI. Cuando tiene una GUI, la GUI obtiene el control hasta que termina con un cierre (). La inicialización de dicha clase de GUI reemplazaría el código específico #app en mi ejemplo. Si no tiene una GUI, entonces no puede usar métodos relacionados con la GUI, como activeLayer (). Edito mi respuesta para incluir un ejemplo.
Detlev
Lo intenté (copié y pegué la primera parte e inserté el bloque GUI en lugar de los puntos suspensivos), y obtuve este error: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 () debería ser solo un ejemplo. Si ha creado una GUI con QtDesigner o desde cero, inserte este nombre de clase en su lugar. Como no conozco el entorno exacto de su aplicación, mostré los principios
Detlev,
1
@wondim Por favor, eche un vistazo a gis.stackexchange.com/questions/40375/…
Detlev