Refactor visibility presets, so that all non-app specific

methods are now in core

A new QgsVisibilityPresetCollection object has been created
which stores a set of visibility presets. QgsProject now
contains a preset collection object.

This allows plugins and other core objects to retrieve and
modify visibility presets.
This commit is contained in:
Nyall Dawson 2015-08-19 19:04:53 +10:00
parent 612df6ae14
commit 73522dce7e
13 changed files with 687 additions and 352 deletions

View File

@ -127,6 +127,7 @@
%Include qgsprojectfiletransform.sip
%Include qgsvectorlayereditutils.sip
%Include qgsvectorlayerfeatureiterator.sip
%Include qgsvisibilitypresetcollection.sip
%Include qgslayerdefinition.sip
%Include composer/qgsaddremoveitemcommand.sip

View File

@ -258,6 +258,11 @@ class QgsProject : QObject
*/
QgsLayerTreeRegistryBridge* layerTreeRegistryBridge() const;
/** Returns pointer to the project's visibility preset collection.
* @note added in QGIS 2.12
*/
QgsVisibilityPresetCollection* visibilityPresetCollection();
protected:
/** Set error message from read/write operation */

View File

@ -0,0 +1,131 @@
/**
\class QgsVisibilityPresetCollection
\ingroup core
\brief Container class that allows storage of visibility presets consisting of visible
map layers and layer styles.
\note added in QGIS 2.12
*/
class QgsVisibilityPresetCollection : QObject
{
%TypeHeaderCode
#include <qgsvisibilitypresetcollection.h>
%End
public:
/** Individual preset record of visible layers and styles.
*/
class PresetRecord
{
public:
bool operator==( const QgsVisibilityPresetCollection::PresetRecord& other ) const;
bool operator!=( const QgsVisibilityPresetCollection::PresetRecord& other ) const;
//! List of layers that are visible
QSet<QString> mVisibleLayerIDs;
/** For layers that have checkable legend symbols and not all symbols are checked - list which ones are
* @note not available in Python bindings
*/
//QMap<QString, QSet<QString> > mPerLayerCheckedLegendSymbols;
//! For layers that use multiple styles - which one is currently selected
QMap<QString, QString> mPerLayerCurrentStyle;
};
QgsVisibilityPresetCollection();
/** Returns whether a preset with a matching name exists.
* @param name name of preset to check
* @returns true if preset exists
*/
bool hasPreset( const QString& name ) const;
/** Inserts a new preset to the collection.
* @param name name of preset
* @param state preset record
* @see update()
*/
void insert( const QString& name, const PresetRecord& state );
/** Updates a preset within the collection.
* @param name name of preset to update
* @param state preset record to replace existing preset
* @see insert()
*/
void update( const QString& name, const PresetRecord& state );
/** Remove existing preset from collection.
* @param name preset name
*/
void removePreset( const QString& name );
//! Remove all presets from the collection.
void clear();
//! Returns a list of existing preset names.
QStringList presets() const;
/** Returns the recorded state of a preset.
* @param name name of preset
*/
PresetRecord presetState( const QString& name ) const;
/** Returns the list of layer IDs that should be visible for the specified preset.
* @note The order of the returned list is not guaranteed to reflect the order of layers
* in the canvas.
* @param name preset name
*/
QStringList presetVisibleLayers( const QString& name ) const;
/** Apply check states of legend nodes of a given layer as defined in the preset.
* @param preset name
* @param layerID layer ID
*/
void applyPresetCheckedLegendNodesToLayer( const QString& name, const QString& layerID );
/** Get layer style overrides (for QgsMapSettings) of the visible layers for given preset.
* @param name preset name
*/
QMap<QString, QString> presetStyleOverrides( const QString& name );
/** Reads the preset collection state from XML
* @param doc DOM document
* @see writeXML
*/
void readXML( const QDomDocument& doc );
/** Writes the preset collection state to XML.
* @param doc DOM document
* @see readXML
*/
void writeXML( QDomDocument& doc );
/** Static method for adding visible layers from a layer tree group to a preset
* record.
* @param parent layer tree group parent
* @param rec preset record to amend
*/
static void addVisibleLayersToPreset( QgsLayerTreeGroup* parent, PresetRecord& rec );
signals:
/** Emitted when presets within the collection are changed.
*/
void presetsChanged();
protected slots:
/** Handles updates of the preset collection when layers are removed from the registry
*/
void registryLayersRemoved( QStringList layerIDs );
//! Update style name if a stored style gets renamed
void layerStyleRenamed( const QString& oldName, const QString& newName );
protected:
/** Reconnects all preset layers to handle style renames
*/
void reconnectToLayersStyleManager();
};

View File

@ -37,7 +37,7 @@
#include "qgsexpressionbuilderdialog.h"
#include "qgsgenericprojectionselector.h"
#include "qgsproject.h"
#include "qgsvisibilitypresets.h"
#include "qgsvisibilitypresetcollection.h"
#include "qgisgui.h"
#include <QMessageBox>
@ -285,12 +285,12 @@ void QgsComposerMapWidget::aboutToShowVisibilityPresetsMenu()
return;
menu->clear();
foreach ( QString presetName, QgsVisibilityPresets::instance()->presets() )
foreach ( QString presetName, QgsProject::instance()->visibilityPresetCollection()->presets() )
{
QAction* a = menu->addAction( presetName, this, SLOT( visibilityPresetSelected() ) );
a->setCheckable( true );
QStringList layers = QgsVisibilityPresets::instance()->presetVisibleLayers( presetName );
QMap<QString, QString> styles = QgsVisibilityPresets::instance()->presetStyleOverrides( presetName );
QStringList layers = QgsProject::instance()->visibilityPresetCollection()->presetVisibleLayers( presetName );
QMap<QString, QString> styles = QgsProject::instance()->visibilityPresetCollection()->presetStyleOverrides( presetName );
if ( layers == mComposerMap->layerSet() && styles == mComposerMap->layerStyleOverrides() )
a->setChecked( true );
}
@ -306,7 +306,7 @@ void QgsComposerMapWidget::visibilityPresetSelected()
return;
QString presetName = action->text();
QStringList lst = QgsVisibilityPresets::instance()->presetVisibleLayers( presetName );
QStringList lst = QgsProject::instance()->visibilityPresetCollection()->presetVisibleLayers( presetName );
if ( mComposerMap )
{
mKeepLayerListCheckBox->setChecked( true );
@ -314,7 +314,7 @@ void QgsComposerMapWidget::visibilityPresetSelected()
mKeepLayerStylesCheckBox->setChecked( true );
mComposerMap->setLayerStyleOverrides( QgsVisibilityPresets::instance()->presetStyleOverrides( presetName ) );
mComposerMap->setLayerStyleOverrides( QgsProject::instance()->visibilityPresetCollection()->presetStyleOverrides( presetName ) );
mComposerMap->cache();
mComposerMap->update();

View File

@ -3756,8 +3756,6 @@ void QgisApp::fileNew( bool thePromptToSaveFlag, bool forceBlank )
fileNewFromDefaultTemplate();
}
QgsVisibilityPresets::instance()->clear();
// set the initial map tool
#ifndef HAVE_TOUCH
mMapCanvas->setMapTool( mMapTools.mPan );

View File

@ -26,7 +26,7 @@
#include "qgsfieldcombobox.h"
#include "qgisapp.h"
#include "qgslayertreemapcanvasbridge.h"
#include "qgsvisibilitypresets.h"
#include "qgsvisibilitypresetcollection.h"
#include <QFileDialog>
#include <QPushButton>
@ -329,7 +329,7 @@ QList< QPair<QgsVectorLayer *, int> > QgsVectorLayerAndAttributeModel::layers()
void QgsVectorLayerAndAttributeModel::applyVisibilityPreset( const QString &name )
{
QSet<QString> visibleLayers = QgsVisibilityPresets::instance()->presetVisibleLayers( name ).toSet();
QSet<QString> visibleLayers = QgsProject::instance()->visibilityPresetCollection()->presetVisibleLayers( name ).toSet();
if ( visibleLayers.isEmpty() )
return;
@ -429,7 +429,7 @@ QgsDxfExportDialog::QgsDxfExportDialog( QWidget *parent, Qt::WindowFlags f )
mScaleWidget->setScale( s.value( "qgis/lastSymbologyExportScale", "1/50000" ).toDouble() );
mMapExtentCheckBox->setChecked( s.value( "qgis/lastDxfMapRectangle", "false" ).toBool() );
QStringList ids = QgsVisibilityPresets::instance()->presets();
QStringList ids = QgsProject::instance()->visibilityPresetCollection()->presets();
ids.prepend( "" );
mVisibilityPresets->addItems( ids );

View File

@ -14,6 +14,7 @@
***************************************************************************/
#include "qgsvisibilitypresets.h"
#include "qgsvisibilitypresetcollection.h"
#include "qgslayertree.h"
#include "qgslayertreemapcanvasbridge.h"
@ -49,32 +50,9 @@ QgsVisibilityPresets::QgsVisibilityPresets()
mActionRemoveCurrentPreset = mMenu->addAction( tr( "Remove Current Preset" ), this, SLOT( removeCurrentPreset() ) );
connect( mMenu, SIGNAL( aboutToShow() ), this, SLOT( menuAboutToShow() ) );
connect( QgsMapLayerRegistry::instance(), SIGNAL( layersRemoved( QStringList ) ),
this, SLOT( registryLayersRemoved( QStringList ) ) );
connect( QgsProject::instance(), SIGNAL( readProject( const QDomDocument & ) ),
this, SLOT( readProject( const QDomDocument & ) ) );
connect( QgsProject::instance(), SIGNAL( writeProject( QDomDocument & ) ),
this, SLOT( writeProject( QDomDocument & ) ) );
}
void QgsVisibilityPresets::addVisibleLayersToPreset( QgsLayerTreeGroup* parent, QgsVisibilityPresets::PresetRecord& rec )
{
foreach ( QgsLayerTreeNode* node, parent->children() )
{
if ( QgsLayerTree::isGroup( node ) )
addVisibleLayersToPreset( QgsLayerTree::toGroup( node ), rec );
else if ( QgsLayerTree::isLayer( node ) )
{
QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( node );
if ( nodeLayer->isVisible() )
rec.mVisibleLayerIDs << nodeLayer->layerId();
}
}
}
void QgsVisibilityPresets::addPerLayerCheckedLegendSymbols( QgsVisibilityPresets::PresetRecord& rec )
void QgsVisibilityPresets::addPerLayerCheckedLegendSymbols( QgsVisibilityPresetCollection::PresetRecord& rec )
{
QgsLayerTreeModel* model = QgisApp::instance()->layerTreeView()->layerTreeModel();
@ -105,7 +83,7 @@ void QgsVisibilityPresets::addPerLayerCheckedLegendSymbols( QgsVisibilityPresets
}
}
void QgsVisibilityPresets::addPerLayerCurrentStyle( QgsVisibilityPresets::PresetRecord& rec )
void QgsVisibilityPresets::addPerLayerCurrentStyle( QgsVisibilityPresetCollection::PresetRecord& rec )
{
QgsLayerTreeModel* model = QgisApp::instance()->layerTreeView()->layerTreeModel();
@ -119,11 +97,11 @@ void QgsVisibilityPresets::addPerLayerCurrentStyle( QgsVisibilityPresets::Preset
}
}
QgsVisibilityPresets::PresetRecord QgsVisibilityPresets::currentState()
QgsVisibilityPresetCollection::PresetRecord QgsVisibilityPresets::currentState()
{
PresetRecord rec;
QgsVisibilityPresetCollection::PresetRecord rec;
QgsLayerTreeGroup* root = QgsProject::instance()->layerTreeRoot();
addVisibleLayersToPreset( root, rec );
QgsVisibilityPresetCollection::addVisibleLayersToPreset( root, rec );
addPerLayerCheckedLegendSymbols( rec );
addPerLayerCurrentStyle( rec );
return rec;
@ -139,43 +117,17 @@ QgsVisibilityPresets* QgsVisibilityPresets::instance()
void QgsVisibilityPresets::addPreset( const QString& name )
{
mPresets.insert( name, currentState() );
reconnectToLayersStyleManager();
QgsProject::instance()->visibilityPresetCollection()->insert( name, currentState() );
}
void QgsVisibilityPresets::updatePreset( const QString& name )
{
if ( !mPresets.contains( name ) )
return;
mPresets[name] = currentState();
reconnectToLayersStyleManager();
QgsProject::instance()->visibilityPresetCollection()->update( name, currentState() );
}
void QgsVisibilityPresets::removePreset( const QString& name )
QStringList QgsVisibilityPresets::orderedPresetVisibleLayers( const QString& name ) const
{
mPresets.remove( name );
reconnectToLayersStyleManager();
}
void QgsVisibilityPresets::clear()
{
mPresets.clear();
reconnectToLayersStyleManager();
}
QStringList QgsVisibilityPresets::presets() const
{
return mPresets.keys();
}
QStringList QgsVisibilityPresets::presetVisibleLayers( const QString& name ) const
{
QSet<QString> visibleIds = mPresets.value( name ).mVisibleLayerIDs;
QStringList visibleIds = QgsProject::instance()->visibilityPresetCollection()->presetVisibleLayers( name );
// also make sure to order the layers according to map canvas order
QgsLayerTreeMapCanvasBridge* bridge = QgisApp::instance()->layerTreeCanvasBridge();
@ -190,71 +142,6 @@ QStringList QgsVisibilityPresets::presetVisibleLayers( const QString& name ) con
return order2;
}
void QgsVisibilityPresets::applyPresetCheckedLegendNodesToLayer( const QString& name, const QString& layerID )
{
if ( !mPresets.contains( name ) )
return;
QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( layerID );
if ( !layer )
return;
const PresetRecord& rec = mPresets[name];
QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer*>( layer );
if ( !vlayer || !vlayer->rendererV2() )
return;
if ( !vlayer->rendererV2()->legendSymbolItemsCheckable() )
return; // no need to do anything
bool someNodesUnchecked = rec.mPerLayerCheckedLegendSymbols.contains( layerID );
foreach ( const QgsLegendSymbolItemV2& item, vlayer->rendererV2()->legendSymbolItemsV2() )
{
bool checked = vlayer->rendererV2()->legendSymbolItemChecked( item.ruleKey() );
bool shouldBeChecked = someNodesUnchecked ? rec.mPerLayerCheckedLegendSymbols[layerID].contains( item.ruleKey() ) : true;
if ( checked != shouldBeChecked )
vlayer->rendererV2()->checkLegendSymbolItem( item.ruleKey(), shouldBeChecked );
}
}
QMap<QString, QString> QgsVisibilityPresets::presetStyleOverrides( const QString& presetName )
{
QMap<QString, QString> styleOverrides;
if ( !mPresets.contains( presetName ) )
return styleOverrides;
QStringList lst = QgsVisibilityPresets::instance()->presetVisibleLayers( presetName );
const QgsVisibilityPresets::PresetRecord& rec = mPresets[presetName];
foreach ( const QString& layerID, lst )
{
QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( layerID );
if ( !layer )
continue;
// use either the stored style name or the current one if none has been stored
QString overrideStyleName = rec.mPerLayerCurrentStyle.value( layerID, layer->styleManager()->currentStyle() );
// store original style and temporarily apply a style
layer->styleManager()->setOverrideStyle( overrideStyleName );
// set the checked legend nodes
applyPresetCheckedLegendNodesToLayer( presetName, layerID );
// save to overrides
QgsMapLayerStyle layerStyle;
layerStyle.readFromLayer( layer );
styleOverrides[layerID] = layerStyle.xmlData();
layer->styleManager()->restoreOverrideStyle();
}
return styleOverrides;
}
QMenu* QgsVisibilityPresets::menu()
{
return mMenu;
@ -282,7 +169,7 @@ void QgsVisibilityPresets::presetTriggerred()
}
void QgsVisibilityPresets::applyStateToLayerTreeGroup( QgsLayerTreeGroup* parent, const PresetRecord& rec )
void QgsVisibilityPresets::applyStateToLayerTreeGroup( QgsLayerTreeGroup* parent, const QgsVisibilityPresetCollection::PresetRecord& rec )
{
foreach ( QgsLayerTreeNode* node, parent->children() )
{
@ -333,61 +220,40 @@ void QgsVisibilityPresets::applyStateToLayerTreeGroup( QgsLayerTreeGroup* parent
void QgsVisibilityPresets::applyState( const QString& presetName )
{
if ( !mPresets.contains( presetName ) )
if ( !QgsProject::instance()->visibilityPresetCollection()->hasPreset( presetName ) )
return;
applyStateToLayerTreeGroup( QgsProject::instance()->layerTreeRoot(), mPresets[presetName] );
applyStateToLayerTreeGroup( QgsProject::instance()->layerTreeRoot(), QgsProject::instance()->visibilityPresetCollection()->presetState( presetName ) );
// also make sure that the preset is up-to-date (not containing any non-existent legend items)
mPresets[presetName] = currentState();
QgsProject::instance()->visibilityPresetCollection()->update( presetName, currentState() );
}
void QgsVisibilityPresets::reconnectToLayersStyleManager()
{
// disconnect( 0, 0, this, SLOT( layerStyleRenamed( QString, QString ) ) );
QSet<QString> layerIDs;
foreach ( const QString& grpName, mPresets.keys() )
{
const PresetRecord& rec = mPresets[grpName];
foreach ( const QString& layerID, rec.mPerLayerCurrentStyle.keys() )
layerIDs << layerID;
}
foreach ( const QString& layerID, layerIDs )
{
if ( QgsMapLayer* ml = QgsMapLayerRegistry::instance()->mapLayer( layerID ) )
connect( ml->styleManager(), SIGNAL( styleRenamed( QString, QString ) ), this, SLOT( layerStyleRenamed( QString, QString ) ) );
}
}
void QgsVisibilityPresets::removeCurrentPreset()
{
foreach ( QAction* a, mMenuPresetActions )
{
if ( a->isChecked() )
{
removePreset( a->text() );
QgsProject::instance()->visibilityPresetCollection()->removePreset( a->text() );
break;
}
}
}
void QgsVisibilityPresets::menuAboutToShow()
{
qDeleteAll( mMenuPresetActions );
mMenuPresetActions.clear();
PresetRecord rec = currentState();
QgsVisibilityPresetCollection::PresetRecord rec = currentState();
bool hasCurrent = false;
foreach ( const QString& grpName, mPresets.keys() )
foreach ( const QString& grpName, QgsProject::instance()->visibilityPresetCollection()->presets() )
{
QAction* a = new QAction( grpName, mMenu );
a->setCheckable( true );
if ( rec == mPresets[grpName] )
if ( !hasCurrent && rec == QgsProject::instance()->visibilityPresetCollection()->presetState( grpName ) )
{
a->setChecked( true );
hasCurrent = true;
@ -399,127 +265,3 @@ void QgsVisibilityPresets::menuAboutToShow()
mActionRemoveCurrentPreset->setEnabled( hasCurrent );
}
void QgsVisibilityPresets::readProject( const QDomDocument& doc )
{
clear();
QDomElement visPresetsElem = doc.firstChildElement( "qgis" ).firstChildElement( "visibility-presets" );
if ( visPresetsElem.isNull() )
return;
QDomElement visPresetElem = visPresetsElem.firstChildElement( "visibility-preset" );
while ( !visPresetElem.isNull() )
{
QString presetName = visPresetElem.attribute( "name" );
PresetRecord rec;
QDomElement visPresetLayerElem = visPresetElem.firstChildElement( "layer" );
while ( !visPresetLayerElem.isNull() )
{
QString layerID = visPresetLayerElem.attribute( "id" );
if ( QgsMapLayerRegistry::instance()->mapLayer( layerID ) )
{
rec.mVisibleLayerIDs << layerID; // only use valid layer IDs
if ( visPresetLayerElem.hasAttribute( "style" ) )
rec.mPerLayerCurrentStyle[layerID] = visPresetLayerElem.attribute( "style" );
}
visPresetLayerElem = visPresetLayerElem.nextSiblingElement( "layer" );
}
QDomElement checkedLegendNodesElem = visPresetElem.firstChildElement( "checked-legend-nodes" );
while ( !checkedLegendNodesElem.isNull() )
{
QSet<QString> checkedLegendNodes;
QDomElement checkedLegendNodeElem = checkedLegendNodesElem.firstChildElement( "checked-legend-node" );
while ( !checkedLegendNodeElem.isNull() )
{
checkedLegendNodes << checkedLegendNodeElem.attribute( "id" );
checkedLegendNodeElem = checkedLegendNodeElem.nextSiblingElement( "checked-legend-node" );
}
QString layerID = checkedLegendNodesElem.attribute( "id" );
if ( QgsMapLayerRegistry::instance()->mapLayer( layerID ) ) // only use valid IDs
rec.mPerLayerCheckedLegendSymbols.insert( layerID, checkedLegendNodes );
checkedLegendNodesElem = checkedLegendNodesElem.nextSiblingElement( "checked-legend-nodes" );
}
mPresets.insert( presetName, rec );
visPresetElem = visPresetElem.nextSiblingElement( "visibility-preset" );
}
reconnectToLayersStyleManager();
}
void QgsVisibilityPresets::writeProject( QDomDocument& doc )
{
QDomElement visPresetsElem = doc.createElement( "visibility-presets" );
foreach ( const QString& grpName, mPresets.keys() )
{
const PresetRecord& rec = mPresets[grpName];
QDomElement visPresetElem = doc.createElement( "visibility-preset" );
visPresetElem.setAttribute( "name", grpName );
foreach ( QString layerID, rec.mVisibleLayerIDs )
{
QDomElement layerElem = doc.createElement( "layer" );
layerElem.setAttribute( "id", layerID );
if ( rec.mPerLayerCurrentStyle.contains( layerID ) )
layerElem.setAttribute( "style", rec.mPerLayerCurrentStyle[layerID] );
visPresetElem.appendChild( layerElem );
}
foreach ( QString layerID, rec.mPerLayerCheckedLegendSymbols.keys() )
{
QDomElement checkedLegendNodesElem = doc.createElement( "checked-legend-nodes" );
checkedLegendNodesElem.setAttribute( "id", layerID );
foreach ( QString checkedLegendNode, rec.mPerLayerCheckedLegendSymbols[layerID] )
{
QDomElement checkedLegendNodeElem = doc.createElement( "checked-legend-node" );
checkedLegendNodeElem.setAttribute( "id", checkedLegendNode );
checkedLegendNodesElem.appendChild( checkedLegendNodeElem );
}
visPresetElem.appendChild( checkedLegendNodesElem );
}
visPresetsElem.appendChild( visPresetElem );
}
doc.firstChildElement( "qgis" ).appendChild( visPresetsElem );
}
void QgsVisibilityPresets::registryLayersRemoved( QStringList layerIDs )
{
foreach ( QString layerID, layerIDs )
{
foreach ( QString presetName, mPresets.keys() )
{
PresetRecord& rec = mPresets[presetName];
rec.mVisibleLayerIDs.remove( layerID );
rec.mPerLayerCheckedLegendSymbols.remove( layerID );
rec.mPerLayerCurrentStyle.remove( layerID );
}
}
}
void QgsVisibilityPresets::layerStyleRenamed( const QString& oldName, const QString& newName )
{
QgsMapLayerStyleManager* styleMgr = qobject_cast<QgsMapLayerStyleManager*>( sender() );
if ( !styleMgr )
return;
QString layerID = styleMgr->layer()->id();
foreach ( QString presetName, mPresets.keys() )
{
PresetRecord& rec = mPresets[presetName];
if ( rec.mPerLayerCurrentStyle.contains( layerID ) )
{
QString styleName = rec.mPerLayerCurrentStyle[layerID];
if ( styleName == oldName )
rec.mPerLayerCurrentStyle[layerID] = newName;
}
}
}

View File

@ -16,6 +16,7 @@
#ifndef QGSVISIBILITYPRESETS_H
#define QGSVISIBILITYPRESETS_H
#include "qgsvisibilitypresetcollection.h"
#include <QMap>
#include <QObject>
#include <QSet>
@ -29,102 +30,61 @@ class QgsLayerTreeNode;
class QgsLayerTreeGroup;
/**
* Controller class that allows creation of visibility presets consisting of currently visible
* map layers in map canvas.
* Contains methods for app-specific visibility preset functions.
*/
class QgsVisibilityPresets : public QObject
class APP_EXPORT QgsVisibilityPresets : public QObject
{
Q_OBJECT
public:
typedef struct PresetRecord
{
bool operator==( const PresetRecord& other ) const
{
return mVisibleLayerIDs == other.mVisibleLayerIDs
&& mPerLayerCheckedLegendSymbols == other.mPerLayerCheckedLegendSymbols
&& mPerLayerCurrentStyle == other.mPerLayerCurrentStyle;
}
bool operator!=( const PresetRecord& other ) const
{
return !( *this == other );
}
//! List of layers that are visible
QSet<QString> mVisibleLayerIDs;
//! For layers that have checkable legend symbols and not all symbols are checked - list which ones are
QMap<QString, QSet<QString> > mPerLayerCheckedLegendSymbols;
//! For layers that use multiple styles - which one is currently selected
QMap<QString, QString> mPerLayerCurrentStyle;
} PresetRecord;
/** Returns the instance QgsVisibilityPresets.
*/
static QgsVisibilityPresets* instance();
//! Add a new preset using the current state of project's layer tree
void addPreset( const QString& name );
//! Update existing preset using the current state of project's layer tree
void updatePreset( const QString& name );
//! Remove existing preset
void removePreset( const QString& name );
//! Remove all presets
void clear();
//! Return list of existing preset names
QStringList presets() const;
//! Return recorded state of a preset
PresetRecord presetState( const QString& presetName ) const { return mPresets[presetName]; }
//! Return list of layer IDs that should be visible for particular preset
QStringList presetVisibleLayers( const QString& name ) const;
//! Apply check states of legend nodes of a given layer as defined in the preset
void applyPresetCheckedLegendNodesToLayer( const QString& name, const QString& layerID );
//! Return list of layer IDs that should be visible for particular preset.
//! The order will match the layer order from the map canvas
QStringList orderedPresetVisibleLayers( const QString& name ) const;
//! Convenience menu that lists available presets and actions for management
QMenu* menu();
//! Get layer style overrides (for QgsMapSettings) of the visible layers for given preset
QMap<QString, QString> presetStyleOverrides( const QString& presetName );
signals:
void presetsChanged();
protected slots:
//! Handles adding a preset to the project's collection
void addPreset();
//! Handles apply a preset to the map canvas
void presetTriggerred();
//! Handles removal of current preset from the project's collection
void removeCurrentPreset();
//! Handles creation of preset menu
void menuAboutToShow();
void readProject( const QDomDocument& doc );
void writeProject( QDomDocument& doc );
void registryLayersRemoved( QStringList layerIDs );
//! Update style name if a stored style gets renamed
void layerStyleRenamed( const QString& oldName, const QString& newName );
protected:
QgsVisibilityPresets(); // singleton
typedef QMap<QString, PresetRecord> PresetRecordMap;
//! Applies current layer state to a preset record
void applyStateToLayerTreeGroup( QgsLayerTreeGroup* parent, const QgsVisibilityPresetCollection::PresetRecord& rec );
//! Applies layer checked legend symbols to a preset record
void addPerLayerCheckedLegendSymbols( QgsVisibilityPresetCollection::PresetRecord& rec );
//! Applies current layer styles to a preset record
void addPerLayerCurrentStyle( QgsVisibilityPresetCollection::PresetRecord& rec );
void addVisibleLayersToPreset( QgsLayerTreeGroup* parent, PresetRecord& rec );
void applyStateToLayerTreeGroup( QgsLayerTreeGroup* parent, const PresetRecord& rec );
void addPerLayerCheckedLegendSymbols( PresetRecord& rec );
void addPerLayerCurrentStyle( PresetRecord& rec );
//! Returns the current state of the map canvas as a preset record
QgsVisibilityPresetCollection::PresetRecord currentState();
PresetRecord currentState();
//! Applies a preset for the project's collection to the canvas
void applyState( const QString& presetName );
void reconnectToLayersStyleManager();
static QgsVisibilityPresets* sInstance;
PresetRecordMap mPresets;
QMenu* mMenu;
QAction* mMenuSeparator;
QAction* mActionRemoveCurrentPreset;

View File

@ -185,6 +185,7 @@ SET(QGIS_CORE_SRCS
qgsvectorlayerrenderer.cpp
qgsvectorlayerundocommand.cpp
qgsvectorsimplifymethod.cpp
qgsvisibilitypresetcollection.cpp
qgsxmlutils.cpp
qgsslconnect.cpp
qgslocalec.cpp
@ -408,6 +409,7 @@ SET(QGIS_CORE_MOC_HDRS
qgsvectordataprovider.h
qgsvectorlayercache.h
qgsvectorlayerjoinbuffer.h
qgsvisibilitypresetcollection.h
qgsgeometryvalidator.h
qgswebview.h
@ -602,6 +604,7 @@ SET(QGIS_CORE_HDRS
qgsvectorlayerrenderer.h
qgsvectorlayerundocommand.h
qgsvectorsimplifymethod.h
qgsvisibilitypresetcollection.h
qgsxmlutils.h
qgswebpage.h
qgswebframe.h

View File

@ -36,6 +36,7 @@
#include "qgsrectangle.h"
#include "qgsrelationmanager.h"
#include "qgsvectorlayer.h"
#include "qgsvisibilitypresetcollection.h"
#include <QApplication>
#include <QFileInfo>
@ -414,6 +415,8 @@ void QgsProject::clear()
mEmbeddedLayers.clear();
mRelationManager->clear();
mVisibilityPresetCollection.reset( new QgsVisibilityPresetCollection() );
mRootGroup->removeAllChildren();
// reset some default project properties
@ -894,6 +897,9 @@ bool QgsProject::read()
mRootGroup->removeCustomProperty( "loading" );
mVisibilityPresetCollection.reset( new QgsVisibilityPresetCollection() );
mVisibilityPresetCollection->readXML( *doc );
// read the project: used by map canvas and legend
emit readProject( *doc );
@ -1093,6 +1099,8 @@ bool QgsProject::write()
imp_->properties_.writeXML( "properties", qgisNode, *doc );
}
mVisibilityPresetCollection->writeXML( *doc );
// now wrap it up and ship it to the project file
doc->normalize(); // XXX I'm not entirely sure what this does
@ -1966,3 +1974,8 @@ QgsLayerTreeGroup *QgsProject::layerTreeRoot() const
{
return mRootGroup;
}
QgsVisibilityPresetCollection* QgsProject::visibilityPresetCollection()
{
return mVisibilityPresetCollection.data();
}

View File

@ -45,6 +45,7 @@ class QgsMapLayer;
class QgsProjectBadLayerHandler;
class QgsRelationManager;
class QgsVectorLayer;
class QgsVisibilityPresetCollection;
/** \ingroup core
* Reads and writes project states.
@ -301,6 +302,11 @@ class CORE_EXPORT QgsProject : public QObject
*/
QgsLayerTreeRegistryBridge* layerTreeRegistryBridge() const { return mLayerTreeRegistryBridge; }
/** Returns pointer to the project's visibility preset collection.
* @note added in QGIS 2.12
*/
QgsVisibilityPresetCollection* visibilityPresetCollection();
protected:
/** Set error message from read/write operation */
@ -394,6 +400,8 @@ class CORE_EXPORT QgsProject : public QObject
QgsLayerTreeRegistryBridge* mLayerTreeRegistryBridge;
QScopedPointer<QgsVisibilityPresetCollection> mVisibilityPresetCollection;
}; // QgsProject

View File

@ -0,0 +1,304 @@
/***************************************************************************
qgsvisibilitypresetcollection.cpp
--------------------------------------
Date : September 2014
Copyright : (C) 2014 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsvisibilitypresetcollection.h"
#include "qgslayertree.h"
#include "qgslayertreemodel.h"
#include "qgslayertreemodellegendnode.h"
#include "qgsmaplayerregistry.h"
#include "qgsmaplayerstylemanager.h"
#include "qgsproject.h"
#include "qgsrendererv2.h"
#include "qgsvectorlayer.h"
#include <QInputDialog>
QgsVisibilityPresetCollection::QgsVisibilityPresetCollection()
{
connect( QgsMapLayerRegistry::instance(), SIGNAL( layersRemoved( QStringList ) ),
this, SLOT( registryLayersRemoved( QStringList ) ) );
}
void QgsVisibilityPresetCollection::addVisibleLayersToPreset( QgsLayerTreeGroup* parent, QgsVisibilityPresetCollection::PresetRecord& rec )
{
foreach ( QgsLayerTreeNode* node, parent->children() )
{
if ( QgsLayerTree::isGroup( node ) )
addVisibleLayersToPreset( QgsLayerTree::toGroup( node ), rec );
else if ( QgsLayerTree::isLayer( node ) )
{
QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( node );
if ( nodeLayer->isVisible() )
rec.mVisibleLayerIDs << nodeLayer->layerId();
}
}
}
bool QgsVisibilityPresetCollection::hasPreset( const QString& name ) const
{
return mPresets.contains( name );
}
void QgsVisibilityPresetCollection::insert( const QString& name, const QgsVisibilityPresetCollection::PresetRecord& state )
{
mPresets.insert( name, state );
reconnectToLayersStyleManager();
}
void QgsVisibilityPresetCollection::update( const QString& name, const PresetRecord& state )
{
if ( !mPresets.contains( name ) )
return;
mPresets[name] = state;
reconnectToLayersStyleManager();
}
void QgsVisibilityPresetCollection::removePreset( const QString& name )
{
if ( !mPresets.contains( name ) )
return;
mPresets.remove( name );
reconnectToLayersStyleManager();
}
void QgsVisibilityPresetCollection::clear()
{
mPresets.clear();
reconnectToLayersStyleManager();
}
QStringList QgsVisibilityPresetCollection::presets() const
{
return mPresets.keys();
}
QStringList QgsVisibilityPresetCollection::presetVisibleLayers( const QString& name ) const
{
QSet<QString> visibleIds = mPresets.value( name ).mVisibleLayerIDs;
return visibleIds.toList();
}
void QgsVisibilityPresetCollection::applyPresetCheckedLegendNodesToLayer( const QString& name, const QString& layerID )
{
if ( !mPresets.contains( name ) )
return;
QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( layerID );
if ( !layer )
return;
const PresetRecord& rec = mPresets[name];
QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer*>( layer );
if ( !vlayer || !vlayer->rendererV2() )
return;
if ( !vlayer->rendererV2()->legendSymbolItemsCheckable() )
return; // no need to do anything
bool someNodesUnchecked = rec.mPerLayerCheckedLegendSymbols.contains( layerID );
foreach ( const QgsLegendSymbolItemV2& item, vlayer->rendererV2()->legendSymbolItemsV2() )
{
bool checked = vlayer->rendererV2()->legendSymbolItemChecked( item.ruleKey() );
bool shouldBeChecked = someNodesUnchecked ? rec.mPerLayerCheckedLegendSymbols[layerID].contains( item.ruleKey() ) : true;
if ( checked != shouldBeChecked )
vlayer->rendererV2()->checkLegendSymbolItem( item.ruleKey(), shouldBeChecked );
}
}
QMap<QString, QString> QgsVisibilityPresetCollection::presetStyleOverrides( const QString& presetName )
{
QMap<QString, QString> styleOverrides;
if ( !mPresets.contains( presetName ) )
return styleOverrides;
QStringList lst = presetVisibleLayers( presetName );
const QgsVisibilityPresetCollection::PresetRecord& rec = mPresets[presetName];
foreach ( const QString& layerID, lst )
{
QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( layerID );
if ( !layer )
continue;
// use either the stored style name or the current one if none has been stored
QString overrideStyleName = rec.mPerLayerCurrentStyle.value( layerID, layer->styleManager()->currentStyle() );
// store original style and temporarily apply a style
layer->styleManager()->setOverrideStyle( overrideStyleName );
// set the checked legend nodes
applyPresetCheckedLegendNodesToLayer( presetName, layerID );
// save to overrides
QgsMapLayerStyle layerStyle;
layerStyle.readFromLayer( layer );
styleOverrides[layerID] = layerStyle.xmlData();
layer->styleManager()->restoreOverrideStyle();
}
return styleOverrides;
}
void QgsVisibilityPresetCollection::reconnectToLayersStyleManager()
{
// disconnect( 0, 0, this, SLOT( layerStyleRenamed( QString, QString ) ) );
QSet<QString> layerIDs;
foreach ( const QString& grpName, mPresets.keys() )
{
const PresetRecord& rec = mPresets[grpName];
foreach ( const QString& layerID, rec.mPerLayerCurrentStyle.keys() )
layerIDs << layerID;
}
foreach ( const QString& layerID, layerIDs )
{
if ( QgsMapLayer* ml = QgsMapLayerRegistry::instance()->mapLayer( layerID ) )
connect( ml->styleManager(), SIGNAL( styleRenamed( QString, QString ) ), this, SLOT( layerStyleRenamed( QString, QString ) ) );
}
}
void QgsVisibilityPresetCollection::readXML( const QDomDocument& doc )
{
clear();
QDomElement visPresetsElem = doc.firstChildElement( "qgis" ).firstChildElement( "visibility-presets" );
if ( visPresetsElem.isNull() )
return;
QDomElement visPresetElem = visPresetsElem.firstChildElement( "visibility-preset" );
while ( !visPresetElem.isNull() )
{
QString presetName = visPresetElem.attribute( "name" );
PresetRecord rec;
QDomElement visPresetLayerElem = visPresetElem.firstChildElement( "layer" );
while ( !visPresetLayerElem.isNull() )
{
QString layerID = visPresetLayerElem.attribute( "id" );
if ( QgsMapLayerRegistry::instance()->mapLayer( layerID ) )
{
rec.mVisibleLayerIDs << layerID; // only use valid layer IDs
if ( visPresetLayerElem.hasAttribute( "style" ) )
rec.mPerLayerCurrentStyle[layerID] = visPresetLayerElem.attribute( "style" );
}
visPresetLayerElem = visPresetLayerElem.nextSiblingElement( "layer" );
}
QDomElement checkedLegendNodesElem = visPresetElem.firstChildElement( "checked-legend-nodes" );
while ( !checkedLegendNodesElem.isNull() )
{
QSet<QString> checkedLegendNodes;
QDomElement checkedLegendNodeElem = checkedLegendNodesElem.firstChildElement( "checked-legend-node" );
while ( !checkedLegendNodeElem.isNull() )
{
checkedLegendNodes << checkedLegendNodeElem.attribute( "id" );
checkedLegendNodeElem = checkedLegendNodeElem.nextSiblingElement( "checked-legend-node" );
}
QString layerID = checkedLegendNodesElem.attribute( "id" );
if ( QgsMapLayerRegistry::instance()->mapLayer( layerID ) ) // only use valid IDs
rec.mPerLayerCheckedLegendSymbols.insert( layerID, checkedLegendNodes );
checkedLegendNodesElem = checkedLegendNodesElem.nextSiblingElement( "checked-legend-nodes" );
}
mPresets.insert( presetName, rec );
visPresetElem = visPresetElem.nextSiblingElement( "visibility-preset" );
}
reconnectToLayersStyleManager();
}
void QgsVisibilityPresetCollection::writeXML( QDomDocument& doc )
{
QDomElement visPresetsElem = doc.createElement( "visibility-presets" );
foreach ( const QString& grpName, mPresets.keys() )
{
const PresetRecord& rec = mPresets[grpName];
QDomElement visPresetElem = doc.createElement( "visibility-preset" );
visPresetElem.setAttribute( "name", grpName );
foreach ( QString layerID, rec.mVisibleLayerIDs )
{
QDomElement layerElem = doc.createElement( "layer" );
layerElem.setAttribute( "id", layerID );
if ( rec.mPerLayerCurrentStyle.contains( layerID ) )
layerElem.setAttribute( "style", rec.mPerLayerCurrentStyle[layerID] );
visPresetElem.appendChild( layerElem );
}
foreach ( QString layerID, rec.mPerLayerCheckedLegendSymbols.keys() )
{
QDomElement checkedLegendNodesElem = doc.createElement( "checked-legend-nodes" );
checkedLegendNodesElem.setAttribute( "id", layerID );
foreach ( QString checkedLegendNode, rec.mPerLayerCheckedLegendSymbols[layerID] )
{
QDomElement checkedLegendNodeElem = doc.createElement( "checked-legend-node" );
checkedLegendNodeElem.setAttribute( "id", checkedLegendNode );
checkedLegendNodesElem.appendChild( checkedLegendNodeElem );
}
visPresetElem.appendChild( checkedLegendNodesElem );
}
visPresetsElem.appendChild( visPresetElem );
}
doc.firstChildElement( "qgis" ).appendChild( visPresetsElem );
}
void QgsVisibilityPresetCollection::registryLayersRemoved( QStringList layerIDs )
{
foreach ( QString layerID, layerIDs )
{
foreach ( QString presetName, mPresets.keys() )
{
PresetRecord& rec = mPresets[presetName];
rec.mVisibleLayerIDs.remove( layerID );
rec.mPerLayerCheckedLegendSymbols.remove( layerID );
rec.mPerLayerCurrentStyle.remove( layerID );
}
}
}
void QgsVisibilityPresetCollection::layerStyleRenamed( const QString& oldName, const QString& newName )
{
QgsMapLayerStyleManager* styleMgr = qobject_cast<QgsMapLayerStyleManager*>( sender() );
if ( !styleMgr )
return;
QString layerID = styleMgr->layer()->id();
foreach ( QString presetName, mPresets.keys() )
{
PresetRecord& rec = mPresets[presetName];
if ( rec.mPerLayerCurrentStyle.contains( layerID ) )
{
QString styleName = rec.mPerLayerCurrentStyle[layerID];
if ( styleName == oldName )
rec.mPerLayerCurrentStyle[layerID] = newName;
}
}
}

View File

@ -0,0 +1,170 @@
/***************************************************************************
qgsvisibilitypresetcollection.h
--------------------------------------
Date : September 2014
Copyright : (C) 2014 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSVISIBILITYPRESETCOLLECTION_H
#define QGSVISIBILITYPRESETCOLLECTION_H
#include <QMap>
#include <QObject>
#include <QSet>
#include <QStringList>
class QDomDocument;
class QgsLayerTreeNode;
class QgsLayerTreeGroup;
/**
\class QgsVisibilityPresetCollection
\ingroup core
\brief Container class that allows storage of visibility presets consisting of visible
map layers and layer styles.
\note added in QGIS 2.12
*/
class CORE_EXPORT QgsVisibilityPresetCollection : public QObject
{
Q_OBJECT
public:
/** Individual preset record of visible layers and styles.
*/
class PresetRecord
{
public:
bool operator==( const PresetRecord& other ) const
{
return mVisibleLayerIDs == other.mVisibleLayerIDs
&& mPerLayerCheckedLegendSymbols == other.mPerLayerCheckedLegendSymbols
&& mPerLayerCurrentStyle == other.mPerLayerCurrentStyle;
}
bool operator!=( const PresetRecord& other ) const
{
return !( *this == other );
}
//! List of layers that are visible
QSet<QString> mVisibleLayerIDs;
/** For layers that have checkable legend symbols and not all symbols are checked - list which ones are
* @note not available in Python bindings
*/
QMap<QString, QSet<QString> > mPerLayerCheckedLegendSymbols;
//! For layers that use multiple styles - which one is currently selected
QMap<QString, QString> mPerLayerCurrentStyle;
};
QgsVisibilityPresetCollection();
/** Returns whether a preset with a matching name exists.
* @param name name of preset to check
* @returns true if preset exists
*/
bool hasPreset( const QString& name ) const;
/** Inserts a new preset to the collection.
* @param name name of preset
* @param state preset record
* @see update()
*/
void insert( const QString& name, const PresetRecord& state );
/** Updates a preset within the collection.
* @param name name of preset to update
* @param state preset record to replace existing preset
* @see insert()
*/
void update( const QString& name, const PresetRecord& state );
/** Remove existing preset from collection.
* @param name preset name
*/
void removePreset( const QString& name );
//! Remove all presets from the collection.
void clear();
//! Returns a list of existing preset names.
QStringList presets() const;
/** Returns the recorded state of a preset.
* @param name name of preset
*/
PresetRecord presetState( const QString& name ) const { return mPresets[name]; }
/** Returns the list of layer IDs that should be visible for the specified preset.
* @note The order of the returned list is not guaranteed to reflect the order of layers
* in the canvas.
* @param name preset name
*/
QStringList presetVisibleLayers( const QString& name ) const;
/** Apply check states of legend nodes of a given layer as defined in the preset.
* @param preset name
* @param layerID layer ID
*/
void applyPresetCheckedLegendNodesToLayer( const QString& name, const QString& layerID );
/** Get layer style overrides (for QgsMapSettings) of the visible layers for given preset.
* @param name preset name
*/
QMap<QString, QString> presetStyleOverrides( const QString& name );
/** Reads the preset collection state from XML
* @param doc DOM document
* @see writeXML
*/
void readXML( const QDomDocument& doc );
/** Writes the preset collection state to XML.
* @param doc DOM document
* @see readXML
*/
void writeXML( QDomDocument& doc );
/** Static method for adding visible layers from a layer tree group to a preset
* record.
* @param parent layer tree group parent
* @param rec preset record to amend
*/
static void addVisibleLayersToPreset( QgsLayerTreeGroup* parent, PresetRecord& rec );
signals:
/** Emitted when presets within the collection are changed.
*/
void presetsChanged();
protected slots:
/** Handles updates of the preset collection when layers are removed from the registry
*/
void registryLayersRemoved( QStringList layerIDs );
//! Update style name if a stored style gets renamed
void layerStyleRenamed( const QString& oldName, const QString& newName );
protected:
/** Reconnects all preset layers to handle style renames
*/
void reconnectToLayersStyleManager();
typedef QMap<QString, PresetRecord> PresetRecordMap;
PresetRecordMap mPresets;
};
#endif // QGSVISIBILITYPRESETCOLLECTION_H