Merge pull request #4478 from nyalldawson/layer_store

Split off map layer storage handling from QgsProject to QgsMapLayerStore
This commit is contained in:
Nyall Dawson 2017-05-06 13:00:05 +10:00 committed by GitHub
commit b5ae888b60
18 changed files with 2002 additions and 722 deletions

View File

@ -92,6 +92,7 @@
%Include qgsmaplayermodel.sip
%Include qgsmaplayerproxymodel.sip
%Include qgsmaplayerrenderer.sip
%Include qgsmaplayerstore.sip
%Include qgsmaplayerstylemanager.sip
%Include qgsmaprenderercache.sip
%Include qgsmaprenderercustompainterjob.sip

View File

@ -75,11 +75,11 @@ class QgsProcessingContext
Sets the expression ``context``.
%End
QgsProject &temporaryLayerStore();
QgsMapLayerStore *temporaryLayerStore();
%Docstring
Returns a reference to the project used for storing temporary layers during
Returns a reference to the layer store used for storing temporary layers during
algorithm execution.
:rtype: QgsProject
:rtype: QgsMapLayerStore
%End
QgsFeatureRequest::InvalidGeometryCheck invalidGeometryCheck() const;

View File

@ -0,0 +1,313 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/qgsmaplayerstore.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
class QgsMapLayerStore : QObject
{
%Docstring
A storage object for map layers, in which the layers are owned by the
store and have their lifetime bound to the store.
.. versionadded:: 3.0
%End
%TypeHeaderCode
#include "qgsmaplayerstore.h"
%End
public:
explicit QgsMapLayerStore( QObject *parent /TransferThis/ = 0 );
%Docstring
Constructor for QgsMapLayerStore.
%End
~QgsMapLayerStore();
int count() const;
%Docstring
Returns the number of layers contained in the store.
:rtype: int
%End
int __len__() const;
%Docstring
Returns the number of layers contained in the store.
:rtype: int
%End
%MethodCode
sipRes = sipCpp->count();
%End
QgsMapLayer *mapLayer( const QString &id ) const;
%Docstring
Retrieve a pointer to a layer by layer ``id``.
\param id ID of layer to retrieve
:return: matching layer, or None if no matching layer found
.. seealso:: mapLayersByName()
.. seealso:: mapLayers()
:rtype: QgsMapLayer
%End
QList<QgsMapLayer *> mapLayersByName( const QString &name ) const;
%Docstring
Retrieve a list of matching layers by layer ``name``.
\param name name of layers to match
:return: list of matching layers
.. seealso:: mapLayer()
.. seealso:: mapLayers()
:rtype: list of QgsMapLayer
%End
QMap<QString, QgsMapLayer *> mapLayers() const;
%Docstring
Returns a map of all layers by layer ID.
.. seealso:: mapLayer()
.. seealso:: mapLayersByName()
.. seealso:: layers()
:rtype: QMap<str, QgsMapLayer *>
%End
QList<QgsMapLayer *> addMapLayers( const QList<QgsMapLayer *> &layers /Transfer/);
%Docstring
\brief
Add a list of ``layers`` to the store. Ownership of the layers is transferred
to the store.
The layersAdded() and layerWasAdded() signals will always be emitted.
\param layers A list of layer which should be added to the store.
\param takeOwnership Ownership will be transferred to the layer store.
If you specify false here you have take care of deleting
the layers yourself. Not available in Python.
:return: a list of the map layers that were added
successfully. If a layer is invalid, or already exists in the store,
it will not be part of the returned list.
.. seealso:: addMapLayer()
:rtype: list of QgsMapLayer
%End
QgsMapLayer *addMapLayer( QgsMapLayer *layer /Transfer/);
%Docstring
\brief
Add a ``layer`` to the store. Ownership of the layer is transferred to the
store.
The layersAdded() and layerWasAdded() signals will always be emitted.
If you are adding multiple layers at once, you should use
addMapLayers() instead.
\param layer A layer to add to the store
\param takeOwnership Ownership will be transferred to the layer store.
If you specify false here you have take care of deleting
the layers yourself. Not available in Python.
:return: None if unable to add layer, otherwise pointer to newly added layer
.. seealso:: addMapLayers
.. note::
Use addMapLayers() if adding more than one layer at a time.
.. seealso:: addMapLayers()
:rtype: QgsMapLayer
%End
void removeMapLayers( const QStringList &layerIds ) /PyName=removeMapLayersById/;
%Docstring
\brief
Remove a set of layers from the store by layer ID.
The specified layers will be removed from the store.
These layers will also be deleted.
\param layerIds list of IDs of the layers to remove
.. seealso:: takeMapLayer()
.. seealso:: removeMapLayer()
.. seealso:: removeAllMapLayers()
.. note::
available in Python bindings as removeMapLayersById.
%End
void removeMapLayers( const QList<QgsMapLayer *> &layers );
%Docstring
\brief
Remove a set of ``layers`` from the store.
The specified layers will be removed from the store.
These layers will also be deleted.
\param layers A list of layers to remove. Null pointers are ignored.
.. seealso:: takeMapLayer()
.. seealso:: removeMapLayer()
.. seealso:: removeAllMapLayers()
%End
void removeMapLayer( const QString &id );
%Docstring
\brief
Remove a layer from the store by layer ``id``.
The specified layer will be removed from the store. The layer will also be deleted.
\param id ID of the layer to remove
.. seealso:: takeMapLayer()
.. seealso:: removeMapLayers()
.. seealso:: removeAllMapLayers()
%End
void removeMapLayer( QgsMapLayer *layer );
%Docstring
\brief
Remove a ``layer`` from the store.
The specified layer will be removed from the store. The layer will also be deleted.
\param layer The layer to remove. Null pointers are ignored.
.. seealso:: takeMapLayer()
.. seealso:: removeMapLayers()
.. seealso:: removeAllMapLayers()
%End
QgsMapLayer *takeMapLayer( QgsMapLayer *layer ) /TransferBack/;
%Docstring
Takes a ``layer`` from the store. If the layer was owned by the store, the
layer will be returned without deleting it. The caller takes ownership of
the layer and is responsible for deleting it.
.. seealso:: removeMapLayer()
:rtype: QgsMapLayer
%End
void removeAllMapLayers();
%Docstring
Removes all registered layers. These layers will also be deleted.
.. note::
Calling this method will cause the removeAll() signal to
be emitted.
.. seealso:: removeMapLayer()
.. seealso:: removeMapLayers()
%End
signals:
void layersWillBeRemoved( const QStringList &layerIds );
%Docstring
Emitted when one or more layers are about to be removed from the store.
\param layerIds A list of IDs for the layers which are to be removed.
.. seealso:: layerWillBeRemoved()
.. seealso:: layersRemoved()
%End
void layersWillBeRemoved( const QList<QgsMapLayer *> &layers );
%Docstring
Emitted when one or more layers are about to be removed from the store.
\param layers A list of layers which are to be removed.
.. seealso:: layerWillBeRemoved()
.. seealso:: layersRemoved()
%End
void layerWillBeRemoved( const QString &layerId );
%Docstring
Emitted when a layer is about to be removed from the store.
\param layerId The ID of the layer to be removed.
.. note::
Consider using layersWillBeRemoved() instead.
.. seealso:: layersWillBeRemoved()
.. seealso:: layerRemoved()
%End
void layerWillBeRemoved( QgsMapLayer *layer );
%Docstring
Emitted when a layer is about to be removed from the store.
\param layer The layer to be removed.
.. note::
Consider using layersWillBeRemoved() instead.
.. seealso:: layersWillBeRemoved()
.. seealso:: layerRemoved()
%End
void layersRemoved( const QStringList &layerIds );
%Docstring
Emitted after one or more layers were removed from the store.
\param layerIds A list of IDs of the layers which were removed.
.. seealso:: layersWillBeRemoved()
%End
void layerRemoved( const QString &layerId );
%Docstring
Emitted after a layer was removed from the store.
\param layerId The ID of the layer removed.
.. note::
Consider using layersRemoved() instead
.. seealso:: layerWillBeRemoved()
%End
void allLayersRemoved();
%Docstring
Emitted when all layers are removed, before layersWillBeRemoved() and
layerWillBeRemoved() signals are emitted. The layersWillBeRemoved() and
layerWillBeRemoved() signals will still be emitted following this signal.
You can use this signal to do easy (and fast) cleanup.
%End
void layersAdded( const QList<QgsMapLayer *> &layers );
%Docstring
Emitted when one or more layers were added to the store.
\param layers List of layers which have been added.
.. seealso:: legendLayersAdded()
.. seealso:: layerWasAdded()
%End
void layerWasAdded( QgsMapLayer *layer );
%Docstring
Emitted when a ``layer`` was added to the store.
.. note::
Consider using layersAdded() instead
.. seealso:: layersAdded()
%End
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/qgsmaplayerstore.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/

View File

@ -422,6 +422,8 @@ class QgsProject : QObject, QgsExpressionContextGenerator
*/
const QgsLabelingEngineSettings &labelingEngineSettings() const;
QgsMapLayerStore *layerStore();
int count() const;
QgsMapLayer *mapLayer( const QString &layerId ) const;

View File

@ -481,7 +481,7 @@ def createVectorWriter(destination, encoding, fields, geometryType, crs, context
layer = QgsVectorLayer(uri, destination, 'memory')
sink = layer.dataProvider()
context.temporaryLayerStore().addMapLayer(layer, False)
context.temporaryLayerStore().addMapLayer(layer)
destination = layer.id()
elif destination.startswith(POSTGIS_LAYER_PREFIX):
uri = QgsDataSourceUri(destination[len(POSTGIS_LAYER_PREFIX):])
@ -518,7 +518,7 @@ def createVectorWriter(destination, encoding, fields, geometryType, crs, context
layer = QgsVectorLayer(uri.uri(), uri.table(), "postgres")
sink = layer.dataProvider()
context.temporaryLayerStore().addMapLayer(layer, False)
context.temporaryLayerStore().addMapLayer(layer)
elif destination.startswith(SPATIALITE_LAYER_PREFIX):
uri = QgsDataSourceUri(destination[len(SPATIALITE_LAYER_PREFIX):])
try:
@ -548,7 +548,7 @@ def createVectorWriter(destination, encoding, fields, geometryType, crs, context
layer = QgsVectorLayer(uri.uri(), uri.table(), "spatialite")
sink = layer.dataProvider()
context.temporaryLayerStore().addMapLayer(layer, False)
context.temporaryLayerStore().addMapLayer(layer)
else:
formats = QgsVectorFileWriter.supportedFiltersAndFormats()
OGRCodes = {}

View File

@ -178,6 +178,7 @@ SET(QGIS_CORE_SRCS
qgsmaplayerlegend.cpp
qgsmaplayermodel.cpp
qgsmaplayerproxymodel.cpp
qgsmaplayerstore.cpp
qgsmaplayerstylemanager.cpp
qgsmaprenderercache.cpp
qgsmaprenderercustompainterjob.cpp
@ -527,6 +528,7 @@ SET(QGIS_CORE_MOC_HDRS
qgsmaplayerlegend.h
qgsmaplayermodel.h
qgsmaplayerproxymodel.h
qgsmaplayerstore.h
qgsmaplayerstylemanager.h
qgsmaprenderercache.h
qgsmaprenderercustompainterjob.h

View File

@ -90,10 +90,10 @@ class CORE_EXPORT QgsProcessingContext
void setExpressionContext( const QgsExpressionContext &context ) { mExpressionContext = context; }
/**
* Returns a reference to the project used for storing temporary layers during
* Returns a reference to the layer store used for storing temporary layers during
* algorithm execution.
*/
QgsProject &temporaryLayerStore() { return tempProject; }
QgsMapLayerStore *temporaryLayerStore() { return &tempLayerStore; }
/**
* Returns the behavior used for checking invalid geometries in input layers.
@ -147,7 +147,7 @@ class CORE_EXPORT QgsProcessingContext
QgsProcessingContext::Flags mFlags = 0;
QPointer< QgsProject > mProject;
//! Temporary project owned by the context, used for storing temporarily loaded map layers
QgsProject tempProject;
QgsMapLayerStore tempLayerStore;
QgsExpressionContext mExpressionContext;
QgsFeatureRequest::InvalidGeometryCheck mInvalidGeometryCheck = QgsFeatureRequest::GeometryNoCheck;
std::function< void( const QgsFeature & ) > mInvalidGeometryCallback;

View File

@ -85,12 +85,27 @@ QList<QgsMapLayer *> QgsProcessingUtils::compatibleLayers( QgsProject *project,
return layers;
}
QgsMapLayer *QgsProcessingUtils::mapLayerFromProject( const QString &string, QgsProject *project )
QgsMapLayer *QgsProcessingUtils::mapLayerFromStore( const QString &string, QgsMapLayerStore *store )
{
if ( string.isEmpty() )
if ( !store || string.isEmpty() )
return nullptr;
QList< QgsMapLayer * > layers = compatibleLayers( project, false );
QList< QgsMapLayer * > layers = store->mapLayers().values();
layers.erase( std::remove_if( layers.begin(), layers.end(), []( QgsMapLayer * layer )
{
switch ( layer->type() )
{
case QgsMapLayer::VectorLayer:
return !canUseLayer( qobject_cast< QgsVectorLayer * >( layer ) );
case QgsMapLayer::RasterLayer:
return !canUseLayer( qobject_cast< QgsRasterLayer * >( layer ) );
case QgsMapLayer::PluginLayer:
return true;
}
return true;
} ), layers.end() );
Q_FOREACH ( QgsMapLayer *l, layers )
{
if ( l->id() == string )
@ -108,6 +123,7 @@ QgsMapLayer *QgsProcessingUtils::mapLayerFromProject( const QString &string, Qgs
}
return nullptr;
}
///@cond PRIVATE
class ProjectionSettingRestorer
{
@ -159,11 +175,15 @@ QgsMapLayer *QgsProcessingUtils::mapLayerFromString( const QString &string, QgsP
return nullptr;
// prefer project layers
QgsMapLayer *layer = mapLayerFromProject( string, context.project() );
if ( layer )
return layer;
QgsMapLayer *layer = nullptr;
if ( context.project() )
{
QgsMapLayer *layer = mapLayerFromStore( string, context.project()->layerStore() );
if ( layer )
return layer;
}
layer = mapLayerFromProject( string, &context.temporaryLayerStore() );
layer = mapLayerFromStore( string, context.temporaryLayerStore() );
if ( layer )
return layer;
@ -173,7 +193,7 @@ QgsMapLayer *QgsProcessingUtils::mapLayerFromString( const QString &string, QgsP
layer = loadMapLayerFromString( string );
if ( layer )
{
context.temporaryLayerStore().addMapLayer( layer );
context.temporaryLayerStore()->addMapLayer( layer );
return layer;
}
else

View File

@ -27,6 +27,7 @@
class QgsProject;
class QgsProcessingContext;
class QgsMapLayerStore;
#include <QString>
@ -134,19 +135,19 @@ class CORE_EXPORT QgsProcessingUtils
static bool canUseLayer( const QgsRasterLayer *layer );
static bool canUseLayer( const QgsVectorLayer *layer,
const QList< QgsWkbTypes::GeometryType > &geometryTypes );
const QList< QgsWkbTypes::GeometryType > &geometryTypes = QList< QgsWkbTypes::GeometryType >() );
/**
* Interprets a \a string as a map layer from a project.
* Interprets a \a string as a map layer from a store.
*
* This method attempts to match a string to a project map layer, using
* This method attempts to match a string to a store map layer, using
* first the layer ID, then layer names, and finally layer source.
* If the string matches a normalized version of any layer source
* for layers in the specified \a project, then those matching layers will be
* for layers in the specified \a store, then those matching layers will be
* returned.
* \see mapLayerFromString()
*/
static QgsMapLayer *mapLayerFromProject( const QString &string, QgsProject *project );
static QgsMapLayer *mapLayerFromStore( const QString &string, QgsMapLayerStore *store );
/**
* Interprets a string as a map layer. The method will attempt to

View File

@ -0,0 +1,197 @@
/***************************************************************************
qgsmaplayerstore.cpp
--------------------
begin : May 2017
copyright : (C) 2017 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#include "qgsmaplayerstore.h"
#include "qgslogger.h"
QgsMapLayerStore::QgsMapLayerStore( QObject *parent )
: QObject( parent )
{}
QgsMapLayerStore::~QgsMapLayerStore()
{
removeAllMapLayers();
}
int QgsMapLayerStore::count() const
{
return mMapLayers.size();
}
QgsMapLayer *QgsMapLayerStore::mapLayer( const QString &layerId ) const
{
return mMapLayers.value( layerId );
}
QList<QgsMapLayer *> QgsMapLayerStore::mapLayersByName( const QString &layerName ) const
{
QList<QgsMapLayer *> myResultList;
Q_FOREACH ( QgsMapLayer *layer, mMapLayers )
{
if ( layer->name() == layerName )
{
myResultList << layer;
}
}
return myResultList;
}
QList<QgsMapLayer *> QgsMapLayerStore::addMapLayers( const QList<QgsMapLayer *> &layers, bool takeOwnership )
{
QList<QgsMapLayer *> myResultList;
Q_FOREACH ( QgsMapLayer *myLayer, layers )
{
if ( !myLayer || !myLayer->isValid() )
{
QgsDebugMsg( "Cannot add invalid layers" );
continue;
}
//check the layer is not already registered!
if ( !mMapLayers.contains( myLayer->id() ) )
{
mMapLayers[myLayer->id()] = myLayer;
myResultList << mMapLayers[myLayer->id()];
if ( takeOwnership )
{
myLayer->setParent( this );
}
connect( myLayer, &QObject::destroyed, this, &QgsMapLayerStore::onMapLayerDeleted );
emit layerWasAdded( myLayer );
}
}
if ( !myResultList.isEmpty() )
{
emit layersAdded( myResultList );
}
return myResultList;
}
QgsMapLayer *
QgsMapLayerStore::addMapLayer( QgsMapLayer *layer, bool takeOwnership )
{
QList<QgsMapLayer *> addedLayers;
addedLayers = addMapLayers( QList<QgsMapLayer *>() << layer, takeOwnership );
return addedLayers.isEmpty() ? nullptr : addedLayers[0];
}
void QgsMapLayerStore::removeMapLayers( const QStringList &layerIds )
{
QList<QgsMapLayer *> layers;
Q_FOREACH ( const QString &myId, layerIds )
{
layers << mMapLayers.value( myId );
}
removeMapLayers( layers );
}
void QgsMapLayerStore::removeMapLayers( const QList<QgsMapLayer *> &layers )
{
if ( layers.isEmpty() )
return;
QStringList layerIds;
QList<QgsMapLayer *> layerList;
Q_FOREACH ( QgsMapLayer *layer, layers )
{
// check layer and the store contains it
if ( layer && mMapLayers.contains( layer->id() ) )
{
layerIds << layer->id();
layerList << layer;
}
}
if ( layerIds.isEmpty() )
return;
emit layersWillBeRemoved( layerIds );
emit layersWillBeRemoved( layerList );
Q_FOREACH ( QgsMapLayer *lyr, layerList )
{
QString myId( lyr->id() );
emit layerWillBeRemoved( myId );
emit layerWillBeRemoved( lyr );
mMapLayers.remove( myId );
if ( lyr->parent() == this )
{
delete lyr;
}
emit layerRemoved( myId );
}
emit layersRemoved( layerIds );
}
void QgsMapLayerStore::removeMapLayer( const QString &layerId )
{
removeMapLayers( QList<QgsMapLayer *>() << mMapLayers.value( layerId ) );
}
void QgsMapLayerStore::removeMapLayer( QgsMapLayer *layer )
{
if ( layer )
removeMapLayers( QList<QgsMapLayer *>() << layer );
}
QgsMapLayer *QgsMapLayerStore::takeMapLayer( QgsMapLayer *layer )
{
if ( !layer )
return nullptr;
if ( mMapLayers.contains( layer->id() ) )
{
emit layersWillBeRemoved( QStringList() << layer->id() );
emit layersWillBeRemoved( QList<QgsMapLayer *>() << layer );
emit layerWillBeRemoved( layer->id() );
emit layerWillBeRemoved( layer );
mMapLayers.remove( layer->id() );
layer->setParent( nullptr );
emit layerRemoved( layer->id() );
emit layersRemoved( QStringList() << layer->id() );
return layer;
}
return nullptr; //don't return layer - it wasn't owned and accordingly we aren't transferring ownership
}
void QgsMapLayerStore::removeAllMapLayers()
{
emit allLayersRemoved();
// now let all observers know to clear themselves,
// and then consequently any of their map legends
removeMapLayers( mMapLayers.keys() );
mMapLayers.clear();
}
void QgsMapLayerStore::onMapLayerDeleted( QObject *obj )
{
QString id = mMapLayers.key( static_cast<QgsMapLayer *>( obj ) );
if ( !id.isNull() )
{
QgsDebugMsg( QString( "Map layer deleted without unregistering! %1" ).arg( id ) );
mMapLayers.remove( id );
}
}
QMap<QString, QgsMapLayer *> QgsMapLayerStore::mapLayers() const
{
return mMapLayers;
}

337
src/core/qgsmaplayerstore.h Normal file
View File

@ -0,0 +1,337 @@
/***************************************************************************
qgsmaplayerstore.h
------------------
begin : May 2017
copyright : (C) 2017 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
#ifndef QGSMAPLAYERSTORE_H
#define QGSMAPLAYERSTORE_H
#include "qgis_core.h"
#include "qgis.h"
#include "qgsmaplayer.h"
#include <QObject>
/**
* \class QgsMapLayerStore
* \ingroup core
* A storage object for map layers, in which the layers are owned by the
* store and have their lifetime bound to the store.
* \since QGIS 3.0
*/
class CORE_EXPORT QgsMapLayerStore : public QObject
{
Q_OBJECT
public:
/**
* Constructor for QgsMapLayerStore.
*/
explicit QgsMapLayerStore( QObject *parent SIP_TRANSFERTHIS = nullptr );
~QgsMapLayerStore();
/**
* Returns the number of layers contained in the store.
*/
int count() const;
#ifdef SIP_RUN
/**
* Returns the number of layers contained in the store.
*/
int __len__() const;
% MethodCode
sipRes = sipCpp->count();
% End
#endif
/**
* Retrieve a pointer to a layer by layer \a id.
* \param id ID of layer to retrieve
* \returns matching layer, or nullptr if no matching layer found
* \see mapLayersByName()
* \see mapLayers()
*/
QgsMapLayer *mapLayer( const QString &id ) const;
/**
* Retrieve a list of matching layers by layer \a name.
* \param name name of layers to match
* \returns list of matching layers
* \see mapLayer()
* \see mapLayers()
*/
QList<QgsMapLayer *> mapLayersByName( const QString &name ) const;
/**
* Returns a map of all layers by layer ID.
* \see mapLayer()
* \see mapLayersByName()
* \see layers()
*/
QMap<QString, QgsMapLayer *> mapLayers() const;
#ifndef SIP_RUN
/**
* Returns a list of registered map layers with a specified layer type.
*
* Example:
*
* QVector<QgsVectorLayer*> vectorLayers = store->layers<QgsVectorLayer*>();
*
* \note not available in Python bindings
* \see mapLayers()
*/
template <typename T>
QVector<T> layers() const
{
QVector<T> layers;
QMap<QString, QgsMapLayer *>::const_iterator layerIt = mMapLayers.constBegin();
for ( ; layerIt != mMapLayers.constEnd(); ++layerIt )
{
T tLayer = qobject_cast<T>( layerIt.value() );
if ( tLayer )
{
layers << tLayer;
}
}
return layers;
}
#endif
/**
* \brief
* Add a list of \a layers to the store. Ownership of the layers is transferred
* to the store.
*
* The layersAdded() and layerWasAdded() signals will always be emitted.
*
* \param layers A list of layer which should be added to the store.
* \param takeOwnership Ownership will be transferred to the layer store.
* If you specify false here you have take care of deleting
* the layers yourself. Not available in Python.
*
* \returns a list of the map layers that were added
* successfully. If a layer is invalid, or already exists in the store,
* it will not be part of the returned list.
*
* \see addMapLayer()
*/
QList<QgsMapLayer *> addMapLayers( const QList<QgsMapLayer *> &layers SIP_TRANSFER,
bool takeOwnership SIP_PYARGREMOVE = true );
/**
* \brief
* Add a \a layer to the store. Ownership of the layer is transferred to the
* store.
*
* The layersAdded() and layerWasAdded() signals will always be emitted.
* If you are adding multiple layers at once, you should use
* addMapLayers() instead.
*
* \param layer A layer to add to the store
* \param takeOwnership Ownership will be transferred to the layer store.
* If you specify false here you have take care of deleting
* the layers yourself. Not available in Python.
*
* \returns nullptr if unable to add layer, otherwise pointer to newly added layer
*
* \see addMapLayers
*
* \note Use addMapLayers() if adding more than one layer at a time.
* \see addMapLayers()
*/
QgsMapLayer *addMapLayer( QgsMapLayer *layer SIP_TRANSFER,
bool takeOwnership SIP_PYARGREMOVE = true );
/**
* \brief
* Remove a set of layers from the store by layer ID.
*
* The specified layers will be removed from the store.
* These layers will also be deleted.
*
* \param layerIds list of IDs of the layers to remove
*
* \see takeMapLayer()
* \see removeMapLayer()
* \see removeAllMapLayers()
* \note available in Python bindings as removeMapLayersById.
*/
void removeMapLayers( const QStringList &layerIds ) SIP_PYNAME( removeMapLayersById );
/**
* \brief
* Remove a set of \a layers from the store.
*
* The specified layers will be removed from the store.
* These layers will also be deleted.
*
* \param layers A list of layers to remove. Null pointers are ignored.
*
* \see takeMapLayer()
* \see removeMapLayer()
* \see removeAllMapLayers()
*/
void removeMapLayers( const QList<QgsMapLayer *> &layers );
/**
* \brief
* Remove a layer from the store by layer \a id.
*
* The specified layer will be removed from the store. The layer will also be deleted.
*
* \param id ID of the layer to remove
*
* \see takeMapLayer()
* \see removeMapLayers()
* \see removeAllMapLayers()
*/
void removeMapLayer( const QString &id );
/**
* \brief
* Remove a \a layer from the store.
*
* The specified layer will be removed from the store. The layer will also be deleted.
*
* \param layer The layer to remove. Null pointers are ignored.
*
* \see takeMapLayer()
* \see removeMapLayers()
* \see removeAllMapLayers()
*/
void removeMapLayer( QgsMapLayer *layer );
/**
* Takes a \a layer from the store. If the layer was owned by the store, the
* layer will be returned without deleting it. The caller takes ownership of
* the layer and is responsible for deleting it.
* \see removeMapLayer()
*/
QgsMapLayer *takeMapLayer( QgsMapLayer *layer ) SIP_TRANSFERBACK;
/**
* Removes all registered layers. These layers will also be deleted.
*
* \note Calling this method will cause the removeAll() signal to
* be emitted.
* \see removeMapLayer()
* \see removeMapLayers()
*/
void removeAllMapLayers();
signals:
/**
* Emitted when one or more layers are about to be removed from the store.
*
* \param layerIds A list of IDs for the layers which are to be removed.
* \see layerWillBeRemoved()
* \see layersRemoved()
*/
void layersWillBeRemoved( const QStringList &layerIds );
/**
* Emitted when one or more layers are about to be removed from the store.
*
* \param layers A list of layers which are to be removed.
* \see layerWillBeRemoved()
* \see layersRemoved()
*/
void layersWillBeRemoved( const QList<QgsMapLayer *> &layers );
/**
* Emitted when a layer is about to be removed from the store.
*
* \param layerId The ID of the layer to be removed.
*
* \note Consider using layersWillBeRemoved() instead.
* \see layersWillBeRemoved()
* \see layerRemoved()
*/
void layerWillBeRemoved( const QString &layerId );
/**
* Emitted when a layer is about to be removed from the store.
*
* \param layer The layer to be removed.
*
* \note Consider using layersWillBeRemoved() instead.
* \see layersWillBeRemoved()
* \see layerRemoved()
*/
void layerWillBeRemoved( QgsMapLayer *layer );
/**
* Emitted after one or more layers were removed from the store.
*
* \param layerIds A list of IDs of the layers which were removed.
* \see layersWillBeRemoved()
*/
void layersRemoved( const QStringList &layerIds );
/**
* Emitted after a layer was removed from the store.
*
* \param layerId The ID of the layer removed.
*
* \note Consider using layersRemoved() instead
* \see layerWillBeRemoved()
*/
void layerRemoved( const QString &layerId );
/**
* Emitted when all layers are removed, before layersWillBeRemoved() and
* layerWillBeRemoved() signals are emitted. The layersWillBeRemoved() and
* layerWillBeRemoved() signals will still be emitted following this signal.
* You can use this signal to do easy (and fast) cleanup.
*/
void allLayersRemoved();
/**
* Emitted when one or more layers were added to the store.
*
* \param layers List of layers which have been added.
*
* \see legendLayersAdded()
* \see layerWasAdded()
*/
void layersAdded( const QList<QgsMapLayer *> &layers );
/**
* Emitted when a \a layer was added to the store.
*
* \note Consider using layersAdded() instead
* \see layersAdded()
*/
void layerWasAdded( QgsMapLayer *layer );
private slots:
void onMapLayerDeleted( QObject *obj );
private:
QMap<QString, QgsMapLayer *> mMapLayers;
};
#endif //QGSMAPLAYERSTORE_H

View File

@ -46,6 +46,7 @@
#include "qgssettings.h"
#include "qgsmaplayerlistutils.h"
#include "qgslayoutmanager.h"
#include "qgsmaplayerstore.h"
#include <QApplication>
#include <QFileInfo>
@ -322,6 +323,7 @@ void removeKey_( const QString &scope,
QgsProject::QgsProject( QObject *parent )
: QObject( parent )
, mLayerStore( new QgsMapLayerStore( this ) )
, mBadLayerHandler( new QgsProjectBadLayerHandler() )
, mSnappingConfig( this )
, mRelationManager( new QgsRelationManager( this ) )
@ -343,6 +345,21 @@ QgsProject::QgsProject( QObject *parent )
connect( this, &QgsProject::layersAdded, this, &QgsProject::onMapLayersAdded );
connect( this, &QgsProject::layersRemoved, this, [ = ] { cleanTransactionGroups(); } );
connect( this, static_cast < void ( QgsProject::* )( const QList<QgsMapLayer *> & ) >( &QgsProject::layersWillBeRemoved ), this, &QgsProject::onMapLayersRemoved );
// proxy map layer store signals to this
connect( mLayerStore.get(), static_cast<void ( QgsMapLayerStore::* )( const QStringList & )>( &QgsMapLayerStore::layersWillBeRemoved ),
this, static_cast<void ( QgsProject::* )( const QStringList & )>( &QgsProject::layersWillBeRemoved ) );
connect( mLayerStore.get(), static_cast<void ( QgsMapLayerStore::* )( const QList<QgsMapLayer *> & )>( &QgsMapLayerStore::layersWillBeRemoved ),
this, static_cast<void ( QgsProject::* )( const QList<QgsMapLayer *> & )>( &QgsProject::layersWillBeRemoved ) );
connect( mLayerStore.get(), static_cast<void ( QgsMapLayerStore::* )( const QString & )>( &QgsMapLayerStore::layerWillBeRemoved ),
this, static_cast<void ( QgsProject::* )( const QString & )>( &QgsProject::layerWillBeRemoved ) );
connect( mLayerStore.get(), static_cast<void ( QgsMapLayerStore::* )( QgsMapLayer * )>( &QgsMapLayerStore::layerWillBeRemoved ),
this, static_cast<void ( QgsProject::* )( QgsMapLayer * )>( &QgsProject::layerWillBeRemoved ) );
connect( mLayerStore.get(), static_cast<void ( QgsMapLayerStore::* )( const QStringList & )>( &QgsMapLayerStore::layersRemoved ), this, &QgsProject::layersRemoved );
connect( mLayerStore.get(), &QgsMapLayerStore::layerRemoved, this, &QgsProject::layerRemoved );
connect( mLayerStore.get(), &QgsMapLayerStore::allLayersRemoved, this, &QgsProject::removeAll );
connect( mLayerStore.get(), &QgsMapLayerStore::layersAdded, this, &QgsProject::layersAdded );
connect( mLayerStore.get(), &QgsMapLayerStore::layerWasAdded, this, &QgsProject::layerWasAdded );
}
@ -884,7 +901,8 @@ bool QgsProject::read()
// Resolve references to other vector layers
// Needs to be done here once all dependent layers are loaded
for ( QMap<QString, QgsMapLayer *>::iterator it = mMapLayers.begin(); it != mMapLayers.end(); it++ )
QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
for ( QMap<QString, QgsMapLayer *>::iterator it = layers.begin(); it != layers.end(); it++ )
{
if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() ) )
vl->resolveReferences( this );
@ -1049,6 +1067,16 @@ const QgsLabelingEngineSettings &QgsProject::labelingEngineSettings() const
return *mLabelingEngineSettings;
}
QgsMapLayerStore *QgsProject::layerStore()
{
return mLayerStore.get();
}
const QgsMapLayerStore *QgsProject::layerStore() const
{
return mLayerStore.get();
}
QList<QgsVectorLayer *> QgsProject::avoidIntersectionsLayers() const
{
QList<QgsVectorLayer *> layers;
@ -2066,31 +2094,23 @@ QMap<QPair<QString, QString>, QgsTransactionGroup *> QgsProject::transactionGrou
//
// QgsMapLayerRegistry methods
// QgsMapLayerStore methods
//
int QgsProject::count() const
{
return mMapLayers.size();
return mLayerStore->count();
}
QgsMapLayer *QgsProject::mapLayer( const QString &layerId ) const
{
return mMapLayers.value( layerId );
return mLayerStore->mapLayer( layerId );
}
QList<QgsMapLayer *> QgsProject::mapLayersByName( const QString &layerName ) const
{
QList<QgsMapLayer *> myResultList;
Q_FOREACH ( QgsMapLayer *layer, mMapLayers )
{
if ( layer->name() == layerName )
{
myResultList << layer;
}
}
return myResultList;
return mLayerStore->mapLayersByName( layerName );
}
QList<QgsMapLayer *> QgsProject::addMapLayers(
@ -2098,31 +2118,9 @@ QList<QgsMapLayer *> QgsProject::addMapLayers(
bool addToLegend,
bool takeOwnership )
{
QList<QgsMapLayer *> myResultList;
Q_FOREACH ( QgsMapLayer *myLayer, layers )
{
if ( !myLayer || !myLayer->isValid() )
{
QgsDebugMsg( "Cannot add invalid layers" );
continue;
}
//check the layer is not already registered!
if ( !mMapLayers.contains( myLayer->id() ) )
{
mMapLayers[myLayer->id()] = myLayer;
myResultList << mMapLayers[myLayer->id()];
if ( takeOwnership )
{
myLayer->setParent( this );
}
connect( myLayer, &QObject::destroyed, this, &QgsProject::onMapLayerDeleted );
emit layerWasAdded( myLayer );
}
}
QList<QgsMapLayer *> myResultList = mLayerStore->addMapLayers( layers, takeOwnership );
if ( !myResultList.isEmpty() )
{
emit layersAdded( myResultList );
if ( addToLegend )
emit legendLayersAdded( myResultList );
}
@ -2141,116 +2139,47 @@ QgsProject::addMapLayer( QgsMapLayer *layer,
void QgsProject::removeMapLayers( const QStringList &layerIds )
{
QList<QgsMapLayer *> layers;
Q_FOREACH ( const QString &myId, layerIds )
{
layers << mMapLayers.value( myId );
}
removeMapLayers( layers );
mLayerStore->removeMapLayers( layerIds );
}
void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
{
if ( layers.isEmpty() )
return;
QStringList layerIds;
QList<QgsMapLayer *> layerList;
Q_FOREACH ( QgsMapLayer *layer, layers )
{
// check layer and the registry contains it
if ( layer && mMapLayers.contains( layer->id() ) )
{
layerIds << layer->id();
layerList << layer;
}
}
if ( layerIds.isEmpty() )
return;
emit layersWillBeRemoved( layerIds );
emit layersWillBeRemoved( layerList );
Q_FOREACH ( QgsMapLayer *lyr, layerList )
{
QString myId( lyr->id() );
emit layerWillBeRemoved( myId );
emit layerWillBeRemoved( lyr );
mMapLayers.remove( myId );
if ( lyr->parent() == this )
{
delete lyr;
}
emit layerRemoved( myId );
}
emit layersRemoved( layerIds );
mLayerStore->removeMapLayers( layers );
}
void QgsProject::removeMapLayer( const QString &layerId )
{
removeMapLayers( QList<QgsMapLayer *>() << mMapLayers.value( layerId ) );
mLayerStore->removeMapLayer( layerId );
}
void QgsProject::removeMapLayer( QgsMapLayer *layer )
{
if ( layer )
removeMapLayers( QList<QgsMapLayer *>() << layer );
mLayerStore->removeMapLayer( layer );
}
QgsMapLayer *QgsProject::takeMapLayer( QgsMapLayer *layer )
{
if ( !layer )
return nullptr;
if ( mMapLayers.contains( layer->id() ) )
{
emit layersWillBeRemoved( QStringList() << layer->id() );
emit layersWillBeRemoved( QList<QgsMapLayer *>() << layer );
emit layerWillBeRemoved( layer->id() );
emit layerWillBeRemoved( layer );
mMapLayers.remove( layer->id() );
layer->setParent( nullptr );
emit layerRemoved( layer->id() );
emit layersRemoved( QStringList() << layer->id() );
return layer;
}
return nullptr; //don't return layer - it wasn't owned and accordingly we aren't transferring ownership
return mLayerStore->takeMapLayer( layer );
}
void QgsProject::removeAllMapLayers()
{
emit removeAll();
// now let all observers know to clear themselves,
// and then consequently any of their map legends
removeMapLayers( mMapLayers.keys() );
mMapLayers.clear();
mLayerStore->removeAllMapLayers();
}
void QgsProject::reloadAllLayers()
{
Q_FOREACH ( QgsMapLayer *layer, mMapLayers )
QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin();
for ( ; it != layers.constEnd(); ++it )
{
layer->reload();
}
}
void QgsProject::onMapLayerDeleted( QObject *obj )
{
QString id = mMapLayers.key( static_cast<QgsMapLayer *>( obj ) );
if ( !id.isNull() )
{
QgsDebugMsg( QString( "Map layer deleted without unregistering! %1" ).arg( id ) );
mMapLayers.remove( id );
it.value()->reload();
}
}
QMap<QString, QgsMapLayer *> QgsProject::mapLayers() const
{
return mMapLayers;
return mLayerStore->mapLayers();
}

View File

@ -39,6 +39,7 @@
#include "qgscoordinatereferencesystem.h"
#include "qgsprojectproperty.h"
#include "qgsmaplayer.h"
#include "qgsmaplayerstore.h"
class QFileInfo;
class QDomDocument;
@ -529,6 +530,18 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
// Functionality from QgsMapLayerRegistry
//
/**
* Returns a pointer to the project's internal layer store.
* /since QGIS 3.0
*/
QgsMapLayerStore *layerStore();
/**
* Returns a pointer to the project's internal layer store.
* /since QGIS 3.0
*/
SIP_SKIP const QgsMapLayerStore *layerStore() const;
//! Returns the number of registered layers.
int count() const;
@ -568,17 +581,7 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
template <typename T> SIP_SKIP
QVector<T> layers() const
{
QVector<T> layers;
QMap<QString, QgsMapLayer *>::const_iterator layerIt = mMapLayers.constBegin();
for ( ; layerIt != mMapLayers.constEnd(); ++layerIt )
{
T tLayer = qobject_cast<T>( layerIt.value() );
if ( tLayer )
{
layers << tLayer;
}
}
return layers;
return mLayerStore->layers<T>();
}
/**
@ -969,8 +972,6 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
void onMapLayersRemoved( const QList<QgsMapLayer *> &layers );
void cleanTransactionGroups( bool force = false );
void onMapLayerDeleted( QObject *obj );
private:
static QgsProject *sProject;
@ -1003,7 +1004,7 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
//! \note not available in Python bindings
void loadEmbeddedNodes( QgsLayerTreeGroup *group ) SIP_SKIP;
QMap<QString, QgsMapLayer *> mMapLayers;
std::unique_ptr< QgsMapLayerStore > mLayerStore;
QString mErrorMessage;

View File

@ -105,6 +105,7 @@ class TestQgsProcessing: public QObject
void compatibleLayers();
void normalizeLayerSource();
void mapLayers();
void mapLayerFromStore();
void mapLayerFromString();
void algorithm();
void features();
@ -335,9 +336,37 @@ void TestQgsProcessing::normalizeLayerSource()
void TestQgsProcessing::mapLayers()
{
// test mapLayerFromProject
QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
QString raster = testDataDir + "landsat.tif";
QString vector = testDataDir + "points.shp";
QgsProject p;
// test loadMapLayerFromString with raster
QgsMapLayer *l = QgsProcessingUtils::loadMapLayerFromString( raster );
QVERIFY( l->isValid() );
QCOMPARE( l->type(), QgsMapLayer::RasterLayer );
delete l;
//test with vector
l = QgsProcessingUtils::loadMapLayerFromString( vector );
QVERIFY( l->isValid() );
QCOMPARE( l->type(), QgsMapLayer::VectorLayer );
delete l;
l = QgsProcessingUtils::loadMapLayerFromString( QString() );
QVERIFY( !l );
l = QgsProcessingUtils::loadMapLayerFromString( QStringLiteral( "so much room for activities!" ) );
QVERIFY( !l );
l = QgsProcessingUtils::loadMapLayerFromString( testDataDir + "multipoint.shp" );
QVERIFY( l->isValid() );
QCOMPARE( l->type(), QgsMapLayer::VectorLayer );
delete l;
}
void TestQgsProcessing::mapLayerFromStore()
{
// test mapLayerFromStore
QgsMapLayerStore store;
// add a bunch of layers to a project
QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
@ -352,33 +381,19 @@ void TestQgsProcessing::mapLayers()
QgsVectorLayer *v1 = new QgsVectorLayer( "Polygon", "V4", "memory" );
QgsVectorLayer *v2 = new QgsVectorLayer( "Point", "v1", "memory" );
p.addMapLayers( QList<QgsMapLayer *>() << r1 << r2 << v1 << v2 );
store.addMapLayers( QList<QgsMapLayer *>() << r1 << r2 << v1 << v2 );
QVERIFY( ! QgsProcessingUtils::mapLayerFromProject( QString(), nullptr ) );
QVERIFY( ! QgsProcessingUtils::mapLayerFromProject( QStringLiteral( "v1" ), nullptr ) );
QVERIFY( ! QgsProcessingUtils::mapLayerFromProject( QString(), &p ) );
QCOMPARE( QgsProcessingUtils::mapLayerFromProject( raster1, &p ), r1 );
QCOMPARE( QgsProcessingUtils::mapLayerFromProject( raster2, &p ), r2 );
QCOMPARE( QgsProcessingUtils::mapLayerFromProject( "R1", &p ), r1 );
QCOMPARE( QgsProcessingUtils::mapLayerFromProject( "ar2", &p ), r2 );
QCOMPARE( QgsProcessingUtils::mapLayerFromProject( "V4", &p ), v1 );
QCOMPARE( QgsProcessingUtils::mapLayerFromProject( "v1", &p ), v2 );
QCOMPARE( QgsProcessingUtils::mapLayerFromProject( r1->id(), &p ), r1 );
QCOMPARE( QgsProcessingUtils::mapLayerFromProject( v1->id(), &p ), v1 );
// test loadMapLayerFromString
QgsMapLayer *l = QgsProcessingUtils::loadMapLayerFromString( raster2 );
QVERIFY( l->isValid() );
QCOMPARE( l->type(), QgsMapLayer::RasterLayer );
delete l;
l = QgsProcessingUtils::loadMapLayerFromString( QString() );
QVERIFY( !l );
l = QgsProcessingUtils::loadMapLayerFromString( QStringLiteral( "so much room for activities!" ) );
QVERIFY( !l );
l = QgsProcessingUtils::loadMapLayerFromString( testDataDir + "multipoint.shp" );
QVERIFY( l->isValid() );
QCOMPARE( l->type(), QgsMapLayer::VectorLayer );
delete l;
QVERIFY( ! QgsProcessingUtils::mapLayerFromStore( QString(), nullptr ) );
QVERIFY( ! QgsProcessingUtils::mapLayerFromStore( QStringLiteral( "v1" ), nullptr ) );
QVERIFY( ! QgsProcessingUtils::mapLayerFromStore( QString(), &store ) );
QCOMPARE( QgsProcessingUtils::mapLayerFromStore( raster1, &store ), r1 );
QCOMPARE( QgsProcessingUtils::mapLayerFromStore( raster2, &store ), r2 );
QCOMPARE( QgsProcessingUtils::mapLayerFromStore( "R1", &store ), r1 );
QCOMPARE( QgsProcessingUtils::mapLayerFromStore( "ar2", &store ), r2 );
QCOMPARE( QgsProcessingUtils::mapLayerFromStore( "V4", &store ), v1 );
QCOMPARE( QgsProcessingUtils::mapLayerFromStore( "v1", &store ), v2 );
QCOMPARE( QgsProcessingUtils::mapLayerFromStore( r1->id(), &store ), r1 );
QCOMPARE( QgsProcessingUtils::mapLayerFromStore( v1->id(), &store ), v1 );
}
void TestQgsProcessing::mapLayerFromString()
@ -423,7 +438,7 @@ void TestQgsProcessing::mapLayerFromString()
// check that layers in context temporary store are used
QgsVectorLayer *v5 = new QgsVectorLayer( "Polygon", "V5", "memory" );
QgsVectorLayer *v6 = new QgsVectorLayer( "Point", "v6", "memory" );
c.temporaryLayerStore().addMapLayers( QList<QgsMapLayer *>() << v5 << v6 );
c.temporaryLayerStore()->addMapLayers( QList<QgsMapLayer *>() << v5 << v6 );
QCOMPARE( QgsProcessingUtils::mapLayerFromString( "V5", c ), v5 );
QCOMPARE( QgsProcessingUtils::mapLayerFromString( "v6", c ), v6 );
QCOMPARE( QgsProcessingUtils::mapLayerFromString( v5->id(), c ), v5 );
@ -440,7 +455,7 @@ void TestQgsProcessing::mapLayerFromString()
QVERIFY( loadedLayer->isValid() );
QCOMPARE( loadedLayer->type(), QgsMapLayer::RasterLayer );
// should now be in temporary store
QCOMPARE( c.temporaryLayerStore().mapLayer( loadedLayer->id() ), loadedLayer );
QCOMPARE( c.temporaryLayerStore()->mapLayer( loadedLayer->id() ), loadedLayer );
// since it's now in temporary store, should be accessible even if we deny loading new layers
QCOMPARE( QgsProcessingUtils::mapLayerFromString( newRaster, c, false ), loadedLayer );

View File

@ -76,6 +76,7 @@ ADD_PYTHON_TEST(PyQgsMapCanvas test_qgsmapcanvas.py)
ADD_PYTHON_TEST(PyQgsMapCanvasAnnotationItem test_qgsmapcanvasannotationitem.py)
ADD_PYTHON_TEST(PyQgsMapLayer test_qgsmaplayer.py)
ADD_PYTHON_TEST(PyQgsMapLayerModel test_qgsmaplayermodel.py)
ADD_PYTHON_TEST(PyQgsMapLayerStore test_qgsmaplayerstore.py)
ADD_PYTHON_TEST(PyQgsMapRenderer test_qgsmaprenderer.py)
ADD_PYTHON_TEST(PyQgsMapRendererCache test_qgsmaprenderercache.py)
ADD_PYTHON_TEST(PyQgsMapThemeCollection test_qgsmapthemecollection.py)
@ -145,7 +146,6 @@ ADD_PYTHON_TEST(PyQgsVectorLayer test_qgsvectorlayer.py)
ADD_PYTHON_TEST(PyQgsVectorLayerEditBuffer test_qgsvectorlayereditbuffer.py)
ADD_PYTHON_TEST(PyQgsVectorLayerUtils test_qgsvectorlayerutils.py)
ADD_PYTHON_TEST(PyQgsZonalStatistics test_qgszonalstatistics.py)
ADD_PYTHON_TEST(PyQgsMapLayerRegistry test_qgsmaplayerregistry.py)
ADD_PYTHON_TEST(PyQgsVirtualLayerProvider test_provider_virtual.py)
ADD_PYTHON_TEST(PyQgsVirtualLayerDefinition test_qgsvirtuallayerdefinition.py)
ADD_PYTHON_TEST(PyQgsLayerDefinition test_qgslayerdefinition.py)

View File

@ -1,536 +0,0 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsProject.
.. 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.
"""
__author__ = 'Alessandro Pasotti'
__date__ = '04/12/2015'
__copyright__ = 'Copyright 2015, The QGIS Project'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'
from qgis.core import QgsProject, QgsVectorLayer, QgsMapLayer
from qgis.testing import start_app, unittest
from qgis.PyQt.QtCore import QT_VERSION_STR
import sip
try:
from qgis.PyQt.QtTest import QSignalSpy
use_signal_spy = True
except:
use_signal_spy = False
start_app()
def createLayer(name):
return QgsVectorLayer("Point?field=x:string", name, "memory")
class TestQgsProjectMapLayers(unittest.TestCase):
def setUp(self):
pass
def testInstance(self):
""" test retrieving global instance """
self.assertTrue(QgsProject.instance())
# register a layer to the singleton
QgsProject.instance().addMapLayer(createLayer('test'))
# check that the same instance is returned
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
QgsProject.instance().removeAllMapLayers()
def test_addMapLayer(self):
""" test adding individual map layers to registry """
QgsProject.instance().removeAllMapLayers()
l1 = createLayer('test')
self.assertEqual(QgsProject.instance().addMapLayer(l1), l1)
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
self.assertEqual(QgsProject.instance().count(), 1)
# adding a second layer should leave existing layers intact
l2 = createLayer('test2')
self.assertEqual(QgsProject.instance().addMapLayer(l2), l2)
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
self.assertEqual(len(QgsProject.instance().mapLayersByName('test2')), 1)
self.assertEqual(QgsProject.instance().count(), 2)
QgsProject.instance().removeAllMapLayers()
def test_addMapLayerAlreadyAdded(self):
""" test that already added layers can't be readded to registry """
QgsProject.instance().removeAllMapLayers()
l1 = createLayer('test')
QgsProject.instance().addMapLayer(l1)
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
self.assertEqual(QgsProject.instance().count(), 1)
self.assertEqual(QgsProject.instance().addMapLayer(l1), None)
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
self.assertEqual(QgsProject.instance().count(), 1)
QgsProject.instance().removeAllMapLayers()
def test_addMapLayerInvalid(self):
""" test that invalid map layersd can't be added to registry """
QgsProject.instance().removeAllMapLayers()
self.assertEqual(QgsProject.instance().addMapLayer(QgsVectorLayer("Point?field=x:string", 'test', "xxx")), None)
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 0)
self.assertEqual(QgsProject.instance().count(), 0)
QgsProject.instance().removeAllMapLayers()
@unittest.skipIf(not use_signal_spy, "No QSignalSpy available")
def test_addMapLayerSignals(self):
""" test that signals are correctly emitted when adding map layer"""
QgsProject.instance().removeAllMapLayers()
layer_was_added_spy = QSignalSpy(QgsProject.instance().layerWasAdded)
layers_added_spy = QSignalSpy(QgsProject.instance().layersAdded)
legend_layers_added_spy = QSignalSpy(QgsProject.instance().legendLayersAdded)
l1 = createLayer('test')
QgsProject.instance().addMapLayer(l1)
# can't seem to actually test the data which was emitted, so best we can do is test
# the signal count
self.assertEqual(len(layer_was_added_spy), 1)
self.assertEqual(len(layers_added_spy), 1)
self.assertEqual(len(legend_layers_added_spy), 1)
# layer not added to legend
QgsProject.instance().addMapLayer(createLayer('test2'), False)
self.assertEqual(len(layer_was_added_spy), 2)
self.assertEqual(len(layers_added_spy), 2)
self.assertEqual(len(legend_layers_added_spy), 1)
# try readding a layer already in the registry
QgsProject.instance().addMapLayer(l1)
# should be no extra signals emitted
self.assertEqual(len(layer_was_added_spy), 2)
self.assertEqual(len(layers_added_spy), 2)
self.assertEqual(len(legend_layers_added_spy), 1)
def test_addMapLayers(self):
""" test adding multiple map layers to registry """
QgsProject.instance().removeAllMapLayers()
l1 = createLayer('test')
l2 = createLayer('test2')
self.assertEqual(set(QgsProject.instance().addMapLayers([l1, l2])), set([l1, l2]))
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
self.assertEqual(len(QgsProject.instance().mapLayersByName('test2')), 1)
self.assertEqual(QgsProject.instance().count(), 2)
# adding more layers should leave existing layers intact
l3 = createLayer('test3')
l4 = createLayer('test4')
self.assertEqual(set(QgsProject.instance().addMapLayers([l3, l4])), set([l3, l4]))
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
self.assertEqual(len(QgsProject.instance().mapLayersByName('test2')), 1)
self.assertEqual(len(QgsProject.instance().mapLayersByName('test3')), 1)
self.assertEqual(len(QgsProject.instance().mapLayersByName('test4')), 1)
self.assertEqual(QgsProject.instance().count(), 4)
QgsProject.instance().removeAllMapLayers()
def test_addMapLayersInvalid(self):
""" test that invalid map layersd can't be added to registry """
QgsProject.instance().removeAllMapLayers()
self.assertEqual(QgsProject.instance().addMapLayers([QgsVectorLayer("Point?field=x:string", 'test', "xxx")]), [])
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 0)
self.assertEqual(QgsProject.instance().count(), 0)
QgsProject.instance().removeAllMapLayers()
def test_addMapLayersAlreadyAdded(self):
""" test that already added layers can't be readded to registry """
QgsProject.instance().removeAllMapLayers()
l1 = createLayer('test')
self.assertEqual(QgsProject.instance().addMapLayers([l1]), [l1])
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
self.assertEqual(QgsProject.instance().count(), 1)
self.assertEqual(QgsProject.instance().addMapLayers([l1]), [])
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
self.assertEqual(QgsProject.instance().count(), 1)
QgsProject.instance().removeAllMapLayers()
@unittest.skipIf(not use_signal_spy, "No QSignalSpy available")
def test_addMapLayersSignals(self):
""" test that signals are correctly emitted when adding map layers"""
QgsProject.instance().removeAllMapLayers()
layer_was_added_spy = QSignalSpy(QgsProject.instance().layerWasAdded)
layers_added_spy = QSignalSpy(QgsProject.instance().layersAdded)
legend_layers_added_spy = QSignalSpy(QgsProject.instance().legendLayersAdded)
l1 = createLayer('test')
l2 = createLayer('test2')
QgsProject.instance().addMapLayers([l1, l2])
# can't seem to actually test the data which was emitted, so best we can do is test
# the signal count
self.assertEqual(len(layer_was_added_spy), 2)
self.assertEqual(len(layers_added_spy), 1)
self.assertEqual(len(legend_layers_added_spy), 1)
# layer not added to legend
QgsProject.instance().addMapLayers([createLayer('test3'), createLayer('test4')], False)
self.assertEqual(len(layer_was_added_spy), 4)
self.assertEqual(len(layers_added_spy), 2)
self.assertEqual(len(legend_layers_added_spy), 1)
# try readding a layer already in the registry
QgsProject.instance().addMapLayers([l1, l2])
# should be no extra signals emitted
self.assertEqual(len(layer_was_added_spy), 4)
self.assertEqual(len(layers_added_spy), 2)
self.assertEqual(len(legend_layers_added_spy), 1)
def test_mapLayerById(self):
""" test retrieving map layer by ID """
QgsProject.instance().removeAllMapLayers()
# test no crash with empty registry
self.assertEqual(QgsProject.instance().mapLayer('bad'), None)
self.assertEqual(QgsProject.instance().mapLayer(None), None)
l1 = createLayer('test')
l2 = createLayer('test2')
QgsProject.instance().addMapLayers([l1, l2])
self.assertEqual(QgsProject.instance().mapLayer('bad'), None)
self.assertEqual(QgsProject.instance().mapLayer(None), None)
self.assertEqual(QgsProject.instance().mapLayer(l1.id()), l1)
self.assertEqual(QgsProject.instance().mapLayer(l2.id()), l2)
def test_mapLayersByName(self):
""" test retrieving map layer by name """
QgsProject.instance().removeAllMapLayers()
# test no crash with empty registry
self.assertEqual(QgsProject.instance().mapLayersByName('bad'), [])
self.assertEqual(QgsProject.instance().mapLayersByName(None), [])
l1 = createLayer('test')
l2 = createLayer('test2')
QgsProject.instance().addMapLayers([l1, l2])
self.assertEqual(QgsProject.instance().mapLayersByName('bad'), [])
self.assertEqual(QgsProject.instance().mapLayersByName(None), [])
self.assertEqual(QgsProject.instance().mapLayersByName('test'), [l1])
self.assertEqual(QgsProject.instance().mapLayersByName('test2'), [l2])
#duplicate name
l3 = createLayer('test')
QgsProject.instance().addMapLayer(l3)
self.assertEqual(set(QgsProject.instance().mapLayersByName('test')), set([l1, l3]))
def test_mapLayers(self):
""" test retrieving map layers list """
QgsProject.instance().removeAllMapLayers()
# test no crash with empty registry
self.assertEqual(QgsProject.instance().mapLayers(), {})
l1 = createLayer('test')
l2 = createLayer('test2')
QgsProject.instance().addMapLayers([l1, l2])
self.assertEqual(QgsProject.instance().mapLayers(), {l1.id(): l1, l2.id(): l2})
def test_removeMapLayersById(self):
""" test removing map layers by ID """
QgsProject.instance().removeAllMapLayers()
# test no crash with empty registry
QgsProject.instance().removeMapLayers(['bad'])
QgsProject.instance().removeMapLayers([None])
l1 = createLayer('test')
l2 = createLayer('test2')
l3 = createLayer('test3')
QgsProject.instance().addMapLayers([l1, l2, l3])
self.assertEqual(QgsProject.instance().count(), 3)
#remove bad layers
QgsProject.instance().removeMapLayers(['bad'])
self.assertEqual(QgsProject.instance().count(), 3)
QgsProject.instance().removeMapLayers([None])
self.assertEqual(QgsProject.instance().count(), 3)
# remove valid layers
l1_id = l1.id()
QgsProject.instance().removeMapLayers([l1_id])
self.assertEqual(QgsProject.instance().count(), 2)
# double remove
QgsProject.instance().removeMapLayers([l1_id])
self.assertEqual(QgsProject.instance().count(), 2)
# test that layer has been deleted
self.assertTrue(sip.isdeleted(l1))
# remove multiple
QgsProject.instance().removeMapLayers([l2.id(), l3.id()])
self.assertEqual(QgsProject.instance().count(), 0)
self.assertTrue(sip.isdeleted(l2))
# try removing a layer not in the registry
l4 = createLayer('test4')
QgsProject.instance().removeMapLayers([l4.id()])
self.assertFalse(sip.isdeleted(l4))
# fails on qt5 due to removeMapLayers list type conversion - needs a PyName alias
# added to removeMapLayers for QGIS 3.0
@unittest.expectedFailure(QT_VERSION_STR[0] == '5')
def test_removeMapLayersByLayer(self):
""" test removing map layers by layer"""
QgsProject.instance().removeAllMapLayers()
# test no crash with empty registry
QgsProject.instance().removeMapLayers([None])
l1 = createLayer('test')
l2 = createLayer('test2')
l3 = createLayer('test3')
QgsProject.instance().addMapLayers([l1, l2, l3])
self.assertEqual(QgsProject.instance().count(), 3)
#remove bad layers
QgsProject.instance().removeMapLayers([None])
self.assertEqual(QgsProject.instance().count(), 3)
# remove valid layers
QgsProject.instance().removeMapLayers([l1])
self.assertEqual(QgsProject.instance().count(), 2)
# test that layer has been deleted
self.assertTrue(sip.isdeleted(l1))
# remove multiple
QgsProject.instance().removeMapLayers([l2, l3])
self.assertEqual(QgsProject.instance().count(), 0)
self.assertTrue(sip.isdeleted(l2))
self.assertTrue(sip.isdeleted(l3))
def test_removeMapLayerById(self):
""" test removing a map layer by ID """
QgsProject.instance().removeAllMapLayers()
# test no crash with empty registry
QgsProject.instance().removeMapLayer('bad')
QgsProject.instance().removeMapLayer(None)
l1 = createLayer('test')
l2 = createLayer('test2')
QgsProject.instance().addMapLayers([l1, l2])
self.assertEqual(QgsProject.instance().count(), 2)
#remove bad layers
QgsProject.instance().removeMapLayer('bad')
self.assertEqual(QgsProject.instance().count(), 2)
QgsProject.instance().removeMapLayer(None)
self.assertEqual(QgsProject.instance().count(), 2)
# remove valid layers
l1_id = l1.id()
QgsProject.instance().removeMapLayer(l1_id)
self.assertEqual(QgsProject.instance().count(), 1)
# double remove
QgsProject.instance().removeMapLayer(l1_id)
self.assertEqual(QgsProject.instance().count(), 1)
# test that layer has been deleted
self.assertTrue(sip.isdeleted(l1))
# remove second layer
QgsProject.instance().removeMapLayer(l2.id())
self.assertEqual(QgsProject.instance().count(), 0)
self.assertTrue(sip.isdeleted(l2))
# try removing a layer not in the registry
l3 = createLayer('test3')
QgsProject.instance().removeMapLayer(l3.id())
self.assertFalse(sip.isdeleted(l3))
def test_removeMapLayerByLayer(self):
""" test removing a map layer by layer """
QgsProject.instance().removeAllMapLayers()
# test no crash with empty registry
QgsProject.instance().removeMapLayer('bad')
QgsProject.instance().removeMapLayer(None)
l1 = createLayer('test')
l2 = createLayer('test2')
QgsProject.instance().addMapLayers([l1, l2])
self.assertEqual(QgsProject.instance().count(), 2)
#remove bad layers
QgsProject.instance().removeMapLayer(None)
self.assertEqual(QgsProject.instance().count(), 2)
l3 = createLayer('test3')
QgsProject.instance().removeMapLayer(l3)
self.assertEqual(QgsProject.instance().count(), 2)
# remove valid layers
QgsProject.instance().removeMapLayer(l1)
self.assertEqual(QgsProject.instance().count(), 1)
# test that layer has been deleted
self.assertTrue(sip.isdeleted(l1))
# remove second layer
QgsProject.instance().removeMapLayer(l2)
self.assertEqual(QgsProject.instance().count(), 0)
self.assertTrue(sip.isdeleted(l2))
# try removing a layer not in the registry
l3 = createLayer('test3')
QgsProject.instance().removeMapLayer(l3)
self.assertFalse(sip.isdeleted(l3))
def test_removeAllMapLayers(self):
""" test removing all map layers from registry """
QgsProject.instance().removeAllMapLayers()
l1 = createLayer('test')
l2 = createLayer('test2')
QgsProject.instance().addMapLayers([l1, l2])
self.assertEqual(QgsProject.instance().count(), 2)
QgsProject.instance().removeAllMapLayers()
self.assertEqual(QgsProject.instance().count(), 0)
self.assertEqual(QgsProject.instance().mapLayersByName('test'), [])
self.assertEqual(QgsProject.instance().mapLayersByName('test2'), [])
@unittest.skipIf(not use_signal_spy, "No QSignalSpy available")
def test_addRemoveLayersSignals(self):
""" test that signals are correctly emitted when removing map layers"""
QgsProject.instance().removeAllMapLayers()
layers_will_be_removed_spy = QSignalSpy(QgsProject.instance().layersWillBeRemoved)
layer_will_be_removed_spy_str = QSignalSpy(QgsProject.instance().layerWillBeRemoved[str])
layer_will_be_removed_spy_layer = QSignalSpy(QgsProject.instance().layerWillBeRemoved[QgsMapLayer])
layers_removed_spy = QSignalSpy(QgsProject.instance().layersRemoved)
layer_removed_spy = QSignalSpy(QgsProject.instance().layerRemoved)
remove_all_spy = QSignalSpy(QgsProject.instance().removeAll)
l1 = createLayer('l1')
l2 = createLayer('l2')
l3 = createLayer('l3')
l4 = createLayer('l4')
QgsProject.instance().addMapLayers([l1, l2, l3, l4])
# remove 1 layer
QgsProject.instance().removeMapLayer(l1)
# can't seem to actually test the data which was emitted, so best we can do is test
# the signal count
self.assertEqual(len(layers_will_be_removed_spy), 1)
self.assertEqual(len(layer_will_be_removed_spy_str), 1)
self.assertEqual(len(layer_will_be_removed_spy_layer), 1)
self.assertEqual(len(layers_removed_spy), 1)
self.assertEqual(len(layer_removed_spy), 1)
self.assertEqual(len(remove_all_spy), 0)
self.assertEqual(QgsProject.instance().count(), 3)
# remove 2 layers at once
QgsProject.instance().removeMapLayers([l2.id(), l3.id()])
self.assertEqual(len(layers_will_be_removed_spy), 2)
self.assertEqual(len(layer_will_be_removed_spy_str), 3)
self.assertEqual(len(layer_will_be_removed_spy_layer), 3)
self.assertEqual(len(layers_removed_spy), 2)
self.assertEqual(len(layer_removed_spy), 3)
self.assertEqual(len(remove_all_spy), 0)
self.assertEqual(QgsProject.instance().count(), 1)
# remove all
QgsProject.instance().removeAllMapLayers()
self.assertEqual(len(layers_will_be_removed_spy), 3)
self.assertEqual(len(layer_will_be_removed_spy_str), 4)
self.assertEqual(len(layer_will_be_removed_spy_layer), 4)
self.assertEqual(len(layers_removed_spy), 3)
self.assertEqual(len(layer_removed_spy), 4)
self.assertEqual(len(remove_all_spy), 1)
#remove some layers which aren't in the registry
QgsProject.instance().removeMapLayers(['asdasd'])
self.assertEqual(len(layers_will_be_removed_spy), 3)
self.assertEqual(len(layer_will_be_removed_spy_str), 4)
self.assertEqual(len(layer_will_be_removed_spy_layer), 4)
self.assertEqual(len(layers_removed_spy), 3)
self.assertEqual(len(layer_removed_spy), 4)
self.assertEqual(len(remove_all_spy), 1)
l5 = createLayer('test5')
QgsProject.instance().removeMapLayer(l5)
self.assertEqual(len(layers_will_be_removed_spy), 3)
self.assertEqual(len(layer_will_be_removed_spy_str), 4)
self.assertEqual(len(layer_will_be_removed_spy_layer), 4)
self.assertEqual(len(layers_removed_spy), 3)
self.assertEqual(len(layer_removed_spy), 4)
self.assertEqual(len(remove_all_spy), 1)
def test_RemoveLayerShouldNotSegFault(self):
QgsProject.instance().removeAllMapLayers()
reg = QgsProject.instance()
# Should not segfault
reg.removeMapLayers(['not_exists'])
reg.removeMapLayer('not_exists2')
# check also that the removal of an unexistent layer does not insert a null layer
for k, layer in list(reg.mapLayers().items()):
assert(layer is not None)
def testTakeLayer(self):
# test taking ownership of a layer from the project
l1 = createLayer('l1')
l2 = createLayer('l2')
p = QgsProject()
# add one layer to project
p.addMapLayer(l1)
self.assertEqual(p.mapLayers(), {l1.id(): l1})
self.assertEqual(l1.parent(), p)
# try taking some layers which don't exist in project
self.assertFalse(p.takeMapLayer(None))
self.assertFalse(p.takeMapLayer(l2))
# but l2 should still exist..
self.assertTrue(l2.isValid())
# take layer from project
self.assertEqual(p.takeMapLayer(l1), l1)
self.assertFalse(p.mapLayers()) # no layers left
# but l1 should still exist
self.assertTrue(l1.isValid())
# layer should have no parent now
self.assertFalse(l1.parent())
# destroy project
p = None
self.assertTrue(l1.isValid())
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,495 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsMapLayerStore.
.. 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.
"""
__author__ = 'Nyall Dawson'
__date__ = '2017-05'
__copyright__ = 'Copyright 2017, The QGIS Project'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'
from qgis.core import QgsMapLayerStore, QgsVectorLayer, QgsMapLayer
from qgis.testing import start_app, unittest
from qgis.PyQt.QtCore import QT_VERSION_STR
import sip
from qgis.PyQt.QtTest import QSignalSpy
start_app()
def createLayer(name):
return QgsVectorLayer("Point?field=x:string", name, "memory")
class TestQgsMapLayerStore(unittest.TestCase):
def setUp(self):
pass
def test_addMapLayer(self):
""" test adding individual map layers to store"""
store = QgsMapLayerStore()
l1 = createLayer('test')
self.assertEqual(store.addMapLayer(l1), l1)
self.assertEqual(len(store.mapLayersByName('test')), 1)
self.assertEqual(store.count(), 1)
self.assertEqual(len(store), 1)
# adding a second layer should leave existing layers intact
l2 = createLayer('test2')
self.assertEqual(store.addMapLayer(l2), l2)
self.assertEqual(len(store.mapLayersByName('test')), 1)
self.assertEqual(len(store.mapLayersByName('test2')), 1)
self.assertEqual(store.count(), 2)
self.assertEqual(len(store), 2)
def test_addMapLayerAlreadyAdded(self):
""" test that already added layers can't be readded to store """
store = QgsMapLayerStore()
l1 = createLayer('test')
store.addMapLayer(l1)
self.assertEqual(len(store.mapLayersByName('test')), 1)
self.assertEqual(store.count(), 1)
self.assertEqual(store.addMapLayer(l1), None)
self.assertEqual(len(store.mapLayersByName('test')), 1)
self.assertEqual(store.count(), 1)
self.assertEqual(len(store), 1)
def test_addMapLayerInvalid(self):
""" test that invalid map layers can't be added to store """
store = QgsMapLayerStore()
self.assertEqual(store.addMapLayer(QgsVectorLayer("Point?field=x:string", 'test', "xxx")), None)
self.assertEqual(len(store.mapLayersByName('test')), 0)
self.assertEqual(store.count(), 0)
def test_addMapLayerSignals(self):
""" test that signals are correctly emitted when adding map layer"""
store = QgsMapLayerStore()
layer_was_added_spy = QSignalSpy(store.layerWasAdded)
layers_added_spy = QSignalSpy(store.layersAdded)
l1 = createLayer('test')
store.addMapLayer(l1)
# can't seem to actually test the data which was emitted, so best we can do is test
# the signal count
self.assertEqual(len(layer_was_added_spy), 1)
self.assertEqual(len(layers_added_spy), 1)
store.addMapLayer(createLayer('test2'))
self.assertEqual(len(layer_was_added_spy), 2)
self.assertEqual(len(layers_added_spy), 2)
# try readding a layer already in the store
store.addMapLayer(l1)
# should be no extra signals emitted
self.assertEqual(len(layer_was_added_spy), 2)
self.assertEqual(len(layers_added_spy), 2)
def test_addMapLayers(self):
""" test adding multiple map layers to store """
store = QgsMapLayerStore()
l1 = createLayer('test')
l2 = createLayer('test2')
self.assertEqual(set(store.addMapLayers([l1, l2])), {l1, l2})
self.assertEqual(len(store.mapLayersByName('test')), 1)
self.assertEqual(len(store.mapLayersByName('test2')), 1)
self.assertEqual(store.count(), 2)
# adding more layers should leave existing layers intact
l3 = createLayer('test3')
l4 = createLayer('test4')
self.assertEqual(set(store.addMapLayers([l3, l4])), {l3, l4})
self.assertEqual(len(store.mapLayersByName('test')), 1)
self.assertEqual(len(store.mapLayersByName('test2')), 1)
self.assertEqual(len(store.mapLayersByName('test3')), 1)
self.assertEqual(len(store.mapLayersByName('test4')), 1)
self.assertEqual(store.count(), 4)
store.removeAllMapLayers()
def test_addMapLayersInvalid(self):
""" test that invalid map layersd can't be added to store """
store = QgsMapLayerStore()
self.assertEqual(store.addMapLayers([QgsVectorLayer("Point?field=x:string", 'test', "xxx")]), [])
self.assertEqual(len(store.mapLayersByName('test')), 0)
self.assertEqual(store.count(), 0)
def test_addMapLayersAlreadyAdded(self):
""" test that already added layers can't be readded to store """
store = QgsMapLayerStore()
l1 = createLayer('test')
self.assertEqual(store.addMapLayers([l1]), [l1])
self.assertEqual(len(store.mapLayersByName('test')), 1)
self.assertEqual(store.count(), 1)
self.assertEqual(store.addMapLayers([l1]), [])
self.assertEqual(len(store.mapLayersByName('test')), 1)
self.assertEqual(store.count(), 1)
def test_addMapLayersSignals(self):
""" test that signals are correctly emitted when adding map layers"""
store = QgsMapLayerStore()
layer_was_added_spy = QSignalSpy(store.layerWasAdded)
layers_added_spy = QSignalSpy(store.layersAdded)
l1 = createLayer('test')
l2 = createLayer('test2')
store.addMapLayers([l1, l2])
# can't seem to actually test the data which was emitted, so best we can do is test
# the signal count
self.assertEqual(len(layer_was_added_spy), 2)
self.assertEqual(len(layers_added_spy), 1)
store.addMapLayers([createLayer('test3'), createLayer('test4')])
self.assertEqual(len(layer_was_added_spy), 4)
self.assertEqual(len(layers_added_spy), 2)
# try readding a layer already in the store
store.addMapLayers([l1, l2])
# should be no extra signals emitted
self.assertEqual(len(layer_was_added_spy), 4)
self.assertEqual(len(layers_added_spy), 2)
def test_mapLayerById(self):
""" test retrieving map layer by ID """
store = QgsMapLayerStore()
# test no crash with empty store
self.assertEqual(store.mapLayer('bad'), None)
self.assertEqual(store.mapLayer(None), None)
l1 = createLayer('test')
l2 = createLayer('test2')
store.addMapLayers([l1, l2])
self.assertEqual(store.mapLayer('bad'), None)
self.assertEqual(store.mapLayer(None), None)
self.assertEqual(store.mapLayer(l1.id()), l1)
self.assertEqual(store.mapLayer(l2.id()), l2)
def test_mapLayersByName(self):
""" test retrieving map layer by name """
store = QgsMapLayerStore()
# test no crash with empty store
self.assertEqual(store.mapLayersByName('bad'), [])
self.assertEqual(store.mapLayersByName(None), [])
l1 = createLayer('test')
l2 = createLayer('test2')
store.addMapLayers([l1, l2])
self.assertEqual(store.mapLayersByName('bad'), [])
self.assertEqual(store.mapLayersByName(None), [])
self.assertEqual(store.mapLayersByName('test'), [l1])
self.assertEqual(store.mapLayersByName('test2'), [l2])
#duplicate name
l3 = createLayer('test')
store.addMapLayer(l3)
self.assertEqual(set(store.mapLayersByName('test')), {l1, l3})
def test_mapLayers(self):
""" test retrieving map layers list """
store = QgsMapLayerStore()
# test no crash with empty store
self.assertEqual(store.mapLayers(), {})
l1 = createLayer('test')
l2 = createLayer('test2')
store.addMapLayers([l1, l2])
self.assertEqual(store.mapLayers(), {l1.id(): l1, l2.id(): l2})
def test_removeMapLayersById(self):
""" test removing map layers by ID """
store = QgsMapLayerStore()
# test no crash with empty store
store.removeMapLayersById(['bad'])
store.removeMapLayersById([None])
l1 = createLayer('test')
l2 = createLayer('test2')
l3 = createLayer('test3')
store.addMapLayers([l1, l2, l3])
self.assertEqual(store.count(), 3)
#remove bad layers
store.removeMapLayersById(['bad'])
self.assertEqual(store.count(), 3)
store.removeMapLayersById([None])
self.assertEqual(store.count(), 3)
# remove valid layers
l1_id = l1.id()
store.removeMapLayersById([l1_id])
self.assertEqual(store.count(), 2)
# double remove
store.removeMapLayersById([l1_id])
self.assertEqual(store.count(), 2)
# test that layer has been deleted
self.assertTrue(sip.isdeleted(l1))
# remove multiple
store.removeMapLayersById([l2.id(), l3.id()])
self.assertEqual(store.count(), 0)
self.assertTrue(sip.isdeleted(l2))
# try removing a layer not in the store
l4 = createLayer('test4')
store.removeMapLayersById([l4.id()])
self.assertFalse(sip.isdeleted(l4))
def test_removeMapLayersByLayer(self):
""" test removing map layers by layer"""
store = QgsMapLayerStore()
# test no crash with empty store
store.removeMapLayers([None])
l1 = createLayer('test')
l2 = createLayer('test2')
l3 = createLayer('test3')
store.addMapLayers([l1, l2, l3])
self.assertEqual(store.count(), 3)
#remove bad layers
store.removeMapLayers([None])
self.assertEqual(store.count(), 3)
# remove valid layers
store.removeMapLayers([l1])
self.assertEqual(store.count(), 2)
# test that layer has been deleted
self.assertTrue(sip.isdeleted(l1))
# remove multiple
store.removeMapLayers([l2, l3])
self.assertEqual(store.count(), 0)
self.assertTrue(sip.isdeleted(l2))
self.assertTrue(sip.isdeleted(l3))
def test_removeMapLayerById(self):
""" test removing a map layer by ID """
store = QgsMapLayerStore()
# test no crash with empty store
store.removeMapLayer('bad')
store.removeMapLayer(None)
l1 = createLayer('test')
l2 = createLayer('test2')
store.addMapLayers([l1, l2])
self.assertEqual(store.count(), 2)
#remove bad layers
store.removeMapLayer('bad')
self.assertEqual(store.count(), 2)
store.removeMapLayer(None)
self.assertEqual(store.count(), 2)
# remove valid layers
l1_id = l1.id()
store.removeMapLayer(l1_id)
self.assertEqual(store.count(), 1)
# double remove
store.removeMapLayer(l1_id)
self.assertEqual(store.count(), 1)
# test that layer has been deleted
self.assertTrue(sip.isdeleted(l1))
# remove second layer
store.removeMapLayer(l2.id())
self.assertEqual(store.count(), 0)
self.assertTrue(sip.isdeleted(l2))
# try removing a layer not in the store
l3 = createLayer('test3')
store.removeMapLayer(l3.id())
self.assertFalse(sip.isdeleted(l3))
def test_removeMapLayerByLayer(self):
""" test removing a map layer by layer """
store = QgsMapLayerStore()
# test no crash with empty store
store.removeMapLayer('bad')
store.removeMapLayer(None)
l1 = createLayer('test')
l2 = createLayer('test2')
store.addMapLayers([l1, l2])
self.assertEqual(store.count(), 2)
#remove bad layers
store.removeMapLayer(None)
self.assertEqual(store.count(), 2)
l3 = createLayer('test3')
store.removeMapLayer(l3)
self.assertEqual(store.count(), 2)
# remove valid layers
store.removeMapLayer(l1)
self.assertEqual(store.count(), 1)
# test that layer has been deleted
self.assertTrue(sip.isdeleted(l1))
# remove second layer
store.removeMapLayer(l2)
self.assertEqual(store.count(), 0)
self.assertTrue(sip.isdeleted(l2))
# try removing a layer not in the store
l3 = createLayer('test3')
store.removeMapLayer(l3)
self.assertFalse(sip.isdeleted(l3))
def test_removeAllMapLayers(self):
""" test removing all map layers from store """
store = QgsMapLayerStore()
l1 = createLayer('test')
l2 = createLayer('test2')
store.addMapLayers([l1, l2])
self.assertEqual(store.count(), 2)
store.removeAllMapLayers()
self.assertEqual(store.count(), 0)
self.assertEqual(store.mapLayersByName('test'), [])
self.assertEqual(store.mapLayersByName('test2'), [])
def test_addRemoveLayersSignals(self):
""" test that signals are correctly emitted when removing map layers"""
store = QgsMapLayerStore()
layers_will_be_removed_spy = QSignalSpy(store.layersWillBeRemoved)
layer_will_be_removed_spy_str = QSignalSpy(store.layerWillBeRemoved[str])
layer_will_be_removed_spy_layer = QSignalSpy(store.layerWillBeRemoved[QgsMapLayer])
layers_removed_spy = QSignalSpy(store.layersRemoved)
layer_removed_spy = QSignalSpy(store.layerRemoved)
remove_all_spy = QSignalSpy(store.allLayersRemoved)
l1 = createLayer('l1')
l2 = createLayer('l2')
l3 = createLayer('l3')
l4 = createLayer('l4')
store.addMapLayers([l1, l2, l3, l4])
# remove 1 layer
store.removeMapLayer(l1)
# can't seem to actually test the data which was emitted, so best we can do is test
# the signal count
self.assertEqual(len(layers_will_be_removed_spy), 1)
self.assertEqual(len(layer_will_be_removed_spy_str), 1)
self.assertEqual(len(layer_will_be_removed_spy_layer), 1)
self.assertEqual(len(layers_removed_spy), 1)
self.assertEqual(len(layer_removed_spy), 1)
self.assertEqual(len(remove_all_spy), 0)
self.assertEqual(store.count(), 3)
# remove 2 layers at once
store.removeMapLayersById([l2.id(), l3.id()])
self.assertEqual(len(layers_will_be_removed_spy), 2)
self.assertEqual(len(layer_will_be_removed_spy_str), 3)
self.assertEqual(len(layer_will_be_removed_spy_layer), 3)
self.assertEqual(len(layers_removed_spy), 2)
self.assertEqual(len(layer_removed_spy), 3)
self.assertEqual(len(remove_all_spy), 0)
self.assertEqual(store.count(), 1)
# remove all
store.removeAllMapLayers()
self.assertEqual(len(layers_will_be_removed_spy), 3)
self.assertEqual(len(layer_will_be_removed_spy_str), 4)
self.assertEqual(len(layer_will_be_removed_spy_layer), 4)
self.assertEqual(len(layers_removed_spy), 3)
self.assertEqual(len(layer_removed_spy), 4)
self.assertEqual(len(remove_all_spy), 1)
#remove some layers which aren't in the store
store.removeMapLayersById(['asdasd'])
self.assertEqual(len(layers_will_be_removed_spy), 3)
self.assertEqual(len(layer_will_be_removed_spy_str), 4)
self.assertEqual(len(layer_will_be_removed_spy_layer), 4)
self.assertEqual(len(layers_removed_spy), 3)
self.assertEqual(len(layer_removed_spy), 4)
self.assertEqual(len(remove_all_spy), 1)
l5 = createLayer('test5')
store.removeMapLayer(l5)
self.assertEqual(len(layers_will_be_removed_spy), 3)
self.assertEqual(len(layer_will_be_removed_spy_str), 4)
self.assertEqual(len(layer_will_be_removed_spy_layer), 4)
self.assertEqual(len(layers_removed_spy), 3)
self.assertEqual(len(layer_removed_spy), 4)
self.assertEqual(len(remove_all_spy), 1)
def test_RemoveLayerShouldNotSegFault(self):
store = QgsMapLayerStore()
# Should not segfault
store.removeMapLayersById(['not_exists'])
store.removeMapLayer('not_exists2')
# check also that the removal of an unexistent layer does not insert a null layer
for k, layer in list(store.mapLayers().items()):
assert(layer is not None)
def testTakeLayer(self):
# test taking ownership of a layer from the store
l1 = createLayer('l1')
l2 = createLayer('l2')
store = QgsMapLayerStore()
# add one layer to store
store.addMapLayer(l1)
self.assertEqual(store.mapLayers(), {l1.id(): l1})
self.assertEqual(l1.parent(), store)
# try taking some layers which don't exist in store
self.assertFalse(store.takeMapLayer(None))
self.assertFalse(store.takeMapLayer(l2))
# but l2 should still exist..
self.assertTrue(l2.isValid())
# take layer from store
self.assertEqual(store.takeMapLayer(l1), l1)
self.assertFalse(store.mapLayers()) # no layers left
# but l1 should still exist
self.assertTrue(l1.isValid())
# layer should have no parent now
self.assertFalse(l1.parent())
# destroy store
store = None
self.assertTrue(l1.isValid())
if __name__ == '__main__':
unittest.main()

View File

@ -22,9 +22,15 @@ from qgis.core import (QgsProject,
QgsApplication,
QgsUnitTypes,
QgsCoordinateReferenceSystem,
QgsVectorLayer)
QgsVectorLayer,
QgsMapLayer)
from qgis.gui import (QgsLayerTreeMapCanvasBridge,
QgsMapCanvas)
from qgis.PyQt.QtTest import QSignalSpy
from qgis.PyQt.QtCore import QT_VERSION_STR
import sip
from qgis.testing import start_app, unittest
from utilities import (unitTestDataPath)
@ -32,6 +38,10 @@ app = start_app()
TEST_DATA_DIR = unitTestDataPath()
def createLayer(name):
return QgsVectorLayer("Point?field=x:string", name, "memory")
class TestQgsProject(unittest.TestCase):
def __init__(self, methodName):
@ -181,6 +191,499 @@ class TestQgsProject(unittest.TestCase):
expected = ['polys', 'lines']
self.assertEqual(sorted(layers_names), sorted(expected))
def testInstance(self):
""" test retrieving global instance """
self.assertTrue(QgsProject.instance())
# register a layer to the singleton
QgsProject.instance().addMapLayer(createLayer('test'))
# check that the same instance is returned
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
QgsProject.instance().removeAllMapLayers()
def test_addMapLayer(self):
""" test adding individual map layers to registry """
QgsProject.instance().removeAllMapLayers()
l1 = createLayer('test')
self.assertEqual(QgsProject.instance().addMapLayer(l1), l1)
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
self.assertEqual(QgsProject.instance().count(), 1)
# adding a second layer should leave existing layers intact
l2 = createLayer('test2')
self.assertEqual(QgsProject.instance().addMapLayer(l2), l2)
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
self.assertEqual(len(QgsProject.instance().mapLayersByName('test2')), 1)
self.assertEqual(QgsProject.instance().count(), 2)
QgsProject.instance().removeAllMapLayers()
def test_addMapLayerAlreadyAdded(self):
""" test that already added layers can't be readded to registry """
QgsProject.instance().removeAllMapLayers()
l1 = createLayer('test')
QgsProject.instance().addMapLayer(l1)
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
self.assertEqual(QgsProject.instance().count(), 1)
self.assertEqual(QgsProject.instance().addMapLayer(l1), None)
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
self.assertEqual(QgsProject.instance().count(), 1)
QgsProject.instance().removeAllMapLayers()
def test_addMapLayerInvalid(self):
""" test that invalid map layersd can't be added to registry """
QgsProject.instance().removeAllMapLayers()
self.assertEqual(QgsProject.instance().addMapLayer(QgsVectorLayer("Point?field=x:string", 'test', "xxx")), None)
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 0)
self.assertEqual(QgsProject.instance().count(), 0)
QgsProject.instance().removeAllMapLayers()
def test_addMapLayerSignals(self):
""" test that signals are correctly emitted when adding map layer"""
QgsProject.instance().removeAllMapLayers()
layer_was_added_spy = QSignalSpy(QgsProject.instance().layerWasAdded)
layers_added_spy = QSignalSpy(QgsProject.instance().layersAdded)
legend_layers_added_spy = QSignalSpy(QgsProject.instance().legendLayersAdded)
l1 = createLayer('test')
QgsProject.instance().addMapLayer(l1)
# can't seem to actually test the data which was emitted, so best we can do is test
# the signal count
self.assertEqual(len(layer_was_added_spy), 1)
self.assertEqual(len(layers_added_spy), 1)
self.assertEqual(len(legend_layers_added_spy), 1)
# layer not added to legend
QgsProject.instance().addMapLayer(createLayer('test2'), False)
self.assertEqual(len(layer_was_added_spy), 2)
self.assertEqual(len(layers_added_spy), 2)
self.assertEqual(len(legend_layers_added_spy), 1)
# try readding a layer already in the registry
QgsProject.instance().addMapLayer(l1)
# should be no extra signals emitted
self.assertEqual(len(layer_was_added_spy), 2)
self.assertEqual(len(layers_added_spy), 2)
self.assertEqual(len(legend_layers_added_spy), 1)
def test_addMapLayers(self):
""" test adding multiple map layers to registry """
QgsProject.instance().removeAllMapLayers()
l1 = createLayer('test')
l2 = createLayer('test2')
self.assertEqual(set(QgsProject.instance().addMapLayers([l1, l2])), set([l1, l2]))
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
self.assertEqual(len(QgsProject.instance().mapLayersByName('test2')), 1)
self.assertEqual(QgsProject.instance().count(), 2)
# adding more layers should leave existing layers intact
l3 = createLayer('test3')
l4 = createLayer('test4')
self.assertEqual(set(QgsProject.instance().addMapLayers([l3, l4])), set([l3, l4]))
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
self.assertEqual(len(QgsProject.instance().mapLayersByName('test2')), 1)
self.assertEqual(len(QgsProject.instance().mapLayersByName('test3')), 1)
self.assertEqual(len(QgsProject.instance().mapLayersByName('test4')), 1)
self.assertEqual(QgsProject.instance().count(), 4)
QgsProject.instance().removeAllMapLayers()
def test_addMapLayersInvalid(self):
""" test that invalid map layersd can't be added to registry """
QgsProject.instance().removeAllMapLayers()
self.assertEqual(QgsProject.instance().addMapLayers([QgsVectorLayer("Point?field=x:string", 'test', "xxx")]), [])
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 0)
self.assertEqual(QgsProject.instance().count(), 0)
QgsProject.instance().removeAllMapLayers()
def test_addMapLayersAlreadyAdded(self):
""" test that already added layers can't be readded to registry """
QgsProject.instance().removeAllMapLayers()
l1 = createLayer('test')
self.assertEqual(QgsProject.instance().addMapLayers([l1]), [l1])
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
self.assertEqual(QgsProject.instance().count(), 1)
self.assertEqual(QgsProject.instance().addMapLayers([l1]), [])
self.assertEqual(len(QgsProject.instance().mapLayersByName('test')), 1)
self.assertEqual(QgsProject.instance().count(), 1)
QgsProject.instance().removeAllMapLayers()
def test_addMapLayersSignals(self):
""" test that signals are correctly emitted when adding map layers"""
QgsProject.instance().removeAllMapLayers()
layer_was_added_spy = QSignalSpy(QgsProject.instance().layerWasAdded)
layers_added_spy = QSignalSpy(QgsProject.instance().layersAdded)
legend_layers_added_spy = QSignalSpy(QgsProject.instance().legendLayersAdded)
l1 = createLayer('test')
l2 = createLayer('test2')
QgsProject.instance().addMapLayers([l1, l2])
# can't seem to actually test the data which was emitted, so best we can do is test
# the signal count
self.assertEqual(len(layer_was_added_spy), 2)
self.assertEqual(len(layers_added_spy), 1)
self.assertEqual(len(legend_layers_added_spy), 1)
# layer not added to legend
QgsProject.instance().addMapLayers([createLayer('test3'), createLayer('test4')], False)
self.assertEqual(len(layer_was_added_spy), 4)
self.assertEqual(len(layers_added_spy), 2)
self.assertEqual(len(legend_layers_added_spy), 1)
# try readding a layer already in the registry
QgsProject.instance().addMapLayers([l1, l2])
# should be no extra signals emitted
self.assertEqual(len(layer_was_added_spy), 4)
self.assertEqual(len(layers_added_spy), 2)
self.assertEqual(len(legend_layers_added_spy), 1)
def test_mapLayerById(self):
""" test retrieving map layer by ID """
QgsProject.instance().removeAllMapLayers()
# test no crash with empty registry
self.assertEqual(QgsProject.instance().mapLayer('bad'), None)
self.assertEqual(QgsProject.instance().mapLayer(None), None)
l1 = createLayer('test')
l2 = createLayer('test2')
QgsProject.instance().addMapLayers([l1, l2])
self.assertEqual(QgsProject.instance().mapLayer('bad'), None)
self.assertEqual(QgsProject.instance().mapLayer(None), None)
self.assertEqual(QgsProject.instance().mapLayer(l1.id()), l1)
self.assertEqual(QgsProject.instance().mapLayer(l2.id()), l2)
def test_mapLayersByName(self):
""" test retrieving map layer by name """
p = QgsProject()
# test no crash with empty registry
self.assertEqual(p.mapLayersByName('bad'), [])
self.assertEqual(p.mapLayersByName(None), [])
l1 = createLayer('test')
l2 = createLayer('test2')
p.addMapLayers([l1, l2])
self.assertEqual(p.mapLayersByName('bad'), [])
self.assertEqual(p.mapLayersByName(None), [])
self.assertEqual(p.mapLayersByName('test'), [l1])
self.assertEqual(p.mapLayersByName('test2'), [l2])
#duplicate name
l3 = createLayer('test')
p.addMapLayer(l3)
self.assertEqual(set(p.mapLayersByName('test')), set([l1, l3]))
def test_mapLayers(self):
""" test retrieving map layers list """
QgsProject.instance().removeAllMapLayers()
# test no crash with empty registry
self.assertEqual(QgsProject.instance().mapLayers(), {})
l1 = createLayer('test')
l2 = createLayer('test2')
QgsProject.instance().addMapLayers([l1, l2])
self.assertEqual(QgsProject.instance().mapLayers(), {l1.id(): l1, l2.id(): l2})
def test_removeMapLayersById(self):
""" test removing map layers by ID """
QgsProject.instance().removeAllMapLayers()
# test no crash with empty registry
QgsProject.instance().removeMapLayers(['bad'])
QgsProject.instance().removeMapLayers([None])
l1 = createLayer('test')
l2 = createLayer('test2')
l3 = createLayer('test3')
QgsProject.instance().addMapLayers([l1, l2, l3])
self.assertEqual(QgsProject.instance().count(), 3)
#remove bad layers
QgsProject.instance().removeMapLayers(['bad'])
self.assertEqual(QgsProject.instance().count(), 3)
QgsProject.instance().removeMapLayers([None])
self.assertEqual(QgsProject.instance().count(), 3)
# remove valid layers
l1_id = l1.id()
QgsProject.instance().removeMapLayers([l1_id])
self.assertEqual(QgsProject.instance().count(), 2)
# double remove
QgsProject.instance().removeMapLayers([l1_id])
self.assertEqual(QgsProject.instance().count(), 2)
# test that layer has been deleted
self.assertTrue(sip.isdeleted(l1))
# remove multiple
QgsProject.instance().removeMapLayers([l2.id(), l3.id()])
self.assertEqual(QgsProject.instance().count(), 0)
self.assertTrue(sip.isdeleted(l2))
# try removing a layer not in the registry
l4 = createLayer('test4')
QgsProject.instance().removeMapLayers([l4.id()])
self.assertFalse(sip.isdeleted(l4))
# fails on qt5 due to removeMapLayers list type conversion - needs a PyName alias
# added to removeMapLayers for QGIS 3.0
@unittest.expectedFailure(QT_VERSION_STR[0] == '5')
def test_removeMapLayersByLayer(self):
""" test removing map layers by layer"""
QgsProject.instance().removeAllMapLayers()
# test no crash with empty registry
QgsProject.instance().removeMapLayers([None])
l1 = createLayer('test')
l2 = createLayer('test2')
l3 = createLayer('test3')
QgsProject.instance().addMapLayers([l1, l2, l3])
self.assertEqual(QgsProject.instance().count(), 3)
#remove bad layers
QgsProject.instance().removeMapLayers([None])
self.assertEqual(QgsProject.instance().count(), 3)
# remove valid layers
QgsProject.instance().removeMapLayers([l1])
self.assertEqual(QgsProject.instance().count(), 2)
# test that layer has been deleted
self.assertTrue(sip.isdeleted(l1))
# remove multiple
QgsProject.instance().removeMapLayers([l2, l3])
self.assertEqual(QgsProject.instance().count(), 0)
self.assertTrue(sip.isdeleted(l2))
self.assertTrue(sip.isdeleted(l3))
def test_removeMapLayerById(self):
""" test removing a map layer by ID """
QgsProject.instance().removeAllMapLayers()
# test no crash with empty registry
QgsProject.instance().removeMapLayer('bad')
QgsProject.instance().removeMapLayer(None)
l1 = createLayer('test')
l2 = createLayer('test2')
QgsProject.instance().addMapLayers([l1, l2])
self.assertEqual(QgsProject.instance().count(), 2)
#remove bad layers
QgsProject.instance().removeMapLayer('bad')
self.assertEqual(QgsProject.instance().count(), 2)
QgsProject.instance().removeMapLayer(None)
self.assertEqual(QgsProject.instance().count(), 2)
# remove valid layers
l1_id = l1.id()
QgsProject.instance().removeMapLayer(l1_id)
self.assertEqual(QgsProject.instance().count(), 1)
# double remove
QgsProject.instance().removeMapLayer(l1_id)
self.assertEqual(QgsProject.instance().count(), 1)
# test that layer has been deleted
self.assertTrue(sip.isdeleted(l1))
# remove second layer
QgsProject.instance().removeMapLayer(l2.id())
self.assertEqual(QgsProject.instance().count(), 0)
self.assertTrue(sip.isdeleted(l2))
# try removing a layer not in the registry
l3 = createLayer('test3')
QgsProject.instance().removeMapLayer(l3.id())
self.assertFalse(sip.isdeleted(l3))
def test_removeMapLayerByLayer(self):
""" test removing a map layer by layer """
QgsProject.instance().removeAllMapLayers()
# test no crash with empty registry
QgsProject.instance().removeMapLayer('bad')
QgsProject.instance().removeMapLayer(None)
l1 = createLayer('test')
l2 = createLayer('test2')
QgsProject.instance().addMapLayers([l1, l2])
self.assertEqual(QgsProject.instance().count(), 2)
#remove bad layers
QgsProject.instance().removeMapLayer(None)
self.assertEqual(QgsProject.instance().count(), 2)
l3 = createLayer('test3')
QgsProject.instance().removeMapLayer(l3)
self.assertEqual(QgsProject.instance().count(), 2)
# remove valid layers
QgsProject.instance().removeMapLayer(l1)
self.assertEqual(QgsProject.instance().count(), 1)
# test that layer has been deleted
self.assertTrue(sip.isdeleted(l1))
# remove second layer
QgsProject.instance().removeMapLayer(l2)
self.assertEqual(QgsProject.instance().count(), 0)
self.assertTrue(sip.isdeleted(l2))
# try removing a layer not in the registry
l3 = createLayer('test3')
QgsProject.instance().removeMapLayer(l3)
self.assertFalse(sip.isdeleted(l3))
def test_removeAllMapLayers(self):
""" test removing all map layers from registry """
QgsProject.instance().removeAllMapLayers()
l1 = createLayer('test')
l2 = createLayer('test2')
QgsProject.instance().addMapLayers([l1, l2])
self.assertEqual(QgsProject.instance().count(), 2)
QgsProject.instance().removeAllMapLayers()
self.assertEqual(QgsProject.instance().count(), 0)
self.assertEqual(QgsProject.instance().mapLayersByName('test'), [])
self.assertEqual(QgsProject.instance().mapLayersByName('test2'), [])
def test_addRemoveLayersSignals(self):
""" test that signals are correctly emitted when removing map layers"""
QgsProject.instance().removeAllMapLayers()
layers_will_be_removed_spy = QSignalSpy(QgsProject.instance().layersWillBeRemoved)
layer_will_be_removed_spy_str = QSignalSpy(QgsProject.instance().layerWillBeRemoved[str])
layer_will_be_removed_spy_layer = QSignalSpy(QgsProject.instance().layerWillBeRemoved[QgsMapLayer])
layers_removed_spy = QSignalSpy(QgsProject.instance().layersRemoved)
layer_removed_spy = QSignalSpy(QgsProject.instance().layerRemoved)
remove_all_spy = QSignalSpy(QgsProject.instance().removeAll)
l1 = createLayer('l1')
l2 = createLayer('l2')
l3 = createLayer('l3')
l4 = createLayer('l4')
QgsProject.instance().addMapLayers([l1, l2, l3, l4])
# remove 1 layer
QgsProject.instance().removeMapLayer(l1)
# can't seem to actually test the data which was emitted, so best we can do is test
# the signal count
self.assertEqual(len(layers_will_be_removed_spy), 1)
self.assertEqual(len(layer_will_be_removed_spy_str), 1)
self.assertEqual(len(layer_will_be_removed_spy_layer), 1)
self.assertEqual(len(layers_removed_spy), 1)
self.assertEqual(len(layer_removed_spy), 1)
self.assertEqual(len(remove_all_spy), 0)
self.assertEqual(QgsProject.instance().count(), 3)
# remove 2 layers at once
QgsProject.instance().removeMapLayers([l2.id(), l3.id()])
self.assertEqual(len(layers_will_be_removed_spy), 2)
self.assertEqual(len(layer_will_be_removed_spy_str), 3)
self.assertEqual(len(layer_will_be_removed_spy_layer), 3)
self.assertEqual(len(layers_removed_spy), 2)
self.assertEqual(len(layer_removed_spy), 3)
self.assertEqual(len(remove_all_spy), 0)
self.assertEqual(QgsProject.instance().count(), 1)
# remove all
QgsProject.instance().removeAllMapLayers()
self.assertEqual(len(layers_will_be_removed_spy), 3)
self.assertEqual(len(layer_will_be_removed_spy_str), 4)
self.assertEqual(len(layer_will_be_removed_spy_layer), 4)
self.assertEqual(len(layers_removed_spy), 3)
self.assertEqual(len(layer_removed_spy), 4)
self.assertEqual(len(remove_all_spy), 1)
#remove some layers which aren't in the registry
QgsProject.instance().removeMapLayers(['asdasd'])
self.assertEqual(len(layers_will_be_removed_spy), 3)
self.assertEqual(len(layer_will_be_removed_spy_str), 4)
self.assertEqual(len(layer_will_be_removed_spy_layer), 4)
self.assertEqual(len(layers_removed_spy), 3)
self.assertEqual(len(layer_removed_spy), 4)
self.assertEqual(len(remove_all_spy), 1)
l5 = createLayer('test5')
QgsProject.instance().removeMapLayer(l5)
self.assertEqual(len(layers_will_be_removed_spy), 3)
self.assertEqual(len(layer_will_be_removed_spy_str), 4)
self.assertEqual(len(layer_will_be_removed_spy_layer), 4)
self.assertEqual(len(layers_removed_spy), 3)
self.assertEqual(len(layer_removed_spy), 4)
self.assertEqual(len(remove_all_spy), 1)
def test_RemoveLayerShouldNotSegFault(self):
QgsProject.instance().removeAllMapLayers()
reg = QgsProject.instance()
# Should not segfault
reg.removeMapLayers(['not_exists'])
reg.removeMapLayer('not_exists2')
# check also that the removal of an unexistent layer does not insert a null layer
for k, layer in list(reg.mapLayers().items()):
assert(layer is not None)
def testTakeLayer(self):
# test taking ownership of a layer from the project
l1 = createLayer('l1')
l2 = createLayer('l2')
p = QgsProject()
# add one layer to project
p.addMapLayer(l1)
self.assertEqual(p.mapLayers(), {l1.id(): l1})
self.assertEqual(l1.parent().parent(), p)
# try taking some layers which don't exist in project
self.assertFalse(p.takeMapLayer(None))
self.assertFalse(p.takeMapLayer(l2))
# but l2 should still exist..
self.assertTrue(l2.isValid())
# take layer from project
self.assertEqual(p.takeMapLayer(l1), l1)
self.assertFalse(p.mapLayers()) # no layers left
# but l1 should still exist
self.assertTrue(l1.isValid())
# layer should have no parent now
self.assertFalse(l1.parent())
# destroy project
p = None
self.assertTrue(l1.isValid())
if __name__ == '__main__':
unittest.main()