Merge pull request #3935 from rouault/group_visibility

[FEATURE] Change of ergonomy of the visibility of layers inside groups
This commit is contained in:
Even Rouault 2017-01-04 15:22:48 +01:00 committed by GitHub
commit f67cdc3965
32 changed files with 429 additions and 341 deletions

View File

@ -691,6 +691,13 @@ QgsCptCitySelectionItem {#qgis_api_break_3_0_QgsCptCitySelectionItem}
- parseXML() has been renamed to parseXml()
QgsCustomLayerOrderWidget {#qgis_api_break_3_0_QgsCustomLayerOrderWidget}
-------------------------
- the signature of the visibilityChanged() signal is changed to visibilityChanged( QgsLayerTreeNode *node )
QgsCRSCache {#qgis_api_break_3_0_QgsCRSCache}
-----------
@ -1075,12 +1082,16 @@ QgsLayerTreeGroup {#qgis_api_break_3_0_QgsLayerTreeGroup}
-----------------
- readChildrenFromXML() has been renamed to readChildrenFromXml()
- isVisible() is moved to QgsLayerTreeNode
- setVisible() is replaced by QgsLayerTreeNode::setItemVisibilityChecked()
- protected methods updateVisibilityFromChildren() and updateChildVisibility() removed
QgsLayerTreeLayer {#qgis_api_break_3_0_QgsLayerTreeLayer}
-----------------
- setLayerName(), layerName() were renamed to setName(), name()
- isVisible() is moved to QgsLayerTreeNode
- setVisible() is replaced by QgsLayerTreeNode::setItemVisibilityChecked()
QgsLayerTreeModel {#qgis_api_break_3_0_QgsLayerTreeMode}
@ -1106,7 +1117,7 @@ QgsLayerTreeNode {#qgis_api_break_3_0_QgsLayerTreeNode}
- readCommonXML() has been renamed to readCommonXml()
- writeCommonXML() has been renamed to writeCommonXml()
- the signature of the visibilityChanged() signal is changed to visibilityChanged( QgsLayerTreeNode *node )
QgsLimitedRandomColorRampDialog {#qgis_api_break_3_0_QgsLimitedRandomRampDialog}
-------------------------------

View File

@ -71,11 +71,6 @@ class QgsLayerTreeGroup : QgsLayerTreeNode
//! Return a clone of the group. The children are cloned too.
virtual QgsLayerTreeGroup* clone() const /Factory/;
//! Return the check state of the group node
Qt::CheckState isVisible() const;
//! Set check state of the group node - will also update children
void setVisible( Qt::CheckState state );
//! Return whether the group is mutually exclusive (only one child can be checked at a time)
//! @note added in 2.12
bool isMutuallyExclusive() const;
@ -86,14 +81,10 @@ class QgsLayerTreeGroup : QgsLayerTreeNode
void setIsMutuallyExclusive( bool enabled, int initialChildIndex = -1 );
protected slots:
void layerDestroyed();
void nodeVisibilityChanged( QgsLayerTreeNode* node );
protected:
//! Set check state of this group from its children
void updateVisibilityFromChildren();
//! Set check state of children (when this group's check state changes) - if not mutually exclusive
void updateChildVisibility();
//! Set check state of children - if mutually exclusive
void updateChildVisibilityMutuallyExclusive();

View File

@ -38,9 +38,6 @@ class QgsLayerTreeLayer : QgsLayerTreeNode
//! @note added in 3.0
void setName( const QString& n );
Qt::CheckState isVisible() const;
void setVisible( Qt::CheckState visible );
static QgsLayerTreeLayer* readXml( QDomElement& element ) /Factory/;
virtual void writeXml( QDomElement& parentElement );

View File

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

View File

@ -94,6 +94,34 @@ class QgsLayerTreeNode : QObject
//! Create a copy of the node. Returns new instance
virtual QgsLayerTreeNode *clone() const = 0 /Factory/;
//! Returns whether a node is really visible (ie checked and all its ancestors checked as well)
//! @note added in 3.0
bool isVisible() const;
//! Returns whether a node is checked (independantly of its ancestors or children)
//! @note added in 3.0
bool itemVisibilityChecked() const;
//! Check or uncheck a node (independantly of its ancestors or children)
//! @note added in 3.0
void setItemVisibilityChecked( bool checked );
//! Check or uncheck a node and all its children (taking into account exclusion rules)
//! @note added in 3.0
virtual void setItemVisibilityCheckedRecursive( bool checked );
//! Check or uncheck a node and all its parents
//! @note added in 3.0
void setItemVisibilityCheckedParentRecursive( bool checked );
//! Return whether this node is checked and all its children.
//! @note added in 3.0
bool isItemVisibilityCheckedRecursive() const;
//! Return whether this node is unchecked and all its children.
//! @note added in 3.0
bool isItemVisibilityUncheckedRecursive() const;
//! Return whether the node should be shown as expanded or collapsed in GUI
bool isExpanded() const;
//! Set whether the node should be shown as expanded or collapsed in GUI
@ -121,7 +149,7 @@ class QgsLayerTreeNode : QObject
//! Emitted when one or more nodes has been removed from a node within the tree
void removedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo );
//! Emitted when check state of a node within the tree has been changed
void visibilityChanged( QgsLayerTreeNode *node, Qt::CheckState state );
void visibilityChanged( QgsLayerTreeNode *node );
//! Emitted when a custom property of a node within the tree has been changed or removed
void customPropertyChanged( QgsLayerTreeNode *node, const QString& key );
//! Emitted when the collapsed/expanded state of a node within the tree has been changed

View File

@ -20,7 +20,8 @@ class QgsCustomLayerOrderWidget : QWidget
protected slots:
void bridgeHasCustomLayerOrderChanged( bool state );
void bridgeCustomLayerOrderChanged( const QStringList& order );
void nodeVisibilityChanged( QgsLayerTreeNode* node, Qt::CheckState state );
//! Slot triggered when the ivsibility of a node changes
void nodeVisibilityChanged( QgsLayerTreeNode* node );
void modelUpdated();
};

View File

@ -20,6 +20,15 @@ class QgsLayerTreeViewDefaultActions : QObject
QAction* actionRenameGroupOrLayer( QObject* parent = 0 ) /Factory/;
QAction* actionShowFeatureCount( QObject* parent = 0 ) /Factory/;
//! Action to check a group and all its children
QAction* actionCheckAndAllChildren( QObject* parent = nullptr );
//! Action to uncheck a group and all its children
QAction* actionUncheckAndAllChildren( QObject* parent = nullptr );
//! Action to check a group and all its parents
QAction* actionCheckAndAllParents( QObject* parent = nullptr );
QAction* actionZoomToLayer( QgsMapCanvas* canvas, QObject* parent = 0 ) /Factory/;
QAction* actionZoomToGroup( QgsMapCanvas* canvas, QObject* parent = 0 ) /Factory/;
// TODO: zoom to selected

View File

@ -422,7 +422,7 @@ void QgsDwgImportDialog::createGroup( QgsLayerTreeGroup *group, QString name, QS
if ( !layerGroup->children().isEmpty() )
{
layerGroup->setExpanded( false );
layerGroup->setVisible( visible ? Qt::Checked : Qt::Unchecked );
layerGroup->setItemVisibilityChecked( visible );
}
else
{

View File

@ -2808,7 +2808,7 @@ void QgisApp::setupConnections()
this, SLOT( markDirty() ) );
connect( mLayerTreeView->layerTreeModel()->rootGroup(), SIGNAL( removedChildren( QgsLayerTreeNode*, int, int ) ),
this, SLOT( updateNewLayerInsertionPoint() ) );
connect( mLayerTreeView->layerTreeModel()->rootGroup(), SIGNAL( visibilityChanged( QgsLayerTreeNode*, Qt::CheckState ) ),
connect( mLayerTreeView->layerTreeModel()->rootGroup(), SIGNAL( visibilityChanged( QgsLayerTreeNode* ) ),
this, SLOT( markDirty() ) );
connect( mLayerTreeView->layerTreeModel()->rootGroup(), SIGNAL( customPropertyChanged( QgsLayerTreeNode*, QString ) ),
this, SLOT( markDirty() ) );
@ -5759,9 +5759,8 @@ void QgisApp::stopRendering()
void QgisApp::hideAllLayers()
{
QgsDebugMsg( "hiding all layers!" );
mLayerTreeView->layerTreeModel()->rootGroup()->setItemVisibilityCheckedRecursive( false );
Q_FOREACH ( QgsLayerTreeLayer* nodeLayer, mLayerTreeView->layerTreeModel()->rootGroup()->findLayers() )
nodeLayer->setVisible( Qt::Unchecked );
}
@ -5769,9 +5768,7 @@ void QgisApp::hideAllLayers()
void QgisApp::showAllLayers()
{
QgsDebugMsg( "Showing all layers!" );
Q_FOREACH ( QgsLayerTreeLayer* nodeLayer, mLayerTreeView->layerTreeModel()->rootGroup()->findLayers() )
nodeLayer->setVisible( Qt::Checked );
mLayerTreeView->layerTreeModel()->rootGroup()->setItemVisibilityCheckedRecursive( true );
}
//reimplements method from base (gui) class
@ -5781,10 +5778,7 @@ void QgisApp::hideSelectedLayers()
Q_FOREACH ( QgsLayerTreeNode* node, mLayerTreeView->selectedNodes() )
{
if ( QgsLayerTree::isGroup( node ) )
QgsLayerTree::toGroup( node )->setVisible( Qt::Unchecked );
else if ( QgsLayerTree::isLayer( node ) )
QgsLayerTree::toLayer( node )->setVisible( Qt::Unchecked );
node->setItemVisibilityChecked( false );
}
}
@ -5796,7 +5790,7 @@ void QgisApp::hideDeselectedLayers()
{
if ( selectedLayerNodes.contains( nodeLayer ) )
continue;
nodeLayer->setVisible( Qt::Unchecked );
nodeLayer->setItemVisibilityChecked( false );
}
}
@ -5807,10 +5801,12 @@ void QgisApp::showSelectedLayers()
Q_FOREACH ( QgsLayerTreeNode* node, mLayerTreeView->selectedNodes() )
{
if ( QgsLayerTree::isGroup( node ) )
QgsLayerTree::toGroup( node )->setVisible( Qt::Checked );
else if ( QgsLayerTree::isLayer( node ) )
QgsLayerTree::toLayer( node )->setVisible( Qt::Checked );
QgsLayerTreeNode* nodeIter = node;
while ( nodeIter )
{
nodeIter->setItemVisibilityChecked( true );
nodeIter = nodeIter->parent();
}
}
}
@ -8373,7 +8369,7 @@ void QgisApp::layerSubsetString()
// hide the old layer
QgsLayerTreeLayer* vLayerTreeLayer = QgsProject::instance()->layerTreeRoot()->findLayer( vlayer->id() );
if ( vLayerTreeLayer )
vLayerTreeLayer->setVisible( Qt::Unchecked );
vLayerTreeLayer->setItemVisibilityChecked( false );
vlayer = newLayer;
}
else
@ -8663,7 +8659,7 @@ void QgisApp::duplicateLayers( const QList<QgsMapLayer *>& lyrList )
QgsLayerTreeLayer* nodeDupLayer = parentGroup->insertLayer( parentGroup->children().indexOf( nodeSelectedLyr ) + 1, dupLayer );
// always set duplicated layers to not visible so layer can be configured before being turned on
nodeDupLayer->setVisible( Qt::Unchecked );
nodeDupLayer->setItemVisibilityChecked( false );
// duplicate the layer style
QString errMsg;

View File

@ -80,6 +80,10 @@ QMenu* QgsAppLayerTreeViewMenuProvider::createContextMenu()
menu->addAction( actions->actionMutuallyExclusiveGroup( menu ) );
menu->addAction( actions->actionCheckAndAllChildren( menu ) );
menu->addAction( actions->actionUncheckAndAllChildren( menu ) );
if ( mView->selectedNodes( true ).count() >= 2 )
menu->addAction( actions->actionGroupSelected( menu ) );
@ -125,6 +129,8 @@ QMenu* QgsAppLayerTreeViewMenuProvider::createContextMenu()
if ( !layer->isInScaleRange( mCanvas->scale() ) )
menu->addAction( tr( "Zoom to &Visible Scale" ), QgisApp::instance(), SLOT( zoomToLayerScale() ) );
menu->addAction( actions->actionCheckAndAllParents( menu ) );
// set layer crs
menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSetCRS.png" ) ), tr( "Set Layer CRS" ), QgisApp::instance(), SLOT( setLayerCrs() ) );

View File

@ -309,7 +309,7 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(
// use visibility as selection
mLayersDependenciesTreeModel->setFlag( QgsLayerTreeModel::AllowNodeChangeVisibility );
mLayersDependenciesTreeGroup->setVisible( Qt::Unchecked );
mLayersDependenciesTreeGroup->setItemVisibilityChecked( false );
QSet<QString> dependencySources;
Q_FOREACH ( const QgsMapLayerDependency& dep, mLayer->dependencies() )
@ -318,7 +318,7 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(
}
Q_FOREACH ( QgsLayerTreeLayer* layer, mLayersDependenciesTreeGroup->findLayers() )
{
layer->setVisible( dependencySources.contains( layer->layerId() ) ? Qt::Checked : Qt::Unchecked );
layer->setItemVisibilityChecked( dependencySources.contains( layer->layerId() ) );
}
mLayersDependenciesTreeView->setModel( mLayersDependenciesTreeModel.data() );

View File

@ -24,26 +24,24 @@
#include <QStringList>
QgsLayerTreeGroup::QgsLayerTreeGroup( const QString& name, Qt::CheckState checked )
: QgsLayerTreeNode( NodeGroup )
QgsLayerTreeGroup::QgsLayerTreeGroup( const QString& name, bool checked )
: QgsLayerTreeNode( NodeGroup, checked )
, mName( name )
, mChecked( checked )
, mChangingChildVisibility( false )
, mMutuallyExclusive( false )
, mMutuallyExclusiveChildIndex( -1 )
{
connect( this, SIGNAL( visibilityChanged( QgsLayerTreeNode*, Qt::CheckState ) ), this, SLOT( nodeVisibilityChanged( QgsLayerTreeNode* ) ) );
connect( this, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeGroup::nodeVisibilityChanged );
}
QgsLayerTreeGroup::QgsLayerTreeGroup( const QgsLayerTreeGroup& other )
: QgsLayerTreeNode( other )
, mName( other.mName )
, mChecked( other.mChecked )
, mChangingChildVisibility( other.mChangingChildVisibility )
, mMutuallyExclusive( other.mMutuallyExclusive )
, mMutuallyExclusiveChildIndex( other.mMutuallyExclusiveChildIndex )
{
connect( this, SIGNAL( visibilityChanged( QgsLayerTreeNode*, Qt::CheckState ) ), this, SLOT( nodeVisibilityChanged( QgsLayerTreeNode* ) ) );
connect( this, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeGroup::nodeVisibilityChanged );
}
QString QgsLayerTreeGroup::name() const
@ -118,7 +116,7 @@ void QgsLayerTreeGroup::insertChildNodes( int index, const QList<QgsLayerTreeNod
// the child could have change its index - or the new children may have been also set as visible
mMutuallyExclusiveChildIndex = mChildren.indexOf( meChild );
}
else if ( mChecked == Qt::Checked )
else if ( mChecked )
{
// we have not picked a child index yet, but we should pick one now
// ... so pick the first one from the newly added
@ -128,8 +126,6 @@ void QgsLayerTreeGroup::insertChildNodes( int index, const QList<QgsLayerTreeNod
}
updateChildVisibilityMutuallyExclusive();
}
updateVisibilityFromChildren();
}
void QgsLayerTreeGroup::addChildNode( QgsLayerTreeNode* node )
@ -173,11 +169,9 @@ void QgsLayerTreeGroup::removeChildren( int from, int count )
// the child could have change its index - or may have been removed completely
mMutuallyExclusiveChildIndex = mChildren.indexOf( meChild );
// we need to uncheck this group
if ( mMutuallyExclusiveChildIndex == -1 )
setVisible( Qt::Unchecked );
//if ( mMutuallyExclusiveChildIndex == -1 )
// setItemVisibilityChecked( false );
}
updateVisibilityFromChildren();
}
void QgsLayerTreeGroup::removeChildrenGroupWithoutLayers()
@ -266,7 +260,7 @@ QgsLayerTreeGroup* QgsLayerTreeGroup::readXml( QDomElement& element )
QString name = element.attribute( QStringLiteral( "name" ) );
bool isExpanded = ( element.attribute( QStringLiteral( "expanded" ), QStringLiteral( "1" ) ) == QLatin1String( "1" ) );
Qt::CheckState checked = QgsLayerTreeUtils::checkStateFromXml( element.attribute( QStringLiteral( "checked" ) ) );
bool checked = QgsLayerTreeUtils::checkStateFromXml( element.attribute( QStringLiteral( "checked" ) ) ) != Qt::Unchecked;
bool isMutuallyExclusive = element.attribute( QStringLiteral( "mutually-exclusive" ), QStringLiteral( "0" ) ) == QLatin1String( "1" );
int mutuallyExclusiveChildIndex = element.attribute( QStringLiteral( "mutually-exclusive-child" ), QStringLiteral( "-1" ) ).toInt();
@ -288,7 +282,7 @@ void QgsLayerTreeGroup::writeXml( QDomElement& parentElement )
QDomElement elem = doc.createElement( QStringLiteral( "layer-tree-group" ) );
elem.setAttribute( QStringLiteral( "name" ), mName );
elem.setAttribute( QStringLiteral( "expanded" ), mExpanded ? "1" : "0" );
elem.setAttribute( QStringLiteral( "checked" ), QgsLayerTreeUtils::checkStateToXml( mChecked ) );
elem.setAttribute( QStringLiteral( "checked" ), mChecked ? QStringLiteral( "Qt::Checked" ) : QStringLiteral( "Qt::Unchecked" ) );
if ( mMutuallyExclusive )
{
elem.setAttribute( QStringLiteral( "mutually-exclusive" ), QStringLiteral( "1" ) );
@ -321,7 +315,7 @@ void QgsLayerTreeGroup::readChildrenFromXml( QDomElement& element )
QString QgsLayerTreeGroup::dump() const
{
QString header = QStringLiteral( "GROUP: %1 visible=%2 expanded=%3\n" ).arg( name() ).arg( mChecked ).arg( mExpanded );
QString header = QStringLiteral( "GROUP: %1 checked=%2 expanded=%3\n" ).arg( name() ).arg( mChecked ).arg( mExpanded );
QStringList childrenDump;
Q_FOREACH ( QgsLayerTreeNode* node, mChildren )
childrenDump << node->dump().split( '\n' );
@ -335,54 +329,9 @@ QgsLayerTreeGroup* QgsLayerTreeGroup::clone() const
return new QgsLayerTreeGroup( *this );
}
void QgsLayerTreeGroup::setVisible( Qt::CheckState state )
{
if ( mChecked == state )
return;
mChecked = state;
emit visibilityChanged( this, state );
if ( mMutuallyExclusive )
{
if ( mMutuallyExclusiveChildIndex < 0 || mMutuallyExclusiveChildIndex >= mChildren.count() )
mMutuallyExclusiveChildIndex = 0; // just choose the first one if we have lost the active one
updateChildVisibilityMutuallyExclusive();
}
else if ( mChecked == Qt::Unchecked || mChecked == Qt::Checked )
{
updateChildVisibility();
}
}
void QgsLayerTreeGroup::updateChildVisibility()
{
mChangingChildVisibility = true; // guard against running again setVisible() triggered from children
// update children to have the correct visibility
Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
{
if ( QgsLayerTree::isGroup( child ) )
QgsLayerTree::toGroup( child )->setVisible( mChecked );
else if ( QgsLayerTree::isLayer( child ) )
QgsLayerTree::toLayer( child )->setVisible( mChecked );
}
mChangingChildVisibility = false;
}
static bool _nodeIsChecked( QgsLayerTreeNode* node )
{
Qt::CheckState state;
if ( QgsLayerTree::isGroup( node ) )
state = QgsLayerTree::toGroup( node )->isVisible();
else if ( QgsLayerTree::isLayer( node ) )
state = QgsLayerTree::toLayer( node )->isVisible();
else
return false;
return state == Qt::Checked || state == Qt::PartiallyChecked;
return node->itemVisibilityChecked();
}
@ -398,7 +347,6 @@ void QgsLayerTreeGroup::setIsMutuallyExclusive( bool enabled, int initialChildIn
if ( !enabled )
{
updateVisibilityFromChildren();
return;
}
@ -433,14 +381,6 @@ QStringList QgsLayerTreeGroup::findLayerIds() const
return lst;
}
void QgsLayerTreeGroup::layerDestroyed()
{
//QgsMapLayer* layer = static_cast<QgsMapLayer*>( sender() );
//removeLayer( layer );
}
void QgsLayerTreeGroup::nodeVisibilityChanged( QgsLayerTreeNode* node )
{
int childIndex = mChildren.indexOf( node );
@ -451,69 +391,12 @@ void QgsLayerTreeGroup::nodeVisibilityChanged( QgsLayerTreeNode* node )
{
if ( _nodeIsChecked( node ) )
mMutuallyExclusiveChildIndex = childIndex;
else if ( mMutuallyExclusiveChildIndex == childIndex )
mMutuallyExclusiveChildIndex = -1;
// we need to update this node's check status in two cases:
// 1. it was unchecked and a child node got checked
// 2. it was checked and the only checked child got unchecked
updateVisibilityFromChildren();
// we also need to make sure there is only one child node checked
// we need to make sure there is only one child node checked
updateChildVisibilityMutuallyExclusive();
}
else
{
updateVisibilityFromChildren();
}
}
void QgsLayerTreeGroup::updateVisibilityFromChildren()
{
if ( mChangingChildVisibility )
return;
if ( mChildren.isEmpty() )
return;
if ( mMutuallyExclusive )
{
// if in mutually exclusive mode, our check state depends only on the check state of the chosen child index
if ( mMutuallyExclusiveChildIndex < 0 || mMutuallyExclusiveChildIndex >= mChildren.count() )
return;
Qt::CheckState meChildState = _nodeIsChecked( mChildren.at( mMutuallyExclusiveChildIndex ) ) ? Qt::Checked : Qt::Unchecked;
setVisible( meChildState );
return;
}
bool hasVisible = false, hasHidden = false;
Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
{
if ( QgsLayerTree::isLayer( child ) )
{
bool layerVisible = QgsLayerTree::toLayer( child )->isVisible() == Qt::Checked;
if ( layerVisible ) hasVisible = true;
if ( !layerVisible ) hasHidden = true;
}
else if ( QgsLayerTree::isGroup( child ) )
{
Qt::CheckState state = QgsLayerTree::toGroup( child )->isVisible();
if ( state == Qt::Checked || state == Qt::PartiallyChecked ) hasVisible = true;
if ( state == Qt::Unchecked || state == Qt::PartiallyChecked ) hasHidden = true;
}
}
Qt::CheckState newState;
if ( hasVisible && !hasHidden )
newState = Qt::Checked;
else if ( hasHidden && !hasVisible )
newState = Qt::Unchecked;
else
newState = Qt::PartiallyChecked;
setVisible( newState );
}
void QgsLayerTreeGroup::updateChildVisibilityMutuallyExclusive()
@ -526,11 +409,23 @@ void QgsLayerTreeGroup::updateChildVisibilityMutuallyExclusive()
int index = 0;
Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
{
Qt::CheckState checked = ( index == mMutuallyExclusiveChildIndex ? mChecked : Qt::Unchecked );
if ( QgsLayerTree::isGroup( child ) )
QgsLayerTree::toGroup( child )->setVisible( checked );
else if ( QgsLayerTree::isLayer( child ) )
QgsLayerTree::toLayer( child )->setVisible( checked );
child->setItemVisibilityChecked( index == mMutuallyExclusiveChildIndex );
++index;
}
mChangingChildVisibility = false;
}
void QgsLayerTreeGroup::setItemVisibilityCheckedRecursive( bool checked )
{
QgsLayerTreeNode::setItemVisibilityChecked( checked );
mChangingChildVisibility = true; // guard against running again setVisible() triggered from children
int index = 0;
Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
{
child->setItemVisibilityCheckedRecursive( checked && ( mMutuallyExclusiveChildIndex < 0 || index == mMutuallyExclusiveChildIndex ) );
++index;
}

View File

@ -32,7 +32,8 @@ class CORE_EXPORT QgsLayerTreeGroup : public QgsLayerTreeNode
{
Q_OBJECT
public:
QgsLayerTreeGroup( const QString& name = QString(), Qt::CheckState checked = Qt::Checked );
//! Constructor
QgsLayerTreeGroup( const QString& name = QString(), bool checked = true );
QgsLayerTreeGroup( const QgsLayerTreeGroup& other );
//! Get group's name
@ -92,10 +93,8 @@ class CORE_EXPORT QgsLayerTreeGroup : public QgsLayerTreeNode
//! Return a clone of the group. The children are cloned too.
virtual QgsLayerTreeGroup* clone() const override;
//! Return the check state of the group node
Qt::CheckState isVisible() const { return mChecked; }
//! Set check state of the group node - will also update children
void setVisible( Qt::CheckState state );
//! Check or uncheck a node and all its children (taking into account exclusion rules)
virtual void setItemVisibilityCheckedRecursive( bool checked ) override;
//! Return whether the group is mutually exclusive (only one child can be checked at a time)
//! @note added in 2.12
@ -107,20 +106,15 @@ class CORE_EXPORT QgsLayerTreeGroup : public QgsLayerTreeNode
void setIsMutuallyExclusive( bool enabled, int initialChildIndex = -1 );
protected slots:
void layerDestroyed();
void nodeVisibilityChanged( QgsLayerTreeNode* node );
protected:
//! Set check state of this group from its children
void updateVisibilityFromChildren();
//! Set check state of children (when this group's check state changes) - if not mutually exclusive
void updateChildVisibility();
//! Set check state of children - if mutually exclusive
void updateChildVisibilityMutuallyExclusive();
protected:
QString mName;
Qt::CheckState mChecked;
bool mChangingChildVisibility;

View File

@ -21,21 +21,19 @@
QgsLayerTreeLayer::QgsLayerTreeLayer( QgsMapLayer *layer )
: QgsLayerTreeNode( NodeLayer )
: QgsLayerTreeNode( NodeLayer, true )
, mLayerId( layer->id() )
, mLayer( nullptr )
, mVisible( Qt::Checked )
{
Q_ASSERT( QgsProject::instance()->mapLayer( mLayerId ) == layer );
attachToLayer();
}
QgsLayerTreeLayer::QgsLayerTreeLayer( const QString& layerId, const QString& name )
: QgsLayerTreeNode( NodeLayer )
: QgsLayerTreeNode( NodeLayer, true )
, mLayerId( layerId )
, mLayerName( name )
, mLayer( nullptr )
, mVisible( Qt::Checked )
{
attachToLayer();
}
@ -45,7 +43,6 @@ QgsLayerTreeLayer::QgsLayerTreeLayer( const QgsLayerTreeLayer& other )
, mLayerId( other.mLayerId )
, mLayerName( other.mLayerName )
, mLayer( nullptr )
, mVisible( other.mVisible )
{
attachToLayer();
}
@ -94,15 +91,6 @@ void QgsLayerTreeLayer::setName( const QString& n )
}
}
void QgsLayerTreeLayer::setVisible( Qt::CheckState state )
{
if ( mVisible == state )
return;
mVisible = state;
emit visibilityChanged( this, state );
}
QgsLayerTreeLayer* QgsLayerTreeLayer::readXml( QDomElement& element )
{
if ( element.tagName() != QLatin1String( "layer-tree-layer" ) )
@ -124,7 +112,7 @@ QgsLayerTreeLayer* QgsLayerTreeLayer::readXml( QDomElement& element )
nodeLayer->readCommonXml( element );
nodeLayer->setVisible( checked );
nodeLayer->setItemVisibilityChecked( checked != Qt::Unchecked );
nodeLayer->setExpanded( isExpanded );
return nodeLayer;
}
@ -135,7 +123,7 @@ void QgsLayerTreeLayer::writeXml( QDomElement& parentElement )
QDomElement elem = doc.createElement( QStringLiteral( "layer-tree-layer" ) );
elem.setAttribute( QStringLiteral( "id" ), mLayerId );
elem.setAttribute( QStringLiteral( "name" ), name() );
elem.setAttribute( QStringLiteral( "checked" ), QgsLayerTreeUtils::checkStateToXml( mVisible ) );
elem.setAttribute( QStringLiteral( "checked" ), mChecked ? QStringLiteral( "Qt::Checked" ) : QStringLiteral( "Qt::Unchecked" ) );
elem.setAttribute( QStringLiteral( "expanded" ), mExpanded ? "1" : "0" );
writeCommonXml( elem );
@ -145,7 +133,7 @@ void QgsLayerTreeLayer::writeXml( QDomElement& parentElement )
QString QgsLayerTreeLayer::dump() const
{
return QStringLiteral( "LAYER: %1 visible=%2 expanded=%3 id=%4\n" ).arg( name() ).arg( mVisible ).arg( mExpanded ).arg( layerId() );
return QStringLiteral( "LAYER: %1 checked=%2 expanded=%3 id=%4\n" ).arg( name() ).arg( mChecked ).arg( mExpanded ).arg( layerId() );
}
QgsLayerTreeLayer* QgsLayerTreeLayer::clone() const

View File

@ -58,9 +58,6 @@ class CORE_EXPORT QgsLayerTreeLayer : public QgsLayerTreeNode
//! @note added in 3.0
void setName( const QString& n ) override;
Qt::CheckState isVisible() const { return mVisible; }
void setVisible( Qt::CheckState visible );
static QgsLayerTreeLayer* readXml( QDomElement& element );
virtual void writeXml( QDomElement& parentElement ) override;
@ -88,7 +85,6 @@ class CORE_EXPORT QgsLayerTreeLayer : public QgsLayerTreeNode
QString mLayerId;
QString mLayerName; // only used if layer does not exist
QgsMapLayer* mLayer; // not owned! may be null
Qt::CheckState mVisible;
};

View File

@ -284,12 +284,12 @@ QVariant QgsLayerTreeModel::data( const QModelIndex &index, int role ) const
if ( qobject_cast<QgsVectorLayer*>( nodeLayer->layer() )->geometryType() == QgsWkbTypes::NullGeometry )
return QVariant(); // do not show checkbox for non-spatial tables
}
return nodeLayer->isVisible();
return nodeLayer->itemVisibilityChecked() ? Qt::Checked : Qt::Unchecked;
}
else if ( QgsLayerTree::isGroup( node ) )
{
QgsLayerTreeGroup* nodeGroup = QgsLayerTree::toGroup( node );
return nodeGroup->isVisible();
return nodeGroup->itemVisibilityChecked() ? Qt::Checked : Qt::Unchecked;
}
}
else if ( role == Qt::FontRole )
@ -307,7 +307,7 @@ QVariant QgsLayerTreeModel::data( const QModelIndex &index, int role ) const
if ( QgsLayerTree::isLayer( node ) )
{
const QgsMapLayer* layer = QgsLayerTree::toLayer( node )->layer();
if ( layer && !layer->isInScaleRange( mLegendMapViewScale ) )
if ( !node->isVisible() || ( layer && !layer->isInScaleRange( mLegendMapViewScale ) ) )
{
brush.setColor( Qt::lightGray );
}
@ -393,20 +393,22 @@ bool QgsLayerTreeModel::setData( const QModelIndex& index, const QVariant& value
if ( !testFlag( AllowNodeChangeVisibility ) )
return false;
if ( QgsLayerTree::isLayer( node ) )
bool checked = static_cast< Qt::CheckState >( value.toInt() ) == Qt::Checked;
if ( checked && node->children().isEmpty() )
{
QgsLayerTreeLayer* layer = QgsLayerTree::toLayer( node );
layer->setVisible( static_cast< Qt::CheckState >( value.toInt() ) );
return true;
node->setItemVisibilityCheckedParentRecursive( checked );
}
if ( QgsLayerTree::isGroup( node ) )
else if ( testFlag( ActionHierarchical ) )
{
QgsLayerTreeGroup* group = QgsLayerTree::toGroup( node );
group->setVisible( static_cast< Qt::CheckState >( value.toInt() ) );
return true;
if ( node->children().isEmpty() )
node->setItemVisibilityCheckedParentRecursive( checked );
else
node->setItemVisibilityCheckedRecursive( checked );
}
else
{
node->setItemVisibilityChecked( checked );
}
return true;
}
else if ( role == Qt::EditRole )
@ -934,7 +936,7 @@ void QgsLayerTreeModel::connectToRootNode()
connect( mRootNode, SIGNAL( addedChildren( QgsLayerTreeNode*, int, int ) ), this, SLOT( nodeAddedChildren( QgsLayerTreeNode*, int, int ) ) );
connect( mRootNode, SIGNAL( willRemoveChildren( QgsLayerTreeNode*, int, int ) ), this, SLOT( nodeWillRemoveChildren( QgsLayerTreeNode*, int, int ) ) );
connect( mRootNode, SIGNAL( removedChildren( QgsLayerTreeNode*, int, int ) ), this, SLOT( nodeRemovedChildren() ) );
connect( mRootNode, SIGNAL( visibilityChanged( QgsLayerTreeNode*, Qt::CheckState ) ), this, SLOT( nodeVisibilityChanged( QgsLayerTreeNode* ) ) );
connect( mRootNode, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeModel::nodeVisibilityChanged );
connect( mRootNode, SIGNAL( nameChanged( QgsLayerTreeNode*, QString ) ), this, SLOT( nodeNameChanged( QgsLayerTreeNode*, QString ) ) );
connect( mRootNode, SIGNAL( customPropertyChanged( QgsLayerTreeNode*, QString ) ), this, SLOT( nodeCustomPropertyChanged( QgsLayerTreeNode*, QString ) ) );

View File

@ -87,6 +87,7 @@ class CORE_EXPORT QgsLayerTreeModel : public QAbstractItemModel
AllowNodeRename = 0x2000, //!< Allow renaming of groups and layers
AllowNodeChangeVisibility = 0x4000, //!< Allow user to set node visibility with a check box
AllowLegendChangeState = 0x8000, //!< Allow check boxes for legend nodes (if supported by layer's legend)
ActionHierarchical = 0x10000, //!< Check/uncheck action has consequences on children (or parents for leaf node)
};
Q_DECLARE_FLAGS( Flags, Flag )

View File

@ -22,8 +22,9 @@
#include <QStringList>
QgsLayerTreeNode::QgsLayerTreeNode( QgsLayerTreeNode::NodeType t )
QgsLayerTreeNode::QgsLayerTreeNode( QgsLayerTreeNode::NodeType t, bool checked )
: mNodeType( t )
, mChecked( checked )
, mParent( nullptr )
, mExpanded( true )
{
@ -32,6 +33,7 @@ QgsLayerTreeNode::QgsLayerTreeNode( QgsLayerTreeNode::NodeType t )
QgsLayerTreeNode::QgsLayerTreeNode( const QgsLayerTreeNode& other )
: QObject()
, mNodeType( other.mNodeType )
, mChecked( other.mChecked )
, mParent( nullptr )
, mExpanded( other.mExpanded )
, mProperties( other.mProperties )
@ -59,11 +61,62 @@ QgsLayerTreeNode* QgsLayerTreeNode::readXml( QDomElement& element )
}
void QgsLayerTreeNode::setItemVisibilityChecked( bool checked )
{
if ( mChecked == checked )
return;
mChecked = checked;
emit visibilityChanged( this );
}
void QgsLayerTreeNode::setItemVisibilityCheckedRecursive( bool checked )
{
setItemVisibilityChecked( checked );
}
void QgsLayerTreeNode::setItemVisibilityCheckedParentRecursive( bool checked )
{
setItemVisibilityChecked( checked );
if ( mParent )
mParent->setItemVisibilityCheckedParentRecursive( checked );
}
bool QgsLayerTreeNode::isVisible() const
{
return mChecked && ( !mParent || mParent->isVisible() );
}
bool QgsLayerTreeNode::isExpanded() const
{
return mExpanded;
}
bool QgsLayerTreeNode::isItemVisibilityCheckedRecursive() const
{
if ( !mChecked )
return false;
Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
{
if ( !child->isItemVisibilityCheckedRecursive() )
return false;
}
return true;
}
bool QgsLayerTreeNode::isItemVisibilityUncheckedRecursive() const
{
if ( mChecked )
return false;
Q_FOREACH ( QgsLayerTreeNode* child, mChildren )
{
if ( !child->isItemVisibilityUncheckedRecursive() )
return false;
}
return true;
}
void QgsLayerTreeNode::setExpanded( bool expanded )
{
@ -134,7 +187,7 @@ void QgsLayerTreeNode::insertChildrenPrivate( int index, QList<QgsLayerTreeNode*
connect( nodes[i], SIGNAL( willRemoveChildren( QgsLayerTreeNode*, int, int ) ), this, SIGNAL( willRemoveChildren( QgsLayerTreeNode*, int, int ) ) );
connect( nodes[i], SIGNAL( removedChildren( QgsLayerTreeNode*, int, int ) ), this, SIGNAL( removedChildren( QgsLayerTreeNode*, int, int ) ) );
connect( nodes[i], SIGNAL( customPropertyChanged( QgsLayerTreeNode*, QString ) ), this, SIGNAL( customPropertyChanged( QgsLayerTreeNode*, QString ) ) );
connect( nodes[i], SIGNAL( visibilityChanged( QgsLayerTreeNode*, Qt::CheckState ) ), this, SIGNAL( visibilityChanged( QgsLayerTreeNode*, Qt::CheckState ) ) );
connect( nodes[i], &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeNode::visibilityChanged );
connect( nodes[i], SIGNAL( expandedChanged( QgsLayerTreeNode*, bool ) ), this, SIGNAL( expandedChanged( QgsLayerTreeNode*, bool ) ) );
connect( nodes[i], SIGNAL( nameChanged( QgsLayerTreeNode*, QString ) ), this, SIGNAL( nameChanged( QgsLayerTreeNode*, QString ) ) );
}

View File

@ -82,6 +82,8 @@ class CORE_EXPORT QgsLayerTreeNode : public QObject
QgsLayerTreeNode *parent() { return mParent; }
//! Get list of children of the node. Children are owned by the parent
QList<QgsLayerTreeNode*> children() { return mChildren; }
//! Get list of children of the node. Children are owned by the parent
const QList<QgsLayerTreeNode*>& children() const { return mChildren; }
//! Return name of the node
//! @note added in 3.0
@ -101,6 +103,34 @@ class CORE_EXPORT QgsLayerTreeNode : public QObject
//! Create a copy of the node. Returns new instance
virtual QgsLayerTreeNode *clone() const = 0;
//! Returns whether a node is really visible (ie checked and all its ancestors checked as well)
//! @note added in 3.0
bool isVisible() const;
//! Returns whether a node is checked (independantly of its ancestors or children)
//! @note added in 3.0
bool itemVisibilityChecked() const { return mChecked; }
//! Check or uncheck a node (independantly of its ancestors or children)
//! @note added in 3.0
void setItemVisibilityChecked( bool checked );
//! Check or uncheck a node and all its children (taking into account exclusion rules)
//! @note added in 3.0
virtual void setItemVisibilityCheckedRecursive( bool checked );
//! Check or uncheck a node and all its parents
//! @note added in 3.0
void setItemVisibilityCheckedParentRecursive( bool checked );
//! Return whether this node is checked and all its children.
//! @note added in 3.0
bool isItemVisibilityCheckedRecursive() const;
//! Return whether this node is unchecked and all its children.
//! @note added in 3.0
bool isItemVisibilityUncheckedRecursive() const;
//! Return whether the node should be shown as expanded or collapsed in GUI
bool isExpanded() const;
//! Set whether the node should be shown as expanded or collapsed in GUI
@ -128,7 +158,7 @@ class CORE_EXPORT QgsLayerTreeNode : public QObject
//! Emitted when one or more nodes has been removed from a node within the tree
void removedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo );
//! Emitted when check state of a node within the tree has been changed
void visibilityChanged( QgsLayerTreeNode *node, Qt::CheckState state );
void visibilityChanged( QgsLayerTreeNode *node );
//! Emitted when a custom property of a node within the tree has been changed or removed
void customPropertyChanged( QgsLayerTreeNode *node, const QString& key );
//! Emitted when the collapsed/expanded state of a node within the tree has been changed
@ -139,12 +169,15 @@ class CORE_EXPORT QgsLayerTreeNode : public QObject
protected:
QgsLayerTreeNode( NodeType t );
//! Constructor
QgsLayerTreeNode( NodeType t, bool checked = true );
QgsLayerTreeNode( const QgsLayerTreeNode &other );
// low-level utility functions
//! Read common XML elements.
void readCommonXml( QDomElement &element );
//! Write common XML elements.
void writeCommonXml( QDomElement &element );
//! Low-level insertion of children to the node. The children must not have any parent yet!
@ -155,6 +188,7 @@ class CORE_EXPORT QgsLayerTreeNode : public QObject
protected:
//! type of the node - determines which subclass is used
NodeType mNodeType;
bool mChecked;
//! pointer to the parent node - null in case of root node
QgsLayerTreeNode *mParent;
//! list of children - node is responsible for their deletion

View File

@ -52,7 +52,7 @@ void QgsLayerTreeRegistryBridge::layersAdded( const QList<QgsMapLayer*>& layers
Q_FOREACH ( QgsMapLayer* layer, layers )
{
QgsLayerTreeLayer* nodeLayer = new QgsLayerTreeLayer( layer );
nodeLayer->setVisible( mNewLayersVisible ? Qt::Checked : Qt::Unchecked );
nodeLayer->setItemVisibilityChecked( mNewLayersVisible );
nodes << nodeLayer;

View File

@ -112,7 +112,7 @@ static QDomElement _writeOldLegendLayer( QDomDocument& doc, QgsLayerTreeLayer* n
QDomElement layerElem = doc.createElement( QStringLiteral( "legendlayer" ) );
layerElem.setAttribute( QStringLiteral( "drawingOrder" ), drawingOrder );
layerElem.setAttribute( QStringLiteral( "open" ), nodeLayer->isExpanded() ? "true" : "false" );
layerElem.setAttribute( QStringLiteral( "checked" ), QgsLayerTreeUtils::checkStateToXml( nodeLayer->isVisible() ) );
layerElem.setAttribute( QStringLiteral( "checked" ), QgsLayerTreeUtils::checkStateToXml( nodeLayer->itemVisibilityChecked() ? Qt::Checked : Qt::Unchecked ) );
layerElem.setAttribute( QStringLiteral( "name" ), nodeLayer->name() );
layerElem.setAttribute( QStringLiteral( "showFeatureCount" ), nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ) ).toInt() );
@ -123,7 +123,7 @@ static QDomElement _writeOldLegendLayer( QDomDocument& doc, QgsLayerTreeLayer* n
QDomElement layerFileElem = doc.createElement( QStringLiteral( "legendlayerfile" ) );
layerFileElem.setAttribute( QStringLiteral( "isInOverview" ), nodeLayer->customProperty( QStringLiteral( "overview" ) ).toInt() );
layerFileElem.setAttribute( QStringLiteral( "layerid" ), nodeLayer->layerId() );
layerFileElem.setAttribute( QStringLiteral( "visible" ), nodeLayer->isVisible() == Qt::Checked ? 1 : 0 );
layerFileElem.setAttribute( QStringLiteral( "visible" ), nodeLayer->isVisible() ? 1 : 0 );
layerElem.appendChild( fileGroupElem );
fileGroupElem.appendChild( layerFileElem );
@ -138,7 +138,7 @@ static QDomElement _writeOldLegendGroup( QDomDocument& doc, QgsLayerTreeGroup* n
QDomElement groupElem = doc.createElement( QStringLiteral( "legendgroup" ) );
groupElem.setAttribute( QStringLiteral( "open" ), nodeGroup->isExpanded() ? "true" : "false" );
groupElem.setAttribute( QStringLiteral( "name" ), nodeGroup->name() );
groupElem.setAttribute( QStringLiteral( "checked" ), QgsLayerTreeUtils::checkStateToXml( nodeGroup->isVisible() ) );
groupElem.setAttribute( QStringLiteral( "checked" ), QgsLayerTreeUtils::checkStateToXml( nodeGroup->itemVisibilityChecked() ? Qt::Checked : Qt::Unchecked ) );
if ( nodeGroup->customProperty( QStringLiteral( "embedded" ) ).toInt() )
{
@ -210,7 +210,7 @@ static void _readOldLegendGroup( const QDomElement& groupElem, QgsLayerTreeGroup
QgsLayerTreeGroup* groupNode = new QgsLayerTreeGroup( groupElem.attribute( QStringLiteral( "name" ) ) );
groupNode->setVisible( QgsLayerTreeUtils::checkStateFromXml( groupElem.attribute( QStringLiteral( "checked" ) ) ) );
groupNode->setItemVisibilityChecked( QgsLayerTreeUtils::checkStateFromXml( groupElem.attribute( QStringLiteral( "checked" ) ) ) != Qt::Unchecked );
groupNode->setExpanded( groupElem.attribute( QStringLiteral( "open" ) ) == QLatin1String( "true" ) );
if ( groupElem.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
@ -241,7 +241,7 @@ static void _readOldLegendLayer( const QDomElement& layerElem, QgsLayerTreeGroup
QString layerId = layerFileElem.attribute( QStringLiteral( "layerid" ) );
QgsLayerTreeLayer* layerNode = new QgsLayerTreeLayer( layerId, layerElem.attribute( QStringLiteral( "name" ) ) );
layerNode->setVisible( QgsLayerTreeUtils::checkStateFromXml( layerElem.attribute( QStringLiteral( "checked" ) ) ) );
layerNode->setItemVisibilityChecked( QgsLayerTreeUtils::checkStateFromXml( layerElem.attribute( QStringLiteral( "checked" ) ) ) != Qt::Unchecked );
layerNode->setExpanded( layerElem.attribute( QStringLiteral( "open" ) ) == QLatin1String( "true" ) );
if ( layerFileElem.attribute( QStringLiteral( "isInOverview" ) ) == QLatin1String( "1" ) )

View File

@ -103,7 +103,7 @@ void QgsMapThemeCollection::applyThemeToLayer( QgsLayerTreeLayer* nodeLayer, Qgs
MapThemeLayerRecord layerRec;
bool isVisible = findRecordForLayer( nodeLayer->layer(), rec, layerRec );
nodeLayer->setVisible( isVisible ? Qt::Checked : Qt::Unchecked );
nodeLayer->setItemVisibilityChecked( isVisible );
if ( !isVisible )
return;

View File

@ -2009,7 +2009,7 @@ QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, co
QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
if ( layer )
{
layer->setVisible( invisibleLayers.contains( layerId ) ? Qt::Unchecked : Qt::Checked );
layer->setItemVisibilityChecked( invisibleLayers.contains( layerId ) );
}
}

View File

@ -54,7 +54,7 @@ QgsCustomLayerOrderWidget::QgsCustomLayerOrderWidget( QgsLayerTreeMapCanvasBridg
connect( mModel, SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( modelUpdated() ) );
connect( mModel, SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( modelUpdated() ) );
connect( bridge->rootGroup(), SIGNAL( visibilityChanged( QgsLayerTreeNode*, Qt::CheckState ) ), this, SLOT( nodeVisibilityChanged( QgsLayerTreeNode*, Qt::CheckState ) ) );
connect( bridge->rootGroup(), &QgsLayerTreeNode::visibilityChanged, this, &QgsCustomLayerOrderWidget::nodeVisibilityChanged );
QVBoxLayout* l = new QVBoxLayout;
l->setMargin( 0 );
@ -76,9 +76,8 @@ void QgsCustomLayerOrderWidget::bridgeCustomLayerOrderChanged( const QStringList
mModel->refreshModel( mBridge->hasCustomLayerOrder() ? mBridge->customLayerOrder() : mBridge->defaultLayerOrder() );
}
void QgsCustomLayerOrderWidget::nodeVisibilityChanged( QgsLayerTreeNode* node, Qt::CheckState state )
void QgsCustomLayerOrderWidget::nodeVisibilityChanged( QgsLayerTreeNode* node )
{
Q_UNUSED( state );
if ( QgsLayerTree::isLayer( node ) )
{
mModel->updateLayerVisibility( QgsLayerTree::toLayer( node )->layerId() );
@ -141,7 +140,7 @@ bool CustomLayerOrderModel::setData( const QModelIndex& index, const QVariant& v
QgsLayerTreeLayer* nodeLayer = mBridge->rootGroup()->findLayer( id );
if ( nodeLayer )
{
nodeLayer->setVisible( static_cast< Qt::CheckState >( value.toInt() ) );
nodeLayer->setItemVisibilityChecked( static_cast< Qt::CheckState >( value.toInt() ) == Qt::Checked );
return true;
}
}

View File

@ -48,7 +48,8 @@ class GUI_EXPORT QgsCustomLayerOrderWidget : public QWidget
protected slots:
void bridgeHasCustomLayerOrderChanged( bool state );
void bridgeCustomLayerOrderChanged( const QStringList& order );
void nodeVisibilityChanged( QgsLayerTreeNode* node, Qt::CheckState state );
//! Slot triggered when the ivsibility of a node changes
void nodeVisibilityChanged( QgsLayerTreeNode* node );
void modelUpdated();

View File

@ -37,7 +37,7 @@ QgsLayerTreeMapCanvasBridge::QgsLayerTreeMapCanvasBridge( QgsLayerTreeGroup *roo
connect( root, SIGNAL( addedChildren( QgsLayerTreeNode*, int, int ) ), this, SLOT( nodeAddedChildren( QgsLayerTreeNode*, int, int ) ) );
connect( root, SIGNAL( customPropertyChanged( QgsLayerTreeNode*, QString ) ), this, SLOT( nodeCustomPropertyChanged( QgsLayerTreeNode*, QString ) ) );
connect( root, SIGNAL( removedChildren( QgsLayerTreeNode*, int, int ) ), this, SLOT( nodeRemovedChildren() ) );
connect( root, SIGNAL( visibilityChanged( QgsLayerTreeNode*, Qt::CheckState ) ), this, SLOT( nodeVisibilityChanged() ) );
connect( root, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeMapCanvasBridge::nodeVisibilityChanged );
setCanvasLayers();
}
@ -127,7 +127,7 @@ void QgsLayerTreeMapCanvasBridge::setCanvasLayers()
QgsLayerTreeLayer* nodeLayer = mRoot->findLayer( layerId );
if ( nodeLayer )
{
if ( nodeLayer->isVisible() == Qt::Checked )
if ( nodeLayer->isVisible() )
canvasLayers << nodeLayer->layer();
if ( nodeLayer->customProperty( QStringLiteral( "overview" ), 0 ).toInt() )
overviewLayers << nodeLayer->layer();
@ -262,7 +262,7 @@ void QgsLayerTreeMapCanvasBridge::setCanvasLayers( QgsLayerTreeNode *node, QList
if ( QgsLayerTree::isLayer( node ) )
{
QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( node );
if ( nodeLayer->isVisible() == Qt::Checked )
if ( nodeLayer->isVisible() )
canvasLayers << nodeLayer->layer();
if ( nodeLayer->customProperty( QStringLiteral( "overview" ), 0 ).toInt() )
overviewLayers << nodeLayer->layer();

View File

@ -382,3 +382,25 @@ void QgsLayerTreeView::collapseAllNodes()
_expandAllNodes( layerTreeModel()->rootGroup(), false, layerTreeModel() );
collapseAll();
}
void QgsLayerTreeView::mouseReleaseEvent( QMouseEvent *event )
{
const QgsLayerTreeModel::Flags oldFlags = layerTreeModel()->flags();
if ( event->modifiers() & Qt::ControlModifier )
layerTreeModel()->setFlags( oldFlags | QgsLayerTreeModel::ActionHierarchical );
else
layerTreeModel()->setFlags( oldFlags & ~QgsLayerTreeModel::ActionHierarchical );
QTreeView::mouseReleaseEvent( event );
layerTreeModel()->setFlags( oldFlags );
}
void QgsLayerTreeView::keyPressEvent( QKeyEvent *event )
{
const QgsLayerTreeModel::Flags oldFlags = layerTreeModel()->flags();
if ( event->modifiers() & Qt::ControlModifier )
layerTreeModel()->setFlags( oldFlags | QgsLayerTreeModel::ActionHierarchical );
else
layerTreeModel()->setFlags( oldFlags & ~QgsLayerTreeModel::ActionHierarchical );
QTreeView::keyPressEvent( event );
layerTreeModel()->setFlags( oldFlags );
}

View File

@ -111,6 +111,9 @@ class GUI_EXPORT QgsLayerTreeView : public QTreeView
QgsMapLayer* layerForIndex( const QModelIndex& index ) const;
void mouseReleaseEvent( QMouseEvent *event ) override;
void keyPressEvent( QKeyEvent *event ) override;
protected slots:
void modelRowsInserted( const QModelIndex& index, int start, int end );

View File

@ -123,6 +123,60 @@ QAction* QgsLayerTreeViewDefaultActions::actionMutuallyExclusiveGroup( QObject*
return a;
}
QAction* QgsLayerTreeViewDefaultActions::actionCheckAndAllChildren( QObject* parent )
{
QgsLayerTreeNode* node = mView->currentNode();
if ( !node || !QgsLayerTree::isGroup( node ) || node->isItemVisibilityCheckedRecursive() )
return nullptr;
QAction* a = new QAction( tr( "Check and all its children (Ctrl-click)" ), parent );
connect( a, &QAction::triggered, this, &QgsLayerTreeViewDefaultActions::checkAndAllChildren );
return a;
}
QAction* QgsLayerTreeViewDefaultActions::actionUncheckAndAllChildren( QObject* parent )
{
QgsLayerTreeNode* node = mView->currentNode();
if ( !node || !QgsLayerTree::isGroup( node ) || node->isItemVisibilityUncheckedRecursive() )
return nullptr;
QAction* a = new QAction( tr( "Uncheck and all its children (Ctrl-click)" ), parent );
connect( a, &QAction::triggered, this, &QgsLayerTreeViewDefaultActions::uncheckAndAllChildren );
return a;
}
QAction* QgsLayerTreeViewDefaultActions::actionCheckAndAllParents( QObject* parent )
{
QgsLayerTreeNode* node = mView->currentNode();
if ( !node || !QgsLayerTree::isLayer( node ) || node->isVisible() )
return nullptr;
QAction* a = new QAction( tr( "Check and all its parents" ), parent );
connect( a, &QAction::triggered, this, &QgsLayerTreeViewDefaultActions::checkAndAllParents );
return a;
}
void QgsLayerTreeViewDefaultActions::checkAndAllChildren()
{
QgsLayerTreeNode* node = mView->currentNode();
if ( !node )
return;
node->setItemVisibilityCheckedRecursive( true );
}
void QgsLayerTreeViewDefaultActions::uncheckAndAllChildren()
{
QgsLayerTreeNode* node = mView->currentNode();
if ( !node )
return;
node->setItemVisibilityCheckedRecursive( false );
}
void QgsLayerTreeViewDefaultActions::checkAndAllParents()
{
QgsLayerTreeNode* node = mView->currentNode();
if ( !node )
return;
node->setItemVisibilityCheckedParentRecursive( true );
}
void QgsLayerTreeViewDefaultActions::addGroup()
{
QgsLayerTreeGroup* group = mView->currentGroupNode();

View File

@ -46,6 +46,15 @@ class GUI_EXPORT QgsLayerTreeViewDefaultActions : public QObject
QAction* actionRenameGroupOrLayer( QObject* parent = nullptr );
QAction* actionShowFeatureCount( QObject* parent = nullptr );
//! Action to check a group and all its children
QAction* actionCheckAndAllChildren( QObject* parent = nullptr );
//! Action to uncheck a group and all its children
QAction* actionUncheckAndAllChildren( QObject* parent = nullptr );
//! Action to check a group and all its parents
QAction* actionCheckAndAllParents( QObject* parent = nullptr );
QAction* actionZoomToLayer( QgsMapCanvas* canvas, QObject* parent = nullptr );
QAction* actionZoomToGroup( QgsMapCanvas* canvas, QObject* parent = nullptr );
// TODO: zoom to selected
@ -75,6 +84,11 @@ class GUI_EXPORT QgsLayerTreeViewDefaultActions : public QObject
//! @note added in 2.12
void mutuallyExclusiveGroup();
private slots:
void checkAndAllChildren();
void uncheckAndAllChildren();
void checkAndAllParents();
protected:
void zoomToLayers( QgsMapCanvas* canvas, const QList<QgsMapLayer*>& layers );

View File

@ -180,12 +180,12 @@ void QgsOfflineEditingPluginGui::restoreState()
void QgsOfflineEditingPluginGui::selectAll()
{
Q_FOREACH ( QgsLayerTreeLayer* nodeLayer, mLayerTree->layerTreeModel()->rootGroup()->findLayers() )
nodeLayer->setVisible( Qt::Checked );
nodeLayer->setItemVisibilityChecked( true );
}
void QgsOfflineEditingPluginGui::unSelectAll()
{
Q_FOREACH ( QgsLayerTreeLayer* nodeLayer, mLayerTree->layerTreeModel()->rootGroup()->findLayers() )
nodeLayer->setVisible( Qt::Unchecked );
nodeLayer->setItemVisibilityChecked( false );
}

View File

@ -37,8 +37,7 @@ class TestQgsLayerTree : public QObject
void cleanupTestCase();
void testGroupNameChanged();
void testLayerNameChanged();
void testCheckStateParentToChild();
void testCheckStateChildToParent();
void testCheckStateHiearchical();
void testCheckStateMutuallyExclusive();
void testCheckStateMutuallyExclusiveEdgeCases();
void testShowHideAllSymbolNodes();
@ -53,13 +52,19 @@ class TestQgsLayerTree : public QObject
void testRendererLegend( QgsFeatureRenderer* renderer );
Qt::CheckState childState( int childIndex )
bool childVisiblity( int childIndex ) const
{
return QgsLayerTree::toGroup( mRoot->children().at( childIndex ) )->isVisible();
return mRoot->children().at( childIndex )->isVisible();
}
void setChildState( int childIndex, Qt::CheckState state )
bool visibilityChecked( int childIndex ) const
{
QgsLayerTree::toGroup( mRoot->children().at( childIndex ) )->setVisible( state );
return mRoot->children().at( childIndex )->itemVisibilityChecked();
}
void setVisibilityChecked( int childIndex, bool state )
{
mRoot->children().at( childIndex )->setItemVisibilityChecked( state );
}
};
@ -142,39 +147,27 @@ void TestQgsLayerTree::testLayerNameChanged()
mRoot->removeChildNode( n );
}
void TestQgsLayerTree::testCheckStateParentToChild()
void TestQgsLayerTree::testCheckStateHiearchical()
{
mRoot->setVisible( Qt::Unchecked );
mRoot->setItemVisibilityCheckedRecursive( false );
QCOMPARE( mRoot->isItemVisibilityCheckedRecursive(), false );
QCOMPARE( mRoot->isItemVisibilityUncheckedRecursive(), true );
QCOMPARE( visibilityChecked( 0 ), false );
QCOMPARE( visibilityChecked( 1 ), false );
QCOMPARE( visibilityChecked( 2 ), false );
// all children unchecked
QCOMPARE( childState( 0 ), Qt::Unchecked );
QCOMPARE( childState( 1 ), Qt::Unchecked );
QCOMPARE( childState( 2 ), Qt::Unchecked );
mRoot->children().at( 0 )->setItemVisibilityCheckedParentRecursive( true );
QCOMPARE( mRoot->itemVisibilityChecked(), true );
mRoot->setVisible( Qt::Checked );
QCOMPARE( mRoot->isItemVisibilityCheckedRecursive(), false );
QCOMPARE( mRoot->isItemVisibilityUncheckedRecursive(), false );
// all children checked
QCOMPARE( childState( 0 ), Qt::Checked );
QCOMPARE( childState( 1 ), Qt::Checked );
QCOMPARE( childState( 2 ), Qt::Checked );
}
void TestQgsLayerTree::testCheckStateChildToParent()
{
QCOMPARE( mRoot->isVisible(), Qt::Checked );
// uncheck a child - parent should be partial
setChildState( 0, Qt::Unchecked );
QCOMPARE( mRoot->isVisible(), Qt::PartiallyChecked );
setChildState( 1, Qt::Unchecked );
QCOMPARE( mRoot->isVisible(), Qt::PartiallyChecked );
// uncheck last child - parent should be unchecked
setChildState( 2, Qt::Unchecked );
QCOMPARE( mRoot->isVisible(), Qt::Unchecked );
// go back to original state
mRoot->setVisible( Qt::Checked );
mRoot->setItemVisibilityCheckedRecursive( true );
QCOMPARE( mRoot->isItemVisibilityCheckedRecursive(), true );
QCOMPARE( mRoot->isItemVisibilityUncheckedRecursive(), false );
QCOMPARE( visibilityChecked( 0 ), true );
QCOMPARE( visibilityChecked( 1 ), true );
QCOMPARE( visibilityChecked( 2 ), true );
}
void TestQgsLayerTree::testCheckStateMutuallyExclusive()
@ -182,84 +175,83 @@ void TestQgsLayerTree::testCheckStateMutuallyExclusive()
mRoot->setIsMutuallyExclusive( true );
// only first should be enabled
QCOMPARE( childState( 0 ), Qt::Checked );
QCOMPARE( childState( 1 ), Qt::Unchecked );
QCOMPARE( childState( 2 ), Qt::Unchecked );
QCOMPARE( mRoot->isVisible(), Qt::Checked ); // fully checked, not just partial
QCOMPARE( childVisiblity( 0 ), true );
QCOMPARE( childVisiblity( 1 ), false );
QCOMPARE( childVisiblity( 2 ), false );
QCOMPARE( mRoot->isVisible(), true ); // fully checked, not just partial
// switch to some other child
setChildState( 2, Qt::Checked );
QCOMPARE( childState( 0 ), Qt::Unchecked );
QCOMPARE( childState( 1 ), Qt::Unchecked );
QCOMPARE( childState( 2 ), Qt::Checked );
QCOMPARE( mRoot->isVisible(), Qt::Checked );
setVisibilityChecked( 2, true );
QCOMPARE( childVisiblity( 0 ), false );
QCOMPARE( childVisiblity( 1 ), false );
QCOMPARE( childVisiblity( 2 ), true );
QCOMPARE( mRoot->isVisible(), true );
// now uncheck the root
mRoot->setVisible( Qt::Unchecked );
QCOMPARE( childState( 0 ), Qt::Unchecked );
QCOMPARE( childState( 1 ), Qt::Unchecked );
QCOMPARE( childState( 2 ), Qt::Unchecked );
QCOMPARE( mRoot->isVisible(), Qt::Unchecked );
mRoot->setItemVisibilityChecked( false );
QCOMPARE( mRoot->itemVisibilityChecked(), false );
// check one of the children - should also check the root
setChildState( 2, Qt::Checked );
QCOMPARE( childState( 0 ), Qt::Unchecked );
QCOMPARE( childState( 1 ), Qt::Unchecked );
QCOMPARE( childState( 2 ), Qt::Checked );
QCOMPARE( mRoot->isVisible(), Qt::Checked );
QCOMPARE( childVisiblity( 0 ), false );
QCOMPARE( childVisiblity( 1 ), false );
QCOMPARE( visibilityChecked( 2 ), true );
QCOMPARE( childVisiblity( 2 ), false );
QCOMPARE( mRoot->isVisible(), false );
// uncheck the child - should also uncheck the root
setChildState( 2, Qt::Unchecked );
QCOMPARE( childState( 0 ), Qt::Unchecked );
QCOMPARE( childState( 1 ), Qt::Unchecked );
QCOMPARE( childState( 2 ), Qt::Unchecked );
QCOMPARE( mRoot->isVisible(), Qt::Unchecked );
// check one of the children - should not modify the root
setVisibilityChecked( 2, true );
QCOMPARE( childVisiblity( 0 ), false );
QCOMPARE( childVisiblity( 1 ), false );
QCOMPARE( childVisiblity( 2 ), false );
QCOMPARE( mRoot->itemVisibilityChecked(), false );
QCOMPARE( mRoot->isVisible(), false );
// check the root back - should have the same node
mRoot->setVisible( Qt::Checked );
QCOMPARE( childState( 0 ), Qt::Unchecked );
QCOMPARE( childState( 1 ), Qt::Unchecked );
QCOMPARE( childState( 2 ), Qt::Checked );
QCOMPARE( mRoot->isVisible(), Qt::Checked );
// uncheck the child - should not modify the root
setVisibilityChecked( 2, false );
QCOMPARE( childVisiblity( 0 ), false );
QCOMPARE( childVisiblity( 1 ), false );
QCOMPARE( childVisiblity( 2 ), false );
QCOMPARE( mRoot->itemVisibilityChecked(), false );
QCOMPARE( mRoot->isVisible(), false );
// check the root back
mRoot->setItemVisibilityChecked( true );
setVisibilityChecked( 2, true );
QCOMPARE( childVisiblity( 0 ), false );
QCOMPARE( childVisiblity( 1 ), false );
QCOMPARE( childVisiblity( 2 ), true );
QCOMPARE( mRoot->isVisible(), true );
// remove a child
mRoot->removeChildNode( mRoot->children().at( 0 ) );
QCOMPARE( childState( 0 ), Qt::Unchecked );
QCOMPARE( childState( 1 ), Qt::Checked );
QCOMPARE( mRoot->isVisible(), Qt::Checked );
QCOMPARE( childVisiblity( 0 ), false );
QCOMPARE( childVisiblity( 1 ), true );
QCOMPARE( mRoot->isVisible(), true );
// add the group back - will not be checked
mRoot->insertGroup( 0, QStringLiteral( "grp1" ) );
QCOMPARE( childState( 0 ), Qt::Unchecked );
QCOMPARE( childState( 1 ), Qt::Unchecked );
QCOMPARE( childState( 2 ), Qt::Checked );
QCOMPARE( mRoot->isVisible(), Qt::Checked );
QCOMPARE( childVisiblity( 0 ), false );
QCOMPARE( childVisiblity( 1 ), false );
QCOMPARE( childVisiblity( 2 ), true );
QCOMPARE( mRoot->isVisible(), true );
// remove a child that is checked
mRoot->removeChildNode( mRoot->children().at( 2 ) );
QCOMPARE( childState( 0 ), Qt::Unchecked );
QCOMPARE( childState( 1 ), Qt::Unchecked );
QCOMPARE( mRoot->isVisible(), Qt::Unchecked );
// check the root again - first item should be checked
mRoot->setVisible( Qt::Checked );
QCOMPARE( childState( 0 ), Qt::Checked );
QCOMPARE( childState( 1 ), Qt::Unchecked );
QCOMPARE( mRoot->isVisible(), Qt::Checked );
QCOMPARE( childVisiblity( 0 ), false );
QCOMPARE( childVisiblity( 1 ), false );
QCOMPARE( mRoot->isVisible(), true );
// add the item back
setVisibilityChecked( 0, true );
mRoot->addGroup( QStringLiteral( "grp3" ) );
QCOMPARE( childState( 0 ), Qt::Checked );
QCOMPARE( childState( 1 ), Qt::Unchecked );
QCOMPARE( childState( 2 ), Qt::Unchecked );
QCOMPARE( mRoot->isVisible(), Qt::Checked );
QCOMPARE( childVisiblity( 0 ), true );
QCOMPARE( childVisiblity( 1 ), false );
QCOMPARE( childVisiblity( 2 ), false );
QCOMPARE( mRoot->isVisible(), true );
mRoot->setIsMutuallyExclusive( false );
QCOMPARE( mRoot->isVisible(), Qt::PartiallyChecked );
// go back to original state
mRoot->setVisible( Qt::Checked );
mRoot->setItemVisibilityChecked( true );
}
void TestQgsLayerTree::testCheckStateMutuallyExclusiveEdgeCases()
@ -268,23 +260,23 @@ void TestQgsLayerTree::testCheckStateMutuallyExclusiveEdgeCases()
QgsLayerTreeGroup* root2 = new QgsLayerTreeGroup();
root2->setIsMutuallyExclusive( true );
root2->addGroup( QStringLiteral( "1" ) );
QCOMPARE( QgsLayerTree::toGroup( root2->children().at( 0 ) )->isVisible(), Qt::Checked );
QCOMPARE( QgsLayerTree::toGroup( root2->children().at( 0 ) )->isVisible(), true );
root2->addGroup( QStringLiteral( "2" ) );
QCOMPARE( QgsLayerTree::toGroup( root2->children().at( 0 ) )->isVisible(), Qt::Checked );
QCOMPARE( QgsLayerTree::toGroup( root2->children().at( 1 ) )->isVisible(), Qt::Unchecked );
QCOMPARE( QgsLayerTree::toGroup( root2->children().at( 0 ) )->isVisible(), true );
QCOMPARE( QgsLayerTree::toGroup( root2->children().at( 1 ) )->isVisible(), false );
delete root2;
// check-uncheck the only child
QgsLayerTreeGroup* root3 = new QgsLayerTreeGroup();
root3->setIsMutuallyExclusive( true );
root3->addGroup( QStringLiteral( "1" ) );
QCOMPARE( QgsLayerTree::toGroup( root3->children().at( 0 ) )->isVisible(), Qt::Checked );
QgsLayerTree::toGroup( root3->children().at( 0 ) )->setVisible( Qt::Unchecked );
QCOMPARE( QgsLayerTree::toGroup( root3->children().at( 0 ) )->isVisible(), Qt::Unchecked );
QCOMPARE( root3->isVisible(), Qt::Unchecked );
QgsLayerTree::toGroup( root3->children().at( 0 ) )->setVisible( Qt::Checked );
QCOMPARE( QgsLayerTree::toGroup( root3->children().at( 0 ) )->isVisible(), Qt::Checked );
QCOMPARE( root3->isVisible(), Qt::Checked );
QCOMPARE( QgsLayerTree::toGroup( root3->children().at( 0 ) )->isVisible(), true );
QgsLayerTree::toGroup( root3->children().at( 0 ) )->setItemVisibilityChecked( false );
QCOMPARE( QgsLayerTree::toGroup( root3->children().at( 0 ) )->isVisible(), false );
QCOMPARE( root3->isVisible(), true );
QgsLayerTree::toGroup( root3->children().at( 0 ) )->setItemVisibilityChecked( true );
QCOMPARE( QgsLayerTree::toGroup( root3->children().at( 0 ) )->isVisible(), true );
QCOMPARE( root3->isVisible(), true );
delete root3;
}