Skip to content
Permalink
Browse files
Merge pull request #6057 from wonder-sk/layer-tree-filter-indicator
Layer tree view indicators API + filtered layer indicator
  • Loading branch information
wonder-sk committed Feb 26, 2018
2 parents d9deb23 + bbb2727 commit 4b22175
Show file tree
Hide file tree
Showing 14 changed files with 772 additions and 3 deletions.
@@ -287,6 +287,7 @@
%Include layertree/qgslayertreemapcanvasbridge.sip
%Include layertree/qgslayertreeview.sip
%Include layertree/qgslayertreeviewdefaultactions.sip
%Include layertree/qgslayertreeviewindicator.sip
%Include layout/qgslayoutcustomdrophandler.sip
%Include layout/qgslayoutdesignerinterface.sip
%Include layout/qgslayoutitemcombobox.sip
@@ -109,6 +109,43 @@ Return list of selected nodes filtered to just layer nodes
QList<QgsMapLayer *> selectedLayers() const;
%Docstring
Get list of selected layers
%End

void addIndicator( QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator );
%Docstring
Adds an indicator to the given layer tree node. Indicators are icons shown next to layer/group names
in the layer tree view. They can be used to show extra information with tree nodes and they allow
user interaction.

Does not take ownership of the indicator. One indicator object may be used for multiple layer tree nodes.

.. seealso:: :py:func:`removeIndicator`

.. seealso:: :py:func:`indicators`

.. versionadded:: 3.2
%End

void removeIndicator( QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator );
%Docstring
Removes a previously added indicator to a layer tree node. Does not delete the indicator.

.. seealso:: :py:func:`addIndicator`

.. seealso:: :py:func:`indicators`

.. versionadded:: 3.2
%End

QList<QgsLayerTreeViewIndicator *> indicators( QgsLayerTreeNode *node ) const;
%Docstring
Returns list of indicators associated with a particular layer tree node.

.. seealso:: :py:func:`addIndicator`

.. seealso:: :py:func:`removeIndicator`

.. versionadded:: 3.2
%End

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



protected slots:

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

protected:

};


@@ -0,0 +1,65 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/layertree/qgslayertreeviewindicator.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/




class QgsLayerTreeViewIndicator : QObject
{
%Docstring
Indicator that can be used in a layer tree view to display icons next to items of the layer tree.
They add extra context to the item and interactivity (using clicked() signal).

Indicators can be added/removed to individual layer tree items using :py:func:`QgsLayerTreeView.addIndicator()`
and QgsLayerTreeView.removeIndicator() calls.

.. versionadded:: 3.2
%End

%TypeHeaderCode
#include "qgslayertreeviewindicator.h"
%End
public:
explicit QgsLayerTreeViewIndicator( QObject *parent /TransferThis/ = 0 );
%Docstring
Constructs an indicator, optionally transferring ownership to a parent QObject
%End

QIcon icon() const;
%Docstring
Indicator icon that will be displayed in the layer tree view
%End
void setIcon( const QIcon &icon );
%Docstring
Sets indicator icon that will be displayed in the layer tree view
%End

QString toolTip() const;
%Docstring
Returns tool tip text that will be shown when user hovers mouse over the indicator
%End
void setToolTip( const QString &tip );
%Docstring
Sets tool tip text
%End

signals:
void clicked( const QModelIndex &index );
%Docstring
Signal that is emitted when user clicks on the indicator
%End

};

/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/layertree/qgslayertreeviewindicator.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
@@ -52,6 +52,7 @@ SET(QGIS_APP_SRCS
qgslabelengineconfigdialog.cpp
qgslabelinggui.cpp
qgslabelingwidget.cpp
qgslayertreeviewfilterindicator.cpp
qgsloadstylefromdbdialog.cpp
qgsmapcanvasdockwidget.cpp
qgsmaplayerstyleguiutils.cpp
@@ -263,6 +264,7 @@ SET (QGIS_APP_MOC_HDRS
qgslabelinggui.h
qgslabelingwidget.h
qgslabelpropertydialog.h
qgslayertreeviewfilterindicator.h
qgsloadstylefromdbdialog.h
qgsmapcanvasdockwidget.h
qgsmaplayerstyleguiutils.h
@@ -201,6 +201,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
#include "qgslayertreeutils.h"
#include "qgslayertreeview.h"
#include "qgslayertreeviewdefaultactions.h"
#include "qgslayertreeviewfilterindicator.h"
#include "qgslayout.h"
#include "qgslayoutatlas.h"
#include "qgslayoutcustomdrophandler.h"
@@ -3623,7 +3624,6 @@ void QgisApp::addUserInputWidget( QWidget *widget )
mUserInputDockWidget->addUserInputWidget( widget );
}


void QgisApp::initLayerTreeView()
{
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()

mLayerTreeView->setModel( model );
mLayerTreeView->setMenuProvider( new QgsAppLayerTreeViewMenuProvider( mLayerTreeView, mMapCanvas ) );
new QgsLayerTreeViewFilterIndicatorProvider( mLayerTreeView ); // gets parented to the layer view

setupLayerTreeViewFromSettings();

@@ -0,0 +1,207 @@
/***************************************************************************
qgslayertreeviewfilterindicator.cpp
--------------------------------------
Date : January 2018
Copyright : (C) 2018 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgslayertreeviewfilterindicator.h"

#include "qgslayertree.h"
#include "qgslayertreemodel.h"
#include "qgslayertreeview.h"
#include "qgsquerybuilder.h"
#include "qgsvectorlayer.h"


QgsLayerTreeViewFilterIndicatorProvider::QgsLayerTreeViewFilterIndicatorProvider( QgsLayerTreeView *view )
: QObject( view )
, mLayerTreeView( view )
{
mIcon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionFilter2.svg" ) );

QgsLayerTree *tree = mLayerTreeView->layerTreeModel()->rootGroup();
onAddedChildren( tree, 0, tree->children().count() - 1 );

connect( tree, &QgsLayerTree::addedChildren, this, &QgsLayerTreeViewFilterIndicatorProvider::onAddedChildren );
connect( tree, &QgsLayerTree::willRemoveChildren, this, &QgsLayerTreeViewFilterIndicatorProvider::onWillRemoveChildren );
}


void QgsLayerTreeViewFilterIndicatorProvider::onAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
{
// recursively connect to providers' dataChanged() signal

QList<QgsLayerTreeNode *> children = node->children();
for ( int i = indexFrom; i <= indexTo; ++i )
{
QgsLayerTreeNode *childNode = children[i];

if ( QgsLayerTree::isGroup( childNode ) )
{
onAddedChildren( childNode, 0, childNode->children().count() - 1 );
}
else if ( QgsLayerTree::isLayer( childNode ) )
{
QgsLayerTreeLayer *childLayerNode = QgsLayerTree::toLayer( childNode );
if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( childLayerNode->layer() ) )
{
if ( vlayer->dataProvider() )
{
connect( vlayer->dataProvider(), &QgsDataProvider::dataChanged, this, &QgsLayerTreeViewFilterIndicatorProvider::onProviderDataChanged );

addOrRemoveIndicator( childLayerNode, vlayer->dataProvider() );
}
}
else if ( !childLayerNode->layer() )
{
// wait for layer to be loaded (e.g. when loading project, first the tree is loaded, afterwards the references to layers are resolved)
connect( childLayerNode, &QgsLayerTreeLayer::layerLoaded, this, &QgsLayerTreeViewFilterIndicatorProvider::onLayerLoaded );
}
}
}
}


void QgsLayerTreeViewFilterIndicatorProvider::onWillRemoveChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
{
// recursively disconnect from providers' dataChanged() signal

QList<QgsLayerTreeNode *> children = node->children();
for ( int i = indexFrom; i <= indexTo; ++i )
{
QgsLayerTreeNode *childNode = children[i];

if ( QgsLayerTree::isGroup( childNode ) )
{
onWillRemoveChildren( childNode, 0, childNode->children().count() - 1 );
}
else if ( QgsLayerTree::isLayer( childNode ) )
{
QgsLayerTreeLayer *childLayerNode = QgsLayerTree::toLayer( childNode );
if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( childLayerNode->layer() ) )
{
if ( vlayer->dataProvider() )
disconnect( vlayer->dataProvider(), &QgsDataProvider::dataChanged, this, &QgsLayerTreeViewFilterIndicatorProvider::onProviderDataChanged );
}
}
}
}


void QgsLayerTreeViewFilterIndicatorProvider::onLayerLoaded()
{
QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
if ( !nodeLayer )
return;

if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() ) )
{
if ( vlayer->dataProvider() )
{
connect( vlayer->dataProvider(), &QgsDataProvider::dataChanged, this, &QgsLayerTreeViewFilterIndicatorProvider::onProviderDataChanged );

addOrRemoveIndicator( nodeLayer, vlayer->dataProvider() );
}
}
}


void QgsLayerTreeViewFilterIndicatorProvider::onProviderDataChanged()
{
QgsVectorDataProvider *provider = qobject_cast<QgsVectorDataProvider *>( sender() );
if ( !provider )
return;

// walk the tree and find layer node that needs to be updated
const QList<QgsLayerTreeLayer *> layerNodes = mLayerTreeView->layerTreeModel()->rootGroup()->findLayers();
for ( QgsLayerTreeLayer *node : layerNodes )
{
if ( node->layer() && node->layer()->dataProvider() == provider )
{
addOrRemoveIndicator( node, provider );
break;
}
}
}


void QgsLayerTreeViewFilterIndicatorProvider::onIndicatorClicked( const QModelIndex &index )
{
QgsLayerTreeNode *node = mLayerTreeView->layerTreeModel()->index2node( index );
if ( !QgsLayerTree::isLayer( node ) )
return;

QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( QgsLayerTree::toLayer( node )->layer() );
if ( !vlayer || !vlayer->dataProvider() )
return;

// launch the query builder
QgsQueryBuilder qb( vlayer );
qb.setSql( vlayer->dataProvider()->subsetString() );
if ( qb.exec() )
vlayer->dataProvider()->setSubsetString( qb.sql() );
}

QgsLayerTreeViewIndicator *QgsLayerTreeViewFilterIndicatorProvider::newIndicator( const QString &filter )
{
QgsLayerTreeViewIndicator *indicator = new QgsLayerTreeViewIndicator( this );
indicator->setIcon( mIcon );
updateIndicator( indicator, filter );
connect( indicator, &QgsLayerTreeViewIndicator::clicked, this, &QgsLayerTreeViewFilterIndicatorProvider::onIndicatorClicked );
mIndicators.insert( indicator );
return indicator;
}

void QgsLayerTreeViewFilterIndicatorProvider::updateIndicator( QgsLayerTreeViewIndicator *indicator, const QString &filter )
{
indicator->setToolTip( QString( "<b>%1:</b><br>%2" ).arg( tr( "Filter" ) ).arg( filter ) );
}


void QgsLayerTreeViewFilterIndicatorProvider::addOrRemoveIndicator( QgsLayerTreeNode *node, QgsVectorDataProvider *provider )
{
QString filter = provider->subsetString();
if ( !filter.isEmpty() )
{
const QList<QgsLayerTreeViewIndicator *> nodeIndicators = mLayerTreeView->indicators( node );

// maybe the indicator exists already
foreach ( QgsLayerTreeViewIndicator *indicator, nodeIndicators )
{
if ( mIndicators.contains( indicator ) )
{
updateIndicator( indicator, filter );
return;
}
}

// it does not exist: need to create a new one
mLayerTreeView->addIndicator( node, newIndicator( filter ) );
}
else
{
const QList<QgsLayerTreeViewIndicator *> nodeIndicators = mLayerTreeView->indicators( node );

// there may be existing indicator we need to get rid of
foreach ( QgsLayerTreeViewIndicator *indicator, nodeIndicators )
{
if ( mIndicators.contains( indicator ) )
{
mLayerTreeView->removeIndicator( node, indicator );
indicator->deleteLater();
return;
}
}

// no indicator was there before, nothing to do
}
}

0 comments on commit 4b22175

Please sign in to comment.