Skip to content
Permalink
Browse files

Merge pull request #2189 from mhugo/legend_filter

Add more options for filtering legend elements
  • Loading branch information
Hugo Mercier
Hugo Mercier committed Nov 3, 2015
2 parents 16def06 + d16cdcf commit 4226aba5552439d849beb75fd225224ceee8f54c
Showing with 991 additions and 116 deletions.
  1. +3 −0 python/core/composer/qgsatlascomposition.sip
  2. +10 −0 python/core/composer/qgscomposerlegend.sip
  3. +15 −0 python/core/layertree/qgslayertreemodel.sip
  4. +7 −0 python/core/layertree/qgslayertreeutils.sip
  5. +1 −0 python/gui/gui.sip
  6. +50 −0 python/gui/qgslegendfilterbutton.sip
  7. +86 −15 src/app/composer/qgscomposerlegendwidget.cpp
  8. +7 −1 src/app/composer/qgscomposerlegendwidget.h
  9. +51 −26 src/app/qgisapp.cpp
  10. +6 −3 src/app/qgisapp.h
  11. +35 −13 src/core/composer/qgsatlascomposition.cpp
  12. +7 −1 src/core/composer/qgsatlascomposition.h
  13. +88 −10 src/core/composer/qgscomposerlegend.cpp
  14. +31 −0 src/core/composer/qgscomposerlegend.h
  15. +37 −9 src/core/layertree/qgslayertreemodel.cpp
  16. +21 −3 src/core/layertree/qgslayertreemodel.h
  17. +1 −1 src/core/layertree/qgslayertreemodellegendnode.cpp
  18. +27 −0 src/core/layertree/qgslayertreeutils.cpp
  19. +8 −0 src/core/layertree/qgslayertreeutils.h
  20. +1 −1 src/core/qgslegendrenderer.cpp
  21. +84 −14 src/core/qgsmaphittest.cpp
  22. +24 −1 src/core/qgsmaphittest.h
  23. +2 −0 src/gui/CMakeLists.txt
  24. +128 −0 src/gui/qgslegendfilterbutton.cpp
  25. +85 −0 src/gui/qgslegendfilterbutton.h
  26. +33 −10 src/ui/composer/qgscomposerlegendwidgetbase.ui
  27. +75 −0 tests/src/core/testqgslegendrenderer.cpp
  28. +68 −8 tests/src/python/test_qgsatlascomposition.py
  29. BIN tests/testdata/control_images/expected_atlas_legend/expected_atlas_legend.png
  30. BIN tests/testdata/control_images/expected_atlas_legend/expected_atlas_legend_mask.png
  31. BIN ...ntrol_images/legend/expected_legend_filter_by_expression/expected_legend_filter_by_expression.png
  32. BIN ..._images/legend/expected_legend_filter_by_expression/expected_legend_filter_by_expression_mask.png
  33. BIN ...ata/control_images/legend/expected_legend_filter_by_polygon/expected_legend_filter_by_polygon.png
  34. BIN ...ontrol_images/legend/expected_legend_filter_by_polygon/expected_legend_filter_by_polygon_mask.png
@@ -260,6 +260,9 @@ public:
/** Returns the current atlas feature. Must be called after prepareForFeature(). */
QgsFeature* currentFeature() /Deprecated/;

/** Returns the current atlas geometry in the given projection system (default to the coverage layer's CRS) */
QgsGeometry currentGeometry( const QgsCoordinateReferenceSystem& projectedTo = QgsCoordinateReferenceSystem() ) const;

public slots:

/** Refreshes the current atlas feature, by refetching its attributes from the vector layer provider
@@ -65,6 +65,16 @@ class QgsComposerLegend : QgsComposerItem
//! @note added in 2.6
bool legendFilterByMapEnabled() const;

//! When set to true, during an atlas rendering, it will filter out legend elements
//! where features are outside the current atlas feature.
//! @note added in 2.14
void setLegendFilterOutAtlas( bool doFilter );

//! Whether to filter out legend elements outside of the current atlas feature
//! @see setLegendFilterOutAtlas()
//! @note added in 2.14
bool legendFilterOutAtlas() const;

//setters and getters
void setTitle( const QString& t );
QString title() const;
@@ -133,8 +133,23 @@ class QgsLayerTreeModel : QAbstractItemModel
//! Ownership of map settings pointer does not change.
//! @note added in 2.6
void setLegendFilterByMap( const QgsMapSettings* settings );

//! Filter display of legend nodes for given map settings
//! @param settings Map settings. Setting a null pointer or invalid settings will disable any filter. Ownership is not changed, a copy is made
//! @param useExtent Whether to use the extent of the map settings as a first spatial filter on legend nodes
//! @param polygon If not empty, this polygon will be used instead of the map extent to filter legend nodes
//! @param useExpressions Whether to use legend node filter expressions
//! @note added in 2.14
void setLegendFilter( const QgsMapSettings* settings, bool useExtent = true, const QgsGeometry& polygon = QgsGeometry(), bool useExpressions = true );

//! Returns the current map settings used for legend filtering
//! @deprecated It has been renamed to legendFilterMapSettings()
const QgsMapSettings* legendFilterByMap() const;

//! Returns the current map settings used for the current legend filter (or null if none is enabled)
//! @note added in 2.14
const QgsMapSettings* legendFilterMapSettings() const;

//! Give the layer tree model hints about the currently associated map view
//! so that legend nodes that use map units can be scaled currectly
//! @note added in 2.6
@@ -37,4 +37,11 @@ class QgsLayerTreeUtils

//! get invisible layers
static QStringList invisibleLayerList( QgsLayerTreeNode *node );

//! Set the expression filter of a legend layer
static void setLegendFilterByExpression( QgsLayerTreeLayer& layer, const QString& expr, bool enabled = true );
//! Return the expression filter of a legend layer
static QString legendFilterByExpression( const QgsLayerTreeLayer& layer, bool* enabled = 0 );
//! Test if one of the layers in a group has an expression filter
static bool hasLegendFilterExpression( const QgsLayerTreeGroup& group );
};
@@ -70,6 +70,7 @@
%Include qgshtmlannotationitem.sip
%Include qgsidentifymenu.sip
%Include qgslegendinterface.sip
%Include qgslegendfilterbutton.sip
%Include qgslonglongvalidator.sip
%Include qgsludialog.sip
%Include qgsmanageconnectionsdialog.sip
@@ -0,0 +1,50 @@
/** \ingroup gui
* \class QgsFilterLegendButton
* A tool button that allows to enable or disable legend filter by contents of the map.
* An additional pop down menu allows to define a boolean expression to refine the filtering.
* @note added in 2.14
*/

class QgsLegendFilterButton: public QToolButton
{
%TypeHeaderCode
#include <qgslegendfilterbutton.h>
%End

public:
/**
* Construct a new filter legend button
*
* @param parent The parent QWidget
*/
QgsLegendFilterButton( QWidget* parent = 0 );
~QgsLegendFilterButton();

/**
* Returns the current text used as filter expression
*/
QString expressionText() const;

/**
* Sets the current text used as filter expression.
* This will update the menu
*/
void setExpressionText( const QString& expression );

/**
* Returns the current associated vectorLayer
* May be null
*/
QgsVectorLayer* vectorLayer() const;
/**
* Sets the associated vectorLayer
* May be null
*/
void setVectorLayer( QgsVectorLayer* layer );

signals:
/**
* Emitted when the expression text changes
*/
void expressionTextChanged();
};
@@ -28,6 +28,7 @@
#include "qgisapp.h"
#include "qgsapplication.h"
#include "qgslayertree.h"
#include "qgslayertreeutils.h"
#include "qgslayertreemodel.h"
#include "qgslayertreemodellegendnode.h"
#include "qgslegendrenderer.h"
@@ -119,6 +120,10 @@ QgsComposerLegendWidget::QgsComposerLegendWidget( QgsComposerLegend* legend )
mItemTreeView->setMenuProvider( new QgsComposerLegendMenuProvider( mItemTreeView, this ) );
connect( legend, SIGNAL( itemChanged() ), this, SLOT( setGuiElements() ) );
mWrapCharLineEdit->setText( legend->wrapChar() );

// connect atlas state to the filter legend by atlas checkbox
connect( &legend->composition()->atlasComposition(), SIGNAL( toggled( bool ) ), this, SLOT( updateFilterLegendByAtlasButton() ) );
connect( &legend->composition()->atlasComposition(), SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ), this, SLOT( updateFilterLegendByAtlasButton() ) );
}

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

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

mLegend->update();
mLegend->updateItem();
mLegend->endCommand();

// do not allow editing of model if auto update is on - we would modify project's layer tree
QList<QWidget*> widgets;
widgets << mMoveDownToolButton << mMoveUpToolButton << mRemoveToolButton << mAddToolButton
<< mEditPushButton << mCountToolButton << mUpdateAllPushButton << mAddGroupToolButton;
<< mEditPushButton << mCountToolButton << mUpdateAllPushButton << mAddGroupToolButton
<< mExpressionFilterButton;
Q_FOREACH ( QWidget* w, widgets )
w->setEnabled( state != Qt::Checked );

if ( state == Qt::Unchecked )
{
// update widgets state based on current selection
selectedChanged( QModelIndex(), QModelIndex() );
}
}

void QgsComposerLegendWidget::on_mMapComboBox_currentIndexChanged( int index )
@@ -616,7 +628,7 @@ void QgsComposerLegendWidget::on_mMapComboBox_currentIndexChanged( int index )
{
mLegend->beginCommand( tr( "Legend map changed" ) );
mLegend->setComposerMap( map );
mLegend->update();
mLegend->updateItem();
mLegend->endCommand();
}
}
@@ -748,7 +760,7 @@ void QgsComposerLegendWidget::on_mRemoveToolButton_clicked()
}

mLegend->adjustBoxSize();
mLegend->update();
mLegend->updateItem();
mLegend->endCommand();
}

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

mLegend->adjustBoxSize();
mLegend->update();
mLegend->updateItem();
mLegend->endCommand();
}

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

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

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

mLegend->beginCommand( tr( "Legend updated" ) );
currentNode->setCustomProperty( "showFeatureCount", checked ? 1 : 0 );
mLegend->update();
mLegend->updateItem();
mLegend->adjustBoxSize();
mLegend->endCommand();
}

void QgsComposerLegendWidget::on_mFilterByMapToolButton_clicked( bool checked )
void QgsComposerLegendWidget::on_mFilterByMapToolButton_toggled( bool checked )
{
mLegend->beginCommand( tr( "Legend updated" ) );
mLegend->setLegendFilterByMapEnabled( checked );
mLegend->update();
mLegend->adjustBoxSize();
mLegend->endCommand();
}

void QgsComposerLegendWidget::on_mExpressionFilterButton_toggled( bool checked )
{
if ( !mLegend )
{
return;
}

//get current item
QModelIndex currentIndex = mItemTreeView->currentIndex();
if ( !currentIndex.isValid() )
{
return;
}

QgsLayerTreeNode* currentNode = mItemTreeView->currentNode();
if ( !QgsLayerTree::isLayer( currentNode ) )
return;

QgsLayerTreeUtils::setLegendFilterByExpression( *qobject_cast<QgsLayerTreeLayer*>( currentNode ),
mExpressionFilterButton->expressionText(),
checked );

mLegend->beginCommand( tr( "Legend updated" ) );
mLegend->updateItem();
mLegend->adjustBoxSize();
mLegend->endCommand();
}
@@ -904,11 +943,24 @@ void QgsComposerLegendWidget::on_mAddGroupToolButton_clicked()
{
mLegend->beginCommand( tr( "Legend group added" ) );
mLegend->modelV2()->rootGroup()->addGroup( tr( "Group" ) );
mLegend->update();
mLegend->updateItem();
mLegend->endCommand();
}
}

void QgsComposerLegendWidget::on_mFilterLegendByAtlasCheckBox_toggled( bool toggled )
{
if ( mLegend )
{
mLegend->setLegendFilterOutAtlas( toggled );
// force update of legend when in preview mode
if ( mLegend->composition()->atlasMode() == QgsComposition::PreviewAtlas )
{
mLegend->composition()->atlasComposition().refreshFeature();
}
}
}

void QgsComposerLegendWidget::updateLegend()
{
if ( mLegend )
@@ -918,8 +970,7 @@ void QgsComposerLegendWidget::updateLegend()
// this will reset the model completely, loosing any changes
mLegend->setAutoUpdateModel( true );
mLegend->setAutoUpdateModel( false );

mLegend->update();
mLegend->updateItem();
mLegend->endCommand();
}
}
@@ -997,11 +1048,16 @@ void QgsComposerLegendWidget::selectedChanged( const QModelIndex & current, cons
Q_UNUSED( previous );
QgsDebugMsg( "Entered" );

if ( mLegend && mLegend->autoUpdateModel() )
return;

mCountToolButton->setChecked( false );
mCountToolButton->setEnabled( false );

if ( mLegend && mLegend->autoUpdateModel() )
return;
mExpressionFilterButton->blockSignals( true );
mExpressionFilterButton->setChecked( false );
mExpressionFilterButton->setEnabled( false );
mExpressionFilterButton->blockSignals( false );

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

mCountToolButton->setChecked( currentNode->customProperty( "showFeatureCount", 0 ).toInt() );
mCountToolButton->setEnabled( true );

bool exprEnabled;
QString expr = QgsLayerTreeUtils::legendFilterByExpression( *qobject_cast<QgsLayerTreeLayer*>( currentNode ), &exprEnabled );
mExpressionFilterButton->blockSignals( true );
mExpressionFilterButton->setExpressionText( expr );
mExpressionFilterButton->setVectorLayer( vl );
mExpressionFilterButton->setEnabled( true );
mExpressionFilterButton->setChecked( exprEnabled );
mExpressionFilterButton->blockSignals( false );
}

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

QgsLegendRenderer::setNodeLegendStyle( mItemTreeView->currentNode(), ( QgsComposerLegendStyle::Style ) a->data().toInt() );
mLegend->update();
mLegend->updateItem();
}

void QgsComposerLegendWidget::updateFilterLegendByAtlasButton()
{
const QgsAtlasComposition& atlas = mLegend->composition()->atlasComposition();
mFilterLegendByAtlasCheckBox->setEnabled( atlas.enabled() && atlas.coverageLayer() && atlas.coverageLayer()->geometryType() == QGis::Polygon );
}
@@ -80,11 +80,14 @@ class QgsComposerLegendWidget: public QgsComposerItemBaseWidget, private Ui::Qgs
void on_mAddToolButton_clicked();
void on_mEditPushButton_clicked();
void on_mCountToolButton_clicked( bool checked );
void on_mFilterByMapToolButton_clicked( bool checked );
void on_mExpressionFilterButton_toggled( bool checked );
void on_mFilterByMapToolButton_toggled( bool checked );
void resetLayerNodeToDefaults();
void on_mUpdateAllPushButton_clicked();
void on_mAddGroupToolButton_clicked();

void on_mFilterLegendByAtlasCheckBox_toggled( bool checked );

void selectedChanged( const QModelIndex & current, const QModelIndex & previous );

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

/** update the enabling state of the filter by atlas button */
void updateFilterLegendByAtlasButton();

private:
QgsComposerLegendWidget();
void blockAllSignals( bool b );

0 comments on commit 4226aba

Please sign in to comment.
You can’t perform that action at this time.