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.
1 Answer 1
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:
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– GreatEmerald2016年01月20日 08:52:12 +00:00Commented 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.Detlev– Detlev2016年01月20日 10:22:58 +00:00Commented 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
GreatEmerald– GreatEmerald2016年01月22日 09:36:57 +00:00Commented 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 principlesDetlev– Detlev2016年01月22日 11:56:43 +00:00Commented Jan 22, 2016 at 11:56
-
1@wondim Please have a look at gis.stackexchange.com/questions/40375/…Detlev– Detlev2019年01月19日 06:17:35 +00:00Commented Jan 19, 2019 at 6:17