Skip to content
Permalink
Browse files

Enable Python in background threads. Fixed drawing of plugin layers.

  • Loading branch information
wonder-sk committed Dec 5, 2013
1 parent 2bece09 commit e1ea342ffcdbdc7a25a4c068a2c09e999abd8d5c
Showing with 60 additions and 0 deletions.
  1. +11 −0 python/core/qgspluginlayer.sip
  2. +27 −0 src/core/qgspluginlayer.cpp
  3. +11 −0 src/core/qgspluginlayer.h
  4. +11 −0 src/python/qgspythonutilsimpl.cpp
@@ -17,4 +17,15 @@ class QgsPluginLayer : QgsMapLayer
//! (defult implementation returns nothing)
//! @note Added in v2.1
virtual QgsLegendSymbologyList legendSymbologyItems( const QSize& iconSize );

/** Return new instance of QgsMapLayerRenderer that will be used for rendering of given context
*
* The default implementation returns map layer renderer which just calls draw().
* This may work, but it is unsafe for multi-threaded rendering because of the run
* conditions that may happen (e.g. something is changed in the layer while it is
* being rendered).
*
* @note added in 2.1
*/
virtual QgsMapLayerRenderer* createMapRenderer( QgsRenderContext& rendererContext ) /Factory/;
};
@@ -14,6 +14,8 @@
***************************************************************************/
#include "qgspluginlayer.h"

#include "qgsmaplayerrenderer.h"

QgsPluginLayer::QgsPluginLayer( QString layerType, QString layerName )
: QgsMapLayer( PluginLayer, layerName ), mPluginLayerType( layerType )
{
@@ -34,3 +36,28 @@ QgsLegendSymbologyList QgsPluginLayer::legendSymbologyItems( const QSize& iconSi
Q_UNUSED( iconSize );
return QgsLegendSymbologyList();
}

/** Fallback layer renderer implementation for layer that do not support map renderer yet. */
class QgsPluginLayerRenderer : public QgsMapLayerRenderer
{
public:
QgsPluginLayerRenderer( QgsPluginLayer* layer, QgsRenderContext& rendererContext )
: QgsMapLayerRenderer( layer->id() )
, mLayer( layer )
, mRendererContext( rendererContext )
{}

virtual bool render()
{
return mLayer->draw( mRendererContext );
}

protected:
QgsPluginLayer* mLayer;
QgsRenderContext& mRendererContext;
};

QgsMapLayerRenderer* QgsPluginLayer::createMapRenderer( QgsRenderContext& rendererContext )
{
return new QgsPluginLayerRenderer( this, rendererContext );
}
@@ -46,6 +46,17 @@ class CORE_EXPORT QgsPluginLayer : public QgsMapLayer
//! @note Added in v2.1
virtual QgsLegendSymbologyList legendSymbologyItems( const QSize& iconSize );

/** Return new instance of QgsMapLayerRenderer that will be used for rendering of given context
*
* The default implementation returns map layer renderer which just calls draw().
* This may work, but it is unsafe for multi-threaded rendering because of the run
* conditions that may happen (e.g. something is changed in the layer while it is
* being rendered).
*
* @note added in 2.1
*/
virtual QgsMapLayerRenderer* createMapRenderer( QgsRenderContext& rendererContext );

protected:
QString mPluginLayerType;
};
@@ -33,6 +33,7 @@
#include <QStringList>
#include <QDir>

PyThreadState* _mainState;

QgsPythonUtilsImpl::QgsPythonUtilsImpl()
{
@@ -53,6 +54,9 @@ void QgsPythonUtilsImpl::initPython( QgisInterface* interface )
// initialize python
Py_Initialize();

// initialize threading AND acquire GIL
PyEval_InitThreads();

mPythonEnabled = true;

mMainModule = PyImport_AddModule( "__main__" ); // borrowed reference
@@ -167,6 +171,13 @@ void QgsPythonUtilsImpl::initPython( QgisInterface* interface )

QString startuppath = homePythonPath() + " + \"/startup.py\"";
runString( "if os.path.exists(" + startuppath + "): from startup import *\n" );

// release GIL!
// Later on, we acquire GIL just before doing some Python calls and
// release GIL again when the work with Python API is done.
// (i.e. there must be PyGILState_Ensure + PyGILState_Release pair
// around any calls to Python API, otherwise we may segfault!)
_mainState = PyEval_SaveThread();
}

void QgsPythonUtilsImpl::exitPython()

0 comments on commit e1ea342

Please sign in to comment.
You can’t perform that action at this time.