Chamando funções PyQGIS de editores externos (Linux)

8

É possível integrar editores Python externos (tais como KDevelop) com QGIS, de modo que seria possível executar funções em qgis.core, qgis.utilsetc. fora do QGIS Python Console?

Seguindo as diretrizes do site QGIS ( http://docs.qgis.org/testing/en/docs/pyqgis_developer_cookbook/intro.html ), tentei isso, mas ele retorna apenas 1 e nada mais:

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

Tal como:

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

Estou executando o openSUSE Tumbleweed de 64 bits.

GreatEmerald
fonte

Respostas:

8

Eu uso a seguinte introdução para aplicativos autônomos:

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

Quando o aplicativo não precisar de uma GUI (por exemplo, faça um geoprocessamento), substitua as reticências por algo assim:

# 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 trabalhar com uma ou mais camadas, não é necessário adicioná-las ao registro da camada do mapa, basta referenciá-las pelo nome da variável (aqui layer1).

Quando você usa uma GUI, por exemplo, uma janela do mapeador, essa seria uma classe python derivada do QMainWindow, normalmente projetada com o QtDesigner. As reticências devem ser substituídas pelo código, como no próximo exemplo:

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

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

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

Para testar essa abordagem sem realmente ter uma GUI, tente esta versão do console 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()

É quase o mesmo que a versão do console (como na verdade não há objetos gráficos), mas considere o quit()que interrompe o aplicativo.

Para iniciar esse aplicativo no Linux:

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

E em uma máquina 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

Uma GUI muito básica pode ser assim:

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

Como é a aparência depois de pressionar o botão Geometria :

demonstração

Começando com este exemplo simples, você pode implementar o widget de legenda, diálogos de propriedades, diálogos de camada, comportamento de seleção e edição e assim por diante.

Detlev
fonte
Usando seu código como está, ele não trava mais, mas resulta no script simplesmente ocioso para sempre. Ele não pode nem ser parado enviando SIGINT, deve ser parado com SIGKILL. Ainda estou faltando alguma coisa?
precisa saber é o seguinte
Você pode executar um aplicativo autônomo COM ou SEM GUI. Quando você tem uma GUI, a GUI obtém o controle até terminar com um quit (). A inicialização dessa classe da GUI substituiria o código específico #app no ​​meu exemplo. Se você não tiver uma GUI, não poderá usar métodos relacionados à GUI, como activeLayer (). Eu edito minha resposta para incluir um exemplo.
Detlev
Tentei fazer isso (colou-copy a primeira parte e inserido o bloco GUI em vez do eliipsis), e tenho esse erro: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 () deve ser apenas um exemplo. Se você criou uma GUI com o QtDesigner ou do zero, insira esse nome de classe. Desde que eu não sei o seu ambiente de aplicativo exata eu mostrei os princípios
Detlev
1
@wondim Por favor, dê uma olhada em gis.stackexchange.com/questions/40375/…
Detlev