[FEATURE] Add a show/hide all context menu for layer tree symbol items

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
This commit is contained in:
Nyall Dawson 2015-12-07 07:39:20 +11:00
parent dc73fb2a09
commit 91f7918e21
5 changed files with 132 additions and 2 deletions

View File

@ -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();
};

View File

@ -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"
@ -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;

View File

@ -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
{
@ -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 )

View File

@ -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();
@ -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();
@ -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 );
};

View File

@ -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
{
@ -34,6 +37,7 @@ class TestQgsLayerTree : public QObject
void testCheckStateChildToParent();
void testCheckStateMutuallyExclusive();
void testCheckStateMutuallyExclusiveEdgeCases();
void testShowHideAllSymbolNodes();
private:
@ -51,6 +55,9 @@ class TestQgsLayerTree : public QObject
void TestQgsLayerTree::initTestCase()
{
QgsApplication::init();
QgsApplication::initQgis();
mRoot = new QgsLayerTreeGroup();
mRoot->addGroup( "grp1" );
mRoot->addGroup( "grp2" );
@ -62,6 +69,7 @@ void TestQgsLayerTree::initTestCase()
void TestQgsLayerTree::cleanupTestCase()
{
delete mRoot;
QgsApplication::exitQgis();
}
void TestQgsLayerTree::testCheckStateParentToChild()
@ -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"