Skip to content

Commit

Permalink
Layer order in map themes must always respect project layer order
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Mar 13, 2017
1 parent 6cfc6a1 commit 9faa628
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 58 deletions.
62 changes: 11 additions & 51 deletions python/core/qgsmapthemecollection.sip
Original file line number Diff line number Diff line change
Expand Up @@ -102,62 +102,22 @@ class QgsMapThemeCollection : QObject
//! Returns a list of existing map theme names.
QStringList mapThemes() const;

/**
* Returns the recorded state of a map theme.
*/
MapThemeRecord mapThemeState( const QString& name ) const;

/**
* Returns the list of layer IDs that are visible for the specified map theme.
*
* @note The order of the returned list is not guaranteed to reflect the order of layers
* in the canvas.
* @note Added in QGIS 3.0
*/
QStringList mapThemeVisibleLayerIds( const QString& name ) const;

/**
* Returns the list of layers that are visible for the specified map theme.
*
* @note The order of the returned list is not guaranteed to reflect the order of layers
* in the canvas.
* @note Added in QGIS 3.0
*/
QList<QgsMapLayer*> mapThemeVisibleLayers( const QString& name ) const;

/**
* Get layer style overrides (for QgsMapSettings) of the visible layers for given map theme.
*/
QMap<QString, QString> mapThemeStyleOverrides( const QString& name );

/**
* Reads the map theme collection state from XML
* @param doc DOM document
* @see writeXml
*/
void readXml( const QDomDocument& doc );
MapThemeRecord mapThemeState( const QString &name ) const;
QStringList mapThemeVisibleLayerIds( const QString &name ) const;
QList<QgsMapLayer *> mapThemeVisibleLayers( const QString &name ) const;
QMap<QString, QString> mapThemeStyleOverrides( const QString &name );
void readXml( const QDomDocument &doc );
void writeXml( QDomDocument &doc );
static MapThemeRecord createThemeFromCurrentState( QgsLayerTreeGroup *root, QgsLayerTreeModel *model );
void applyTheme( const QString &name, QgsLayerTreeGroup *root, QgsLayerTreeModel *model );
QgsProject *project();
void setProject( QgsProject *project );
QList< QgsMapLayer * > masterLayerOrder() const;

/** Writes the map theme collection state to XML.
* @param doc DOM document
* @see readXml
*/
void writeXml( QDomDocument& doc );

/**
* Static method to create theme from the current state of layer visibilities in layer tree,
* current style of layers and check state of legend items (from a layer tree model).
* @note added in QGIS 3.0
*/
static MapThemeRecord createThemeFromCurrentState( QgsLayerTreeGroup* root, QgsLayerTreeModel* model );

/**
* Apply theme given by its name and modify layer tree, current style of layers and checked
* legend items of passed layer tree model.
* @note added in QGIS 3.0
*/
void applyTheme( const QString& name, QgsLayerTreeGroup* root, QgsLayerTreeModel* model );
QgsProject* project();
void setProject( QgsProject* project );
signals:

void mapThemesChanged();
Expand Down
25 changes: 18 additions & 7 deletions src/core/qgsmapthemecollection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,14 @@ void QgsMapThemeCollection::setProject( QgsProject *project )
emit projectChanged();
}

QList<QgsMapLayer *> QgsMapThemeCollection::masterLayerOrder() const
{
if ( !mProject )
return QList< QgsMapLayer * >();

return mProject->layerOrder();
}


bool QgsMapThemeCollection::hasMapTheme( const QString &name ) const
{
Expand Down Expand Up @@ -229,23 +237,26 @@ QStringList QgsMapThemeCollection::mapThemes() const
QStringList QgsMapThemeCollection::mapThemeVisibleLayerIds( const QString &name ) const
{
QStringList layerIds;
Q_FOREACH ( const MapThemeLayerRecord &layerRec, mMapThemes.value( name ).mLayerRecords )
Q_FOREACH ( QgsMapLayer *layer, mapThemeVisibleLayers( name ) )
{
if ( layerRec.layer() )
layerIds << layerRec.layer()->id();
layerIds << layer->id();
}
return layerIds;
}


QList<QgsMapLayer *> QgsMapThemeCollection::mapThemeVisibleLayers( const QString &name ) const
{
QList<QgsMapLayer *> layers;
Q_FOREACH ( const MapThemeLayerRecord &layerRec, mMapThemes.value( name ).mLayerRecords )
const QList<MapThemeLayerRecord> &recs = mMapThemes.value( name ).mLayerRecords;
Q_FOREACH ( QgsMapLayer *layer, masterLayerOrder() )
{
if ( layerRec.layer() )
layers << layerRec.layer();
Q_FOREACH ( const MapThemeLayerRecord &layerRec, recs )
{
if ( layerRec.layer() == layer )
layers << layerRec.layer();
}
}

return layers;
}

Expand Down
7 changes: 7 additions & 0 deletions src/core/qgsmapthemecollection.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,13 @@ class CORE_EXPORT QgsMapThemeCollection : public QObject
*/
void setProject( QgsProject *project );

/**
* Returns the master layer order (this will always match the project's QgsProject::layerOrder() ).
* All map themes will maintain the same layer order as the master layer order.
* @note added in QGIS 3.0
*/
QList< QgsMapLayer * > masterLayerOrder() const;

signals:

/**
Expand Down
52 changes: 52 additions & 0 deletions tests/src/python/test_qgsmapthemecollection.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,58 @@ def testThemeChanged(self):
app.processEvents()
self.assertEqual(len(theme_changed_spy), 6) # signal should be emitted - layer is in record

def testMasterLayerOrder(self):
""" test master 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])

prj.setLayerOrder([layer2, layer])
self.assertEqual(prj.mapThemeCollection().masterLayerOrder(), [layer2, layer])

prj.setLayerOrder([layer, layer2, layer3])
# make some themes...
theme1 = QgsMapThemeCollection.MapThemeRecord()
theme1.setLayerRecords([QgsMapThemeCollection.MapThemeLayerRecord(layer3),
QgsMapThemeCollection.MapThemeLayerRecord(layer)])

theme2 = QgsMapThemeCollection.MapThemeRecord()
theme2.setLayerRecords([QgsMapThemeCollection.MapThemeLayerRecord(layer3),
QgsMapThemeCollection.MapThemeLayerRecord(layer2),
QgsMapThemeCollection.MapThemeLayerRecord(layer)])

theme3 = QgsMapThemeCollection.MapThemeRecord()
theme3.setLayerRecords([QgsMapThemeCollection.MapThemeLayerRecord(layer2),
QgsMapThemeCollection.MapThemeLayerRecord(layer)])

prj.mapThemeCollection().insert('theme1', theme1)
prj.mapThemeCollection().insert('theme2', theme2)
prj.mapThemeCollection().insert('theme3', theme3)

#order of layers in theme should respect master order
self.assertEqual(prj.mapThemeCollection().mapThemeVisibleLayers('theme1'), [layer, layer3])
self.assertEqual(prj.mapThemeCollection().mapThemeVisibleLayers('theme2'), [layer, layer2, layer3])
self.assertEqual(prj.mapThemeCollection().mapThemeVisibleLayers('theme3'), [layer, layer2])

# also check ids!
self.assertEqual(prj.mapThemeCollection().mapThemeVisibleLayerIds('theme1'), [layer.id(), layer3.id()])
self.assertEqual(prj.mapThemeCollection().mapThemeVisibleLayerIds('theme2'), [layer.id(), layer2.id(), layer3.id()])
self.assertEqual(prj.mapThemeCollection().mapThemeVisibleLayerIds('theme3'), [layer.id(), layer2.id()])

# reset master order
prj.setLayerOrder([layer2, layer3, layer])
self.assertEqual(prj.mapThemeCollection().mapThemeVisibleLayers('theme1'), [layer3, layer])
self.assertEqual(prj.mapThemeCollection().mapThemeVisibleLayers('theme2'), [layer2, layer3, layer])
self.assertEqual(prj.mapThemeCollection().mapThemeVisibleLayers('theme3'), [layer2, layer])
self.assertEqual(prj.mapThemeCollection().mapThemeVisibleLayerIds('theme1'), [layer3.id(), layer.id()])
self.assertEqual(prj.mapThemeCollection().mapThemeVisibleLayerIds('theme2'), [layer2.id(), layer3.id(), layer.id()])
self.assertEqual(prj.mapThemeCollection().mapThemeVisibleLayerIds('theme3'), [layer2.id(), layer.id()])


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

0 comments on commit 9faa628

Please sign in to comment.