Skip to content

Commit 4b22175

Browse files
authored
Merge pull request #6057 from wonder-sk/layer-tree-filter-indicator
Layer tree view indicators API + filtered layer indicator
2 parents d9deb23 + bbb2727 commit 4b22175

14 files changed

+772
-3
lines changed

python/gui/gui_auto.sip

Lines changed: 1 addition & 0 deletions
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

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,43 @@ 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+
122+
.. seealso:: :py:func:`removeIndicator`
123+
124+
.. seealso:: :py:func:`indicators`
125+
126+
.. versionadded:: 3.2
127+
%End
128+
129+
void removeIndicator( QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator );
130+
%Docstring
131+
Removes a previously added indicator to a layer tree node. Does not delete the indicator.
132+
133+
.. seealso:: :py:func:`addIndicator`
134+
135+
.. seealso:: :py:func:`indicators`
136+
137+
.. versionadded:: 3.2
138+
%End
139+
140+
QList<QgsLayerTreeViewIndicator *> indicators( QgsLayerTreeNode *node ) const;
141+
%Docstring
142+
Returns list of indicators associated with a particular layer tree node.
143+
144+
.. seealso:: :py:func:`addIndicator`
145+
146+
.. seealso:: :py:func:`removeIndicator`
147+
148+
.. versionadded:: 3.2
112149
%End
113150

114151
public slots:
@@ -153,7 +190,6 @@ Emitted when a current layer is changed
153190
virtual void dropEvent( QDropEvent *event );
154191

155192

156-
157193
protected slots:
158194

159195
void modelRowsInserted( const QModelIndex &index, int start, int end );
@@ -166,6 +202,7 @@ Emitted when a current layer is changed
166202
void onModelReset();
167203

168204
protected:
205+
169206
};
170207

171208

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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+
%Docstring
30+
Constructs an indicator, optionally transferring ownership to a parent QObject
31+
%End
32+
33+
QIcon icon() const;
34+
%Docstring
35+
Indicator icon that will be displayed in the layer tree view
36+
%End
37+
void setIcon( const QIcon &icon );
38+
%Docstring
39+
Sets indicator icon that will be displayed in the layer tree view
40+
%End
41+
42+
QString toolTip() const;
43+
%Docstring
44+
Returns tool tip text that will be shown when user hovers mouse over the indicator
45+
%End
46+
void setToolTip( const QString &tip );
47+
%Docstring
48+
Sets tool tip text
49+
%End
50+
51+
signals:
52+
void clicked( const QModelIndex &index );
53+
%Docstring
54+
Signal that is emitted when user clicks on the indicator
55+
%End
56+
57+
};
58+
59+
/************************************************************************
60+
* This file has been generated automatically from *
61+
* *
62+
* src/gui/layertree/qgslayertreeviewindicator.h *
63+
* *
64+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
65+
************************************************************************/

src/app/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
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

Lines changed: 2 additions & 1 deletion
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 QgsLayerTreeViewFilterIndicatorProvider( mLayerTreeView ); // gets parented to the layer view
36493650

36503651
setupLayerTreeViewFromSettings();
36513652

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
/***************************************************************************
2+
qgslayertreeviewfilterindicator.cpp
3+
--------------------------------------
4+
Date : January 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+
QgsLayerTreeViewFilterIndicatorProvider::QgsLayerTreeViewFilterIndicatorProvider( QgsLayerTreeView *view )
26+
: QObject( view )
27+
, mLayerTreeView( view )
28+
{
29+
mIcon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionFilter2.svg" ) );
30+
31+
QgsLayerTree *tree = mLayerTreeView->layerTreeModel()->rootGroup();
32+
onAddedChildren( tree, 0, tree->children().count() - 1 );
33+
34+
connect( tree, &QgsLayerTree::addedChildren, this, &QgsLayerTreeViewFilterIndicatorProvider::onAddedChildren );
35+
connect( tree, &QgsLayerTree::willRemoveChildren, this, &QgsLayerTreeViewFilterIndicatorProvider::onWillRemoveChildren );
36+
}
37+
38+
39+
void QgsLayerTreeViewFilterIndicatorProvider::onAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
40+
{
41+
// recursively connect to providers' dataChanged() signal
42+
43+
QList<QgsLayerTreeNode *> children = node->children();
44+
for ( int i = indexFrom; i <= indexTo; ++i )
45+
{
46+
QgsLayerTreeNode *childNode = children[i];
47+
48+
if ( QgsLayerTree::isGroup( childNode ) )
49+
{
50+
onAddedChildren( childNode, 0, childNode->children().count() - 1 );
51+
}
52+
else if ( QgsLayerTree::isLayer( childNode ) )
53+
{
54+
QgsLayerTreeLayer *childLayerNode = QgsLayerTree::toLayer( childNode );
55+
if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( childLayerNode->layer() ) )
56+
{
57+
if ( vlayer->dataProvider() )
58+
{
59+
connect( vlayer->dataProvider(), &QgsDataProvider::dataChanged, this, &QgsLayerTreeViewFilterIndicatorProvider::onProviderDataChanged );
60+
61+
addOrRemoveIndicator( childLayerNode, vlayer->dataProvider() );
62+
}
63+
}
64+
else if ( !childLayerNode->layer() )
65+
{
66+
// wait for layer to be loaded (e.g. when loading project, first the tree is loaded, afterwards the references to layers are resolved)
67+
connect( childLayerNode, &QgsLayerTreeLayer::layerLoaded, this, &QgsLayerTreeViewFilterIndicatorProvider::onLayerLoaded );
68+
}
69+
}
70+
}
71+
}
72+
73+
74+
void QgsLayerTreeViewFilterIndicatorProvider::onWillRemoveChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
75+
{
76+
// recursively disconnect from providers' dataChanged() signal
77+
78+
QList<QgsLayerTreeNode *> children = node->children();
79+
for ( int i = indexFrom; i <= indexTo; ++i )
80+
{
81+
QgsLayerTreeNode *childNode = children[i];
82+
83+
if ( QgsLayerTree::isGroup( childNode ) )
84+
{
85+
onWillRemoveChildren( childNode, 0, childNode->children().count() - 1 );
86+
}
87+
else if ( QgsLayerTree::isLayer( childNode ) )
88+
{
89+
QgsLayerTreeLayer *childLayerNode = QgsLayerTree::toLayer( childNode );
90+
if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( childLayerNode->layer() ) )
91+
{
92+
if ( vlayer->dataProvider() )
93+
disconnect( vlayer->dataProvider(), &QgsDataProvider::dataChanged, this, &QgsLayerTreeViewFilterIndicatorProvider::onProviderDataChanged );
94+
}
95+
}
96+
}
97+
}
98+
99+
100+
void QgsLayerTreeViewFilterIndicatorProvider::onLayerLoaded()
101+
{
102+
QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
103+
if ( !nodeLayer )
104+
return;
105+
106+
if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() ) )
107+
{
108+
if ( vlayer->dataProvider() )
109+
{
110+
connect( vlayer->dataProvider(), &QgsDataProvider::dataChanged, this, &QgsLayerTreeViewFilterIndicatorProvider::onProviderDataChanged );
111+
112+
addOrRemoveIndicator( nodeLayer, vlayer->dataProvider() );
113+
}
114+
}
115+
}
116+
117+
118+
void QgsLayerTreeViewFilterIndicatorProvider::onProviderDataChanged()
119+
{
120+
QgsVectorDataProvider *provider = qobject_cast<QgsVectorDataProvider *>( sender() );
121+
if ( !provider )
122+
return;
123+
124+
// walk the tree and find layer node that needs to be updated
125+
const QList<QgsLayerTreeLayer *> layerNodes = mLayerTreeView->layerTreeModel()->rootGroup()->findLayers();
126+
for ( QgsLayerTreeLayer *node : layerNodes )
127+
{
128+
if ( node->layer() && node->layer()->dataProvider() == provider )
129+
{
130+
addOrRemoveIndicator( node, provider );
131+
break;
132+
}
133+
}
134+
}
135+
136+
137+
void QgsLayerTreeViewFilterIndicatorProvider::onIndicatorClicked( const QModelIndex &index )
138+
{
139+
QgsLayerTreeNode *node = mLayerTreeView->layerTreeModel()->index2node( index );
140+
if ( !QgsLayerTree::isLayer( node ) )
141+
return;
142+
143+
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( QgsLayerTree::toLayer( node )->layer() );
144+
if ( !vlayer || !vlayer->dataProvider() )
145+
return;
146+
147+
// launch the query builder
148+
QgsQueryBuilder qb( vlayer );
149+
qb.setSql( vlayer->dataProvider()->subsetString() );
150+
if ( qb.exec() )
151+
vlayer->dataProvider()->setSubsetString( qb.sql() );
152+
}
153+
154+
QgsLayerTreeViewIndicator *QgsLayerTreeViewFilterIndicatorProvider::newIndicator( const QString &filter )
155+
{
156+
QgsLayerTreeViewIndicator *indicator = new QgsLayerTreeViewIndicator( this );
157+
indicator->setIcon( mIcon );
158+
updateIndicator( indicator, filter );
159+
connect( indicator, &QgsLayerTreeViewIndicator::clicked, this, &QgsLayerTreeViewFilterIndicatorProvider::onIndicatorClicked );
160+
mIndicators.insert( indicator );
161+
return indicator;
162+
}
163+
164+
void QgsLayerTreeViewFilterIndicatorProvider::updateIndicator( QgsLayerTreeViewIndicator *indicator, const QString &filter )
165+
{
166+
indicator->setToolTip( QString( "<b>%1:</b><br>%2" ).arg( tr( "Filter" ) ).arg( filter ) );
167+
}
168+
169+
170+
void QgsLayerTreeViewFilterIndicatorProvider::addOrRemoveIndicator( QgsLayerTreeNode *node, QgsVectorDataProvider *provider )
171+
{
172+
QString filter = provider->subsetString();
173+
if ( !filter.isEmpty() )
174+
{
175+
const QList<QgsLayerTreeViewIndicator *> nodeIndicators = mLayerTreeView->indicators( node );
176+
177+
// maybe the indicator exists already
178+
foreach ( QgsLayerTreeViewIndicator *indicator, nodeIndicators )
179+
{
180+
if ( mIndicators.contains( indicator ) )
181+
{
182+
updateIndicator( indicator, filter );
183+
return;
184+
}
185+
}
186+
187+
// it does not exist: need to create a new one
188+
mLayerTreeView->addIndicator( node, newIndicator( filter ) );
189+
}
190+
else
191+
{
192+
const QList<QgsLayerTreeViewIndicator *> nodeIndicators = mLayerTreeView->indicators( node );
193+
194+
// there may be existing indicator we need to get rid of
195+
foreach ( QgsLayerTreeViewIndicator *indicator, nodeIndicators )
196+
{
197+
if ( mIndicators.contains( indicator ) )
198+
{
199+
mLayerTreeView->removeIndicator( node, indicator );
200+
indicator->deleteLater();
201+
return;
202+
}
203+
}
204+
205+
// no indicator was there before, nothing to do
206+
}
207+
}

0 commit comments

Comments
 (0)