8

Is it possible to integrate external Python editors (such as KDevelop) with QGIS, so that it would be possible to run functions in qgis.core, qgis.utils etc. outside of the QGIS Python Console?

Following the guidelines on the QGIS website (http://docs.qgis.org/testing/en/docs/pyqgis_developer_cookbook/intro.html), I tried this, but it just returns 1 and nothing else:

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

Such as:

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

I am running openSUSE Tumbleweed, 64-bit.

8

I use following intro for stand-alone applications:

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

When the app do not need a GUI (eg do some geoprocessing), replace the ellipsis with something like that:

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

To work with one or more layers you do not have to add them to map layer registry, just reference them by their variable name (here layer1).

When you use a GUI, for example a mapper window, this would be a python class derived from QMainWindow, typically designed with QtDesigner. The ellipsis have to be replaced with code as in the next example:

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

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

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

To test this approach without actually having a GUI try this modified console version:

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

It is almost the same as the console version (as there are actually no graphical objects), but regard the quit() which stops the application.

To start such app on Linux:

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

And on a Windows machine:

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

A very basic GUI may look like this:

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

How its look after pressing button Geometry:

demo

Starting with this simple example you may wish to implement the legend widget, property dialogs, layer dialogs, selection and editing behaviour, and so on.

  • Using your code as-is, it doesn't crash any more, but it does result in the script simply idling there forever. It cannot even be stopped by sending SIGINT, it has to be stopped with SIGKILL. Am I still missing something? – GreatEmerald Jan 20 '16 at 8:52
  • You may run a stand-alone app WITH or WITHOUT a GUI. When you have a GUI, then the GUI gets the control until it ends with a quit(). Initialization of such GUI class would replace the #app specific code in my example. If you do not have a GUI then you cannot use GUI related methods, like activeLayer(). I edit my answer to include an example. – Detlev Jan 20 '16 at 10:22
  • I tried that (copy-pasted the first part and inserted the GUI block instead of the eliipsis), and got this 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 Jan 22 '16 at 9:36
  • @GreatEmerald Main() should be just an example. If you have build a GUI with QtDesigner or from scratch then insert this class name instead. Since I dont know your exact app environment I showed the principles – Detlev Jan 22 '16 at 11:56
  • 1
    @wondim Please have a look at gis.stackexchange.com/questions/40375/… – Detlev Jan 19 at 6:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.