Skip to content

Commit

Permalink
Symbol aware legend expression (#9648)
Browse files Browse the repository at this point in the history
  • Loading branch information
roya0045 authored and m-kuhn committed Jul 15, 2019
1 parent eb73702 commit 248af94
Show file tree
Hide file tree
Showing 24 changed files with 571 additions and 57 deletions.
1 change: 1 addition & 0 deletions images/images.qrc
Expand Up @@ -177,6 +177,7 @@
<file>themes/default/mActionDistributeTop.svg</file>
<file>themes/default/mActionDistributeVCenter.svg</file>
<file>themes/default/mActionDistributeVSpace.svg</file>
<file>themes/default/mActionAddExpression.svg</file>
<file>themes/default/mActionAddLayer.svg</file>
<file>themes/default/mActionAddMeshLayer.svg</file>
<file>themes/default/mActionAddAllToOverview.svg</file>
Expand Down
1 change: 1 addition & 0 deletions images/themes/default/mActionAddExpression.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions python/core/auto_generated/layertree/qgslayertreelayer.sip.in
Expand Up @@ -122,6 +122,20 @@ Also resolves textual references to layers from the project (calls resolveRefere
Resolves reference to layer from stored layer ID (if it has not been resolved already)

.. versionadded:: 3.0
%End

void setLabelExpression( const QString &expression );
%Docstring
set the expression to evaluate

.. versionadded:: 3.10
%End

QString labelExpression() const;
%Docstring
Returns the expression member of the LayerTreeNode

.. versionadded:: 3.10
%End

signals:
Expand Down
Expand Up @@ -12,6 +12,7 @@




class QgsLayerTreeModelLegendNode : QObject
{
%Docstring
Expand All @@ -26,6 +27,12 @@ and customized look.

%TypeHeaderCode
#include "qgslayertreemodellegendnode.h"
%End
%ConvertToSubClassCode
if ( qobject_cast<QgsSymbolLegendNode *> ( sipCpp ) )
sipType = sipType_QgsSymbolLegendNode;
else
sipType = 0;
%End
public:

Expand Down Expand Up @@ -309,6 +316,23 @@ Returns text format of the label to be shown on top of the symbol.
Sets format of text to be shown on top of the symbol.

.. versionadded:: 3.2
%End

QString symbolLabel() const;
%Docstring
Label of the symbol, user defined label will be used, otherwise will default to the label made by QGIS.

.. versionadded:: 3.10
%End

QString evaluateLabel( const QgsExpressionContext &context = QgsExpressionContext(), const QString &label = QString() );
%Docstring
Evaluates and returns the text label of the current node

:param context: extra QgsExpressionContext to use for evaluating the expression
:param label: text to evaluate instead of the layer layertree string

.. versionadded:: 3.10
%End

public slots:
Expand Down
18 changes: 16 additions & 2 deletions python/core/auto_generated/layout/qgslayoutitemlegend.sip.in
Expand Up @@ -24,16 +24,31 @@ Overrides some functionality of QgsLayerTreeModel to better fit the needs of lay
#include "qgslayoutitemlegend.h"
%End
public:
QgsLegendModel( QgsLayerTree *rootNode, QObject *parent /TransferThis/ = 0 );
QgsLegendModel( QgsLayerTree *rootNode, QObject *parent /TransferThis/ = 0, QgsLayoutItemLegend *layout = 0 );
%Docstring
Construct the model based on the given layer tree
%End

QgsLegendModel( QgsLayerTree *rootNode, QgsLayoutItemLegend *layout );
%Docstring
Alternative constructor.
%End

virtual QVariant data( const QModelIndex &index, int role ) const;


virtual Qt::ItemFlags flags( const QModelIndex &index ) const;


signals:

void refreshLegend();
%Docstring
Emitted to refresh the legend.

.. versionadded:: 3.10
%End

};


Expand Down Expand Up @@ -513,7 +528,6 @@ Returns the legend's renderer settings object.
virtual QgsExpressionContext createExpressionContext() const;



public slots:

virtual void refresh();
Expand Down
2 changes: 1 addition & 1 deletion python/core/auto_generated/qgsvectorlayer.sip.in
Expand Up @@ -2279,7 +2279,6 @@ Configuration and logic to apply automatically on any edit happening on this lay




public slots:

void select( QgsFeatureId featureId );
Expand Down Expand Up @@ -2653,6 +2652,7 @@ Emitted when the feature count for symbols on this layer has been recalculated.
.. versionadded:: 3.0
%End


protected:
virtual void setExtent( const QgsRectangle &rect ) ${SIP_FINAL};

Expand Down
15 changes: 14 additions & 1 deletion python/core/auto_generated/qgsvectorlayerfeaturecounter.sip.in
Expand Up @@ -29,16 +29,29 @@ QgsVectorLayer.countSymbolFeatures() and connect to the signal
Create a new feature counter for ``layer``.
%End


virtual bool run();

%Docstring
Calculates the feature count and Ids per symbol
%End


long featureCount( const QString &legendKey ) const;
%Docstring
Gets the feature count for a particular ``legendKey``.
Returns the feature count for a particular ``legendKey``.
If the key has not been found, -1 will be returned.
%End


QgsFeatureIds featureIds( const QString &symbolkey ) const;
%Docstring
Returns the feature Ids for a particular ``legendKey``.
If the key has not been found an empty QSet will be returned.

.. versionadded:: 3.10
%End

signals:

void symbolsCounted();
Expand Down
58 changes: 58 additions & 0 deletions src/app/layout/qgslayoutlegendwidget.cpp
Expand Up @@ -38,6 +38,7 @@
#include "qgslayoutitemlegend.h"
#include "qgslayoutmeasurementconverter.h"
#include "qgsunittypes.h"
#include "qgsexpressionbuilderdialog.h"

#include <QMessageBox>
#include <QInputDialog>
Expand Down Expand Up @@ -104,6 +105,7 @@ QgsLayoutLegendWidget::QgsLayoutLegendWidget( QgsLayoutItemLegend *legend )
connect( mEditPushButton, &QToolButton::clicked, this, &QgsLayoutLegendWidget::mEditPushButton_clicked );
connect( mCountToolButton, &QToolButton::clicked, this, &QgsLayoutLegendWidget::mCountToolButton_clicked );
connect( mExpressionFilterButton, &QgsLegendFilterButton::toggled, this, &QgsLayoutLegendWidget::mExpressionFilterButton_toggled );
connect( mLayerExpressionButton, &QToolButton::clicked, this, &QgsLayoutLegendWidget::mLayerExpressionButton_clicked );
connect( mFilterByMapToolButton, &QToolButton::toggled, this, &QgsLayoutLegendWidget::mFilterByMapToolButton_toggled );
connect( mUpdateAllPushButton, &QToolButton::clicked, this, &QgsLayoutLegendWidget::mUpdateAllPushButton_clicked );
connect( mAddGroupToolButton, &QToolButton::clicked, this, &QgsLayoutLegendWidget::mAddGroupToolButton_clicked );
Expand Down Expand Up @@ -136,6 +138,7 @@ QgsLayoutLegendWidget::QgsLayoutLegendWidget( QgsLayoutItemLegend *legend )
mMoveUpToolButton->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowUp.svg" ) ) );
mMoveDownToolButton->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowDown.svg" ) ) );
mCountToolButton->setIcon( QIcon( QgsApplication::iconPath( "mActionSum.svg" ) ) );
mLayerExpressionButton->setIcon( QIcon( QgsApplication::iconPath( "mActionAddExpression.svg" ) ) );

mFontColorButton->setColorDialogTitle( tr( "Select Font Color" ) );
mFontColorButton->setContext( QStringLiteral( "composer" ) );
Expand Down Expand Up @@ -994,6 +997,45 @@ void QgsLayoutLegendWidget::mExpressionFilterButton_toggled( bool checked )
mLegend->endCommand();
}

void QgsLayoutLegendWidget::mLayerExpressionButton_clicked()
{

if ( !mLegend )
{
return;
}

QModelIndex currentIndex = mItemTreeView->currentIndex();
if ( !currentIndex.isValid() )
return;

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

QgsLayerTreeLayer *layerNode = qobject_cast<QgsLayerTreeLayer *>( currentNode );
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerNode->layer() );

if ( !vl )
return;

QString currentExpression;
if ( layerNode->labelExpression().isEmpty() )
currentExpression = QStringLiteral( "@symbol_label" );
else
currentExpression = layerNode->labelExpression();
QgsExpressionContext legendContext = mLegend->createExpressionContext();
legendContext.appendScope( vl->createExpressionContextScope() );
QgsExpressionBuilderDialog expressiondialog( vl, currentExpression, nullptr, "generic", legendContext );
if ( expressiondialog.exec() )
layerNode->setLabelExpression( expressiondialog.expressionText() );

mLegend->beginCommand( tr( "Update Legend" ) );
mLegend->updateLegend();
mLegend->adjustBoxSize();
mLegend->endCommand();
}

void QgsLayoutLegendWidget::mUpdateAllPushButton_clicked()
{
updateLegend();
Expand Down Expand Up @@ -1105,12 +1147,27 @@ void QgsLayoutLegendWidget::selectedChanged( const QModelIndex &current, const Q
Q_UNUSED( current )
Q_UNUSED( previous )

mLayerExpressionButton->setEnabled( false );

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

QgsLayerTreeLayer *currentLayerNode = QgsLayerTree::toLayer( currentNode );
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( currentLayerNode->layer() );
if ( !vl )
return;

mLayerExpressionButton->setEnabled( true );
return;
}

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


mExpressionFilterButton->blockSignals( true );
mExpressionFilterButton->setChecked( false );
mExpressionFilterButton->setEnabled( false );
Expand All @@ -1127,6 +1184,7 @@ void QgsLayoutLegendWidget::selectedChanged( const QModelIndex &current, const Q

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

bool exprEnabled;
QString expr = QgsLayerTreeUtils::legendFilterByExpression( *qobject_cast<QgsLayerTreeLayer *>( currentNode ), &exprEnabled );
Expand Down
1 change: 1 addition & 0 deletions src/app/layout/qgslayoutlegendwidget.h
Expand Up @@ -85,6 +85,7 @@ class QgsLayoutLegendWidget: public QgsLayoutItemBaseWidget, private Ui::QgsLayo
void resetLayerNodeToDefaults();
void mUpdateAllPushButton_clicked();
void mAddGroupToolButton_clicked();
void mLayerExpressionButton_clicked();

void mFilterLegendByAtlasCheckBox_toggled( bool checked );

Expand Down
4 changes: 4 additions & 0 deletions src/core/expression/qgsexpression.cpp
Expand Up @@ -830,6 +830,10 @@ void QgsExpression::initVariableHelp()
sVariableHelpTexts.insert( QStringLiteral( "symbol_color" ), QCoreApplication::translate( "symbol_color", "Color of symbol used to render the feature." ) );
sVariableHelpTexts.insert( QStringLiteral( "symbol_angle" ), QCoreApplication::translate( "symbol_angle", "Angle of symbol used to render the feature (valid for marker symbols only)." ) );

sVariableHelpTexts.insert( QStringLiteral( "symbol_label" ), QCoreApplication::translate( "symbol_label", "Label of the symbol, user defined label will be used, otherwise will default to the label made by QGIS." ) );
sVariableHelpTexts.insert( QStringLiteral( "symbol_id" ), QCoreApplication::translate( "symbol_id", "Id of the symbol." ) );
sVariableHelpTexts.insert( QStringLiteral( "symbol_count" ), QCoreApplication::translate( "symbol_count", "Total number of features defined by this symbol." ) );

//cluster variables
sVariableHelpTexts.insert( QStringLiteral( "cluster_color" ), QCoreApplication::translate( "cluster_color", "Color of symbols within a cluster, or NULL if symbols have mixed colors." ) );
sVariableHelpTexts.insert( QStringLiteral( "cluster_size" ), QCoreApplication::translate( "cluster_size", "Number of symbols contained within a cluster." ) );
Expand Down
6 changes: 6 additions & 0 deletions src/core/layertree/qgslayertreelayer.cpp
Expand Up @@ -188,3 +188,9 @@ void QgsLayerTreeLayer::layerNameChanged()
Q_ASSERT( mRef );
emit nameChanged( this, mRef->name() );
}

void QgsLayerTreeLayer::setLabelExpression( const QString &expression )
{
mLabelExpression = expression;
}

16 changes: 16 additions & 0 deletions src/core/layertree/qgslayertreelayer.h
Expand Up @@ -128,6 +128,20 @@ class CORE_EXPORT QgsLayerTreeLayer : public QgsLayerTreeNode
*/
void resolveReferences( const QgsProject *project, bool looseMatching = false ) override;

/**
* set the expression to evaluate
*
* \since QGIS 3.10
*/
void setLabelExpression( const QString &expression );

/**
* Returns the expression member of the LayerTreeNode
*
* \since QGIS 3.10
*/
QString labelExpression() const { return mLabelExpression; }

signals:

/**
Expand All @@ -148,6 +162,8 @@ class CORE_EXPORT QgsLayerTreeLayer : public QgsLayerTreeNode
QgsMapLayerRef mRef;
//! Layer name - only used if layer does not exist or if mUseLayerName is false
QString mLayerName;
//! Expression to evaluate in the legend
QString mLabelExpression;

//!
bool mUseLayerName = true;
Expand Down

0 comments on commit 248af94

Please sign in to comment.