Allow retrieval of project layer order through QgsProject

Previously this was only accessible through app
This commit is contained in:
Nyall Dawson 2017-03-09 13:13:09 +10:00
parent 9842fcbfc7
commit 6cfc6a1b98
5 changed files with 168 additions and 7 deletions

View File

@ -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 legendLayersAdded( const QList<QgsMapLayer *> &layers );
void layerWasAdded( QgsMapLayer* layer );
void legendLayersAdded( const QList<QgsMapLayer*>& layers );
void layerOrderChanged();
public slots:
/**

View File

@ -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,6 +2117,8 @@ 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
@ -2098,6 +2126,7 @@ void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
{
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 ) );

View File

@ -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;

View File

@ -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 );

View File

@ -18,12 +18,19 @@ import os
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 @@ class TestQgsProject(unittest.TestCase):
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()