Skip to content

Commit d16cdcf

Browse files
author
Hugo Mercier
committedNov 3, 2015
Add more options for filtering legend elements
This introduces two new options to filter legend elements: - filter by expression: a boolean expression can be set. Only symbols of features that make the expression evaluated to true will be kept in the legend - filter by polygon: only symbols of features that are inside the given polygon will be part of the legend. The polygon filtering is used in particular for a new option in the composer legend that allows to filter out anything that is not included in the current atlas polygon.
1 parent 16def06 commit d16cdcf

34 files changed

+991
-116
lines changed
 

‎python/core/composer/qgsatlascomposition.sip

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,9 @@ public:
260260
/** Returns the current atlas feature. Must be called after prepareForFeature(). */
261261
QgsFeature* currentFeature() /Deprecated/;
262262

263+
/** Returns the current atlas geometry in the given projection system (default to the coverage layer's CRS) */
264+
QgsGeometry currentGeometry( const QgsCoordinateReferenceSystem& projectedTo = QgsCoordinateReferenceSystem() ) const;
265+
263266
public slots:
264267

265268
/** Refreshes the current atlas feature, by refetching its attributes from the vector layer provider

‎python/core/composer/qgscomposerlegend.sip

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,16 @@ class QgsComposerLegend : QgsComposerItem
6565
//! @note added in 2.6
6666
bool legendFilterByMapEnabled() const;
6767

68+
//! When set to true, during an atlas rendering, it will filter out legend elements
69+
//! where features are outside the current atlas feature.
70+
//! @note added in 2.14
71+
void setLegendFilterOutAtlas( bool doFilter );
72+
73+
//! Whether to filter out legend elements outside of the current atlas feature
74+
//! @see setLegendFilterOutAtlas()
75+
//! @note added in 2.14
76+
bool legendFilterOutAtlas() const;
77+
6878
//setters and getters
6979
void setTitle( const QString& t );
7080
QString title() const;

‎python/core/layertree/qgslayertreemodel.sip

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,23 @@ class QgsLayerTreeModel : QAbstractItemModel
133133
//! Ownership of map settings pointer does not change.
134134
//! @note added in 2.6
135135
void setLegendFilterByMap( const QgsMapSettings* settings );
136+
137+
//! Filter display of legend nodes for given map settings
138+
//! @param settings Map settings. Setting a null pointer or invalid settings will disable any filter. Ownership is not changed, a copy is made
139+
//! @param useExtent Whether to use the extent of the map settings as a first spatial filter on legend nodes
140+
//! @param polygon If not empty, this polygon will be used instead of the map extent to filter legend nodes
141+
//! @param useExpressions Whether to use legend node filter expressions
142+
//! @note added in 2.14
143+
void setLegendFilter( const QgsMapSettings* settings, bool useExtent = true, const QgsGeometry& polygon = QgsGeometry(), bool useExpressions = true );
144+
145+
//! Returns the current map settings used for legend filtering
146+
//! @deprecated It has been renamed to legendFilterMapSettings()
136147
const QgsMapSettings* legendFilterByMap() const;
137148

149+
//! Returns the current map settings used for the current legend filter (or null if none is enabled)
150+
//! @note added in 2.14
151+
const QgsMapSettings* legendFilterMapSettings() const;
152+
138153
//! Give the layer tree model hints about the currently associated map view
139154
//! so that legend nodes that use map units can be scaled currectly
140155
//! @note added in 2.6

‎python/core/layertree/qgslayertreeutils.sip

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,11 @@ class QgsLayerTreeUtils
3737

3838
//! get invisible layers
3939
static QStringList invisibleLayerList( QgsLayerTreeNode *node );
40+
41+
//! Set the expression filter of a legend layer
42+
static void setLegendFilterByExpression( QgsLayerTreeLayer& layer, const QString& expr, bool enabled = true );
43+
//! Return the expression filter of a legend layer
44+
static QString legendFilterByExpression( const QgsLayerTreeLayer& layer, bool* enabled = 0 );
45+
//! Test if one of the layers in a group has an expression filter
46+
static bool hasLegendFilterExpression( const QgsLayerTreeGroup& group );
4047
};

‎python/gui/gui.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
%Include qgshtmlannotationitem.sip
7171
%Include qgsidentifymenu.sip
7272
%Include qgslegendinterface.sip
73+
%Include qgslegendfilterbutton.sip
7374
%Include qgslonglongvalidator.sip
7475
%Include qgsludialog.sip
7576
%Include qgsmanageconnectionsdialog.sip

‎python/gui/qgslegendfilterbutton.sip

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/** \ingroup gui
2+
* \class QgsFilterLegendButton
3+
* A tool button that allows to enable or disable legend filter by contents of the map.
4+
* An additional pop down menu allows to define a boolean expression to refine the filtering.
5+
* @note added in 2.14
6+
*/
7+
8+
class QgsLegendFilterButton: public QToolButton
9+
{
10+
%TypeHeaderCode
11+
#include <qgslegendfilterbutton.h>
12+
%End
13+
14+
public:
15+
/**
16+
* Construct a new filter legend button
17+
*
18+
* @param parent The parent QWidget
19+
*/
20+
QgsLegendFilterButton( QWidget* parent = 0 );
21+
~QgsLegendFilterButton();
22+
23+
/**
24+
* Returns the current text used as filter expression
25+
*/
26+
QString expressionText() const;
27+
28+
/**
29+
* Sets the current text used as filter expression.
30+
* This will update the menu
31+
*/
32+
void setExpressionText( const QString& expression );
33+
34+
/**
35+
* Returns the current associated vectorLayer
36+
* May be null
37+
*/
38+
QgsVectorLayer* vectorLayer() const;
39+
/**
40+
* Sets the associated vectorLayer
41+
* May be null
42+
*/
43+
void setVectorLayer( QgsVectorLayer* layer );
44+
45+
signals:
46+
/**
47+
* Emitted when the expression text changes
48+
*/
49+
void expressionTextChanged();
50+
};

‎src/app/composer/qgscomposerlegendwidget.cpp

Lines changed: 86 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "qgisapp.h"
2929
#include "qgsapplication.h"
3030
#include "qgslayertree.h"
31+
#include "qgslayertreeutils.h"
3132
#include "qgslayertreemodel.h"
3233
#include "qgslayertreemodellegendnode.h"
3334
#include "qgslegendrenderer.h"
@@ -119,6 +120,10 @@ QgsComposerLegendWidget::QgsComposerLegendWidget( QgsComposerLegend* legend )
119120
mItemTreeView->setMenuProvider( new QgsComposerLegendMenuProvider( mItemTreeView, this ) );
120121
connect( legend, SIGNAL( itemChanged() ), this, SLOT( setGuiElements() ) );
121122
mWrapCharLineEdit->setText( legend->wrapChar() );
123+
124+
// connect atlas state to the filter legend by atlas checkbox
125+
connect( &legend->composition()->atlasComposition(), SIGNAL( toggled( bool ) ), this, SLOT( updateFilterLegendByAtlasButton() ) );
126+
connect( &legend->composition()->atlasComposition(), SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ), this, SLOT( updateFilterLegendByAtlasButton() ) );
122127
}
123128

124129
setGuiElements();
@@ -574,15 +579,22 @@ void QgsComposerLegendWidget::on_mCheckBoxAutoUpdate_stateChanged( int state )
574579

575580
mLegend->setAutoUpdateModel( state == Qt::Checked );
576581

577-
mLegend->update();
582+
mLegend->updateItem();
578583
mLegend->endCommand();
579584

580585
// do not allow editing of model if auto update is on - we would modify project's layer tree
581586
QList<QWidget*> widgets;
582587
widgets << mMoveDownToolButton << mMoveUpToolButton << mRemoveToolButton << mAddToolButton
583-
<< mEditPushButton << mCountToolButton << mUpdateAllPushButton << mAddGroupToolButton;
588+
<< mEditPushButton << mCountToolButton << mUpdateAllPushButton << mAddGroupToolButton
589+
<< mExpressionFilterButton;
584590
Q_FOREACH ( QWidget* w, widgets )
585591
w->setEnabled( state != Qt::Checked );
592+
593+
if ( state == Qt::Unchecked )
594+
{
595+
// update widgets state based on current selection
596+
selectedChanged( QModelIndex(), QModelIndex() );
597+
}
586598
}
587599

588600
void QgsComposerLegendWidget::on_mMapComboBox_currentIndexChanged( int index )
@@ -616,7 +628,7 @@ void QgsComposerLegendWidget::on_mMapComboBox_currentIndexChanged( int index )
616628
{
617629
mLegend->beginCommand( tr( "Legend map changed" ) );
618630
mLegend->setComposerMap( map );
619-
mLegend->update();
631+
mLegend->updateItem();
620632
mLegend->endCommand();
621633
}
622634
}
@@ -748,7 +760,7 @@ void QgsComposerLegendWidget::on_mRemoveToolButton_clicked()
748760
}
749761

750762
mLegend->adjustBoxSize();
751-
mLegend->update();
763+
mLegend->updateItem();
752764
mLegend->endCommand();
753765
}
754766

@@ -811,7 +823,7 @@ void QgsComposerLegendWidget::on_mEditPushButton_clicked()
811823
}
812824

813825
mLegend->adjustBoxSize();
814-
mLegend->update();
826+
mLegend->updateItem();
815827
mLegend->endCommand();
816828
}
817829

@@ -853,7 +865,7 @@ void QgsComposerLegendWidget::resetLayerNodeToDefaults()
853865

854866
mItemTreeView->layerTreeModel()->refreshLayerLegend( nodeLayer );
855867

856-
mLegend->update();
868+
mLegend->updateItem();
857869
mLegend->adjustBoxSize();
858870
mLegend->endCommand();
859871
}
@@ -879,16 +891,43 @@ void QgsComposerLegendWidget::on_mCountToolButton_clicked( bool checked )
879891

880892
mLegend->beginCommand( tr( "Legend updated" ) );
881893
currentNode->setCustomProperty( "showFeatureCount", checked ? 1 : 0 );
882-
mLegend->update();
894+
mLegend->updateItem();
883895
mLegend->adjustBoxSize();
884896
mLegend->endCommand();
885897
}
886898

887-
void QgsComposerLegendWidget::on_mFilterByMapToolButton_clicked( bool checked )
899+
void QgsComposerLegendWidget::on_mFilterByMapToolButton_toggled( bool checked )
888900
{
889901
mLegend->beginCommand( tr( "Legend updated" ) );
890902
mLegend->setLegendFilterByMapEnabled( checked );
891-
mLegend->update();
903+
mLegend->adjustBoxSize();
904+
mLegend->endCommand();
905+
}
906+
907+
void QgsComposerLegendWidget::on_mExpressionFilterButton_toggled( bool checked )
908+
{
909+
if ( !mLegend )
910+
{
911+
return;
912+
}
913+
914+
//get current item
915+
QModelIndex currentIndex = mItemTreeView->currentIndex();
916+
if ( !currentIndex.isValid() )
917+
{
918+
return;
919+
}
920+
921+
QgsLayerTreeNode* currentNode = mItemTreeView->currentNode();
922+
if ( !QgsLayerTree::isLayer( currentNode ) )
923+
return;
924+
925+
QgsLayerTreeUtils::setLegendFilterByExpression( *qobject_cast<QgsLayerTreeLayer*>( currentNode ),
926+
mExpressionFilterButton->expressionText(),
927+
checked );
928+
929+
mLegend->beginCommand( tr( "Legend updated" ) );
930+
mLegend->updateItem();
892931
mLegend->adjustBoxSize();
893932
mLegend->endCommand();
894933
}
@@ -904,11 +943,24 @@ void QgsComposerLegendWidget::on_mAddGroupToolButton_clicked()
904943
{
905944
mLegend->beginCommand( tr( "Legend group added" ) );
906945
mLegend->modelV2()->rootGroup()->addGroup( tr( "Group" ) );
907-
mLegend->update();
946+
mLegend->updateItem();
908947
mLegend->endCommand();
909948
}
910949
}
911950

951+
void QgsComposerLegendWidget::on_mFilterLegendByAtlasCheckBox_toggled( bool toggled )
952+
{
953+
if ( mLegend )
954+
{
955+
mLegend->setLegendFilterOutAtlas( toggled );
956+
// force update of legend when in preview mode
957+
if ( mLegend->composition()->atlasMode() == QgsComposition::PreviewAtlas )
958+
{
959+
mLegend->composition()->atlasComposition().refreshFeature();
960+
}
961+
}
962+
}
963+
912964
void QgsComposerLegendWidget::updateLegend()
913965
{
914966
if ( mLegend )
@@ -918,8 +970,7 @@ void QgsComposerLegendWidget::updateLegend()
918970
// this will reset the model completely, loosing any changes
919971
mLegend->setAutoUpdateModel( true );
920972
mLegend->setAutoUpdateModel( false );
921-
922-
mLegend->update();
973+
mLegend->updateItem();
923974
mLegend->endCommand();
924975
}
925976
}
@@ -997,11 +1048,16 @@ void QgsComposerLegendWidget::selectedChanged( const QModelIndex & current, cons
9971048
Q_UNUSED( previous );
9981049
QgsDebugMsg( "Entered" );
9991050

1051+
if ( mLegend && mLegend->autoUpdateModel() )
1052+
return;
1053+
10001054
mCountToolButton->setChecked( false );
10011055
mCountToolButton->setEnabled( false );
10021056

1003-
if ( mLegend && mLegend->autoUpdateModel() )
1004-
return;
1057+
mExpressionFilterButton->blockSignals( true );
1058+
mExpressionFilterButton->setChecked( false );
1059+
mExpressionFilterButton->setEnabled( false );
1060+
mExpressionFilterButton->blockSignals( false );
10051061

10061062
QgsLayerTreeNode* currentNode = mItemTreeView->currentNode();
10071063
if ( !QgsLayerTree::isLayer( currentNode ) )
@@ -1014,6 +1070,15 @@ void QgsComposerLegendWidget::selectedChanged( const QModelIndex & current, cons
10141070

10151071
mCountToolButton->setChecked( currentNode->customProperty( "showFeatureCount", 0 ).toInt() );
10161072
mCountToolButton->setEnabled( true );
1073+
1074+
bool exprEnabled;
1075+
QString expr = QgsLayerTreeUtils::legendFilterByExpression( *qobject_cast<QgsLayerTreeLayer*>( currentNode ), &exprEnabled );
1076+
mExpressionFilterButton->blockSignals( true );
1077+
mExpressionFilterButton->setExpressionText( expr );
1078+
mExpressionFilterButton->setVectorLayer( vl );
1079+
mExpressionFilterButton->setEnabled( true );
1080+
mExpressionFilterButton->setChecked( exprEnabled );
1081+
mExpressionFilterButton->blockSignals( false );
10171082
}
10181083

10191084
void QgsComposerLegendWidget::setCurrentNodeStyleFromAction()
@@ -1023,5 +1088,11 @@ void QgsComposerLegendWidget::setCurrentNodeStyleFromAction()
10231088
return;
10241089

10251090
QgsLegendRenderer::setNodeLegendStyle( mItemTreeView->currentNode(), ( QgsComposerLegendStyle::Style ) a->data().toInt() );
1026-
mLegend->update();
1091+
mLegend->updateItem();
1092+
}
1093+
1094+
void QgsComposerLegendWidget::updateFilterLegendByAtlasButton()
1095+
{
1096+
const QgsAtlasComposition& atlas = mLegend->composition()->atlasComposition();
1097+
mFilterLegendByAtlasCheckBox->setEnabled( atlas.enabled() && atlas.coverageLayer() && atlas.coverageLayer()->geometryType() == QGis::Polygon );
10271098
}

‎src/app/composer/qgscomposerlegendwidget.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,14 @@ class QgsComposerLegendWidget: public QgsComposerItemBaseWidget, private Ui::Qgs
8080
void on_mAddToolButton_clicked();
8181
void on_mEditPushButton_clicked();
8282
void on_mCountToolButton_clicked( bool checked );
83-
void on_mFilterByMapToolButton_clicked( bool checked );
83+
void on_mExpressionFilterButton_toggled( bool checked );
84+
void on_mFilterByMapToolButton_toggled( bool checked );
8485
void resetLayerNodeToDefaults();
8586
void on_mUpdateAllPushButton_clicked();
8687
void on_mAddGroupToolButton_clicked();
8788

89+
void on_mFilterLegendByAtlasCheckBox_toggled( bool checked );
90+
8891
void selectedChanged( const QModelIndex & current, const QModelIndex & previous );
8992

9093
void setCurrentNodeStyleFromAction();
@@ -96,6 +99,9 @@ class QgsComposerLegendWidget: public QgsComposerItemBaseWidget, private Ui::Qgs
9699
/** Sets GUI according to state of mLegend*/
97100
void setGuiElements();
98101

102+
/** update the enabling state of the filter by atlas button */
103+
void updateFilterLegendByAtlasButton();
104+
99105
private:
100106
QgsComposerLegendWidget();
101107
void blockAllSignals( bool b );

0 commit comments

Comments
 (0)
Please sign in to comment.