Skip to content

Commit

Permalink
[FEATURE] Edit legend symbols directly from layer tree
Browse files Browse the repository at this point in the history
This adds a new "edit symbol" item to the right-click menu for a
renderer child legend item (eg categories for the categorised
renderer). Selecting it opens a symbol editor dialog which allows
for directly editing the classes symbol. It's much faster than
opening the layer properties and going through the style tab.
  • Loading branch information
nyalldawson committed Dec 8, 2015
1 parent 83e139a commit 6e140b9
Show file tree
Hide file tree
Showing 20 changed files with 265 additions and 2 deletions.
14 changes: 14 additions & 0 deletions python/core/layertree/qgslayertreemodellegendnode.sip
Expand Up @@ -145,6 +145,20 @@ class QgsSymbolV2LegendNode : QgsLayerTreeModelLegendNode
//! @note added in 2.10
QSize minimumIconSize() const;

/** Returns the symbol used by the legend node.
* @see setSymbol()
* @note added in QGIS 2.14
*/
const QgsSymbolV2* symbol() const;

/** Sets the symbol to be used by the legend node. The symbol change is also propagated
* to the associated vector layer's renderer.
* @param symbol new symbol for node. Ownership is transferred.
* @see symbol()
* @note added in QGIS 2.14
*/
void setSymbol( QgsSymbolV2* symbol /Transfer/ );

public slots:

/** Checks all items belonging to the same layer as this node.
Expand Down
2 changes: 2 additions & 0 deletions python/core/symbology-ng/qgscategorizedsymbolrendererv2.sip
Expand Up @@ -155,6 +155,8 @@ class QgsCategorizedSymbolRendererV2 : QgsFeatureRendererV2
// @note added in 2.5
virtual bool legendSymbolItemChecked( const QString& key );

virtual void setLegendSymbolItem( const QString& key, QgsSymbolV2* symbol /Transfer/ );

//! item in symbology was checked
// @note added in 2.5
virtual void checkLegendSymbolItem( const QString& key, bool state = true );
Expand Down
2 changes: 2 additions & 0 deletions python/core/symbology-ng/qgsgraduatedsymbolrendererv2.sip
Expand Up @@ -282,6 +282,8 @@ class QgsGraduatedSymbolRendererV2 : QgsFeatureRendererV2
//! @note added in 2.6
virtual void checkLegendSymbolItem( const QString& key, bool state = true );

virtual void setLegendSymbolItem( const QString& key, QgsSymbolV2* symbol /Transfer/ );

//! If supported by the renderer, return classification attribute for the use in legend
//! @note added in 2.6
virtual QString legendClassificationAttribute();
Expand Down
3 changes: 3 additions & 0 deletions python/core/symbology-ng/qgslegendsymbolitemv2.sip
Expand Up @@ -48,6 +48,9 @@ class QgsLegendSymbolItemV2
//! Key of the parent legend node. For legends with tree hierarchy
//! @note added in 2.8
QString parentRuleKey() const;

//! Set symbol of the item. Takes ownership of symbol.
void setSymbol( QgsSymbolV2* s /Transfer/ );
};


Expand Down
7 changes: 7 additions & 0 deletions python/core/symbology-ng/qgsrendererv2.sip
Expand Up @@ -200,6 +200,13 @@ class QgsFeatureRendererV2
//! @note added in 2.5
virtual void checkLegendSymbolItem( const QString& key, bool state = true );

/** Sets the symbol to be used for a legend symbol item.
* @param key rule key for legend symbol
* @param symbol new symbol for legend item. Ownership is transferred to renderer.
* @note added in QGIS 2.14
*/
virtual void setLegendSymbolItem( const QString& key, QgsSymbolV2* symbol /Transfer/ );

//! return a list of item text / symbol
//! @note not available in python bindings
// virtual QgsLegendSymbolList legendSymbolItems( double scaleDenominator = -1, const QString& rule = "" );
Expand Down
2 changes: 2 additions & 0 deletions python/core/symbology-ng/qgsrulebasedrendererv2.sip
Expand Up @@ -358,6 +358,8 @@ class QgsRuleBasedRendererV2 : QgsFeatureRendererV2
//! @note added in 2.5
virtual void checkLegendSymbolItem( const QString& key, bool state = true );

virtual void setLegendSymbolItem( const QString& key, QgsSymbolV2* symbol /Transfer/ );

//! return a list of item text / symbol
//! @note not available in python bindings
// virtual QgsLegendSymbolList legendSymbolItems();
Expand Down
41 changes: 40 additions & 1 deletion src/app/qgsapplayertreeviewmenuprovider.cpp
Expand Up @@ -9,12 +9,16 @@
#include "qgslayertreemodellegendnode.h"
#include "qgslayertreeviewdefaultactions.h"
#include "qgsmaplayerstyleguiutils.h"
#include "qgsmaplayerregistry.h"
#include "qgsproject.h"
#include "qgsrasterlayer.h"
#include "qgsrendererv2.h"
#include "qgssymbolv2.h"
#include "qgsstylev2.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorlayer.h"
#include "qgslayertreeregistrybridge.h"

#include "qgssymbolv2selectordialog.h"

QgsAppLayerTreeViewMenuProvider::QgsAppLayerTreeViewMenuProvider( QgsLayerTreeView* view, QgsMapCanvas* canvas )
: mView( view )
Expand Down Expand Up @@ -200,6 +204,14 @@ QMenu* QgsAppLayerTreeViewMenuProvider::createContextMenu()
symbolNode, SLOT( checkAllItems() ) );
menu->addAction( QgsApplication::getThemeIcon( "/mActionHideAllLayers.png" ), tr( "&Hide All Items" ),
symbolNode, SLOT( uncheckAllItems() ) );
menu->addSeparator();
QAction* editSymbolAction = new QAction( tr( "Edit Symbol..." ), menu );
//store the layer id and rule key in action, so we can later retrieve the corresponding
//legend node, if it still exists
editSymbolAction->setProperty( "layerId", symbolNode->layerNode()->layerId() );
editSymbolAction->setProperty( "ruleKey", symbolNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() );
connect( editSymbolAction, SIGNAL( triggered() ), this, SLOT( editSymbolLegendNodeSymbol() ) );
menu->addAction( editSymbolAction );
}
}
}
Expand Down Expand Up @@ -352,3 +364,30 @@ void QgsAppLayerTreeViewMenuProvider::addCustomLayerActions( QMenu* menu, QgsMap
menu->addSeparator();
}
}

void QgsAppLayerTreeViewMenuProvider::editSymbolLegendNodeSymbol()
{
QAction* action = qobject_cast< QAction*>( sender() );
if ( !action )
return;

QString layerId = action->property( "layerId" ).toString();
QString ruleKey = action->property( "ruleKey" ).toString();

QgsSymbolV2LegendNode* node = dynamic_cast<QgsSymbolV2LegendNode*>( mView->layerTreeModel()->findLegendNode( layerId, ruleKey ) );
if ( !node )
return;

const QgsSymbolV2* originalSymbol = node->symbol();
if ( !originalSymbol )
return;

QScopedPointer< QgsSymbolV2 > symbol( originalSymbol->clone() );
QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer*>( node->layerNode()->layer() );
QgsSymbolV2SelectorDialog dlg( symbol.data(), QgsStyleV2::defaultStyle(), vlayer, mView );
dlg.setMapCanvas( mCanvas );
if ( dlg.exec() )
{
node->setSymbol( symbol.take() );
}
}
4 changes: 4 additions & 0 deletions src/app/qgsapplayertreeviewmenuprovider.h
Expand Up @@ -44,6 +44,10 @@ class QgsAppLayerTreeViewMenuProvider : public QObject, public QgsLayerTreeViewM
QgsMapCanvas* mCanvas;

QMap< QgsMapLayer::LayerType, QList< LegendLayerAction > > mLegendLayerActionMap;

private slots:

void editSymbolLegendNodeSymbol();
};

#endif // QGSAPPLAYERTREEVIEWMENUPROVIDER_H
23 changes: 23 additions & 0 deletions src/core/layertree/qgslayertreemodellegendnode.cpp
Expand Up @@ -185,6 +185,29 @@ QSize QgsSymbolV2LegendNode::minimumIconSize() const
return minSz;
}

const QgsSymbolV2* QgsSymbolV2LegendNode::symbol() const
{
return mItem.symbol();
}

void QgsSymbolV2LegendNode::setSymbol( QgsSymbolV2* symbol )
{
if ( !symbol )
return;

QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer*>( mLayerNode->layer() );
if ( !vlayer || !vlayer->rendererV2() )
return;

mItem.setSymbol( symbol );
vlayer->rendererV2()->setLegendSymbolItem( mItem.ruleKey(), symbol->clone() );

mPixmap = QPixmap();

emit dataChanged();
vlayer->triggerRepaint();
}

void QgsSymbolV2LegendNode::checkAllItems()
{
checkAll( true );
Expand Down
14 changes: 14 additions & 0 deletions src/core/layertree/qgslayertreemodellegendnode.h
Expand Up @@ -175,6 +175,20 @@ class CORE_EXPORT QgsSymbolV2LegendNode : public QgsLayerTreeModelLegendNode
//! @note added in 2.10
QSize minimumIconSize() const;

/** Returns the symbol used by the legend node.
* @see setSymbol()
* @note added in QGIS 2.14
*/
const QgsSymbolV2* symbol() const;

/** Sets the symbol to be used by the legend node. The symbol change is also propagated
* to the associated vector layer's renderer.
* @param symbol new symbol for node. Ownership is transferred.
* @see symbol()
* @note added in QGIS 2.14
*/
void setSymbol( QgsSymbolV2* symbol );

public slots:

/** Checks all items belonging to the same layer as this node.
Expand Down
10 changes: 10 additions & 0 deletions src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp
Expand Up @@ -954,6 +954,16 @@ bool QgsCategorizedSymbolRendererV2::legendSymbolItemChecked( const QString& key
return true;
}

void QgsCategorizedSymbolRendererV2::setLegendSymbolItem( const QString& key, QgsSymbolV2* symbol )
{
bool ok;
int index = key.toInt( &ok );
if ( ok )
updateCategorySymbol( index, symbol );
else
delete symbol;
}

void QgsCategorizedSymbolRendererV2::checkLegendSymbolItem( const QString& key, bool state )
{
bool ok;
Expand Down
2 changes: 2 additions & 0 deletions src/core/symbology-ng/qgscategorizedsymbolrendererv2.h
Expand Up @@ -186,6 +186,8 @@ class CORE_EXPORT QgsCategorizedSymbolRendererV2 : public QgsFeatureRendererV2
// @note added in 2.5
virtual bool legendSymbolItemChecked( const QString& key ) override;

virtual void setLegendSymbolItem( const QString& key, QgsSymbolV2* symbol ) override;

//! item in symbology was checked
// @note added in 2.5
virtual void checkLegendSymbolItem( const QString& key, bool state = true ) override;
Expand Down
9 changes: 9 additions & 0 deletions src/core/symbology-ng/qgsgraduatedsymbolrendererv2.cpp
Expand Up @@ -1433,6 +1433,15 @@ void QgsGraduatedSymbolRendererV2::checkLegendSymbolItem( const QString& key, bo
updateRangeRenderState( index, state );
}

void QgsGraduatedSymbolRendererV2::setLegendSymbolItem( const QString& key, QgsSymbolV2* symbol )
{
bool ok;
int index = key.toInt( &ok );
if ( ok )
updateRangeSymbol( index, symbol );
else
delete symbol;
}

void QgsGraduatedSymbolRendererV2::addClass( QgsSymbolV2* symbol )
{
Expand Down
2 changes: 2 additions & 0 deletions src/core/symbology-ng/qgsgraduatedsymbolrendererv2.h
Expand Up @@ -325,6 +325,8 @@ class CORE_EXPORT QgsGraduatedSymbolRendererV2 : public QgsFeatureRendererV2
//! @note added in 2.6
virtual void checkLegendSymbolItem( const QString& key, bool state = true ) override;

virtual void setLegendSymbolItem( const QString& key, QgsSymbolV2* symbol ) override;

//! If supported by the renderer, return classification attribute for the use in legend
//! @note added in 2.6
virtual QString legendClassificationAttribute() const override { return classAttribute(); }
Expand Down
1 change: 0 additions & 1 deletion src/core/symbology-ng/qgslegendsymbolitemv2.h
Expand Up @@ -66,7 +66,6 @@ class CORE_EXPORT QgsLegendSymbolItemV2
//! @note added in 2.8
QString parentRuleKey() const { return mParentKey; }

protected:
//! Set symbol of the item. Takes ownership of symbol.
void setSymbol( QgsSymbolV2* s );

Expand Down
6 changes: 6 additions & 0 deletions src/core/symbology-ng/qgsrendererv2.cpp
Expand Up @@ -671,6 +671,12 @@ void QgsFeatureRendererV2::checkLegendSymbolItem( const QString& key, bool state
Q_UNUSED( state );
}

void QgsFeatureRendererV2::setLegendSymbolItem( const QString& key, QgsSymbolV2* symbol )
{
Q_UNUSED( key );
delete symbol;
}

QgsLegendSymbolList QgsFeatureRendererV2::legendSymbolItems( double scaleDenominator, const QString& rule )
{
Q_UNUSED( scaleDenominator );
Expand Down
7 changes: 7 additions & 0 deletions src/core/symbology-ng/qgsrendererv2.h
Expand Up @@ -228,6 +228,13 @@ class CORE_EXPORT QgsFeatureRendererV2
//! @note added in 2.5
virtual void checkLegendSymbolItem( const QString& key, bool state = true );

/** Sets the symbol to be used for a legend symbol item.
* @param key rule key for legend symbol
* @param symbol new symbol for legend item. Ownership is transferred to renderer.
* @note added in QGIS 2.14
*/
virtual void setLegendSymbolItem( const QString& key, QgsSymbolV2* symbol );

//! return a list of item text / symbol
//! @note not available in python bindings
virtual QgsLegendSymbolList legendSymbolItems( double scaleDenominator = -1, const QString& rule = "" );
Expand Down
9 changes: 9 additions & 0 deletions src/core/symbology-ng/qgsrulebasedrendererv2.cpp
Expand Up @@ -990,6 +990,15 @@ void QgsRuleBasedRendererV2::checkLegendSymbolItem( const QString& key, bool sta
rule->setActive( state );
}

void QgsRuleBasedRendererV2::setLegendSymbolItem( const QString& key, QgsSymbolV2* symbol )
{
Rule* rule = mRootRule->findRuleByKey( key );
if ( rule )
rule->setSymbol( symbol );
else
delete symbol;
}

QgsLegendSymbolList QgsRuleBasedRendererV2::legendSymbolItems( double scaleDenominator, const QString& rule )
{
return mRootRule->legendSymbolItems( scaleDenominator, rule );
Expand Down
2 changes: 2 additions & 0 deletions src/core/symbology-ng/qgsrulebasedrendererv2.h
Expand Up @@ -418,6 +418,8 @@ class CORE_EXPORT QgsRuleBasedRendererV2 : public QgsFeatureRendererV2
//! @note added in 2.5
virtual void checkLegendSymbolItem( const QString& key, bool state = true ) override;

virtual void setLegendSymbolItem( const QString& key, QgsSymbolV2* symbol ) override;

//! return a list of item text / symbol
//! @note not available in python bindings
virtual QgsLegendSymbolList legendSymbolItems( double scaleDenominator = -1, const QString& rule = "" ) override;
Expand Down

1 comment on commit 6e140b9

@slarosa
Copy link
Member

@slarosa slarosa commented on 6e140b9 Dec 8, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice. thanks.

Please sign in to comment.