Skip to content

Commit 3ce9c70

Browse files
committed
Merge pull request #1755 from wonder-sk/multiple-styles
[FEATURE] Support for multiple styles per map layer Available in legend context menu in Styles sub-menu where it is possible to add/remove styles and quickly switch between them.
2 parents 0b954c1 + 2144be0 commit 3ce9c70

18 files changed

+846
-11
lines changed

python/core/core.sip

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
%Include qgsmaplayerlegend.sip
5555
%Include qgsmaplayerregistry.sip
5656
%Include qgsmaplayerrenderer.sip
57+
%Include qgsmaplayerstylemanager.sip
5758
%Include qgsmaprenderer.sip
5859
%Include qgsmaprenderercache.sip
5960
%Include qgsmaprenderercustompainterjob.sip

python/core/qgsmaplayer.sip

+20
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,21 @@ class QgsMapLayer : QObject
370370
*/
371371
QgsMapLayerLegend* legend() const;
372372

373+
/**
374+
* Enable or disable layer's style manager. When disabled (default), the styleManager() will return null pointer.
375+
* By enabling the style manager will be created with one default style (same as the layer's active style).
376+
* By disabling the style manager all associated styles will be lost (only the layer's active style will stay).
377+
* @note added in 2.8
378+
*/
379+
void enableStyleManager( bool enable = true );
380+
381+
/**
382+
* Get access to the layer's style manager. Style manager allows switching between multiple styles.
383+
* If the style manager is not enabled, null pointer will be returned.
384+
* @note added in 2.8
385+
*/
386+
QgsMapLayerStyleManager* styleManager() const;
387+
373388
/**Returns the minimum scale denominator at which the layer is visible.
374389
* Scale based visibility is only used if hasScaleBasedVisibility is true.
375390
* @returns minimum scale denominator at which the layer will render
@@ -517,6 +532,11 @@ class QgsMapLayer : QObject
517532
/** Write custom properties to project file. */
518533
void writeCustomProperties( QDomNode & layerNode, QDomDocument & doc ) const;
519534

535+
/** Read style manager's configuration (if any). To be called by subclasses. */
536+
void readStyleManager( const QDomNode& layerNode );
537+
/** Write style manager's configuration (if exists). To be called by subclasses. */
538+
void writeStyleManager( QDomNode& layerNode, QDomDocument& doc ) const;
539+
520540
/** debugging member - invoked when a connect() is made to this object */
521541
void connectNotify( const char * signal );
522542

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
2+
class QgsMapLayerStyle
3+
{
4+
%TypeHeaderCode
5+
#include <qgsmaplayerstylemanager.h>
6+
%End
7+
public:
8+
//! construct invalid style
9+
QgsMapLayerStyle();
10+
11+
//! Tell whether the style is valid (i.e. there is something stored in it)
12+
bool isValid() const;
13+
14+
//! Return information about the style - for debugging purposes only
15+
QString dump() const;
16+
17+
//! Store layer's active style information in the instance
18+
void readFromLayer( QgsMapLayer* layer );
19+
//! Apply stored layer's style information to the layer
20+
void writeToLayer( QgsMapLayer* layer ) const;
21+
22+
//! Read style configuration (for project file reading)
23+
void readXml( const QDomElement& styleElement );
24+
//! Write style configuration (for project file writing)
25+
void writeXml( QDomElement& styleElement ) const;
26+
};
27+
28+
29+
class QgsMapLayerStyleManager
30+
{
31+
%TypeHeaderCode
32+
#include <qgsmaplayerstylemanager.h>
33+
%End
34+
public:
35+
//! Construct a style manager associated with a map layer (must not be null)
36+
QgsMapLayerStyleManager( QgsMapLayer* layer );
37+
38+
//! Read configuration (for project loading)
39+
void readXml( const QDomElement& mgrElement );
40+
//! Write configuration (for project saving)
41+
void writeXml( QDomElement& mgrElement ) const;
42+
43+
//! Return list of all defined style names
44+
QStringList styles() const;
45+
//! Return data of a stored style - accessed by its unique name
46+
QgsMapLayerStyle style( const QString& name ) const;
47+
48+
//! Add a style with given name and data
49+
//! @return true on success (name is unique and style is valid)
50+
bool addStyle( const QString& name, const QgsMapLayerStyle& style );
51+
//! Add style by cloning the current one
52+
//! @return true on success
53+
bool addStyleFromLayer( const QString& name );
54+
//! Remove a stored style
55+
//! @return true on success (style exists and it is not the last one)
56+
bool removeStyle( const QString& name );
57+
58+
//! Return name of the current style
59+
QString currentStyle() const;
60+
//! Set a different style as the current style - will apply it to the layer
61+
//! @return true on success
62+
bool setCurrentStyle( const QString& name );
63+
};

src/app/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ SET(QGIS_APP_SRCS
4545
qgslabelinggui.cpp
4646
qgslabelpreview.cpp
4747
qgsloadstylefromdbdialog.cpp
48+
qgsmaplayerstyleguiutils.cpp
4849
qgsmapmouseevent.cpp
4950
qgssavestyletodbdialog.cpp
5051
qgsguivectorlayertools.cpp
@@ -200,6 +201,7 @@ SET (QGIS_APP_MOC_HDRS
200201
qgslabelinggui.h
201202
qgslabelpropertydialog.h
202203
qgsloadstylefromdbdialog.h
204+
qgsmaplayerstyleguiutils.h
203205
qgssavestyletodbdialog.h
204206
qgsshortcutsmanager.h
205207
qgsapplayertreeviewmenuprovider.h

src/app/qgisapp.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@
149149
#include "qgsmapcanvas.h"
150150
#include "qgsmaplayer.h"
151151
#include "qgsmaplayerregistry.h"
152+
#include "qgsmaplayerstyleguiutils.h"
152153
#include "qgsmapoverviewcanvas.h"
153154
#include "qgsmaprenderer.h"
154155
#include "qgsmapsettings.h"
@@ -897,6 +898,8 @@ QgisApp::~QgisApp()
897898
delete QgsProject::instance();
898899

899900
delete mPythonUtils;
901+
902+
QgsMapLayerStyleGuiUtils::cleanup();
900903
}
901904

902905
void QgisApp::dragEnterEvent( QDragEnterEvent *event )

src/app/qgsapplayertreeviewmenuprovider.cpp

+16-10
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "qgslayertree.h"
88
#include "qgslayertreemodel.h"
99
#include "qgslayertreeviewdefaultactions.h"
10+
#include "qgsmaplayerstyleguiutils.h"
1011
#include "qgsproject.h"
1112
#include "qgsrasterlayer.h"
1213
#include "qgsvectordataprovider.h"
@@ -87,6 +88,21 @@ QMenu* QgsAppLayerTreeViewMenuProvider::createContextMenu()
8788
// assign layer crs to project
8889
menu->addAction( QgsApplication::getThemeIcon( "/mActionSetProjectCRS.png" ), tr( "Set &Project CRS from Layer" ), QgisApp::instance(), SLOT( setProjectCRSFromLayer() ) );
8990

91+
// style-related actions
92+
if ( mView->selectedLayerNodes().count() == 1 )
93+
{
94+
QMenu* menuStyleManager = QgsMapLayerStyleGuiUtils::instance()->createStyleManagerMenu( layer );
95+
96+
QgisApp* app = QgisApp::instance();
97+
menuStyleManager->addSeparator();
98+
menuStyleManager->addAction( tr( "Copy Style" ), app, SLOT( copyStyle() ) );
99+
if ( app->clipboard()->hasFormat( QGSCLIPBOARD_STYLE_MIME ) )
100+
{
101+
menuStyleManager->addAction( tr( "Paste Style" ), app, SLOT( pasteStyle() ) );
102+
}
103+
menu->addMenu( menuStyleManager );
104+
}
105+
90106
menu->addSeparator();
91107

92108
if ( layer && layer->type() == QgsMapLayer::VectorLayer )
@@ -157,16 +173,6 @@ QMenu* QgsAppLayerTreeViewMenuProvider::createContextMenu()
157173

158174
if ( mView->selectedNodes( true ).count() >= 2 )
159175
menu->addAction( actions->actionGroupSelected( menu ) );
160-
161-
if ( mView->selectedLayerNodes().count() == 1 )
162-
{
163-
QgisApp* app = QgisApp::instance();
164-
menu->addAction( tr( "Copy Style" ), app, SLOT( copyStyle() ) );
165-
if ( app->clipboard()->hasFormat( QGSCLIPBOARD_STYLE_MIME ) )
166-
{
167-
menu->addAction( tr( "Paste Style" ), app, SLOT( pasteStyle() ) );
168-
}
169-
}
170176
}
171177

172178
}

src/app/qgsapplayertreeviewmenuprovider.h

-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ class QgsAppLayerTreeViewMenuProvider : public QObject, public QgsLayerTreeViewM
3636
void removeLegendLayerActionsForLayer( QgsMapLayer* layer );
3737
QList< LegendLayerAction > legendLayerActions( QgsMapLayer::LayerType type ) const;
3838

39-
4039
protected:
4140

4241
void addCustomLayerActions( QMenu* menu, QgsMapLayer* layer );

src/app/qgsmaplayerstyleguiutils.cpp

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/***************************************************************************
2+
qgsmaplayerstyleguiutils.cpp
3+
--------------------------------------
4+
Date : January 2015
5+
Copyright : (C) 2015 by Martin Dobias
6+
Email : wonder dot sk at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgsmaplayerstyleguiutils.h"
17+
18+
#include <QAction>
19+
#include <QInputDialog>
20+
#include <QMenu>
21+
22+
#include "qgslogger.h"
23+
#include "qgsmapcanvas.h"
24+
#include "qgsmaplayer.h"
25+
#include "qgsmaplayerstylemanager.h"
26+
27+
28+
QMenu* QgsMapLayerStyleGuiUtils::createStyleManagerMenu( QgsMapLayer* layer )
29+
{
30+
QMenu* m = new QMenu( tr( "Styles" ) );
31+
QAction* actionAdd = m->addAction( tr( "Add..." ), this, SLOT( addStyle() ) );
32+
actionAdd->setData( QVariant::fromValue<QObject*>( layer ) );
33+
34+
QgsMapLayerStyleManager* mgr = layer->styleManager();
35+
36+
if ( !mgr )
37+
return m;
38+
39+
QMenu* mRemove = m->addMenu( tr( "Remove" ) );
40+
m->addSeparator();
41+
42+
foreach ( QString name, mgr->styles() )
43+
{
44+
bool active = name == mgr->currentStyle();
45+
if ( name.isEmpty() )
46+
name = defaultStyleName();
47+
QAction* actionUse = m->addAction( name, this, SLOT( useStyle() ) );
48+
actionUse->setCheckable( true );
49+
actionUse->setChecked( active );
50+
actionUse->setData( QVariant::fromValue<QObject*>( layer ) );
51+
52+
QAction* actionRemove = mRemove->addAction( name, this, SLOT( removeStyle() ) );
53+
actionRemove->setData( QVariant::fromValue<QObject*>( layer ) );
54+
}
55+
56+
return m;
57+
}
58+
59+
QString QgsMapLayerStyleGuiUtils::defaultStyleName()
60+
{
61+
return tr( "(default)" );
62+
}
63+
64+
65+
void QgsMapLayerStyleGuiUtils::addStyle()
66+
{
67+
QAction* a = qobject_cast<QAction*>( sender() );
68+
if ( !a )
69+
return;
70+
QgsMapLayer* layer = qobject_cast<QgsMapLayer*>( a->data().value<QObject*>() );
71+
if ( !layer )
72+
return;
73+
74+
bool ok;
75+
QString text = QInputDialog::getText( 0, tr( "New style" ),
76+
tr( "Style name:" ), QLineEdit::Normal,
77+
"new style", &ok );
78+
if ( !ok || text.isEmpty() )
79+
return;
80+
81+
layer->enableStyleManager(); // make sure it exists
82+
83+
bool res = layer->styleManager()->addStyleFromLayer( text );
84+
85+
if ( res ) // make it active!
86+
layer->styleManager()->setCurrentStyle( text );
87+
else
88+
QgsDebugMsg( "Failed to add style: " + text );
89+
}
90+
91+
void QgsMapLayerStyleGuiUtils::useStyle()
92+
{
93+
QAction* a = qobject_cast<QAction*>( sender() );
94+
if ( !a )
95+
return;
96+
QgsMapLayer* layer = qobject_cast<QgsMapLayer*>( a->data().value<QObject*>() );
97+
if ( !layer )
98+
return;
99+
QString name = a->text();
100+
if ( name == defaultStyleName() )
101+
name.clear();
102+
103+
bool res = layer->styleManager()->setCurrentStyle( name );
104+
if ( !res )
105+
QgsDebugMsg( "Failed to set current style: " + name );
106+
107+
layer->triggerRepaint();
108+
}
109+
110+
111+
void QgsMapLayerStyleGuiUtils::removeStyle()
112+
{
113+
QAction* a = qobject_cast<QAction*>( sender() );
114+
if ( !a )
115+
return;
116+
QgsMapLayer* layer = qobject_cast<QgsMapLayer*>( a->data().value<QObject*>() );
117+
if ( !layer )
118+
return;
119+
120+
if ( layer->styleManager()->styles().count() == 1 )
121+
{
122+
// let's get rid of the style manager altogether
123+
layer->enableStyleManager( false );
124+
return;
125+
}
126+
127+
QString name = a->text();
128+
if ( name == defaultStyleName() )
129+
name.clear();
130+
131+
bool needsRefresh = ( layer->styleManager()->currentStyle() == name );
132+
133+
bool res = layer->styleManager()->removeStyle( name );
134+
if ( !res )
135+
QgsDebugMsg( "Failed to remove style: " + name );
136+
137+
if ( needsRefresh )
138+
layer->triggerRepaint();
139+
}

src/app/qgsmaplayerstyleguiutils.h

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/***************************************************************************
2+
qgsmaplayerstyleguiutils.h
3+
--------------------------------------
4+
Date : January 2015
5+
Copyright : (C) 2015 by Martin Dobias
6+
Email : wonder dot sk at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSMAPLAYERSTYLEGUIUTILS_H
17+
#define QGSMAPLAYERSTYLEGUIUTILS_H
18+
19+
#include <QObject>
20+
21+
#include "qgssingleton.h"
22+
23+
class QgsMapLayer;
24+
class QMenu;
25+
26+
/** Various GUI utility functions for dealing with map layer's style manager */
27+
class QgsMapLayerStyleGuiUtils : public QObject, public QgsSingleton<QgsMapLayerStyleGuiUtils>
28+
{
29+
Q_OBJECT
30+
public:
31+
32+
//! Return menu instance with actions for the give map layer
33+
QMenu* createStyleManagerMenu( QgsMapLayer* layer );
34+
35+
private:
36+
QString defaultStyleName();
37+
38+
private slots:
39+
void addStyle();
40+
void useStyle();
41+
void removeStyle();
42+
43+
};
44+
45+
#endif // QGSMAPLAYERSTYLEGUIUTILS_H

src/core/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ SET(QGIS_CORE_SRCS
106106
qgsmaplayer.cpp
107107
qgsmaplayerlegend.cpp
108108
qgsmaplayerregistry.cpp
109+
qgsmaplayerstylemanager.cpp
109110
qgsmaprenderer.cpp
110111
qgsmaprenderercache.cpp
111112
qgsmaprenderercustompainterjob.cpp
@@ -495,6 +496,7 @@ SET(QGIS_CORE_HDRS
495496
qgsmaplayer.h
496497
qgsmaplayerlegend.h
497498
qgsmaplayerregistry.h
499+
qgsmaplayerstylemanager.h
498500
qgsmaprenderer.h
499501
qgsmaprenderercache.h
500502
qgsmaprenderercustompainterjob.h

0 commit comments

Comments
 (0)