Skip to content

Commit

Permalink
[FEATURE] Add a show/hide all context menu for layer tree symbol items
Browse files Browse the repository at this point in the history
Allows toggling on/off all the symbol items for categorized/graduated/
rule based layers via the right click menu on an item. Previously
you'd have to manually toggle each item one-by-one.

Fix #13458
  • Loading branch information
nyalldawson committed Dec 6, 2015
1 parent dc73fb2 commit 91f7918
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 2 deletions.
15 changes: 15 additions & 0 deletions python/core/layertree/qgslayertreemodellegendnode.sip
Expand Up @@ -144,6 +144,21 @@ class QgsSymbolV2LegendNode : QgsLayerTreeModelLegendNode
//! Get the minimum icon size to prevent cropping
//! @note added in 2.10
QSize minimumIconSize() const;

public slots:

/** Checks all items belonging to the same layer as this node.
* @note added in QGIS 2.14
* @see uncheckAllItems()
*/
void checkAllItems();

/** Unchecks all items belonging to the same layer as this node.
* @note added in QGIS 2.14
* @see checkAllItems()
*/
void uncheckAllItems();

};


Expand Down
15 changes: 13 additions & 2 deletions src/app/qgsapplayertreeviewmenuprovider.cpp
Expand Up @@ -6,6 +6,7 @@
#include "qgsclipboard.h"
#include "qgslayertree.h"
#include "qgslayertreemodel.h"
#include "qgslayertreemodellegendnode.h"
#include "qgslayertreeviewdefaultactions.h"
#include "qgsmaplayerstyleguiutils.h"
#include "qgsproject.h"
Expand Down Expand Up @@ -188,9 +189,19 @@ QMenu* QgsAppLayerTreeViewMenuProvider::createContextMenu()
}

}
else
else if ( QgsLayerTreeModelLegendNode* node = mView->layerTreeModel()->index2legendNode( idx ) )
{
// symbology item?
if ( QgsSymbolV2LegendNode* symbolNode = dynamic_cast< QgsSymbolV2LegendNode* >( node ) )
{
// symbology item
if ( symbolNode->flags() & Qt::ItemIsUserCheckable )
{
menu->addAction( QgsApplication::getThemeIcon( "/mActionShowAllLayers.png" ), tr( "&Show All Items" ),
symbolNode, SLOT( checkAllItems() ) );
menu->addAction( QgsApplication::getThemeIcon( "/mActionHideAllLayers.png" ), tr( "&Hide All Items" ),
symbolNode, SLOT( uncheckAllItems() ) );
}
}
}

return menu;
Expand Down
26 changes: 26 additions & 0 deletions src/core/layertree/qgslayertreemodellegendnode.cpp
Expand Up @@ -185,6 +185,16 @@ QSize QgsSymbolV2LegendNode::minimumIconSize() const
return minSz;
}

void QgsSymbolV2LegendNode::checkAllItems()
{
checkAll( true );
}

void QgsSymbolV2LegendNode::uncheckAllItems()
{
checkAll( false );
}

inline
QgsRenderContext * QgsSymbolV2LegendNode::createTemporaryRenderContext() const
{
Expand All @@ -203,6 +213,22 @@ QgsRenderContext * QgsSymbolV2LegendNode::createTemporaryRenderContext() const
return validData ? context.take() : 0;
}

void QgsSymbolV2LegendNode::checkAll( bool state )
{
QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer*>( mLayerNode->layer() );
if ( !vlayer || !vlayer->rendererV2() )
return;

QgsLegendSymbolListV2 symbolList = vlayer->rendererV2()->legendSymbolItemsV2();
Q_FOREACH ( const QgsLegendSymbolItemV2& item, symbolList )
{
vlayer->rendererV2()->checkLegendSymbolItem( item.ruleKey(), state );
}

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

QVariant QgsSymbolV2LegendNode::data( int role ) const
{
if ( role == Qt::DisplayRole )
Expand Down
20 changes: 20 additions & 0 deletions src/core/layertree/qgslayertreemodellegendnode.h
Expand Up @@ -145,6 +145,8 @@ class CORE_EXPORT QgsLayerTreeModelLegendNode : public QObject
*/
class CORE_EXPORT QgsSymbolV2LegendNode : public QgsLayerTreeModelLegendNode
{
Q_OBJECT

public:
QgsSymbolV2LegendNode( QgsLayerTreeLayer* nodeLayer, const QgsLegendSymbolItemV2& item, QObject* parent = 0 );
~QgsSymbolV2LegendNode();
Expand Down Expand Up @@ -173,6 +175,20 @@ class CORE_EXPORT QgsSymbolV2LegendNode : public QgsLayerTreeModelLegendNode
//! @note added in 2.10
QSize minimumIconSize() const;

public slots:

/** Checks all items belonging to the same layer as this node.
* @note added in QGIS 2.14
* @see uncheckAllItems()
*/
void checkAllItems();

/** Unchecks all items belonging to the same layer as this node.
* @note added in QGIS 2.14
* @see checkAllItems()
*/
void uncheckAllItems();

private:
void updateLabel();

Expand All @@ -189,6 +205,10 @@ class CORE_EXPORT QgsSymbolV2LegendNode : public QgsLayerTreeModelLegendNode
// return a temporary context or null if legendMapViewData are not valid
QgsRenderContext * createTemporaryRenderContext() const;

/** Sets all items belonging to the same layer as this node to the same check state.
* @param state check state
*/
void checkAll( bool state );
};


Expand Down
58 changes: 58 additions & 0 deletions tests/src/core/testqgslayertree.cpp
Expand Up @@ -21,6 +21,9 @@
#include <qgsvectorlayer.h>
#include <qgsvectorlayerdiagramprovider.h>
#include <qgsvectorlayerlabelprovider.h>
#include <qgscategorizedsymbolrendererv2.h>
#include <qgslayertreemodel.h>
#include <qgslayertreemodellegendnode.h>

class TestQgsLayerTree : public QObject
{
Expand All @@ -34,6 +37,7 @@ class TestQgsLayerTree : public QObject
void testCheckStateChildToParent();
void testCheckStateMutuallyExclusive();
void testCheckStateMutuallyExclusiveEdgeCases();
void testShowHideAllSymbolNodes();

private:

Expand All @@ -51,6 +55,9 @@ class TestQgsLayerTree : public QObject

void TestQgsLayerTree::initTestCase()
{
QgsApplication::init();
QgsApplication::initQgis();

mRoot = new QgsLayerTreeGroup();
mRoot->addGroup( "grp1" );
mRoot->addGroup( "grp2" );
Expand All @@ -62,6 +69,7 @@ void TestQgsLayerTree::initTestCase()
void TestQgsLayerTree::cleanupTestCase()
{
delete mRoot;
QgsApplication::exitQgis();
}

void TestQgsLayerTree::testCheckStateParentToChild()
Expand Down Expand Up @@ -210,6 +218,56 @@ void TestQgsLayerTree::testCheckStateMutuallyExclusiveEdgeCases()
delete root3;
}

void TestQgsLayerTree::testShowHideAllSymbolNodes()
{
//new memory layer
QgsVectorLayer* vl = new QgsVectorLayer( "Point?field=col1:integer", "vl", "memory" );
QVERIFY( vl->isValid() );

QgsMapLayerRegistry::instance()->addMapLayers( QList<QgsMapLayer*>() << vl );

//create a categorized renderer for layer
QgsCategorizedSymbolRendererV2* renderer = new QgsCategorizedSymbolRendererV2();
renderer->setClassAttribute( "col1" );
renderer->setSourceSymbol( QgsSymbolV2::defaultSymbol( QGis::Point ) );
renderer->addCategory( QgsRendererCategoryV2( "a", QgsSymbolV2::defaultSymbol( QGis::Point ), "a" ) );
renderer->addCategory( QgsRendererCategoryV2( "b", QgsSymbolV2::defaultSymbol( QGis::Point ), "b" ) );
renderer->addCategory( QgsRendererCategoryV2( "c", QgsSymbolV2::defaultSymbol( QGis::Point ), "c" ) );
vl->setRendererV2( renderer );

//create legend with symbology nodes for categorized renderer
QgsLayerTreeGroup* root = new QgsLayerTreeGroup();
QgsLayerTreeLayer* n = new QgsLayerTreeLayer( vl );
root->addChildNode( n );
QgsLayerTreeModel* m = new QgsLayerTreeModel( root, 0 );
m->refreshLayerLegend( n );

//test that all nodes are initially checked
QList<QgsLayerTreeModelLegendNode*> nodes = m->layerLegendNodes( n );
QCOMPARE( nodes.length(), 3 );
Q_FOREACH ( QgsLayerTreeModelLegendNode* ln, nodes )
{
QVERIFY( ln->data( Qt::CheckStateRole ) == Qt::Checked );
}
//uncheck all and test that all nodes are unchecked
static_cast< QgsSymbolV2LegendNode* >( nodes.at( 0 ) )->uncheckAllItems();
Q_FOREACH ( QgsLayerTreeModelLegendNode* ln, nodes )
{
QVERIFY( ln->data( Qt::CheckStateRole ) == Qt::Unchecked );
}
//check all and test that all nodes are checked
static_cast< QgsSymbolV2LegendNode* >( nodes.at( 0 ) )->checkAllItems();
Q_FOREACH ( QgsLayerTreeModelLegendNode* ln, nodes )
{
QVERIFY( ln->data( Qt::CheckStateRole ) == Qt::Checked );
}

//cleanup
delete m;
delete root;
QgsMapLayerRegistry::instance()->removeMapLayers( QList<QgsMapLayer*>() << vl );
}


QTEST_MAIN( TestQgsLayerTree )
#include "testqgslayertree.moc"

1 comment on commit 91f7918

@andreasneumann
Copy link
Member

Choose a reason for hiding this comment

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

Thanks Nyall - that's convenient!

Please sign in to comment.