Skip to content

Commit f56d70f

Browse files
committed
[FEATURE] Indicators for layer tree view + filter indicator implementation
This adds a mini-framework for display of extra icons in layer tree views next to layer and group names. Tool tip text can be associated with indicators to give extra context for indicators. In addition, a signal gets emitted when user clicks indicators and custom actions can be defined. The main window's layer tree view (ToC) gets support for indicators that are shown when a vector layer has a filter applied. This makes it easier for users to understand that they are looking at a subset of all data. Clicking the indicator's icon brings up query builder.
1 parent 68ee969 commit f56d70f

14 files changed

+708
-3
lines changed

python/gui/gui_auto.sip

+1
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@
287287
%Include layertree/qgslayertreemapcanvasbridge.sip
288288
%Include layertree/qgslayertreeview.sip
289289
%Include layertree/qgslayertreeviewdefaultactions.sip
290+
%Include layertree/qgslayertreeviewindicator.sip
290291
%Include layout/qgslayoutcustomdrophandler.sip
291292
%Include layout/qgslayoutdesignerinterface.sip
292293
%Include layout/qgslayoutitemcombobox.sip

python/gui/layertree/qgslayertreeview.sip.in

+32-1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,37 @@ Return list of selected nodes filtered to just layer nodes
109109
QList<QgsMapLayer *> selectedLayers() const;
110110
%Docstring
111111
Get list of selected layers
112+
%End
113+
114+
void addIndicator( QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator );
115+
%Docstring
116+
Adds an indicator to the given layer tree node. Indicators are icons shown next to layer/group names
117+
in the layer tree view. They can be used to show extra information with tree nodes and they allow
118+
user interaction.
119+
120+
Does not take ownership of the indicator. One indicator object may be used for multiple layer tree nodes.
121+
\sa removeIndicator
122+
\sa indicators
123+
124+
.. versionadded:: 3.2
125+
%End
126+
127+
void removeIndicator( QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator );
128+
%Docstring
129+
Removes a previously added indicator to a layer tree node. Does not delete the indicator.
130+
\sa addIndicator
131+
\sa indicators
132+
133+
.. versionadded:: 3.2
134+
%End
135+
136+
QList<QgsLayerTreeViewIndicator *> indicators( QgsLayerTreeNode *node ) const;
137+
%Docstring
138+
Returns list of indicators associated with a particular layer tree node.
139+
\sa addIndicator
140+
\sa removeIndicator
141+
142+
.. versionadded:: 3.2
112143
%End
113144

114145
public slots:
@@ -153,7 +184,6 @@ Emitted when a current layer is changed
153184
virtual void dropEvent( QDropEvent *event );
154185

155186

156-
157187
protected slots:
158188

159189
void modelRowsInserted( const QModelIndex &index, int start, int end );
@@ -166,6 +196,7 @@ Emitted when a current layer is changed
166196
void onModelReset();
167197

168198
protected:
199+
169200
};
170201

171202

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/gui/layertree/qgslayertreeviewindicator.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
12+
class QgsLayerTreeViewIndicator : QObject
13+
{
14+
%Docstring
15+
Indicator that can be used in a layer tree view to display icons next to items of the layer tree.
16+
They add extra context to the item and interactivity (using clicked() signal).
17+
18+
Indicators can be added/removed to individual layer tree items using :py:func:`QgsLayerTreeView.addIndicator()`
19+
and QgsLayerTreeView.removeIndicator() calls.
20+
21+
.. versionadded:: 3.2
22+
%End
23+
24+
%TypeHeaderCode
25+
#include "qgslayertreeviewindicator.h"
26+
%End
27+
public:
28+
explicit QgsLayerTreeViewIndicator( QObject *parent /TransferThis/ = 0 );
29+
30+
QIcon icon() const;
31+
%Docstring
32+
Indicator icon that will be displayed in the layer tree view
33+
%End
34+
void setIcon( const QIcon &icon );
35+
%Docstring
36+
Sets indicator icon that will be displayed in the layer tree view
37+
%End
38+
39+
QString toolTip() const;
40+
%Docstring
41+
Returns tool tip text that will be shown when user hovers mouse over the indicator
42+
%End
43+
void setToolTip( const QString &tip );
44+
%Docstring
45+
Sets tool tip text
46+
%End
47+
48+
signals:
49+
void clicked( const QModelIndex &index );
50+
%Docstring
51+
Signal that is emitted when user clicks on the indicator
52+
%End
53+
54+
};
55+
56+
/************************************************************************
57+
* This file has been generated automatically from *
58+
* *
59+
* src/gui/layertree/qgslayertreeviewindicator.h *
60+
* *
61+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
62+
************************************************************************/

src/app/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ SET(QGIS_APP_SRCS
5252
qgslabelengineconfigdialog.cpp
5353
qgslabelinggui.cpp
5454
qgslabelingwidget.cpp
55+
qgslayertreeviewfilterindicator.cpp
5556
qgsloadstylefromdbdialog.cpp
5657
qgsmapcanvasdockwidget.cpp
5758
qgsmaplayerstyleguiutils.cpp
@@ -263,6 +264,7 @@ SET (QGIS_APP_MOC_HDRS
263264
qgslabelinggui.h
264265
qgslabelingwidget.h
265266
qgslabelpropertydialog.h
267+
qgslayertreeviewfilterindicator.h
266268
qgsloadstylefromdbdialog.h
267269
qgsmapcanvasdockwidget.h
268270
qgsmaplayerstyleguiutils.h

src/app/qgisapp.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
201201
#include "qgslayertreeutils.h"
202202
#include "qgslayertreeview.h"
203203
#include "qgslayertreeviewdefaultactions.h"
204+
#include "qgslayertreeviewfilterindicator.h"
204205
#include "qgslayout.h"
205206
#include "qgslayoutatlas.h"
206207
#include "qgslayoutcustomdrophandler.h"
@@ -3623,7 +3624,6 @@ void QgisApp::addUserInputWidget( QWidget *widget )
36233624
mUserInputDockWidget->addUserInputWidget( widget );
36243625
}
36253626

3626-
36273627
void QgisApp::initLayerTreeView()
36283628
{
36293629
mLayerTreeView->setWhatsThis( tr( "Map legend that displays all the layers currently on the map canvas. Click on the checkbox to turn a layer on or off. Double-click on a layer in the legend to customize its appearance and set other properties." ) );
@@ -3646,6 +3646,7 @@ void QgisApp::initLayerTreeView()
36463646

36473647
mLayerTreeView->setModel( model );
36483648
mLayerTreeView->setMenuProvider( new QgsAppLayerTreeViewMenuProvider( mLayerTreeView, mMapCanvas ) );
3649+
new QgsLayerTreeViewFilterIndicatorManager( mLayerTreeView ); // gets parented to the layer view
36493650

36503651
setupLayerTreeViewFromSettings();
36513652

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/***************************************************************************
2+
qgslayertreeviewfilterindicator.cpp
3+
--------------------------------------
4+
Date : Januray 2018
5+
Copyright : (C) 2018 by Martin Dobias
6+
Email : wonder dot sk at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgslayertreeviewfilterindicator.h"
17+
18+
#include "qgslayertree.h"
19+
#include "qgslayertreemodel.h"
20+
#include "qgslayertreeview.h"
21+
#include "qgsquerybuilder.h"
22+
#include "qgsvectorlayer.h"
23+
24+
25+
QgsLayerTreeViewFilterIndicatorManager::QgsLayerTreeViewFilterIndicatorManager( QgsLayerTreeView *view )
26+
: QObject( view )
27+
, mLayerTreeView( view )
28+
{
29+
mIndicator = new QgsLayerTreeViewIndicator( this );
30+
mIndicator->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionFilter2.svg" ) ) );
31+
mIndicator->setToolTip( "Filtered" );
32+
connect( mIndicator, &QgsLayerTreeViewIndicator::clicked, this, &QgsLayerTreeViewFilterIndicatorManager::onIndicatorClicked );
33+
34+
QgsLayerTree *tree = mLayerTreeView->layerTreeModel()->rootGroup();
35+
onAddedChildren( tree, 0, tree->children().count() - 1 );
36+
37+
connect( tree, &QgsLayerTree::addedChildren, this, &QgsLayerTreeViewFilterIndicatorManager::onAddedChildren );
38+
connect( tree, &QgsLayerTree::willRemoveChildren, this, &QgsLayerTreeViewFilterIndicatorManager::onWillRemoveChildren );
39+
}
40+
41+
42+
void QgsLayerTreeViewFilterIndicatorManager::onAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
43+
{
44+
// recursively connect to providers' dataChanged() signal
45+
46+
QList<QgsLayerTreeNode *> children = node->children();
47+
for ( int i = indexFrom; i <= indexTo; ++i )
48+
{
49+
QgsLayerTreeNode *childNode = children[i];
50+
51+
if ( QgsLayerTree::isGroup( childNode ) )
52+
{
53+
onAddedChildren( childNode, 0, childNode->children().count() - 1 );
54+
}
55+
else if ( QgsLayerTree::isLayer( childNode ) )
56+
{
57+
QgsLayerTreeLayer *childLayerNode = QgsLayerTree::toLayer( childNode );
58+
if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( childLayerNode->layer() ) )
59+
{
60+
if ( vlayer->dataProvider() )
61+
{
62+
connect( vlayer->dataProvider(), &QgsDataProvider::dataChanged, this, &QgsLayerTreeViewFilterIndicatorManager::onProviderDataChanged );
63+
64+
addOrRemoveIndicator( childLayerNode, vlayer->dataProvider() );
65+
}
66+
}
67+
else if ( !childLayerNode->layer() )
68+
{
69+
// wait for layer to be loaded (e.g. when loading project, first the tree is loaded, afterwards the references to layers are resolved)
70+
connect( childLayerNode, &QgsLayerTreeLayer::layerLoaded, this, &QgsLayerTreeViewFilterIndicatorManager::onLayerLoaded );
71+
}
72+
}
73+
}
74+
}
75+
76+
77+
void QgsLayerTreeViewFilterIndicatorManager::onWillRemoveChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
78+
{
79+
// recursively disconnect from providers' dataChanged() signal
80+
81+
QList<QgsLayerTreeNode *> children = node->children();
82+
for ( int i = indexFrom; i <= indexTo; ++i )
83+
{
84+
QgsLayerTreeNode *childNode = children[i];
85+
86+
if ( QgsLayerTree::isGroup( childNode ) )
87+
{
88+
onWillRemoveChildren( childNode, 0, childNode->children().count() - 1 );
89+
}
90+
else if ( QgsLayerTree::isLayer( childNode ) )
91+
{
92+
QgsLayerTreeLayer *childLayerNode = QgsLayerTree::toLayer( childNode );
93+
if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( childLayerNode->layer() ) )
94+
{
95+
if ( vlayer->dataProvider() )
96+
disconnect( vlayer->dataProvider(), &QgsDataProvider::dataChanged, this, &QgsLayerTreeViewFilterIndicatorManager::onProviderDataChanged );
97+
}
98+
}
99+
}
100+
}
101+
102+
103+
void QgsLayerTreeViewFilterIndicatorManager::onLayerLoaded()
104+
{
105+
QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
106+
if ( !nodeLayer )
107+
return;
108+
109+
if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() ) )
110+
{
111+
if ( vlayer->dataProvider() )
112+
{
113+
connect( vlayer->dataProvider(), &QgsDataProvider::dataChanged, this, &QgsLayerTreeViewFilterIndicatorManager::onProviderDataChanged );
114+
115+
addOrRemoveIndicator( nodeLayer, vlayer->dataProvider() );
116+
}
117+
}
118+
}
119+
120+
121+
void QgsLayerTreeViewFilterIndicatorManager::onProviderDataChanged()
122+
{
123+
QgsVectorDataProvider *provider = qobject_cast<QgsVectorDataProvider *>( sender() );
124+
if ( !provider )
125+
return;
126+
127+
// walk the tree and find layer node that needs to be updated
128+
const QList<QgsLayerTreeLayer *> layerNodes = mLayerTreeView->layerTreeModel()->rootGroup()->findLayers();
129+
for ( QgsLayerTreeLayer *node : layerNodes )
130+
{
131+
if ( node->layer() && node->layer()->dataProvider() == provider )
132+
{
133+
addOrRemoveIndicator( node, provider );
134+
break;
135+
}
136+
}
137+
}
138+
139+
140+
void QgsLayerTreeViewFilterIndicatorManager::onIndicatorClicked( const QModelIndex &index )
141+
{
142+
QgsLayerTreeNode *node = mLayerTreeView->layerTreeModel()->index2node( index );
143+
if ( !QgsLayerTree::isLayer( node ) )
144+
return;
145+
146+
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( QgsLayerTree::toLayer( node )->layer() );
147+
if ( !vlayer || !vlayer->dataProvider() )
148+
return;
149+
150+
// launch the query builder
151+
QgsQueryBuilder qb( vlayer );
152+
qb.setSql( vlayer->dataProvider()->subsetString() );
153+
if ( qb.exec() )
154+
vlayer->dataProvider()->setSubsetString( qb.sql() );
155+
}
156+
157+
158+
void QgsLayerTreeViewFilterIndicatorManager::addOrRemoveIndicator( QgsLayerTreeNode *node, QgsVectorDataProvider *provider )
159+
{
160+
QString filter = provider->subsetString();
161+
if ( !filter.isEmpty() )
162+
mLayerTreeView->addIndicator( node, mIndicator );
163+
else
164+
mLayerTreeView->removeIndicator( node, mIndicator );
165+
}
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/***************************************************************************
2+
qgslayertreeviewfilterindicator.h
3+
--------------------------------------
4+
Date : Januray 2018
5+
Copyright : (C) 2018 by Martin Dobias
6+
Email : wonder dot sk at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSLAYERTREEVIEWFILTERINDICATOR_H
17+
#define QGSLAYERTREEVIEWFILTERINDICATOR_H
18+
19+
#include "qgslayertreeviewindicator.h"
20+
21+
class QgsLayerTreeNode;
22+
class QgsLayerTreeView;
23+
class QgsVectorDataProvider;
24+
25+
26+
//! Adds indicators showing whether vector layers have a filter applied.
27+
class QgsLayerTreeViewFilterIndicatorManager : public QObject
28+
{
29+
Q_OBJECT
30+
public:
31+
explicit QgsLayerTreeViewFilterIndicatorManager( QgsLayerTreeView *view );
32+
33+
private slots:
34+
//! Connects to signals of layers newly added to the tree
35+
void onAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo );
36+
//! Disconnects from layers about to be removed from the tree
37+
void onWillRemoveChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo );
38+
//! Starts listening to layer provider's dataChanged signal
39+
void onLayerLoaded();
40+
//! Adds/removes indicator of a layer
41+
void onProviderDataChanged();
42+
43+
void onIndicatorClicked( const QModelIndex &index );
44+
45+
private:
46+
void addOrRemoveIndicator( QgsLayerTreeNode *node, QgsVectorDataProvider *provider );
47+
48+
private:
49+
QgsLayerTreeView *mLayerTreeView;
50+
QgsLayerTreeViewIndicator *mIndicator = nullptr;
51+
};
52+
53+
#endif // QGSLAYERTREEVIEWFILTERINDICATOR_H

0 commit comments

Comments
 (0)