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.

asked Jan 18, 2016 at 9:35

1 Answer 1

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.

answered Jan 18, 2016 at 13:07
11
  • 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? Commented Jan 20, 2016 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. Commented Jan 20, 2016 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 Commented Jan 22, 2016 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 Commented Jan 22, 2016 at 11:56
  • 1
    @wondim Please have a look at gis.stackexchange.com/questions/40375/… Commented Jan 19, 2019 at 6:17

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.