Skip to content

Commit

Permalink
[FEATURE] Legend: optional text on top of symbols for vector layers
Browse files Browse the repository at this point in the history
In some cases it is useful to add extra information to the symbols in the legend.
This work allows definition of additional labels in vector layer properties > Legend tab.
  • Loading branch information
wonder-sk committed Apr 18, 2018
1 parent 15bf357 commit 49b02bf
Show file tree
Hide file tree
Showing 17 changed files with 570 additions and 3 deletions.
28 changes: 28 additions & 0 deletions python/core/layertree/qgslayertreemodellegendnode.sip.in
Expand Up @@ -231,6 +231,34 @@ to the associated vector layer's renderer.
.. seealso:: :py:func:`symbol`

.. versionadded:: 2.14
%End

QString textOnSymbolLabel() const;
%Docstring
Returns label of text to be shown on top of the symbol.

.. versionadded:: 3.2
%End

void setTextOnSymbolLabel( const QString &label );
%Docstring
Sets label of text to be shown on top of the symbol.

.. versionadded:: 3.2
%End

QgsTextFormat textOnSymbolTextFormat() const;
%Docstring
Returns text format of the label to be shown on top of the symbol.

.. versionadded:: 3.2
%End

void setTextOnSymbolTextFormat( const QgsTextFormat &format );
%Docstring
Sets format of text to be shown on top of the symbol.

.. versionadded:: 3.2
%End

public slots:
Expand Down
66 changes: 66 additions & 0 deletions python/core/qgsmaplayerlegend.sip.in
Expand Up @@ -11,6 +11,7 @@




class QgsMapLayerLegend : QObject
{
%Docstring
Expand All @@ -31,6 +32,20 @@ Constructor for QgsMapLayerLegend
%End


virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context );
%Docstring
Reads configuration from a DOM element previously written by writeXml()

.. versionadded:: 3.2
%End

virtual QDomElement writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const;
%Docstring
Writes configuration to a DOM element, to be used later with readXml()

.. versionadded:: 3.2
%End

virtual QList<QgsLayerTreeModelLegendNode *> createLayerTreeModelLegendNodes( QgsLayerTreeLayer *nodeLayer ) = 0 /Factory/;
%Docstring
Return list of legend nodes to be used for a particular layer tree layer node.
Expand Down Expand Up @@ -84,6 +99,7 @@ update according to layer node's custom properties (order of items, user labels




class QgsDefaultVectorLayerLegend : QgsMapLayerLegend
{
%Docstring
Expand All @@ -98,8 +114,58 @@ Default legend implementation for vector layers
public:
explicit QgsDefaultVectorLayerLegend( QgsVectorLayer *vl );

bool textOnSymbolEnabled() const;
%Docstring
Returns whether the "text on symbol" functionality is enabled. When enabled, legend symbols
may have extra text rendered on top. The content of labels and their style is controlled
by textOnSymbolContent() and textOnSymbolTextFormat().

.. versionadded:: 3.2
%End

void setTextOnSymbolEnabled( bool enabled );
%Docstring
Sets whether the "text on symbol" functionality is enabled. When enabled, legend symbols
may have extra text rendered on top. The content of labels and their style is controlled
by textOnSymbolContent() and textOnSymbolTextFormat().

.. versionadded:: 3.2
%End

QgsTextFormat textOnSymbolTextFormat() const;
%Docstring
Returns text format of symbol labels for "text on symbol" functionality.

.. versionadded:: 3.2
%End

void setTextOnSymbolTextFormat( const QgsTextFormat &format );
%Docstring
Sets text format of symbol labels for "text on symbol" functionality.

.. versionadded:: 3.2
%End

QHash<QString, QString> textOnSymbolContent() const;
%Docstring
Returns per-symbol content of labels for "text on symbol" functionality. In the passed dictionary
the keys are rule keys of legend items, the values are labels to be shown.

.. versionadded:: 3.2
%End

void setTextOnSymbolContent( const QHash<QString, QString> &content );
%Docstring
Sets per-symbol content of labels for "text on symbol" functionality. In the passed dictionary
the keys are rule keys of legend items, the values are labels to be shown.

.. versionadded:: 3.2
%End

virtual QList<QgsLayerTreeModelLegendNode *> createLayerTreeModelLegendNodes( QgsLayerTreeLayer *nodeLayer ) /Factory/;

virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context );
virtual QDomElement writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const;

};

Expand Down
7 changes: 7 additions & 0 deletions python/gui/qgstextformatwidget.sip.in
Expand Up @@ -48,6 +48,13 @@ Constructor for QgsTextFormatWidget.
QgsTextFormat format() const;
%Docstring
Returns the current formatting settings defined by the widget.
%End

void setFormat( const QgsTextFormat &format );
%Docstring
Sets the current formatting settings

.. versionadded:: 3.2
%End

public slots:
Expand Down
2 changes: 2 additions & 0 deletions src/app/CMakeLists.txt
Expand Up @@ -133,6 +133,7 @@ SET(QGIS_APP_SRCS
qgstextannotationdialog.cpp
qgssvgannotationdialog.cpp
qgsundowidget.cpp
qgsvectorlayerlegendwidget.cpp
qgsvectorlayerproperties.cpp
qgsmapthemes.cpp
qgshandlebadlayers.cpp
Expand Down Expand Up @@ -361,6 +362,7 @@ SET (QGIS_APP_MOC_HDRS
qgssvgannotationdialog.h
qgstextannotationdialog.h
qgsundowidget.h
qgsvectorlayerlegendwidget.h
qgsvectorlayerproperties.h
qgsmapthemes.h
qgshandlebadlayers.h
Expand Down
140 changes: 140 additions & 0 deletions src/app/qgsvectorlayerlegendwidget.cpp
@@ -0,0 +1,140 @@
/***************************************************************************
qgsvectorlayerlegendwidget.cpp
---------------------
Date : April 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 "qgsvectorlayerlegendwidget.h"

#include <QBoxLayout>
#include <QStandardItemModel>
#include <QTreeView>

#include "qgsmaplayerlegend.h"
#include "qgsrenderer.h"
#include "qgssymbollayerutils.h"
#include "qgstextformatwidget.h"
#include "qgsvectorlayer.h"


QgsVectorLayerLegendWidget::QgsVectorLayerLegendWidget( QWidget *parent )
: QWidget( parent )
{
mLegendTreeView = new QTreeView;
mLegendTreeView->setRootIsDecorated( false );

mTextOnSymbolFormatButton = new QPushButton( tr( "Set Text Format..." ) );
connect( mTextOnSymbolFormatButton, &QPushButton::clicked, this, &QgsVectorLayerLegendWidget::openTextFormatWidget );

mTextOnSymbolGroupBox = new QgsCollapsibleGroupBox;

QVBoxLayout *groupLayout = new QVBoxLayout;
groupLayout->addWidget( mLegendTreeView );
groupLayout->addWidget( mTextOnSymbolFormatButton );

mTextOnSymbolGroupBox->setTitle( tr( "Text on Symbols" ) );
mTextOnSymbolGroupBox->setCheckable( true );
mTextOnSymbolGroupBox->setLayout( groupLayout );
mTextOnSymbolGroupBox->setCollapsed( true );

QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget( mTextOnSymbolGroupBox );
setLayout( layout );
}


void QgsVectorLayerLegendWidget::setLayer( QgsVectorLayer *layer )
{
mLayer = layer;

QgsDefaultVectorLayerLegend *legend = qobject_cast<QgsDefaultVectorLayerLegend *>( layer->legend() );
if ( !legend )
return;

mTextOnSymbolGroupBox->setChecked( legend->textOnSymbolEnabled() );
mTextOnSymbolTextFormat = legend->textOnSymbolTextFormat();
QHash<QString, QString> content = legend->textOnSymbolContent();

QStandardItemModel *model = new QStandardItemModel;
model->setColumnCount( 2 );
model->setHorizontalHeaderLabels( QStringList() << tr( "Symbol" ) << tr( "Text" ) );

const QgsLegendSymbolList lst = layer->renderer()->legendSymbolItems();
for ( const QgsLegendSymbolItem &symbolItem : lst )
{
if ( !symbolItem.symbol() )
continue;

QgsRenderContext context;
QSize iconSize( 16, 16 );
QIcon icon = QgsSymbolLayerUtils::symbolPreviewPixmap( symbolItem.symbol(), iconSize, 0, &context );

QStandardItem *item1 = new QStandardItem( icon, symbolItem.label() );
item1->setEditable( false );
QStandardItem *item2 = new QStandardItem;
if ( symbolItem.ruleKey().isEmpty() )
{
item1->setEnabled( false );
item2->setEnabled( true );
}
else
{
item1->setData( symbolItem.ruleKey() );
if ( content.contains( symbolItem.ruleKey() ) )
item2->setText( content.value( symbolItem.ruleKey() ) );
}
model->appendRow( QList<QStandardItem *>() << item1 << item2 );
}
mLegendTreeView->setModel( model );
mLegendTreeView->resizeColumnToContents( 0 );
}


void QgsVectorLayerLegendWidget::applyToLayer()
{
QgsDefaultVectorLayerLegend *legend = new QgsDefaultVectorLayerLegend( mLayer );
legend->setTextOnSymbolEnabled( mTextOnSymbolGroupBox->isChecked() );
legend->setTextOnSymbolTextFormat( mTextOnSymbolTextFormat );

QHash<QString, QString> content;
if ( QStandardItemModel *model = qobject_cast<QStandardItemModel *>( mLegendTreeView->model() ) )
{
for ( int i = 0; i < model->rowCount(); ++i )
{
QString ruleKey = model->item( i, 0 )->data().toString();
QString label = model->item( i, 1 )->text();
if ( !label.isEmpty() )
content[ruleKey] = label;
}
}
legend->setTextOnSymbolContent( content );

mLayer->setLegend( legend );
}


void QgsVectorLayerLegendWidget::openTextFormatWidget()
{
QgsTextFormatWidget *textOnSymbolFormatWidget = new QgsTextFormatWidget( mTextOnSymbolTextFormat );
QDialogButtonBox *dialogButtonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget( textOnSymbolFormatWidget );
layout->addWidget( dialogButtonBox );
QDialog dlg;
connect( dialogButtonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept );
connect( dialogButtonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject );
dlg.setLayout( layout );
if ( !dlg.exec() )
return;

mTextOnSymbolTextFormat = textOnSymbolFormatWidget->format();
}
58 changes: 58 additions & 0 deletions src/app/qgsvectorlayerlegendwidget.h
@@ -0,0 +1,58 @@
/***************************************************************************
qgsvectorlayerlegendwidget.h
---------------------
Date : April 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. *
* *
***************************************************************************/

#ifndef QGSVECTORLAYERLEGENDWIDGET_H
#define QGSVECTORLAYERLEGENDWIDGET_H

#include <QWidget>

#include "qgstextrenderer.h"

class QLabel;
class QPushButton;
class QTreeView;

class QgsCollapsibleGroupBox;
class QgsVectorLayer;

/**
* A widget for configuration of options specific to vector layer's legend.
*/
class QgsVectorLayerLegendWidget : public QWidget
{
Q_OBJECT
public:
explicit QgsVectorLayerLegendWidget( QWidget *parent = nullptr );

//! Initialize widget with a map layer
void setLayer( QgsVectorLayer *layer );

//! Store changes made in the widget to the layer
void applyToLayer();

private slots:
void openTextFormatWidget();

private:
QTreeView *mLegendTreeView = nullptr;
QPushButton *mTextOnSymbolFormatButton = nullptr;
QgsCollapsibleGroupBox *mTextOnSymbolGroupBox = nullptr;
QLabel *mTextOnSymbolLabel = nullptr;

QgsVectorLayer *mLayer = nullptr;
QgsTextFormat mTextOnSymbolTextFormat;
};

#endif // QGSVECTORLAYERLEGENDWIDGET_H
2 changes: 2 additions & 0 deletions src/app/qgsvectorlayerproperties.cpp
Expand Up @@ -302,6 +302,7 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(
mDiagramFrame->setLayout( diagLayout );

// Legend tab
mLegendWidget->setLayer( mLayer );
mLegendConfigEmbeddedWidget->setLayer( mLayer );

// WMS Name as layer short name
Expand Down Expand Up @@ -572,6 +573,7 @@ void QgsVectorLayerProperties::apply()
}

// apply legend settings
mLegendWidget->applyToLayer();
mLegendConfigEmbeddedWidget->applyToLayer();

// save metadata
Expand Down

0 comments on commit 49b02bf

Please sign in to comment.