Skip to content
Permalink
Browse files

Allow retrieval of project layer order through QgsProject

Previously this was only accessible through app
  • Loading branch information
nyalldawson committed Mar 13, 2017
1 parent 9842fcb commit 6cfc6a1b98792f75c56a61207c2eedf3f74ce036
@@ -487,6 +487,9 @@ class QgsProject : QObject, QgsExpressionContextGenerator
*/
void reloadAllLayers();

QList< QgsMapLayer * > layerOrder() const;
void setLayerOrder( const QList< QgsMapLayer * > &order );

signals:
//! emitted when project is being read
void readProject( const QDomDocument& );
@@ -623,12 +626,12 @@ class QgsProject : QObject, QgsExpressionContextGenerator
*/
//TODO QGIS 3.0 - rename to past tense
void removeAll();
void layersAdded( const QList<QgsMapLayer *> &layers );

void layersAdded( const QList<QgsMapLayer *>& layers );

void layerWasAdded( QgsMapLayer* layer );
void layerWasAdded( QgsMapLayer *layer );
void legendLayersAdded( const QList<QgsMapLayer *> &layers );

void legendLayersAdded( const QList<QgsMapLayer*>& layers );
void layerOrderChanged();

public slots:
/**
@@ -44,6 +44,7 @@
#include "qgsvectordataprovider.h"
#include "qgsprojectbadlayerhandler.h"
#include "qgssettings.h"
#include "qgsmaplayerlistutils.h"

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

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

// load layer order
QList< QgsMapLayer * > layerOrder;
QDomNodeList layerOrderNodes = doc->elementsByTagName( QStringLiteral( "layerorder" ) );
if ( layerOrderNodes.count() )
{
QDomElement layerOrderElem = layerOrderNodes.at( 0 ).toElement();
for ( int i = 0; i < layerOrderElem.childNodes().count(); ++i )
{
QDomElement layerElem = layerOrderElem.childNodes().at( i ).toElement();
layerOrder << mMapLayers.value( layerElem.attribute( QStringLiteral( "id" ) ) );
}
}
setLayerOrder( layerOrder );

// now that layers are loaded, we can resolve layer tree's references to the layers
mRootGroup->resolveReferences( this );

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

qgisNode.appendChild( projectLayersNode );

QDomElement layerOrderNode = doc->createElement( QStringLiteral( "layerorder" ) );
Q_FOREACH ( QgsMapLayer *layer, layerOrder() )
{
QDomElement mapLayerElem = doc->createElement( QStringLiteral( "layer" ) );
mapLayerElem.setAttribute( QStringLiteral( "id" ), layer->id() );
layerOrderNode.appendChild( mapLayerElem );
}
qgisNode.appendChild( layerOrderNode );


// now add the optional extra properties

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

bool layerOrderHasChanged = false;
QList< QgsMapLayer * > currentOrder = layerOrder();
Q_FOREACH ( QgsMapLayer *layer, layers )
{
// check layer and the registry contains it
if ( layer && mMapLayers.contains( layer->id() ) )
{
layerIds << layer->id();
layerList << layer;
layerOrderHasChanged = layerOrderHasChanged || currentOrder.contains( layer );
}
}

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

emit layersRemoved( layerIds );
if ( layerOrderHasChanged )
emit layerOrderChanged();
}

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

QList<QgsMapLayer *> QgsProject::layerOrder() const
{
return _qgis_listQPointerToRaw( mLayerOrder );
}

void QgsProject::setLayerOrder( const QList<QgsMapLayer *> &order )
{
if ( order == layerOrder() )
return;

mLayerOrder = _qgis_listRawToQPointer( order );
emit layerOrderChanged();
}

void QgsProject::onMapLayerDeleted( QObject *obj )
{
QString id = mMapLayers.key( static_cast<QgsMapLayer *>( obj ) );
@@ -38,6 +38,7 @@
#include "qgsexpressioncontextgenerator.h"
#include "qgscoordinatereferencesystem.h"
#include "qgsprojectproperty.h"
#include "qgsmaplayer.h"

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

/**
* Returns an ordered list of layers. This list reflects the order of layers as
* drawn in the main map canvas for the project.
* @note added in QGIS 3.0
* @see setLayerOrder()
*/
QList< QgsMapLayer * > layerOrder() const;

/**
* Sets the \a order for layers in the project. This list reflects the order of layers shown in
* the layer tree for the project.
* @note added in QGIS 3.0
* @see layerOrder()
*/
void setLayerOrder( const QList< QgsMapLayer * > &order );

signals:
//! emitted when project is being read
void readProject( const QDomDocument & );
@@ -892,6 +909,13 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
*/
void legendLayersAdded( const QList<QgsMapLayer *> &layers );

/**
* Emitted when the order of layers in the project is changed.
* @note added in QGIS 3.0
* @see setLayerOrder()
*/
void layerOrderChanged();

public slots:

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

QMap<QString, QgsMapLayer *> mMapLayers;

QgsWeakMapLayerPointerList mLayerOrder;

QString mErrorMessage;

QgsProjectBadLayerHandler *mBadLayerHandler = nullptr;
@@ -140,6 +140,7 @@ void QgsLayerTreeMapCanvasBridge::setCanvasLayers()
int currentLayerCount = layerNodes.count();
bool firstLayers = mAutoSetupOnFirstLayer && mLastLayerCount == 0 && currentLayerCount != 0;

QgsProject::instance()->setLayerOrder( canvasLayers );
mCanvas->setLayers( canvasLayers );
if ( mOverviewCanvas )
mOverviewCanvas->setLayers( overviewLayers );
@@ -18,12 +18,19 @@

import qgis # NOQA

from qgis.core import QgsProject, QgsApplication, QgsUnitTypes, QgsCoordinateReferenceSystem

from qgis.core import (QgsProject,
QgsApplication,
QgsUnitTypes,
QgsCoordinateReferenceSystem,
QgsVectorLayer)
from qgis.gui import (QgsLayerTreeMapCanvasBridge,
QgsMapCanvas)
from qgis.testing import start_app, unittest
from utilities import (unitTestDataPath)
from qgis.PyQt.QtCore import QDir
from qgis.PyQt.QtTest import QSignalSpy

start_app()
app = start_app()
TEST_DATA_DIR = unitTestDataPath()


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

def testLayerOrder(self):
""" test project layer order"""
prj = QgsProject()
layer = QgsVectorLayer("Point?field=fldtxt:string",
"layer1", "memory")
layer2 = QgsVectorLayer("Point?field=fldtxt:string",
"layer2", "memory")
layer3 = QgsVectorLayer("Point?field=fldtxt:string",
"layer3", "memory")
prj.addMapLayers([layer, layer2, layer3])

layer_order_changed_spy = QSignalSpy(prj.layerOrderChanged)
prj.setLayerOrder([layer2, layer])
self.assertEqual(len(layer_order_changed_spy), 1)
prj.setLayerOrder([layer2, layer])
self.assertEqual(len(layer_order_changed_spy), 1) # no signal, order not changed

self.assertEqual(prj.layerOrder(), [layer2, layer])
prj.setLayerOrder([layer])
self.assertEqual(prj.layerOrder(), [layer])
self.assertEqual(len(layer_order_changed_spy), 2)

# remove a layer
prj.setLayerOrder([layer2, layer, layer3])
self.assertEqual(len(layer_order_changed_spy), 3)
prj.removeMapLayer(layer)
self.assertEqual(prj.layerOrder(), [layer2, layer3])
self.assertEqual(len(layer_order_changed_spy), 4)

# save and restore
file_name = os.path.join(str(QDir.tempPath()), 'proj.qgs')
prj.setFileName(file_name)
prj.write()
prj2 = QgsProject()
prj2.setFileName(file_name)
prj2.read()
self.assertEqual([l.id() for l in prj2.layerOrder()], [layer2.id(), layer3.id()])

# clear project
prj.clear()
self.assertEqual(prj.layerOrder(), [])

def testLayerOrderUpdatedThroughBridge(self):
""" test that project layer order is updated when layer tree changes """

prj = QgsProject.instance()
layer = QgsVectorLayer("Point?field=fldtxt:string",
"layer1", "memory")
layer2 = QgsVectorLayer("Point?field=fldtxt:string",
"layer2", "memory")
layer3 = QgsVectorLayer("Point?field=fldtxt:string",
"layer3", "memory")
prj.addMapLayers([layer, layer2, layer3])

canvas = QgsMapCanvas()
bridge = QgsLayerTreeMapCanvasBridge(prj.layerTreeRoot(), canvas)

#custom layer order
bridge.setHasCustomLayerOrder(True)
bridge.setCustomLayerOrder([layer3.id(), layer.id(), layer2.id()])
app.processEvents()
self.assertEqual([l.id() for l in prj.layerOrder()], [layer3.id(), layer.id(), layer2.id()])

# no custom layer order
bridge.setHasCustomLayerOrder(False)
app.processEvents()
self.assertEqual([l.id() for l in prj.layerOrder()], [layer.id(), layer2.id(), layer3.id()])

# mess around with the layer tree order
root = prj.layerTreeRoot()
layer_node = root.findLayer(layer2.id())
cloned_node = layer_node.clone()
parent = layer_node.parent()
parent.insertChildNode(0, cloned_node)
parent.removeChildNode(layer_node)
app.processEvents()
# make sure project respects this
self.assertEqual([l.id() for l in prj.layerOrder()], [layer2.id(), layer.id(), layer3.id()])


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

0 comments on commit 6cfc6a1

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