11

if I try to load the openlayers plugin in a standalone application it always fails with False, although paths are set, and plugin shows available.

(tested on Ubuntu 14.04, QGIS 2.4, python-2.7)

#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys, os
import qgis
import qgis.gui, qgis.utils
from qgis.core import *
from PyQt4 import QtGui
# open an app
app = QtGui.QApplication(sys.argv)
# supply path to where is your qgis installed
QgsApplication.setPrefixPath(u'/usr', True)
# load providers
QgsApplication.initQgis()
# set up the plugins
qgis.utils.plugin_paths = \
 ['/usr/share/qgis/python/plugins',
 os.path.expanduser('~/.qgis2/python/plugins'),]
qgis.utils.updateAvailablePlugins()
print qgis.utils.available_plugins
print "...load:", qgis.utils.loadPlugin(u'openlayers_plugin')
print "...start:", qgis.utils.startPlugin(u'openlayers_plugin')
print "active:\n",qgis.utils.active_plugins
canvas = qgis.gui.QgsMapCanvas()
canvas.show()

what is the issue here ? post How to fetch openlayers layers from pyqgis? refers to the embedded python console, where the plugin was magically available before.

thanks!

asked Sep 23, 2014 at 9:16
2
  • Why do you need the open layers plugin outside in a script? Commented Sep 23, 2014 at 9:52
  • Another solution to this problem was posted here Commented Jun 8, 2016 at 12:17

2 Answers 2

8

Being very keen on the TDD paradigm, I've spent some time putting together a dummy-interface (based on some code fragments I found on the internet) which enables calling QGIS and QGIS-plugins standalone.

The interface I came up with looks as follows and I used it for all my unit-tests since:

# coding=utf-8
"""QGIS plugin implementation.
.. note:: This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.
.. note:: The basis of this source code was copied from the 'postgis viewer' application
 with original authors:
 Copyright (c) 2010 by Ivan Mincik, [email protected]
 Copyright (c) 2011 German Carrillo, [email protected]
 Copyright (c) 2014 Tim Sutton, [email protected]
"""
from qgis._core import QgsVectorLayer
from PyQt4.QtCore import QObject, pyqtSlot, pyqtSignal, QCoreApplication, QSize
from PyQt4.QtGui import QWidget
from qgis.core import QgsMapLayerRegistry, QgsApplication, QgsVectorLayer
from qgis.gui import QgsMapCanvasLayer, QgsMapCanvas
import logging
import sys
from mole import config
LOGGER = logging.getLogger('QGIS')
def set_up_interface():
 """
 Sets up a QGIS pseudo-application which enables calling methods as if when calling them from QGIS-console.
 :return qgis_app: Pseudo QGIS-instance
 :rtype: QgsApplication
 :return canvas: The map canvas
 :rtype: QgsMapCanvas
 :return iface: A dummy interface, giving access to needed method-calls
 :rtype: QgisInterface
 """
 gui_flag = True # All test will run qgis in gui mode
 qgis_app = QgsApplication(sys.argv, gui_flag)
 prefix_path = config.qgis_prefix_path()
 qgis_app.setPrefixPath(prefix_path, True)
 qgis_app.initQgis()
 QCoreApplication.setOrganizationName('QGIS')
 QCoreApplication.setApplicationName('QGIS2')
 # parent = QWidget()
 # canvas = QgsMapCanvas(parent)
 # canvas.resize(QSize(400, 400))
 canvas = MyMapCanvas()
 iface = QgisInterface(canvas)
 return qgis_app, canvas, iface
#noinspection PyMethodMayBeStatic,PyPep8Naming
class QgisInterface(QObject):
 """Class to expose QGIS objects and functions to plugins.
 This class is here for enabling us to run unit tests only,
 so most methods are simply stubs.
 """
 currentLayerChanged = pyqtSignal(QgsMapCanvasLayer)
 def __init__(self, canvas):
 """Constructor
 :param canvas:
 """
 QObject.__init__(self)
 self.canvas = canvas
 self.legend_interface = MyLegendInterface()
 self.active_layer = None
 # Set up slots so we can mimic the behaviour of QGIS when layers
 # are added.
 LOGGER.debug('Initialising canvas...')
 # noinspection PyArgumentList
 QgsMapLayerRegistry.instance().layersAdded.connect(self.addLayer)
 # noinspection PyArgumentList
 QgsMapLayerRegistry.instance().layerWasAdded.connect(self.addLayer)
 # noinspection PyArgumentList
 QgsMapLayerRegistry.instance().removeAll.connect(self.removeAllLayers)
 # For processing module
 self.destCrs = None
 @pyqtSlot('QgsMapLayer')
 def addLayer(self, layer):
 """Handle a layer being added to the registry so it shows up in canvas.
 :param layer: list<QgsMapLayer> list of map layers that were added
 .. note: The QgsInterface api does not include this method, it is added
 here as a helper to facilitate testing.
 .. note: The addLayer method was deprecated in QGIS 1.8 so you should
 not need this method much.
 """
 # set the recently added layer as active
 # LOGGER.debug('Layer Count Before: %s' % len(self.canvas.layers()))
 current_layers = self.canvas.layers()
 final_layers = [] + current_layers
 final_layers.append(QgsMapCanvasLayer(layer))
 self.canvas.setLayerSet(final_layers)
 self.active_layer = layer
 @pyqtSlot()
 def removeAllLayers(self):
 """Remove layers from the canvas before they get deleted."""
 self.canvas.setLayerSet([])
 def newProject(self):
 """Create new project."""
 # noinspection PyArgumentList
 QgsMapLayerRegistry.instance().removeAllMapLayers()
 def legendInterface(self):
 """Get the legend."""
 return self.legend_interface
 def activeLayer(self):
 """Get pointer to the active layer (layer selected in the legend)."""
 return self.active_layer
 def setActiveLayer(self, layer):
 """Set the given layer as active.
 :param layer: Layer that shall be set active
 :type layer: QgsMapLayer
 """
 self.active_layer = layer
 class actionAddFeature(object):
 def __init__(self):
 pass
 def trigger(self):
 pass
 class actionZoomToLayer(object):
 def __init__(self):
 pass
 def trigger(self):
 pass
 # ---------------- API Mock for QgsInterface follows -------------------
 def zoomFull(self):
 """Zoom to the map full extent."""
 pass
 def zoomToPrevious(self):
 """Zoom to previous view extent."""
 pass
 def zoomToNext(self):
 """Zoom to next view extent."""
 pass
 def zoomToActiveLayer(self):
 """Zoom to extent of active layer."""
 pass
 def addVectorLayer(self, path, base_name, provider_key):
 """Add a vector layer.
 :param path: Path to layer.
 :type path: str
 :param base_name: Base name for layer.
 :type base_name: str
 :param provider_key: Provider key e.g. 'ogr'
 :type provider_key: str
 """
 pass
 def addRasterLayer(self, path, base_name):
 """Add a raster layer given a raster layer file name
 :param path: Path to layer.
 :type path: str
 :param base_name: Base name for layer.
 :type base_name: str
 """
 pass
 def addToolBarIcon(self, action):
 """Add an icon to the plugins toolbar.
 :param action: Action to add to the toolbar.
 :type action: QAction
 """
 pass
 def removeToolBarIcon(self, action):
 """Remove an action (icon) from the plugin toolbar.
 :param action: Action to add to the toolbar.
 :type action: QAction
 """
 pass
 def addToolBar(self, name):
 """Add toolbar with specified name.
 :param name: Name for the toolbar.
 :type name: str
 """
 pass
 def mapCanvas(self):
 """Return a pointer to the map canvas."""
 return self.canvas
 def mainWindow(self):
 """Return a pointer to the main window.
 In case of QGIS it returns an instance of QgisApp.
 """
 pass
 def addDockWidget(self, area, dock_widget):
 """Add a dock widget to the main window.
 :param area: Where in the ui the dock should be placed.
 :type area:
 :param dock_widget: A dock widget to add to the UI.
 :type dock_widget: QDockWidget
 """
 pass
class MyLegendInterface(object):
 def __init__(self):
 self.layer_visibility = {}
 def setLayerVisible(self, layer, yes_no):
 self.layer_visibility[layer.name()] = yes_no
 def isLayerVisible(self, layer):
 try:
 return self.layer_visibility[layer.name()]
 except KeyError:
 print('Layer {} has not been set (in)visible yet.'.format(layer.name()))
 return False
class MyMapCanvas(object):
 def __init__(self):
 self.layer_set = []
 def layers(self):
 return self.layer_set
 def layer(self, index):
 layer = self.layer_set[index].layer()
 return layer
 def setLayerSet(self, layer_set):
 self.layer_set = layer_set
 def layerCount(self):
 return len(self.layer_set)

If you want to test/use QGIS plus the interaction with an installed plugin now, do the following (in your unittest setUp e.g.):

qgis_app, canvas, iface = set_up_interface()
plugin_name = 'openlayers_plugin'
utils.plugin_paths = [os.path.expanduser('~/.qgis2/python/plugins')]
utils.updateAvailablePlugins()
utils.loadPlugin(plugin_name)
utils.iface = self.iface
utils.startPlugin(plugin_name) 

For some more usage-examples and some real-life examples on unit-testing QGIS-apps you can check out our github-page (https://github.com/UdK-VPT/Open_eQuarter/tree/master/mole). The folder tests contains all unit-tests (which are mostly testing the modules in the the qgisinteraction package, which contains a module, that interacts with the point_sampling_tool-plugin).

answered Apr 27, 2015 at 15:40
1
  • I think I've got this to work but had to do utils.iface = ifacetwo lines earlier and had to mock out a QgsMessageBar in the iface... Commented Jun 28, 2016 at 7:30
4

This is never, well maybe with some hacks, going to work well. Plugins normally use the QgisInterface object which gives access to the QGIS interface and methods. You don't have this object in your standalone script. Most plugins, if not all, are not designed to run outside of QGIS like this.

answered Sep 23, 2014 at 9:52
1
  • 3
    This is important to know, but regrettable nevertheless. As an experienced beginner in QGis I think it were great to be able to develop certain data processing steps with all of QGis' tools in the full GUI environment, while later being able to have a stripped-down just-do-the-job script version. Something like a DummyQgisInterface should be possible... Commented Feb 22, 2015 at 12:21

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.