Skip to content

Commit 6cfc6a1

Browse files
committedMar 13, 2017
Allow retrieval of project layer order through QgsProject
Previously this was only accessible through app
1 parent 9842fcb commit 6cfc6a1

File tree

5 files changed

+168
-7
lines changed

5 files changed

+168
-7
lines changed
 

‎python/core/qgsproject.sip

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,9 @@ class QgsProject : QObject, QgsExpressionContextGenerator
487487
*/
488488
void reloadAllLayers();
489489

490+
QList< QgsMapLayer * > layerOrder() const;
491+
void setLayerOrder( const QList< QgsMapLayer * > &order );
492+
490493
signals:
491494
//! emitted when project is being read
492495
void readProject( const QDomDocument& );
@@ -623,12 +626,12 @@ class QgsProject : QObject, QgsExpressionContextGenerator
623626
*/
624627
//TODO QGIS 3.0 - rename to past tense
625628
void removeAll();
629+
void layersAdded( const QList<QgsMapLayer *> &layers );
626630

627-
void layersAdded( const QList<QgsMapLayer *>& layers );
628-
629-
void layerWasAdded( QgsMapLayer* layer );
631+
void layerWasAdded( QgsMapLayer *layer );
632+
void legendLayersAdded( const QList<QgsMapLayer *> &layers );
630633

631-
void legendLayersAdded( const QList<QgsMapLayer*>& layers );
634+
void layerOrderChanged();
632635

633636
public slots:
634637
/**

‎src/core/qgsproject.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include "qgsvectordataprovider.h"
4545
#include "qgsprojectbadlayerhandler.h"
4646
#include "qgssettings.h"
47+
#include "qgsmaplayerlistutils.h"
4748

4849
#include <QApplication>
4950
#include <QFileInfo>
@@ -460,6 +461,7 @@ void QgsProject::clear()
460461
mEvaluateDefaultValues = false;
461462
mDirty = false;
462463
mCustomVariables.clear();
464+
mLayerOrder.clear();
463465

464466
mEmbeddedLayers.clear();
465467
mRelationManager->clear();
@@ -881,6 +883,20 @@ bool QgsProject::read()
881883
// load embedded groups and layers
882884
loadEmbeddedNodes( mRootGroup );
883885

886+
// load layer order
887+
QList< QgsMapLayer * > layerOrder;
888+
QDomNodeList layerOrderNodes = doc->elementsByTagName( QStringLiteral( "layerorder" ) );
889+
if ( layerOrderNodes.count() )
890+
{
891+
QDomElement layerOrderElem = layerOrderNodes.at( 0 ).toElement();
892+
for ( int i = 0; i < layerOrderElem.childNodes().count(); ++i )
893+
{
894+
QDomElement layerElem = layerOrderElem.childNodes().at( i ).toElement();
895+
layerOrder << mMapLayers.value( layerElem.attribute( QStringLiteral( "id" ) ) );
896+
}
897+
}
898+
setLayerOrder( layerOrder );
899+
884900
// now that layers are loaded, we can resolve layer tree's references to the layers
885901
mRootGroup->resolveReferences( this );
886902

@@ -1244,6 +1260,16 @@ bool QgsProject::write()
12441260

12451261
qgisNode.appendChild( projectLayersNode );
12461262

1263+
QDomElement layerOrderNode = doc->createElement( QStringLiteral( "layerorder" ) );
1264+
Q_FOREACH ( QgsMapLayer *layer, layerOrder() )
1265+
{
1266+
QDomElement mapLayerElem = doc->createElement( QStringLiteral( "layer" ) );
1267+
mapLayerElem.setAttribute( QStringLiteral( "id" ), layer->id() );
1268+
layerOrderNode.appendChild( mapLayerElem );
1269+
}
1270+
qgisNode.appendChild( layerOrderNode );
1271+
1272+
12471273
// now add the optional extra properties
12481274

12491275
dump_( mProperties );
@@ -2091,13 +2117,16 @@ void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
20912117
QStringList layerIds;
20922118
QList<QgsMapLayer *> layerList;
20932119

2120+
bool layerOrderHasChanged = false;
2121+
QList< QgsMapLayer * > currentOrder = layerOrder();
20942122
Q_FOREACH ( QgsMapLayer *layer, layers )
20952123
{
20962124
// check layer and the registry contains it
20972125
if ( layer && mMapLayers.contains( layer->id() ) )
20982126
{
20992127
layerIds << layer->id();
21002128
layerList << layer;
2129+
layerOrderHasChanged = layerOrderHasChanged || currentOrder.contains( layer );
21012130
}
21022131
}
21032132

@@ -2121,6 +2150,8 @@ void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
21212150
}
21222151

21232152
emit layersRemoved( layerIds );
2153+
if ( layerOrderHasChanged )
2154+
emit layerOrderChanged();
21242155
}
21252156

21262157
void QgsProject::removeMapLayer( const QString &layerId )
@@ -2151,6 +2182,20 @@ void QgsProject::reloadAllLayers()
21512182
}
21522183
}
21532184

2185+
QList<QgsMapLayer *> QgsProject::layerOrder() const
2186+
{
2187+
return _qgis_listQPointerToRaw( mLayerOrder );
2188+
}
2189+
2190+
void QgsProject::setLayerOrder( const QList<QgsMapLayer *> &order )
2191+
{
2192+
if ( order == layerOrder() )
2193+
return;
2194+
2195+
mLayerOrder = _qgis_listRawToQPointer( order );
2196+
emit layerOrderChanged();
2197+
}
2198+
21542199
void QgsProject::onMapLayerDeleted( QObject *obj )
21552200
{
21562201
QString id = mMapLayers.key( static_cast<QgsMapLayer *>( obj ) );

‎src/core/qgsproject.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "qgsexpressioncontextgenerator.h"
3939
#include "qgscoordinatereferencesystem.h"
4040
#include "qgsprojectproperty.h"
41+
#include "qgsmaplayer.h"
4142

4243
class QFileInfo;
4344
class QDomDocument;
@@ -690,6 +691,22 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
690691
*/
691692
void reloadAllLayers();
692693

694+
/**
695+
* Returns an ordered list of layers. This list reflects the order of layers as
696+
* drawn in the main map canvas for the project.
697+
* @note added in QGIS 3.0
698+
* @see setLayerOrder()
699+
*/
700+
QList< QgsMapLayer * > layerOrder() const;
701+
702+
/**
703+
* Sets the \a order for layers in the project. This list reflects the order of layers shown in
704+
* the layer tree for the project.
705+
* @note added in QGIS 3.0
706+
* @see layerOrder()
707+
*/
708+
void setLayerOrder( const QList< QgsMapLayer * > &order );
709+
693710
signals:
694711
//! emitted when project is being read
695712
void readProject( const QDomDocument & );
@@ -892,6 +909,13 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
892909
*/
893910
void legendLayersAdded( const QList<QgsMapLayer *> &layers );
894911

912+
/**
913+
* Emitted when the order of layers in the project is changed.
914+
* @note added in QGIS 3.0
915+
* @see setLayerOrder()
916+
*/
917+
void layerOrderChanged();
918+
895919
public slots:
896920

897921
/**
@@ -951,6 +975,8 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
951975

952976
QMap<QString, QgsMapLayer *> mMapLayers;
953977

978+
QgsWeakMapLayerPointerList mLayerOrder;
979+
954980
QString mErrorMessage;
955981

956982
QgsProjectBadLayerHandler *mBadLayerHandler = nullptr;

‎src/gui/layertree/qgslayertreemapcanvasbridge.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ void QgsLayerTreeMapCanvasBridge::setCanvasLayers()
140140
int currentLayerCount = layerNodes.count();
141141
bool firstLayers = mAutoSetupOnFirstLayer && mLastLayerCount == 0 && currentLayerCount != 0;
142142

143+
QgsProject::instance()->setLayerOrder( canvasLayers );
143144
mCanvas->setLayers( canvasLayers );
144145
if ( mOverviewCanvas )
145146
mOverviewCanvas->setLayers( overviewLayers );

‎tests/src/python/test_qgsproject.py

Lines changed: 89 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,19 @@
1818

1919
import qgis # NOQA
2020

21-
from qgis.core import QgsProject, QgsApplication, QgsUnitTypes, QgsCoordinateReferenceSystem
22-
21+
from qgis.core import (QgsProject,
22+
QgsApplication,
23+
QgsUnitTypes,
24+
QgsCoordinateReferenceSystem,
25+
QgsVectorLayer)
26+
from qgis.gui import (QgsLayerTreeMapCanvasBridge,
27+
QgsMapCanvas)
2328
from qgis.testing import start_app, unittest
2429
from utilities import (unitTestDataPath)
30+
from qgis.PyQt.QtCore import QDir
31+
from qgis.PyQt.QtTest import QSignalSpy
2532

26-
start_app()
33+
app = start_app()
2734
TEST_DATA_DIR = unitTestDataPath()
2835

2936

@@ -176,6 +183,85 @@ def testEmbeddedGroup(self):
176183
expected = ['polys', 'lines']
177184
self.assertEqual(sorted(layers_names), sorted(expected))
178185

186+
def testLayerOrder(self):
187+
""" test project layer order"""
188+
prj = QgsProject()
189+
layer = QgsVectorLayer("Point?field=fldtxt:string",
190+
"layer1", "memory")
191+
layer2 = QgsVectorLayer("Point?field=fldtxt:string",
192+
"layer2", "memory")
193+
layer3 = QgsVectorLayer("Point?field=fldtxt:string",
194+
"layer3", "memory")
195+
prj.addMapLayers([layer, layer2, layer3])
196+
197+
layer_order_changed_spy = QSignalSpy(prj.layerOrderChanged)
198+
prj.setLayerOrder([layer2, layer])
199+
self.assertEqual(len(layer_order_changed_spy), 1)
200+
prj.setLayerOrder([layer2, layer])
201+
self.assertEqual(len(layer_order_changed_spy), 1) # no signal, order not changed
202+
203+
self.assertEqual(prj.layerOrder(), [layer2, layer])
204+
prj.setLayerOrder([layer])
205+
self.assertEqual(prj.layerOrder(), [layer])
206+
self.assertEqual(len(layer_order_changed_spy), 2)
207+
208+
# remove a layer
209+
prj.setLayerOrder([layer2, layer, layer3])
210+
self.assertEqual(len(layer_order_changed_spy), 3)
211+
prj.removeMapLayer(layer)
212+
self.assertEqual(prj.layerOrder(), [layer2, layer3])
213+
self.assertEqual(len(layer_order_changed_spy), 4)
214+
215+
# save and restore
216+
file_name = os.path.join(str(QDir.tempPath()), 'proj.qgs')
217+
prj.setFileName(file_name)
218+
prj.write()
219+
prj2 = QgsProject()
220+
prj2.setFileName(file_name)
221+
prj2.read()
222+
self.assertEqual([l.id() for l in prj2.layerOrder()], [layer2.id(), layer3.id()])
223+
224+
# clear project
225+
prj.clear()
226+
self.assertEqual(prj.layerOrder(), [])
227+
228+
def testLayerOrderUpdatedThroughBridge(self):
229+
""" test that project layer order is updated when layer tree changes """
230+
231+
prj = QgsProject.instance()
232+
layer = QgsVectorLayer("Point?field=fldtxt:string",
233+
"layer1", "memory")
234+
layer2 = QgsVectorLayer("Point?field=fldtxt:string",
235+
"layer2", "memory")
236+
layer3 = QgsVectorLayer("Point?field=fldtxt:string",
237+
"layer3", "memory")
238+
prj.addMapLayers([layer, layer2, layer3])
239+
240+
canvas = QgsMapCanvas()
241+
bridge = QgsLayerTreeMapCanvasBridge(prj.layerTreeRoot(), canvas)
242+
243+
#custom layer order
244+
bridge.setHasCustomLayerOrder(True)
245+
bridge.setCustomLayerOrder([layer3.id(), layer.id(), layer2.id()])
246+
app.processEvents()
247+
self.assertEqual([l.id() for l in prj.layerOrder()], [layer3.id(), layer.id(), layer2.id()])
248+
249+
# no custom layer order
250+
bridge.setHasCustomLayerOrder(False)
251+
app.processEvents()
252+
self.assertEqual([l.id() for l in prj.layerOrder()], [layer.id(), layer2.id(), layer3.id()])
253+
254+
# mess around with the layer tree order
255+
root = prj.layerTreeRoot()
256+
layer_node = root.findLayer(layer2.id())
257+
cloned_node = layer_node.clone()
258+
parent = layer_node.parent()
259+
parent.insertChildNode(0, cloned_node)
260+
parent.removeChildNode(layer_node)
261+
app.processEvents()
262+
# make sure project respects this
263+
self.assertEqual([l.id() for l in prj.layerOrder()], [layer2.id(), layer.id(), layer3.id()])
264+
179265

180266
if __name__ == '__main__':
181267
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.