Skip to content

Commit 60a1701

Browse files
committed
[FEATURE] Change of ergonomy of the visibility of layers inside groups
See qgis/QGIS-Enhancement-Proposals#86 - Checking/unchecking a group doesn't change the check state of its children. A node is visible if and only if it is checked and all its parents too. - There is no more a semi-checked state for a group - Ctrl-clic on a unchecked group will check the group and all its descendants. - Ctrl-clic on a unchecked layer will check the lager and all its parents. - Ctrl-clic on a checked group will uncheck the group and all its descendants. - Ctrl-clic on a checked layer will uncheck the layer and all its parents. - Those actions are available in contextual menu items in the tree view. - Invisible layers because they or their parent(s) is unchecked are greyed out.
1 parent 3ba2f8e commit 60a1701

32 files changed

+423
-339
lines changed

doc/api_break.dox

+13-2
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,13 @@ QgsCptCitySelectionItem {#qgis_api_break_3_0_QgsCptCitySelectionItem}
687687

688688
- parseXML() has been renamed to parseXml()
689689

690+
691+
QgsCustomLayerOrderWidget {#qgis_api_break_3_0_QgsCustomLayerOrderWidget}
692+
-------------------------
693+
694+
- the signature of the visibilityChanged() signal is changed to visibilityChanged( QgsLayerTreeNode *node )
695+
696+
690697
QgsCRSCache {#qgis_api_break_3_0_QgsCRSCache}
691698
-----------
692699

@@ -1059,12 +1066,16 @@ QgsLayerTreeGroup {#qgis_api_break_3_0_QgsLayerTreeGroup}
10591066
-----------------
10601067

10611068
- readChildrenFromXML() has been renamed to readChildrenFromXml()
1062-
1069+
- isVisible() is moved to QgsLayerTreeNode
1070+
- setVisible() is replaced by QgsLayerTreeNode::setItemVisibilityChecked()
1071+
- protected methods updateVisibilityFromChildren() and updateChildVisibility() removed
10631072

10641073
QgsLayerTreeLayer {#qgis_api_break_3_0_QgsLayerTreeLayer}
10651074
-----------------
10661075

10671076
- setLayerName(), layerName() were renamed to setName(), name()
1077+
- isVisible() is moved to QgsLayerTreeNode
1078+
- setVisible() is replaced by QgsLayerTreeNode::setItemVisibilityChecked()
10681079

10691080

10701081
QgsLayerTreeModel {#qgis_api_break_3_0_QgsLayerTreeMode}
@@ -1090,7 +1101,7 @@ QgsLayerTreeNode {#qgis_api_break_3_0_QgsLayerTreeNode}
10901101

10911102
- readCommonXML() has been renamed to readCommonXml()
10921103
- writeCommonXML() has been renamed to writeCommonXml()
1093-
1104+
- the signature of the visibilityChanged() signal is changed to visibilityChanged( QgsLayerTreeNode *node )
10941105

10951106
QgsLimitedRandomColorRampDialog {#qgis_api_break_3_0_QgsLimitedRandomRampDialog}
10961107
-------------------------------

python/core/layertree/qgslayertreegroup.sip

+1-10
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,6 @@ class QgsLayerTreeGroup : QgsLayerTreeNode
7171
//! Return a clone of the group. The children are cloned too.
7272
virtual QgsLayerTreeGroup* clone() const /Factory/;
7373

74-
//! Return the check state of the group node
75-
Qt::CheckState isVisible() const;
76-
//! Set check state of the group node - will also update children
77-
void setVisible( Qt::CheckState state );
78-
7974
//! Return whether the group is mutually exclusive (only one child can be checked at a time)
8075
//! @note added in 2.12
8176
bool isMutuallyExclusive() const;
@@ -86,14 +81,10 @@ class QgsLayerTreeGroup : QgsLayerTreeNode
8681
void setIsMutuallyExclusive( bool enabled, int initialChildIndex = -1 );
8782

8883
protected slots:
89-
void layerDestroyed();
9084
void nodeVisibilityChanged( QgsLayerTreeNode* node );
9185

9286
protected:
93-
//! Set check state of this group from its children
94-
void updateVisibilityFromChildren();
95-
//! Set check state of children (when this group's check state changes) - if not mutually exclusive
96-
void updateChildVisibility();
87+
9788
//! Set check state of children - if mutually exclusive
9889
void updateChildVisibilityMutuallyExclusive();
9990

python/core/layertree/qgslayertreelayer.sip

-3
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,6 @@ class QgsLayerTreeLayer : QgsLayerTreeNode
3838
//! @note added in 3.0
3939
void setName( const QString& n );
4040

41-
Qt::CheckState isVisible() const;
42-
void setVisible( Qt::CheckState visible );
43-
4441
static QgsLayerTreeLayer* readXml( QDomElement& element ) /Factory/;
4542
virtual void writeXml( QDomElement& parentElement );
4643

python/core/layertree/qgslayertreemodel.sip

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class QgsLayerTreeModel : QAbstractItemModel
6161
AllowNodeRename, //!< Allow renaming of groups and layers
6262
AllowNodeChangeVisibility, //!< Allow user to set node visibility with a check box
6363
AllowLegendChangeState, //!< Allow check boxes for legend nodes (if supported by layer's legend)
64+
ActionHierarchical, //!< Check/uncheck action has consequences on children (or parents for leaf node)
6465
};
6566
typedef QFlags<QgsLayerTreeModel::Flag> Flags;
6667

python/core/layertree/qgslayertreenode.sip

+29-1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,34 @@ class QgsLayerTreeNode : QObject
9494
//! Create a copy of the node. Returns new instance
9595
virtual QgsLayerTreeNode *clone() const = 0 /Factory/;
9696

97+
//! Returns whether a node is really visible (ie checked and all its ancestors checked as well)
98+
//! @note added in 3.0
99+
bool isVisible() const;
100+
101+
//! Returns whether a node is checked (independantly of its ancestors or children)
102+
//! @note added in 3.0
103+
bool itemVisibilityChecked() const;
104+
105+
//! Check or uncheck a node (independantly of its ancestors or children)
106+
//! @note added in 3.0
107+
void setItemVisibilityChecked( bool checked );
108+
109+
//! Check or uncheck a node and all its children (taking into account exclusion rules)
110+
//! @note added in 3.0
111+
virtual void setItemVisibilityCheckedRecursive( bool checked );
112+
113+
//! Check or uncheck a node and all its parents
114+
//! @note added in 3.0
115+
void setItemVisibilityCheckedParentRecursive( bool checked );
116+
117+
//! Return whether this node is checked and all its children.
118+
//! @note added in 3.0
119+
bool isItemVisibilityCheckedRecursive() const;
120+
121+
//! Return whether this node is unchecked and all its children.
122+
//! @note added in 3.0
123+
bool isItemVisibilityUncheckedRecursive() const;
124+
97125
//! Return whether the node should be shown as expanded or collapsed in GUI
98126
bool isExpanded() const;
99127
//! Set whether the node should be shown as expanded or collapsed in GUI
@@ -121,7 +149,7 @@ class QgsLayerTreeNode : QObject
121149
//! Emitted when one or more nodes has been removed from a node within the tree
122150
void removedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo );
123151
//! Emitted when check state of a node within the tree has been changed
124-
void visibilityChanged( QgsLayerTreeNode *node, Qt::CheckState state );
152+
void visibilityChanged( QgsLayerTreeNode *node );
125153
//! Emitted when a custom property of a node within the tree has been changed or removed
126154
void customPropertyChanged( QgsLayerTreeNode *node, const QString& key );
127155
//! Emitted when the collapsed/expanded state of a node within the tree has been changed

python/gui/layertree/qgscustomlayerorderwidget.sip

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ class QgsCustomLayerOrderWidget : QWidget
2020
protected slots:
2121
void bridgeHasCustomLayerOrderChanged( bool state );
2222
void bridgeCustomLayerOrderChanged( const QStringList& order );
23-
void nodeVisibilityChanged( QgsLayerTreeNode* node, Qt::CheckState state );
23+
//! Slot triggered when the ivsibility of a node changes
24+
void nodeVisibilityChanged( QgsLayerTreeNode* node );
2425

2526
void modelUpdated();
2627
};

python/gui/layertree/qgslayertreeviewdefaultactions.sip

+9
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ class QgsLayerTreeViewDefaultActions : QObject
2020
QAction* actionRenameGroupOrLayer( QObject* parent = 0 ) /Factory/;
2121
QAction* actionShowFeatureCount( QObject* parent = 0 ) /Factory/;
2222

23+
//! Action to check a group and all its children
24+
QAction* actionCheckAndAllChildren( QObject* parent = nullptr );
25+
26+
//! Action to uncheck a group and all its children
27+
QAction* actionUncheckAndAllChildren( QObject* parent = nullptr );
28+
29+
//! Action to check a group and all its parents
30+
QAction* actionCheckAndAllParents( QObject* parent = nullptr );
31+
2332
QAction* actionZoomToLayer( QgsMapCanvas* canvas, QObject* parent = 0 ) /Factory/;
2433
QAction* actionZoomToGroup( QgsMapCanvas* canvas, QObject* parent = 0 ) /Factory/;
2534
// TODO: zoom to selected

src/app/dwg/qgsdwgimportdialog.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ void QgsDwgImportDialog::createGroup( QgsLayerTreeGroup *group, QString name, QS
422422
if ( !layerGroup->children().isEmpty() )
423423
{
424424
layerGroup->setExpanded( false );
425-
layerGroup->setVisible( visible ? Qt::Checked : Qt::Unchecked );
425+
layerGroup->setItemVisibilityChecked( visible );
426426
}
427427
else
428428
{

src/app/qgisapp.cpp

+13-17
Original file line numberDiff line numberDiff line change
@@ -2808,7 +2808,7 @@ void QgisApp::setupConnections()
28082808
this, SLOT( markDirty() ) );
28092809
connect( mLayerTreeView->layerTreeModel()->rootGroup(), SIGNAL( removedChildren( QgsLayerTreeNode*, int, int ) ),
28102810
this, SLOT( updateNewLayerInsertionPoint() ) );
2811-
connect( mLayerTreeView->layerTreeModel()->rootGroup(), SIGNAL( visibilityChanged( QgsLayerTreeNode*, Qt::CheckState ) ),
2811+
connect( mLayerTreeView->layerTreeModel()->rootGroup(), SIGNAL( visibilityChanged( QgsLayerTreeNode* ) ),
28122812
this, SLOT( markDirty() ) );
28132813
connect( mLayerTreeView->layerTreeModel()->rootGroup(), SIGNAL( customPropertyChanged( QgsLayerTreeNode*, QString ) ),
28142814
this, SLOT( markDirty() ) );
@@ -5759,19 +5759,16 @@ void QgisApp::stopRendering()
57595759
void QgisApp::hideAllLayers()
57605760
{
57615761
QgsDebugMsg( "hiding all layers!" );
5762+
mLayerTreeView->layerTreeModel()->rootGroup()->setItemVisibilityCheckedRecursive( false );
57625763

5763-
Q_FOREACH ( QgsLayerTreeLayer* nodeLayer, mLayerTreeView->layerTreeModel()->rootGroup()->findLayers() )
5764-
nodeLayer->setVisible( Qt::Unchecked );
57655764
}
57665765

57675766

57685767
// reimplements method from base (gui) class
57695768
void QgisApp::showAllLayers()
57705769
{
57715770
QgsDebugMsg( "Showing all layers!" );
5772-
5773-
Q_FOREACH ( QgsLayerTreeLayer* nodeLayer, mLayerTreeView->layerTreeModel()->rootGroup()->findLayers() )
5774-
nodeLayer->setVisible( Qt::Checked );
5771+
mLayerTreeView->layerTreeModel()->rootGroup()->setItemVisibilityCheckedRecursive( true );
57755772
}
57765773

57775774
//reimplements method from base (gui) class
@@ -5781,10 +5778,7 @@ void QgisApp::hideSelectedLayers()
57815778

57825779
Q_FOREACH ( QgsLayerTreeNode* node, mLayerTreeView->selectedNodes() )
57835780
{
5784-
if ( QgsLayerTree::isGroup( node ) )
5785-
QgsLayerTree::toGroup( node )->setVisible( Qt::Unchecked );
5786-
else if ( QgsLayerTree::isLayer( node ) )
5787-
QgsLayerTree::toLayer( node )->setVisible( Qt::Unchecked );
5781+
node->setItemVisibilityChecked( false );
57885782
}
57895783
}
57905784

@@ -5796,7 +5790,7 @@ void QgisApp::hideDeselectedLayers()
57965790
{
57975791
if ( selectedLayerNodes.contains( nodeLayer ) )
57985792
continue;
5799-
nodeLayer->setVisible( Qt::Unchecked );
5793+
nodeLayer->setItemVisibilityChecked( false );
58005794
}
58015795
}
58025796

@@ -5807,10 +5801,12 @@ void QgisApp::showSelectedLayers()
58075801

58085802
Q_FOREACH ( QgsLayerTreeNode* node, mLayerTreeView->selectedNodes() )
58095803
{
5810-
if ( QgsLayerTree::isGroup( node ) )
5811-
QgsLayerTree::toGroup( node )->setVisible( Qt::Checked );
5812-
else if ( QgsLayerTree::isLayer( node ) )
5813-
QgsLayerTree::toLayer( node )->setVisible( Qt::Checked );
5804+
QgsLayerTreeNode* nodeIter = node;
5805+
while ( nodeIter )
5806+
{
5807+
nodeIter->setItemVisibilityChecked( true );
5808+
nodeIter = nodeIter->parent();
5809+
}
58145810
}
58155811
}
58165812

@@ -8373,7 +8369,7 @@ void QgisApp::layerSubsetString()
83738369
// hide the old layer
83748370
QgsLayerTreeLayer* vLayerTreeLayer = QgsProject::instance()->layerTreeRoot()->findLayer( vlayer->id() );
83758371
if ( vLayerTreeLayer )
8376-
vLayerTreeLayer->setVisible( Qt::Unchecked );
8372+
vLayerTreeLayer->setItemVisibilityChecked( false );
83778373
vlayer = newLayer;
83788374
}
83798375
else
@@ -8663,7 +8659,7 @@ void QgisApp::duplicateLayers( const QList<QgsMapLayer *>& lyrList )
86638659
QgsLayerTreeLayer* nodeDupLayer = parentGroup->insertLayer( parentGroup->children().indexOf( nodeSelectedLyr ) + 1, dupLayer );
86648660

86658661
// always set duplicated layers to not visible so layer can be configured before being turned on
8666-
nodeDupLayer->setVisible( Qt::Unchecked );
8662+
nodeDupLayer->setItemVisibilityChecked( false );
86678663

86688664
// duplicate the layer style
86698665
QString errMsg;

src/app/qgsapplayertreeviewmenuprovider.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ QMenu* QgsAppLayerTreeViewMenuProvider::createContextMenu()
8080

8181
menu->addAction( actions->actionMutuallyExclusiveGroup( menu ) );
8282

83+
menu->addAction( actions->actionCheckAndAllChildren( menu ) );
84+
85+
menu->addAction( actions->actionUncheckAndAllChildren( menu ) );
86+
8387
if ( mView->selectedNodes( true ).count() >= 2 )
8488
menu->addAction( actions->actionGroupSelected( menu ) );
8589

@@ -125,6 +129,8 @@ QMenu* QgsAppLayerTreeViewMenuProvider::createContextMenu()
125129
if ( !layer->isInScaleRange( mCanvas->scale() ) )
126130
menu->addAction( tr( "Zoom to &Visible Scale" ), QgisApp::instance(), SLOT( zoomToLayerScale() ) );
127131

132+
menu->addAction( actions->actionCheckAndAllParents( menu ) );
133+
128134
// set layer crs
129135
menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSetCRS.png" ) ), tr( "Set Layer CRS" ), QgisApp::instance(), SLOT( setLayerCrs() ) );
130136

src/app/qgsvectorlayerproperties.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(
309309
// use visibility as selection
310310
mLayersDependenciesTreeModel->setFlag( QgsLayerTreeModel::AllowNodeChangeVisibility );
311311

312-
mLayersDependenciesTreeGroup->setVisible( Qt::Unchecked );
312+
mLayersDependenciesTreeGroup->setItemVisibilityChecked( false );
313313

314314
QSet<QString> dependencySources;
315315
Q_FOREACH ( const QgsMapLayerDependency& dep, mLayer->dependencies() )
@@ -318,7 +318,7 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(
318318
}
319319
Q_FOREACH ( QgsLayerTreeLayer* layer, mLayersDependenciesTreeGroup->findLayers() )
320320
{
321-
layer->setVisible( dependencySources.contains( layer->layerId() ) ? Qt::Checked : Qt::Unchecked );
321+
layer->setItemVisibilityChecked( dependencySources.contains( layer->layerId() ) );
322322
}
323323

324324
mLayersDependenciesTreeView->setModel( mLayersDependenciesTreeModel.data() );

0 commit comments

Comments
 (0)