Skip to content

Commit b5ae888

Browse files
authoredMay 6, 2017
Merge pull request #4478 from nyalldawson/layer_store
Split off map layer storage handling from QgsProject to QgsMapLayerStore
2 parents 27ab5a0 + 32e06f4 commit b5ae888

18 files changed

+2003
-723
lines changed
 

‎python/core/core.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
%Include qgsmaplayermodel.sip
9393
%Include qgsmaplayerproxymodel.sip
9494
%Include qgsmaplayerrenderer.sip
95+
%Include qgsmaplayerstore.sip
9596
%Include qgsmaplayerstylemanager.sip
9697
%Include qgsmaprenderercache.sip
9798
%Include qgsmaprenderercustompainterjob.sip

‎python/core/processing/qgsprocessingcontext.sip

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,11 @@ class QgsProcessingContext
7575
Sets the expression ``context``.
7676
%End
7777

78-
QgsProject &temporaryLayerStore();
78+
QgsMapLayerStore *temporaryLayerStore();
7979
%Docstring
80-
Returns a reference to the project used for storing temporary layers during
80+
Returns a reference to the layer store used for storing temporary layers during
8181
algorithm execution.
82-
:rtype: QgsProject
82+
:rtype: QgsMapLayerStore
8383
%End
8484

8585
QgsFeatureRequest::InvalidGeometryCheck invalidGeometryCheck() const;

‎python/core/qgsmaplayerstore.sip

Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/core/qgsmaplayerstore.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
12+
13+
14+
class QgsMapLayerStore : QObject
15+
{
16+
%Docstring
17+
A storage object for map layers, in which the layers are owned by the
18+
store and have their lifetime bound to the store.
19+
.. versionadded:: 3.0
20+
%End
21+
22+
%TypeHeaderCode
23+
#include "qgsmaplayerstore.h"
24+
%End
25+
public:
26+
27+
explicit QgsMapLayerStore( QObject *parent /TransferThis/ = 0 );
28+
%Docstring
29+
Constructor for QgsMapLayerStore.
30+
%End
31+
32+
~QgsMapLayerStore();
33+
34+
int count() const;
35+
%Docstring
36+
Returns the number of layers contained in the store.
37+
:rtype: int
38+
%End
39+
40+
41+
int __len__() const;
42+
%Docstring
43+
Returns the number of layers contained in the store.
44+
:rtype: int
45+
%End
46+
%MethodCode
47+
sipRes = sipCpp->count();
48+
%End
49+
50+
QgsMapLayer *mapLayer( const QString &id ) const;
51+
%Docstring
52+
Retrieve a pointer to a layer by layer ``id``.
53+
\param id ID of layer to retrieve
54+
:return: matching layer, or None if no matching layer found
55+
.. seealso:: mapLayersByName()
56+
.. seealso:: mapLayers()
57+
:rtype: QgsMapLayer
58+
%End
59+
60+
QList<QgsMapLayer *> mapLayersByName( const QString &name ) const;
61+
%Docstring
62+
Retrieve a list of matching layers by layer ``name``.
63+
\param name name of layers to match
64+
:return: list of matching layers
65+
.. seealso:: mapLayer()
66+
.. seealso:: mapLayers()
67+
:rtype: list of QgsMapLayer
68+
%End
69+
70+
QMap<QString, QgsMapLayer *> mapLayers() const;
71+
%Docstring
72+
Returns a map of all layers by layer ID.
73+
.. seealso:: mapLayer()
74+
.. seealso:: mapLayersByName()
75+
.. seealso:: layers()
76+
:rtype: QMap<str, QgsMapLayer *>
77+
%End
78+
79+
80+
QList<QgsMapLayer *> addMapLayers( const QList<QgsMapLayer *> &layers /Transfer/);
81+
82+
%Docstring
83+
\brief
84+
Add a list of ``layers`` to the store. Ownership of the layers is transferred
85+
to the store.
86+
87+
The layersAdded() and layerWasAdded() signals will always be emitted.
88+
89+
\param layers A list of layer which should be added to the store.
90+
\param takeOwnership Ownership will be transferred to the layer store.
91+
If you specify false here you have take care of deleting
92+
the layers yourself. Not available in Python.
93+
94+
:return: a list of the map layers that were added
95+
successfully. If a layer is invalid, or already exists in the store,
96+
it will not be part of the returned list.
97+
98+
.. seealso:: addMapLayer()
99+
:rtype: list of QgsMapLayer
100+
%End
101+
102+
QgsMapLayer *addMapLayer( QgsMapLayer *layer /Transfer/);
103+
104+
%Docstring
105+
\brief
106+
Add a ``layer`` to the store. Ownership of the layer is transferred to the
107+
store.
108+
109+
The layersAdded() and layerWasAdded() signals will always be emitted.
110+
If you are adding multiple layers at once, you should use
111+
addMapLayers() instead.
112+
113+
\param layer A layer to add to the store
114+
\param takeOwnership Ownership will be transferred to the layer store.
115+
If you specify false here you have take care of deleting
116+
the layers yourself. Not available in Python.
117+
118+
:return: None if unable to add layer, otherwise pointer to newly added layer
119+
120+
.. seealso:: addMapLayers
121+
122+
.. note::
123+
124+
Use addMapLayers() if adding more than one layer at a time.
125+
.. seealso:: addMapLayers()
126+
:rtype: QgsMapLayer
127+
%End
128+
129+
void removeMapLayers( const QStringList &layerIds ) /PyName=removeMapLayersById/;
130+
%Docstring
131+
\brief
132+
Remove a set of layers from the store by layer ID.
133+
134+
The specified layers will be removed from the store.
135+
These layers will also be deleted.
136+
137+
\param layerIds list of IDs of the layers to remove
138+
139+
.. seealso:: takeMapLayer()
140+
.. seealso:: removeMapLayer()
141+
.. seealso:: removeAllMapLayers()
142+
.. note::
143+
144+
available in Python bindings as removeMapLayersById.
145+
%End
146+
147+
void removeMapLayers( const QList<QgsMapLayer *> &layers );
148+
%Docstring
149+
\brief
150+
Remove a set of ``layers`` from the store.
151+
152+
The specified layers will be removed from the store.
153+
These layers will also be deleted.
154+
155+
\param layers A list of layers to remove. Null pointers are ignored.
156+
157+
.. seealso:: takeMapLayer()
158+
.. seealso:: removeMapLayer()
159+
.. seealso:: removeAllMapLayers()
160+
%End
161+
162+
void removeMapLayer( const QString &id );
163+
%Docstring
164+
\brief
165+
Remove a layer from the store by layer ``id``.
166+
167+
The specified layer will be removed from the store. The layer will also be deleted.
168+
169+
\param id ID of the layer to remove
170+
171+
.. seealso:: takeMapLayer()
172+
.. seealso:: removeMapLayers()
173+
.. seealso:: removeAllMapLayers()
174+
%End
175+
176+
void removeMapLayer( QgsMapLayer *layer );
177+
%Docstring
178+
\brief
179+
Remove a ``layer`` from the store.
180+
181+
The specified layer will be removed from the store. The layer will also be deleted.
182+
183+
\param layer The layer to remove. Null pointers are ignored.
184+
185+
.. seealso:: takeMapLayer()
186+
.. seealso:: removeMapLayers()
187+
.. seealso:: removeAllMapLayers()
188+
%End
189+
190+
QgsMapLayer *takeMapLayer( QgsMapLayer *layer ) /TransferBack/;
191+
%Docstring
192+
Takes a ``layer`` from the store. If the layer was owned by the store, the
193+
layer will be returned without deleting it. The caller takes ownership of
194+
the layer and is responsible for deleting it.
195+
.. seealso:: removeMapLayer()
196+
:rtype: QgsMapLayer
197+
%End
198+
199+
void removeAllMapLayers();
200+
%Docstring
201+
Removes all registered layers. These layers will also be deleted.
202+
203+
.. note::
204+
205+
Calling this method will cause the removeAll() signal to
206+
be emitted.
207+
.. seealso:: removeMapLayer()
208+
.. seealso:: removeMapLayers()
209+
%End
210+
211+
signals:
212+
213+
void layersWillBeRemoved( const QStringList &layerIds );
214+
%Docstring
215+
Emitted when one or more layers are about to be removed from the store.
216+
217+
\param layerIds A list of IDs for the layers which are to be removed.
218+
.. seealso:: layerWillBeRemoved()
219+
.. seealso:: layersRemoved()
220+
%End
221+
222+
void layersWillBeRemoved( const QList<QgsMapLayer *> &layers );
223+
%Docstring
224+
Emitted when one or more layers are about to be removed from the store.
225+
226+
\param layers A list of layers which are to be removed.
227+
.. seealso:: layerWillBeRemoved()
228+
.. seealso:: layersRemoved()
229+
%End
230+
231+
void layerWillBeRemoved( const QString &layerId );
232+
%Docstring
233+
Emitted when a layer is about to be removed from the store.
234+
235+
\param layerId The ID of the layer to be removed.
236+
237+
.. note::
238+
239+
Consider using layersWillBeRemoved() instead.
240+
.. seealso:: layersWillBeRemoved()
241+
.. seealso:: layerRemoved()
242+
%End
243+
244+
void layerWillBeRemoved( QgsMapLayer *layer );
245+
%Docstring
246+
Emitted when a layer is about to be removed from the store.
247+
248+
\param layer The layer to be removed.
249+
250+
.. note::
251+
252+
Consider using layersWillBeRemoved() instead.
253+
.. seealso:: layersWillBeRemoved()
254+
.. seealso:: layerRemoved()
255+
%End
256+
257+
void layersRemoved( const QStringList &layerIds );
258+
%Docstring
259+
Emitted after one or more layers were removed from the store.
260+
261+
\param layerIds A list of IDs of the layers which were removed.
262+
.. seealso:: layersWillBeRemoved()
263+
%End
264+
265+
void layerRemoved( const QString &layerId );
266+
%Docstring
267+
Emitted after a layer was removed from the store.
268+
269+
\param layerId The ID of the layer removed.
270+
271+
.. note::
272+
273+
Consider using layersRemoved() instead
274+
.. seealso:: layerWillBeRemoved()
275+
%End
276+
277+
void allLayersRemoved();
278+
%Docstring
279+
Emitted when all layers are removed, before layersWillBeRemoved() and
280+
layerWillBeRemoved() signals are emitted. The layersWillBeRemoved() and
281+
layerWillBeRemoved() signals will still be emitted following this signal.
282+
You can use this signal to do easy (and fast) cleanup.
283+
%End
284+
285+
void layersAdded( const QList<QgsMapLayer *> &layers );
286+
%Docstring
287+
Emitted when one or more layers were added to the store.
288+
289+
\param layers List of layers which have been added.
290+
291+
.. seealso:: legendLayersAdded()
292+
.. seealso:: layerWasAdded()
293+
%End
294+
295+
void layerWasAdded( QgsMapLayer *layer );
296+
%Docstring
297+
Emitted when a ``layer`` was added to the store.
298+
299+
.. note::
300+
301+
Consider using layersAdded() instead
302+
.. seealso:: layersAdded()
303+
%End
304+
305+
};
306+
307+
/************************************************************************
308+
* This file has been generated automatically from *
309+
* *
310+
* src/core/qgsmaplayerstore.h *
311+
* *
312+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
313+
************************************************************************/

‎python/core/qgsproject.sip

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,8 @@ class QgsProject : QObject, QgsExpressionContextGenerator
422422
*/
423423
const QgsLabelingEngineSettings &labelingEngineSettings() const;
424424

425+
QgsMapLayerStore *layerStore();
426+
425427
int count() const;
426428

427429
QgsMapLayer *mapLayer( const QString &layerId ) const;

‎python/plugins/processing/tools/vector.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ def createVectorWriter(destination, encoding, fields, geometryType, crs, context
481481

482482
layer = QgsVectorLayer(uri, destination, 'memory')
483483
sink = layer.dataProvider()
484-
context.temporaryLayerStore().addMapLayer(layer, False)
484+
context.temporaryLayerStore().addMapLayer(layer)
485485
destination = layer.id()
486486
elif destination.startswith(POSTGIS_LAYER_PREFIX):
487487
uri = QgsDataSourceUri(destination[len(POSTGIS_LAYER_PREFIX):])
@@ -518,7 +518,7 @@ def _runSQL(sql):
518518

519519
layer = QgsVectorLayer(uri.uri(), uri.table(), "postgres")
520520
sink = layer.dataProvider()
521-
context.temporaryLayerStore().addMapLayer(layer, False)
521+
context.temporaryLayerStore().addMapLayer(layer)
522522
elif destination.startswith(SPATIALITE_LAYER_PREFIX):
523523
uri = QgsDataSourceUri(destination[len(SPATIALITE_LAYER_PREFIX):])
524524
try:
@@ -548,7 +548,7 @@ def _runSQL(sql):
548548

549549
layer = QgsVectorLayer(uri.uri(), uri.table(), "spatialite")
550550
sink = layer.dataProvider()
551-
context.temporaryLayerStore().addMapLayer(layer, False)
551+
context.temporaryLayerStore().addMapLayer(layer)
552552
else:
553553
formats = QgsVectorFileWriter.supportedFiltersAndFormats()
554554
OGRCodes = {}

‎src/core/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ SET(QGIS_CORE_SRCS
178178
qgsmaplayerlegend.cpp
179179
qgsmaplayermodel.cpp
180180
qgsmaplayerproxymodel.cpp
181+
qgsmaplayerstore.cpp
181182
qgsmaplayerstylemanager.cpp
182183
qgsmaprenderercache.cpp
183184
qgsmaprenderercustompainterjob.cpp
@@ -527,6 +528,7 @@ SET(QGIS_CORE_MOC_HDRS
527528
qgsmaplayerlegend.h
528529
qgsmaplayermodel.h
529530
qgsmaplayerproxymodel.h
531+
qgsmaplayerstore.h
530532
qgsmaplayerstylemanager.h
531533
qgsmaprenderercache.h
532534
qgsmaprenderercustompainterjob.h

‎src/core/processing/qgsprocessingcontext.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,10 @@ class CORE_EXPORT QgsProcessingContext
9090
void setExpressionContext( const QgsExpressionContext &context ) { mExpressionContext = context; }
9191

9292
/**
93-
* Returns a reference to the project used for storing temporary layers during
93+
* Returns a reference to the layer store used for storing temporary layers during
9494
* algorithm execution.
9595
*/
96-
QgsProject &temporaryLayerStore() { return tempProject; }
96+
QgsMapLayerStore *temporaryLayerStore() { return &tempLayerStore; }
9797

9898
/**
9999
* Returns the behavior used for checking invalid geometries in input layers.
@@ -147,7 +147,7 @@ class CORE_EXPORT QgsProcessingContext
147147
QgsProcessingContext::Flags mFlags = 0;
148148
QPointer< QgsProject > mProject;
149149
//! Temporary project owned by the context, used for storing temporarily loaded map layers
150-
QgsProject tempProject;
150+
QgsMapLayerStore tempLayerStore;
151151
QgsExpressionContext mExpressionContext;
152152
QgsFeatureRequest::InvalidGeometryCheck mInvalidGeometryCheck = QgsFeatureRequest::GeometryNoCheck;
153153
std::function< void( const QgsFeature & ) > mInvalidGeometryCallback;

‎src/core/processing/qgsprocessingutils.cpp

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,27 @@ QList<QgsMapLayer *> QgsProcessingUtils::compatibleLayers( QgsProject *project,
8585
return layers;
8686
}
8787

88-
QgsMapLayer *QgsProcessingUtils::mapLayerFromProject( const QString &string, QgsProject *project )
88+
QgsMapLayer *QgsProcessingUtils::mapLayerFromStore( const QString &string, QgsMapLayerStore *store )
8989
{
90-
if ( string.isEmpty() )
90+
if ( !store || string.isEmpty() )
9191
return nullptr;
9292

93-
QList< QgsMapLayer * > layers = compatibleLayers( project, false );
93+
QList< QgsMapLayer * > layers = store->mapLayers().values();
94+
95+
layers.erase( std::remove_if( layers.begin(), layers.end(), []( QgsMapLayer * layer )
96+
{
97+
switch ( layer->type() )
98+
{
99+
case QgsMapLayer::VectorLayer:
100+
return !canUseLayer( qobject_cast< QgsVectorLayer * >( layer ) );
101+
case QgsMapLayer::RasterLayer:
102+
return !canUseLayer( qobject_cast< QgsRasterLayer * >( layer ) );
103+
case QgsMapLayer::PluginLayer:
104+
return true;
105+
}
106+
return true;
107+
} ), layers.end() );
108+
94109
Q_FOREACH ( QgsMapLayer *l, layers )
95110
{
96111
if ( l->id() == string )
@@ -108,6 +123,7 @@ QgsMapLayer *QgsProcessingUtils::mapLayerFromProject( const QString &string, Qgs
108123
}
109124
return nullptr;
110125
}
126+
111127
///@cond PRIVATE
112128
class ProjectionSettingRestorer
113129
{
@@ -159,11 +175,15 @@ QgsMapLayer *QgsProcessingUtils::mapLayerFromString( const QString &string, QgsP
159175
return nullptr;
160176

161177
// prefer project layers
162-
QgsMapLayer *layer = mapLayerFromProject( string, context.project() );
163-
if ( layer )
164-
return layer;
178+
QgsMapLayer *layer = nullptr;
179+
if ( context.project() )
180+
{
181+
QgsMapLayer *layer = mapLayerFromStore( string, context.project()->layerStore() );
182+
if ( layer )
183+
return layer;
184+
}
165185

166-
layer = mapLayerFromProject( string, &context.temporaryLayerStore() );
186+
layer = mapLayerFromStore( string, context.temporaryLayerStore() );
167187
if ( layer )
168188
return layer;
169189

@@ -173,7 +193,7 @@ QgsMapLayer *QgsProcessingUtils::mapLayerFromString( const QString &string, QgsP
173193
layer = loadMapLayerFromString( string );
174194
if ( layer )
175195
{
176-
context.temporaryLayerStore().addMapLayer( layer );
196+
context.temporaryLayerStore()->addMapLayer( layer );
177197
return layer;
178198
}
179199
else

‎src/core/processing/qgsprocessingutils.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
class QgsProject;
2929
class QgsProcessingContext;
30+
class QgsMapLayerStore;
3031

3132
#include <QString>
3233

@@ -134,19 +135,19 @@ class CORE_EXPORT QgsProcessingUtils
134135

135136
static bool canUseLayer( const QgsRasterLayer *layer );
136137
static bool canUseLayer( const QgsVectorLayer *layer,
137-
const QList< QgsWkbTypes::GeometryType > &geometryTypes );
138+
const QList< QgsWkbTypes::GeometryType > &geometryTypes = QList< QgsWkbTypes::GeometryType >() );
138139

139140
/**
140-
* Interprets a \a string as a map layer from a project.
141+
* Interprets a \a string as a map layer from a store.
141142
*
142-
* This method attempts to match a string to a project map layer, using
143+
* This method attempts to match a string to a store map layer, using
143144
* first the layer ID, then layer names, and finally layer source.
144145
* If the string matches a normalized version of any layer source
145-
* for layers in the specified \a project, then those matching layers will be
146+
* for layers in the specified \a store, then those matching layers will be
146147
* returned.
147148
* \see mapLayerFromString()
148149
*/
149-
static QgsMapLayer *mapLayerFromProject( const QString &string, QgsProject *project );
150+
static QgsMapLayer *mapLayerFromStore( const QString &string, QgsMapLayerStore *store );
150151

151152
/**
152153
* Interprets a string as a map layer. The method will attempt to

‎src/core/qgsmaplayerstore.cpp

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
/***************************************************************************
2+
qgsmaplayerstore.cpp
3+
--------------------
4+
begin : May 2017
5+
copyright : (C) 2017 by Nyall Dawson
6+
email : nyall dot dawson at gmail dot com
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
18+
#include "qgsmaplayerstore.h"
19+
#include "qgslogger.h"
20+
21+
QgsMapLayerStore::QgsMapLayerStore( QObject *parent )
22+
: QObject( parent )
23+
{}
24+
25+
QgsMapLayerStore::~QgsMapLayerStore()
26+
{
27+
removeAllMapLayers();
28+
}
29+
30+
int QgsMapLayerStore::count() const
31+
{
32+
return mMapLayers.size();
33+
}
34+
35+
QgsMapLayer *QgsMapLayerStore::mapLayer( const QString &layerId ) const
36+
{
37+
return mMapLayers.value( layerId );
38+
}
39+
40+
QList<QgsMapLayer *> QgsMapLayerStore::mapLayersByName( const QString &layerName ) const
41+
{
42+
QList<QgsMapLayer *> myResultList;
43+
Q_FOREACH ( QgsMapLayer *layer, mMapLayers )
44+
{
45+
if ( layer->name() == layerName )
46+
{
47+
myResultList << layer;
48+
}
49+
}
50+
return myResultList;
51+
}
52+
53+
QList<QgsMapLayer *> QgsMapLayerStore::addMapLayers( const QList<QgsMapLayer *> &layers, bool takeOwnership )
54+
{
55+
QList<QgsMapLayer *> myResultList;
56+
Q_FOREACH ( QgsMapLayer *myLayer, layers )
57+
{
58+
if ( !myLayer || !myLayer->isValid() )
59+
{
60+
QgsDebugMsg( "Cannot add invalid layers" );
61+
continue;
62+
}
63+
//check the layer is not already registered!
64+
if ( !mMapLayers.contains( myLayer->id() ) )
65+
{
66+
mMapLayers[myLayer->id()] = myLayer;
67+
myResultList << mMapLayers[myLayer->id()];
68+
if ( takeOwnership )
69+
{
70+
myLayer->setParent( this );
71+
}
72+
connect( myLayer, &QObject::destroyed, this, &QgsMapLayerStore::onMapLayerDeleted );
73+
emit layerWasAdded( myLayer );
74+
}
75+
}
76+
if ( !myResultList.isEmpty() )
77+
{
78+
emit layersAdded( myResultList );
79+
}
80+
return myResultList;
81+
}
82+
83+
QgsMapLayer *
84+
QgsMapLayerStore::addMapLayer( QgsMapLayer *layer, bool takeOwnership )
85+
{
86+
QList<QgsMapLayer *> addedLayers;
87+
addedLayers = addMapLayers( QList<QgsMapLayer *>() << layer, takeOwnership );
88+
return addedLayers.isEmpty() ? nullptr : addedLayers[0];
89+
}
90+
91+
void QgsMapLayerStore::removeMapLayers( const QStringList &layerIds )
92+
{
93+
QList<QgsMapLayer *> layers;
94+
Q_FOREACH ( const QString &myId, layerIds )
95+
{
96+
layers << mMapLayers.value( myId );
97+
}
98+
99+
removeMapLayers( layers );
100+
}
101+
102+
void QgsMapLayerStore::removeMapLayers( const QList<QgsMapLayer *> &layers )
103+
{
104+
if ( layers.isEmpty() )
105+
return;
106+
107+
QStringList layerIds;
108+
QList<QgsMapLayer *> layerList;
109+
110+
Q_FOREACH ( QgsMapLayer *layer, layers )
111+
{
112+
// check layer and the store contains it
113+
if ( layer && mMapLayers.contains( layer->id() ) )
114+
{
115+
layerIds << layer->id();
116+
layerList << layer;
117+
}
118+
}
119+
120+
if ( layerIds.isEmpty() )
121+
return;
122+
123+
emit layersWillBeRemoved( layerIds );
124+
emit layersWillBeRemoved( layerList );
125+
126+
Q_FOREACH ( QgsMapLayer *lyr, layerList )
127+
{
128+
QString myId( lyr->id() );
129+
emit layerWillBeRemoved( myId );
130+
emit layerWillBeRemoved( lyr );
131+
mMapLayers.remove( myId );
132+
if ( lyr->parent() == this )
133+
{
134+
delete lyr;
135+
}
136+
emit layerRemoved( myId );
137+
}
138+
139+
emit layersRemoved( layerIds );
140+
}
141+
142+
void QgsMapLayerStore::removeMapLayer( const QString &layerId )
143+
{
144+
removeMapLayers( QList<QgsMapLayer *>() << mMapLayers.value( layerId ) );
145+
}
146+
147+
void QgsMapLayerStore::removeMapLayer( QgsMapLayer *layer )
148+
{
149+
if ( layer )
150+
removeMapLayers( QList<QgsMapLayer *>() << layer );
151+
}
152+
153+
QgsMapLayer *QgsMapLayerStore::takeMapLayer( QgsMapLayer *layer )
154+
{
155+
if ( !layer )
156+
return nullptr;
157+
158+
if ( mMapLayers.contains( layer->id() ) )
159+
{
160+
emit layersWillBeRemoved( QStringList() << layer->id() );
161+
emit layersWillBeRemoved( QList<QgsMapLayer *>() << layer );
162+
emit layerWillBeRemoved( layer->id() );
163+
emit layerWillBeRemoved( layer );
164+
165+
mMapLayers.remove( layer->id() );
166+
layer->setParent( nullptr );
167+
emit layerRemoved( layer->id() );
168+
emit layersRemoved( QStringList() << layer->id() );
169+
return layer;
170+
}
171+
return nullptr; //don't return layer - it wasn't owned and accordingly we aren't transferring ownership
172+
}
173+
174+
void QgsMapLayerStore::removeAllMapLayers()
175+
{
176+
emit allLayersRemoved();
177+
// now let all observers know to clear themselves,
178+
// and then consequently any of their map legends
179+
removeMapLayers( mMapLayers.keys() );
180+
mMapLayers.clear();
181+
}
182+
183+
void QgsMapLayerStore::onMapLayerDeleted( QObject *obj )
184+
{
185+
QString id = mMapLayers.key( static_cast<QgsMapLayer *>( obj ) );
186+
187+
if ( !id.isNull() )
188+
{
189+
QgsDebugMsg( QString( "Map layer deleted without unregistering! %1" ).arg( id ) );
190+
mMapLayers.remove( id );
191+
}
192+
}
193+
194+
QMap<QString, QgsMapLayer *> QgsMapLayerStore::mapLayers() const
195+
{
196+
return mMapLayers;
197+
}

‎src/core/qgsmaplayerstore.h

Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
/***************************************************************************
2+
qgsmaplayerstore.h
3+
------------------
4+
begin : May 2017
5+
copyright : (C) 2017 by Nyall Dawson
6+
email : nyall dot dawson at gmail dot com
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
18+
19+
#ifndef QGSMAPLAYERSTORE_H
20+
#define QGSMAPLAYERSTORE_H
21+
22+
#include "qgis_core.h"
23+
#include "qgis.h"
24+
#include "qgsmaplayer.h"
25+
#include <QObject>
26+
27+
/**
28+
* \class QgsMapLayerStore
29+
* \ingroup core
30+
* A storage object for map layers, in which the layers are owned by the
31+
* store and have their lifetime bound to the store.
32+
* \since QGIS 3.0
33+
*/
34+
35+
class CORE_EXPORT QgsMapLayerStore : public QObject
36+
{
37+
Q_OBJECT
38+
39+
public:
40+
41+
/**
42+
* Constructor for QgsMapLayerStore.
43+
*/
44+
explicit QgsMapLayerStore( QObject *parent SIP_TRANSFERTHIS = nullptr );
45+
46+
~QgsMapLayerStore();
47+
48+
/**
49+
* Returns the number of layers contained in the store.
50+
*/
51+
int count() const;
52+
53+
#ifdef SIP_RUN
54+
55+
/**
56+
* Returns the number of layers contained in the store.
57+
*/
58+
int __len__() const;
59+
% MethodCode
60+
sipRes = sipCpp->count();
61+
% End
62+
#endif
63+
64+
/**
65+
* Retrieve a pointer to a layer by layer \a id.
66+
* \param id ID of layer to retrieve
67+
* \returns matching layer, or nullptr if no matching layer found
68+
* \see mapLayersByName()
69+
* \see mapLayers()
70+
*/
71+
QgsMapLayer *mapLayer( const QString &id ) const;
72+
73+
/**
74+
* Retrieve a list of matching layers by layer \a name.
75+
* \param name name of layers to match
76+
* \returns list of matching layers
77+
* \see mapLayer()
78+
* \see mapLayers()
79+
*/
80+
QList<QgsMapLayer *> mapLayersByName( const QString &name ) const;
81+
82+
/**
83+
* Returns a map of all layers by layer ID.
84+
* \see mapLayer()
85+
* \see mapLayersByName()
86+
* \see layers()
87+
*/
88+
QMap<QString, QgsMapLayer *> mapLayers() const;
89+
90+
#ifndef SIP_RUN
91+
92+
/**
93+
* Returns a list of registered map layers with a specified layer type.
94+
*
95+
* Example:
96+
*
97+
* QVector<QgsVectorLayer*> vectorLayers = store->layers<QgsVectorLayer*>();
98+
*
99+
* \note not available in Python bindings
100+
* \see mapLayers()
101+
*/
102+
template <typename T>
103+
QVector<T> layers() const
104+
{
105+
QVector<T> layers;
106+
QMap<QString, QgsMapLayer *>::const_iterator layerIt = mMapLayers.constBegin();
107+
for ( ; layerIt != mMapLayers.constEnd(); ++layerIt )
108+
{
109+
T tLayer = qobject_cast<T>( layerIt.value() );
110+
if ( tLayer )
111+
{
112+
layers << tLayer;
113+
}
114+
}
115+
return layers;
116+
}
117+
#endif
118+
119+
/**
120+
* \brief
121+
* Add a list of \a layers to the store. Ownership of the layers is transferred
122+
* to the store.
123+
*
124+
* The layersAdded() and layerWasAdded() signals will always be emitted.
125+
*
126+
* \param layers A list of layer which should be added to the store.
127+
* \param takeOwnership Ownership will be transferred to the layer store.
128+
* If you specify false here you have take care of deleting
129+
* the layers yourself. Not available in Python.
130+
*
131+
* \returns a list of the map layers that were added
132+
* successfully. If a layer is invalid, or already exists in the store,
133+
* it will not be part of the returned list.
134+
*
135+
* \see addMapLayer()
136+
*/
137+
QList<QgsMapLayer *> addMapLayers( const QList<QgsMapLayer *> &layers SIP_TRANSFER,
138+
bool takeOwnership SIP_PYARGREMOVE = true );
139+
140+
/**
141+
* \brief
142+
* Add a \a layer to the store. Ownership of the layer is transferred to the
143+
* store.
144+
*
145+
* The layersAdded() and layerWasAdded() signals will always be emitted.
146+
* If you are adding multiple layers at once, you should use
147+
* addMapLayers() instead.
148+
*
149+
* \param layer A layer to add to the store
150+
* \param takeOwnership Ownership will be transferred to the layer store.
151+
* If you specify false here you have take care of deleting
152+
* the layers yourself. Not available in Python.
153+
*
154+
* \returns nullptr if unable to add layer, otherwise pointer to newly added layer
155+
*
156+
* \see addMapLayers
157+
*
158+
* \note Use addMapLayers() if adding more than one layer at a time.
159+
* \see addMapLayers()
160+
*/
161+
QgsMapLayer *addMapLayer( QgsMapLayer *layer SIP_TRANSFER,
162+
bool takeOwnership SIP_PYARGREMOVE = true );
163+
164+
/**
165+
* \brief
166+
* Remove a set of layers from the store by layer ID.
167+
*
168+
* The specified layers will be removed from the store.
169+
* These layers will also be deleted.
170+
*
171+
* \param layerIds list of IDs of the layers to remove
172+
*
173+
* \see takeMapLayer()
174+
* \see removeMapLayer()
175+
* \see removeAllMapLayers()
176+
* \note available in Python bindings as removeMapLayersById.
177+
*/
178+
void removeMapLayers( const QStringList &layerIds ) SIP_PYNAME( removeMapLayersById );
179+
180+
/**
181+
* \brief
182+
* Remove a set of \a layers from the store.
183+
*
184+
* The specified layers will be removed from the store.
185+
* These layers will also be deleted.
186+
*
187+
* \param layers A list of layers to remove. Null pointers are ignored.
188+
*
189+
* \see takeMapLayer()
190+
* \see removeMapLayer()
191+
* \see removeAllMapLayers()
192+
*/
193+
void removeMapLayers( const QList<QgsMapLayer *> &layers );
194+
195+
/**
196+
* \brief
197+
* Remove a layer from the store by layer \a id.
198+
*
199+
* The specified layer will be removed from the store. The layer will also be deleted.
200+
*
201+
* \param id ID of the layer to remove
202+
*
203+
* \see takeMapLayer()
204+
* \see removeMapLayers()
205+
* \see removeAllMapLayers()
206+
*/
207+
void removeMapLayer( const QString &id );
208+
209+
/**
210+
* \brief
211+
* Remove a \a layer from the store.
212+
*
213+
* The specified layer will be removed from the store. The layer will also be deleted.
214+
*
215+
* \param layer The layer to remove. Null pointers are ignored.
216+
*
217+
* \see takeMapLayer()
218+
* \see removeMapLayers()
219+
* \see removeAllMapLayers()
220+
*/
221+
void removeMapLayer( QgsMapLayer *layer );
222+
223+
/**
224+
* Takes a \a layer from the store. If the layer was owned by the store, the
225+
* layer will be returned without deleting it. The caller takes ownership of
226+
* the layer and is responsible for deleting it.
227+
* \see removeMapLayer()
228+
*/
229+
QgsMapLayer *takeMapLayer( QgsMapLayer *layer ) SIP_TRANSFERBACK;
230+
231+
/**
232+
* Removes all registered layers. These layers will also be deleted.
233+
*
234+
* \note Calling this method will cause the removeAll() signal to
235+
* be emitted.
236+
* \see removeMapLayer()
237+
* \see removeMapLayers()
238+
*/
239+
void removeAllMapLayers();
240+
241+
signals:
242+
243+
/**
244+
* Emitted when one or more layers are about to be removed from the store.
245+
*
246+
* \param layerIds A list of IDs for the layers which are to be removed.
247+
* \see layerWillBeRemoved()
248+
* \see layersRemoved()
249+
*/
250+
void layersWillBeRemoved( const QStringList &layerIds );
251+
252+
/**
253+
* Emitted when one or more layers are about to be removed from the store.
254+
*
255+
* \param layers A list of layers which are to be removed.
256+
* \see layerWillBeRemoved()
257+
* \see layersRemoved()
258+
*/
259+
void layersWillBeRemoved( const QList<QgsMapLayer *> &layers );
260+
261+
/**
262+
* Emitted when a layer is about to be removed from the store.
263+
*
264+
* \param layerId The ID of the layer to be removed.
265+
*
266+
* \note Consider using layersWillBeRemoved() instead.
267+
* \see layersWillBeRemoved()
268+
* \see layerRemoved()
269+
*/
270+
void layerWillBeRemoved( const QString &layerId );
271+
272+
/**
273+
* Emitted when a layer is about to be removed from the store.
274+
*
275+
* \param layer The layer to be removed.
276+
*
277+
* \note Consider using layersWillBeRemoved() instead.
278+
* \see layersWillBeRemoved()
279+
* \see layerRemoved()
280+
*/
281+
void layerWillBeRemoved( QgsMapLayer *layer );
282+
283+
/**
284+
* Emitted after one or more layers were removed from the store.
285+
*
286+
* \param layerIds A list of IDs of the layers which were removed.
287+
* \see layersWillBeRemoved()
288+
*/
289+
void layersRemoved( const QStringList &layerIds );
290+
291+
/**
292+
* Emitted after a layer was removed from the store.
293+
*
294+
* \param layerId The ID of the layer removed.
295+
*
296+
* \note Consider using layersRemoved() instead
297+
* \see layerWillBeRemoved()
298+
*/
299+
void layerRemoved( const QString &layerId );
300+
301+
/**
302+
* Emitted when all layers are removed, before layersWillBeRemoved() and
303+
* layerWillBeRemoved() signals are emitted. The layersWillBeRemoved() and
304+
* layerWillBeRemoved() signals will still be emitted following this signal.
305+
* You can use this signal to do easy (and fast) cleanup.
306+
*/
307+
void allLayersRemoved();
308+
309+
/**
310+
* Emitted when one or more layers were added to the store.
311+
*
312+
* \param layers List of layers which have been added.
313+
*
314+
* \see legendLayersAdded()
315+
* \see layerWasAdded()
316+
*/
317+
void layersAdded( const QList<QgsMapLayer *> &layers );
318+
319+
/**
320+
* Emitted when a \a layer was added to the store.
321+
*
322+
* \note Consider using layersAdded() instead
323+
* \see layersAdded()
324+
*/
325+
void layerWasAdded( QgsMapLayer *layer );
326+
327+
private slots:
328+
329+
void onMapLayerDeleted( QObject *obj );
330+
331+
private:
332+
333+
QMap<QString, QgsMapLayer *> mMapLayers;
334+
335+
};
336+
337+
#endif //QGSMAPLAYERSTORE_H

‎src/core/qgsproject.cpp

Lines changed: 47 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include "qgssettings.h"
4747
#include "qgsmaplayerlistutils.h"
4848
#include "qgslayoutmanager.h"
49+
#include "qgsmaplayerstore.h"
4950

5051
#include <QApplication>
5152
#include <QFileInfo>
@@ -322,6 +323,7 @@ void removeKey_( const QString &scope,
322323

323324
QgsProject::QgsProject( QObject *parent )
324325
: QObject( parent )
326+
, mLayerStore( new QgsMapLayerStore( this ) )
325327
, mBadLayerHandler( new QgsProjectBadLayerHandler() )
326328
, mSnappingConfig( this )
327329
, mRelationManager( new QgsRelationManager( this ) )
@@ -343,6 +345,21 @@ QgsProject::QgsProject( QObject *parent )
343345
connect( this, &QgsProject::layersAdded, this, &QgsProject::onMapLayersAdded );
344346
connect( this, &QgsProject::layersRemoved, this, [ = ] { cleanTransactionGroups(); } );
345347
connect( this, static_cast < void ( QgsProject::* )( const QList<QgsMapLayer *> & ) >( &QgsProject::layersWillBeRemoved ), this, &QgsProject::onMapLayersRemoved );
348+
349+
// proxy map layer store signals to this
350+
connect( mLayerStore.get(), static_cast<void ( QgsMapLayerStore::* )( const QStringList & )>( &QgsMapLayerStore::layersWillBeRemoved ),
351+
this, static_cast<void ( QgsProject::* )( const QStringList & )>( &QgsProject::layersWillBeRemoved ) );
352+
connect( mLayerStore.get(), static_cast<void ( QgsMapLayerStore::* )( const QList<QgsMapLayer *> & )>( &QgsMapLayerStore::layersWillBeRemoved ),
353+
this, static_cast<void ( QgsProject::* )( const QList<QgsMapLayer *> & )>( &QgsProject::layersWillBeRemoved ) );
354+
connect( mLayerStore.get(), static_cast<void ( QgsMapLayerStore::* )( const QString & )>( &QgsMapLayerStore::layerWillBeRemoved ),
355+
this, static_cast<void ( QgsProject::* )( const QString & )>( &QgsProject::layerWillBeRemoved ) );
356+
connect( mLayerStore.get(), static_cast<void ( QgsMapLayerStore::* )( QgsMapLayer * )>( &QgsMapLayerStore::layerWillBeRemoved ),
357+
this, static_cast<void ( QgsProject::* )( QgsMapLayer * )>( &QgsProject::layerWillBeRemoved ) );
358+
connect( mLayerStore.get(), static_cast<void ( QgsMapLayerStore::* )( const QStringList & )>( &QgsMapLayerStore::layersRemoved ), this, &QgsProject::layersRemoved );
359+
connect( mLayerStore.get(), &QgsMapLayerStore::layerRemoved, this, &QgsProject::layerRemoved );
360+
connect( mLayerStore.get(), &QgsMapLayerStore::allLayersRemoved, this, &QgsProject::removeAll );
361+
connect( mLayerStore.get(), &QgsMapLayerStore::layersAdded, this, &QgsProject::layersAdded );
362+
connect( mLayerStore.get(), &QgsMapLayerStore::layerWasAdded, this, &QgsProject::layerWasAdded );
346363
}
347364

348365

@@ -884,7 +901,8 @@ bool QgsProject::read()
884901

885902
// Resolve references to other vector layers
886903
// Needs to be done here once all dependent layers are loaded
887-
for ( QMap<QString, QgsMapLayer *>::iterator it = mMapLayers.begin(); it != mMapLayers.end(); it++ )
904+
QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
905+
for ( QMap<QString, QgsMapLayer *>::iterator it = layers.begin(); it != layers.end(); it++ )
888906
{
889907
if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() ) )
890908
vl->resolveReferences( this );
@@ -1049,6 +1067,16 @@ const QgsLabelingEngineSettings &QgsProject::labelingEngineSettings() const
10491067
return *mLabelingEngineSettings;
10501068
}
10511069

1070+
QgsMapLayerStore *QgsProject::layerStore()
1071+
{
1072+
return mLayerStore.get();
1073+
}
1074+
1075+
const QgsMapLayerStore *QgsProject::layerStore() const
1076+
{
1077+
return mLayerStore.get();
1078+
}
1079+
10521080
QList<QgsVectorLayer *> QgsProject::avoidIntersectionsLayers() const
10531081
{
10541082
QList<QgsVectorLayer *> layers;
@@ -2066,63 +2094,33 @@ QMap<QPair<QString, QString>, QgsTransactionGroup *> QgsProject::transactionGrou
20662094

20672095

20682096
//
2069-
// QgsMapLayerRegistry methods
2097+
// QgsMapLayerStore methods
20702098
//
20712099

20722100

20732101
int QgsProject::count() const
20742102
{
2075-
return mMapLayers.size();
2103+
return mLayerStore->count();
20762104
}
20772105

20782106
QgsMapLayer *QgsProject::mapLayer( const QString &layerId ) const
20792107
{
2080-
return mMapLayers.value( layerId );
2108+
return mLayerStore->mapLayer( layerId );
20812109
}
20822110

20832111
QList<QgsMapLayer *> QgsProject::mapLayersByName( const QString &layerName ) const
20842112
{
2085-
QList<QgsMapLayer *> myResultList;
2086-
Q_FOREACH ( QgsMapLayer *layer, mMapLayers )
2087-
{
2088-
if ( layer->name() == layerName )
2089-
{
2090-
myResultList << layer;
2091-
}
2092-
}
2093-
return myResultList;
2113+
return mLayerStore->mapLayersByName( layerName );
20942114
}
20952115

20962116
QList<QgsMapLayer *> QgsProject::addMapLayers(
20972117
const QList<QgsMapLayer *> &layers,
20982118
bool addToLegend,
20992119
bool takeOwnership )
21002120
{
2101-
QList<QgsMapLayer *> myResultList;
2102-
Q_FOREACH ( QgsMapLayer *myLayer, layers )
2103-
{
2104-
if ( !myLayer || !myLayer->isValid() )
2105-
{
2106-
QgsDebugMsg( "Cannot add invalid layers" );
2107-
continue;
2108-
}
2109-
//check the layer is not already registered!
2110-
if ( !mMapLayers.contains( myLayer->id() ) )
2111-
{
2112-
mMapLayers[myLayer->id()] = myLayer;
2113-
myResultList << mMapLayers[myLayer->id()];
2114-
if ( takeOwnership )
2115-
{
2116-
myLayer->setParent( this );
2117-
}
2118-
connect( myLayer, &QObject::destroyed, this, &QgsProject::onMapLayerDeleted );
2119-
emit layerWasAdded( myLayer );
2120-
}
2121-
}
2121+
QList<QgsMapLayer *> myResultList = mLayerStore->addMapLayers( layers, takeOwnership );
21222122
if ( !myResultList.isEmpty() )
21232123
{
2124-
emit layersAdded( myResultList );
2125-
21262124
if ( addToLegend )
21272125
emit legendLayersAdded( myResultList );
21282126
}
@@ -2141,116 +2139,47 @@ QgsProject::addMapLayer( QgsMapLayer *layer,
21412139

21422140
void QgsProject::removeMapLayers( const QStringList &layerIds )
21432141
{
2144-
QList<QgsMapLayer *> layers;
2145-
Q_FOREACH ( const QString &myId, layerIds )
2146-
{
2147-
layers << mMapLayers.value( myId );
2148-
}
2149-
2150-
removeMapLayers( layers );
2142+
mLayerStore->removeMapLayers( layerIds );
21512143
}
21522144

21532145
void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
21542146
{
2155-
if ( layers.isEmpty() )
2156-
return;
2157-
2158-
QStringList layerIds;
2159-
QList<QgsMapLayer *> layerList;
2160-
2161-
Q_FOREACH ( QgsMapLayer *layer, layers )
2162-
{
2163-
// check layer and the registry contains it
2164-
if ( layer && mMapLayers.contains( layer->id() ) )
2165-
{
2166-
layerIds << layer->id();
2167-
layerList << layer;
2168-
}
2169-
}
2170-
2171-
if ( layerIds.isEmpty() )
2172-
return;
2173-
2174-
emit layersWillBeRemoved( layerIds );
2175-
emit layersWillBeRemoved( layerList );
2176-
2177-
Q_FOREACH ( QgsMapLayer *lyr, layerList )
2178-
{
2179-
QString myId( lyr->id() );
2180-
emit layerWillBeRemoved( myId );
2181-
emit layerWillBeRemoved( lyr );
2182-
mMapLayers.remove( myId );
2183-
if ( lyr->parent() == this )
2184-
{
2185-
delete lyr;
2186-
}
2187-
emit layerRemoved( myId );
2188-
}
2189-
2190-
emit layersRemoved( layerIds );
2147+
mLayerStore->removeMapLayers( layers );
21912148
}
21922149

21932150
void QgsProject::removeMapLayer( const QString &layerId )
21942151
{
2195-
removeMapLayers( QList<QgsMapLayer *>() << mMapLayers.value( layerId ) );
2152+
mLayerStore->removeMapLayer( layerId );
21962153
}
21972154

21982155
void QgsProject::removeMapLayer( QgsMapLayer *layer )
21992156
{
2200-
if ( layer )
2201-
removeMapLayers( QList<QgsMapLayer *>() << layer );
2157+
mLayerStore->removeMapLayer( layer );
22022158
}
22032159

22042160
QgsMapLayer *QgsProject::takeMapLayer( QgsMapLayer *layer )
22052161
{
2206-
if ( !layer )
2207-
return nullptr;
2208-
2209-
if ( mMapLayers.contains( layer->id() ) )
2210-
{
2211-
emit layersWillBeRemoved( QStringList() << layer->id() );
2212-
emit layersWillBeRemoved( QList<QgsMapLayer *>() << layer );
2213-
emit layerWillBeRemoved( layer->id() );
2214-
emit layerWillBeRemoved( layer );
2215-
2216-
mMapLayers.remove( layer->id() );
2217-
layer->setParent( nullptr );
2218-
emit layerRemoved( layer->id() );
2219-
emit layersRemoved( QStringList() << layer->id() );
2220-
return layer;
2221-
}
2222-
return nullptr; //don't return layer - it wasn't owned and accordingly we aren't transferring ownership
2162+
return mLayerStore->takeMapLayer( layer );
22232163
}
22242164

22252165
void QgsProject::removeAllMapLayers()
22262166
{
2227-
emit removeAll();
2228-
// now let all observers know to clear themselves,
2229-
// and then consequently any of their map legends
2230-
removeMapLayers( mMapLayers.keys() );
2231-
mMapLayers.clear();
2167+
mLayerStore->removeAllMapLayers();
22322168
}
22332169

22342170
void QgsProject::reloadAllLayers()
22352171
{
2236-
Q_FOREACH ( QgsMapLayer *layer, mMapLayers )
2172+
QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
2173+
QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin();
2174+
for ( ; it != layers.constEnd(); ++it )
22372175
{
2238-
layer->reload();
2239-
}
2240-
}
2241-
2242-
void QgsProject::onMapLayerDeleted( QObject *obj )
2243-
{
2244-
QString id = mMapLayers.key( static_cast<QgsMapLayer *>( obj ) );
2245-
2246-
if ( !id.isNull() )
2247-
{
2248-
QgsDebugMsg( QString( "Map layer deleted without unregistering! %1" ).arg( id ) );
2249-
mMapLayers.remove( id );
2176+
it.value()->reload();
22502177
}
22512178
}
22522179

22532180
QMap<QString, QgsMapLayer *> QgsProject::mapLayers() const
22542181
{
2255-
return mMapLayers;
2182+
return mLayerStore->mapLayers();
22562183
}
2184+
2185+

‎src/core/qgsproject.h

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "qgscoordinatereferencesystem.h"
4040
#include "qgsprojectproperty.h"
4141
#include "qgsmaplayer.h"
42+
#include "qgsmaplayerstore.h"
4243

4344
class QFileInfo;
4445
class QDomDocument;
@@ -529,6 +530,18 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
529530
// Functionality from QgsMapLayerRegistry
530531
//
531532

533+
/**
534+
* Returns a pointer to the project's internal layer store.
535+
* /since QGIS 3.0
536+
*/
537+
QgsMapLayerStore *layerStore();
538+
539+
/**
540+
* Returns a pointer to the project's internal layer store.
541+
* /since QGIS 3.0
542+
*/
543+
SIP_SKIP const QgsMapLayerStore *layerStore() const;
544+
532545
//! Returns the number of registered layers.
533546
int count() const;
534547

@@ -568,17 +581,7 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
568581
template <typename T> SIP_SKIP
569582
QVector<T> layers() const
570583
{
571-
QVector<T> layers;
572-
QMap<QString, QgsMapLayer *>::const_iterator layerIt = mMapLayers.constBegin();
573-
for ( ; layerIt != mMapLayers.constEnd(); ++layerIt )
574-
{
575-
T tLayer = qobject_cast<T>( layerIt.value() );
576-
if ( tLayer )
577-
{
578-
layers << tLayer;
579-
}
580-
}
581-
return layers;
584+
return mLayerStore->layers<T>();
582585
}
583586

584587
/**
@@ -969,8 +972,6 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
969972
void onMapLayersRemoved( const QList<QgsMapLayer *> &layers );
970973
void cleanTransactionGroups( bool force = false );
971974

972-
void onMapLayerDeleted( QObject *obj );
973-
974975
private:
975976

976977
static QgsProject *sProject;
@@ -1003,7 +1004,7 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
10031004
//! \note not available in Python bindings
10041005
void loadEmbeddedNodes( QgsLayerTreeGroup *group ) SIP_SKIP;
10051006

1006-
QMap<QString, QgsMapLayer *> mMapLayers;
1007+
std::unique_ptr< QgsMapLayerStore > mLayerStore;
10071008

10081009
QString mErrorMessage;
10091010

‎tests/src/core/testqgsprocessing.cpp

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ class TestQgsProcessing: public QObject
105105
void compatibleLayers();
106106
void normalizeLayerSource();
107107
void mapLayers();
108+
void mapLayerFromStore();
108109
void mapLayerFromString();
109110
void algorithm();
110111
void features();
@@ -335,9 +336,37 @@ void TestQgsProcessing::normalizeLayerSource()
335336

336337
void TestQgsProcessing::mapLayers()
337338
{
338-
// test mapLayerFromProject
339+
QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
340+
QString raster = testDataDir + "landsat.tif";
341+
QString vector = testDataDir + "points.shp";
339342

340-
QgsProject p;
343+
// test loadMapLayerFromString with raster
344+
QgsMapLayer *l = QgsProcessingUtils::loadMapLayerFromString( raster );
345+
QVERIFY( l->isValid() );
346+
QCOMPARE( l->type(), QgsMapLayer::RasterLayer );
347+
delete l;
348+
349+
//test with vector
350+
l = QgsProcessingUtils::loadMapLayerFromString( vector );
351+
QVERIFY( l->isValid() );
352+
QCOMPARE( l->type(), QgsMapLayer::VectorLayer );
353+
delete l;
354+
355+
l = QgsProcessingUtils::loadMapLayerFromString( QString() );
356+
QVERIFY( !l );
357+
l = QgsProcessingUtils::loadMapLayerFromString( QStringLiteral( "so much room for activities!" ) );
358+
QVERIFY( !l );
359+
l = QgsProcessingUtils::loadMapLayerFromString( testDataDir + "multipoint.shp" );
360+
QVERIFY( l->isValid() );
361+
QCOMPARE( l->type(), QgsMapLayer::VectorLayer );
362+
delete l;
363+
}
364+
365+
void TestQgsProcessing::mapLayerFromStore()
366+
{
367+
// test mapLayerFromStore
368+
369+
QgsMapLayerStore store;
341370

342371
// add a bunch of layers to a project
343372
QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
@@ -352,33 +381,19 @@ void TestQgsProcessing::mapLayers()
352381

353382
QgsVectorLayer *v1 = new QgsVectorLayer( "Polygon", "V4", "memory" );
354383
QgsVectorLayer *v2 = new QgsVectorLayer( "Point", "v1", "memory" );
355-
p.addMapLayers( QList<QgsMapLayer *>() << r1 << r2 << v1 << v2 );
356-
357-
QVERIFY( ! QgsProcessingUtils::mapLayerFromProject( QString(), nullptr ) );
358-
QVERIFY( ! QgsProcessingUtils::mapLayerFromProject( QStringLiteral( "v1" ), nullptr ) );
359-
QVERIFY( ! QgsProcessingUtils::mapLayerFromProject( QString(), &p ) );
360-
QCOMPARE( QgsProcessingUtils::mapLayerFromProject( raster1, &p ), r1 );
361-
QCOMPARE( QgsProcessingUtils::mapLayerFromProject( raster2, &p ), r2 );
362-
QCOMPARE( QgsProcessingUtils::mapLayerFromProject( "R1", &p ), r1 );
363-
QCOMPARE( QgsProcessingUtils::mapLayerFromProject( "ar2", &p ), r2 );
364-
QCOMPARE( QgsProcessingUtils::mapLayerFromProject( "V4", &p ), v1 );
365-
QCOMPARE( QgsProcessingUtils::mapLayerFromProject( "v1", &p ), v2 );
366-
QCOMPARE( QgsProcessingUtils::mapLayerFromProject( r1->id(), &p ), r1 );
367-
QCOMPARE( QgsProcessingUtils::mapLayerFromProject( v1->id(), &p ), v1 );
368-
369-
// test loadMapLayerFromString
370-
QgsMapLayer *l = QgsProcessingUtils::loadMapLayerFromString( raster2 );
371-
QVERIFY( l->isValid() );
372-
QCOMPARE( l->type(), QgsMapLayer::RasterLayer );
373-
delete l;
374-
l = QgsProcessingUtils::loadMapLayerFromString( QString() );
375-
QVERIFY( !l );
376-
l = QgsProcessingUtils::loadMapLayerFromString( QStringLiteral( "so much room for activities!" ) );
377-
QVERIFY( !l );
378-
l = QgsProcessingUtils::loadMapLayerFromString( testDataDir + "multipoint.shp" );
379-
QVERIFY( l->isValid() );
380-
QCOMPARE( l->type(), QgsMapLayer::VectorLayer );
381-
delete l;
384+
store.addMapLayers( QList<QgsMapLayer *>() << r1 << r2 << v1 << v2 );
385+
386+
QVERIFY( ! QgsProcessingUtils::mapLayerFromStore( QString(), nullptr ) );
387+
QVERIFY( ! QgsProcessingUtils::mapLayerFromStore( QStringLiteral( "v1" ), nullptr ) );
388+
QVERIFY( ! QgsProcessingUtils::mapLayerFromStore( QString(), &store ) );
389+
QCOMPARE( QgsProcessingUtils::mapLayerFromStore( raster1, &store ), r1 );
390+
QCOMPARE( QgsProcessingUtils::mapLayerFromStore( raster2, &store ), r2 );
391+
QCOMPARE( QgsProcessingUtils::mapLayerFromStore( "R1", &store ), r1 );
392+
QCOMPARE( QgsProcessingUtils::mapLayerFromStore( "ar2", &store ), r2 );
393+
QCOMPARE( QgsProcessingUtils::mapLayerFromStore( "V4", &store ), v1 );
394+
QCOMPARE( QgsProcessingUtils::mapLayerFromStore( "v1", &store ), v2 );
395+
QCOMPARE( QgsProcessingUtils::mapLayerFromStore( r1->id(), &store ), r1 );
396+
QCOMPARE( QgsProcessingUtils::mapLayerFromStore( v1->id(), &store ), v1 );
382397
}
383398

384399
void TestQgsProcessing::mapLayerFromString()
@@ -423,7 +438,7 @@ void TestQgsProcessing::mapLayerFromString()
423438
// check that layers in context temporary store are used
424439
QgsVectorLayer *v5 = new QgsVectorLayer( "Polygon", "V5", "memory" );
425440
QgsVectorLayer *v6 = new QgsVectorLayer( "Point", "v6", "memory" );
426-
c.temporaryLayerStore().addMapLayers( QList<QgsMapLayer *>() << v5 << v6 );
441+
c.temporaryLayerStore()->addMapLayers( QList<QgsMapLayer *>() << v5 << v6 );
427442
QCOMPARE( QgsProcessingUtils::mapLayerFromString( "V5", c ), v5 );
428443
QCOMPARE( QgsProcessingUtils::mapLayerFromString( "v6", c ), v6 );
429444
QCOMPARE( QgsProcessingUtils::mapLayerFromString( v5->id(), c ), v5 );
@@ -440,7 +455,7 @@ void TestQgsProcessing::mapLayerFromString()
440455
QVERIFY( loadedLayer->isValid() );
441456
QCOMPARE( loadedLayer->type(), QgsMapLayer::RasterLayer );
442457
// should now be in temporary store
443-
QCOMPARE( c.temporaryLayerStore().mapLayer( loadedLayer->id() ), loadedLayer );
458+
QCOMPARE( c.temporaryLayerStore()->mapLayer( loadedLayer->id() ), loadedLayer );
444459

445460
// since it's now in temporary store, should be accessible even if we deny loading new layers
446461
QCOMPARE( QgsProcessingUtils::mapLayerFromString( newRaster, c, false ), loadedLayer );

‎tests/src/python/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ ADD_PYTHON_TEST(PyQgsMapCanvas test_qgsmapcanvas.py)
7676
ADD_PYTHON_TEST(PyQgsMapCanvasAnnotationItem test_qgsmapcanvasannotationitem.py)
7777
ADD_PYTHON_TEST(PyQgsMapLayer test_qgsmaplayer.py)
7878
ADD_PYTHON_TEST(PyQgsMapLayerModel test_qgsmaplayermodel.py)
79+
ADD_PYTHON_TEST(PyQgsMapLayerStore test_qgsmaplayerstore.py)
7980
ADD_PYTHON_TEST(PyQgsMapRenderer test_qgsmaprenderer.py)
8081
ADD_PYTHON_TEST(PyQgsMapRendererCache test_qgsmaprenderercache.py)
8182
ADD_PYTHON_TEST(PyQgsMapThemeCollection test_qgsmapthemecollection.py)
@@ -145,7 +146,6 @@ ADD_PYTHON_TEST(PyQgsVectorLayer test_qgsvectorlayer.py)
145146
ADD_PYTHON_TEST(PyQgsVectorLayerEditBuffer test_qgsvectorlayereditbuffer.py)
146147
ADD_PYTHON_TEST(PyQgsVectorLayerUtils test_qgsvectorlayerutils.py)
147148
ADD_PYTHON_TEST(PyQgsZonalStatistics test_qgszonalstatistics.py)
148-
ADD_PYTHON_TEST(PyQgsMapLayerRegistry test_qgsmaplayerregistry.py)
149149
ADD_PYTHON_TEST(PyQgsVirtualLayerProvider test_provider_virtual.py)
150150
ADD_PYTHON_TEST(PyQgsVirtualLayerDefinition test_qgsvirtuallayerdefinition.py)
151151
ADD_PYTHON_TEST(PyQgsLayerDefinition test_qgslayerdefinition.py)

‎tests/src/python/test_qgsmaplayerregistry.py

Lines changed: 0 additions & 536 deletions
This file was deleted.

‎tests/src/python/test_qgsmaplayerstore.py

Lines changed: 495 additions & 0 deletions
Large diffs are not rendered by default.

‎tests/src/python/test_qgsproject.py

Lines changed: 504 additions & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.