mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
Merge pull request #6817 from wonder-sk/legend-text-on-symbols
Legend: optional text on top of symbols for vector layers
This commit is contained in:
commit
2d7addc9f7
@ -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:
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsMapLayerLegend : QObject
|
||||
{
|
||||
%Docstring
|
||||
@ -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.
|
||||
@ -84,6 +99,7 @@ update according to layer node's custom properties (order of items, user labels
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsDefaultVectorLayerLegend : QgsMapLayerLegend
|
||||
{
|
||||
%Docstring
|
||||
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
@ -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:
|
||||
|
@ -133,6 +133,7 @@ SET(QGIS_APP_SRCS
|
||||
qgstextannotationdialog.cpp
|
||||
qgssvgannotationdialog.cpp
|
||||
qgsundowidget.cpp
|
||||
qgsvectorlayerlegendwidget.cpp
|
||||
qgsvectorlayerproperties.cpp
|
||||
qgsmapthemes.cpp
|
||||
qgshandlebadlayers.cpp
|
||||
@ -361,6 +362,7 @@ SET (QGIS_APP_MOC_HDRS
|
||||
qgssvgannotationdialog.h
|
||||
qgstextannotationdialog.h
|
||||
qgsundowidget.h
|
||||
qgsvectorlayerlegendwidget.h
|
||||
qgsvectorlayerproperties.h
|
||||
qgsmapthemes.h
|
||||
qgshandlebadlayers.h
|
||||
|
194
src/app/qgsvectorlayerlegendwidget.cpp
Normal file
194
src/app/qgsvectorlayerlegendwidget.cpp
Normal file
@ -0,0 +1,194 @@
|
||||
/***************************************************************************
|
||||
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 "qgsexpressionbuilderdialog.h"
|
||||
#include "qgsmapcanvas.h"
|
||||
#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 );
|
||||
|
||||
mTextOnSymbolFromExpressionButton = new QPushButton( tr( "Set Labels from Expression…" ) );
|
||||
connect( mTextOnSymbolFromExpressionButton, &QPushButton::clicked, this, &QgsVectorLayerLegendWidget::labelsFromExpression );
|
||||
|
||||
mTextOnSymbolGroupBox = new QgsCollapsibleGroupBox;
|
||||
|
||||
QHBoxLayout *buttonsLayout = new QHBoxLayout;
|
||||
buttonsLayout->addWidget( mTextOnSymbolFormatButton );
|
||||
buttonsLayout->addWidget( mTextOnSymbolFromExpressionButton );
|
||||
|
||||
QVBoxLayout *groupLayout = new QVBoxLayout;
|
||||
groupLayout->addWidget( mLegendTreeView );
|
||||
groupLayout->addLayout( buttonsLayout );
|
||||
|
||||
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();
|
||||
populateLegendTreeView( legend->textOnSymbolContent() );
|
||||
}
|
||||
|
||||
|
||||
void QgsVectorLayerLegendWidget::populateLegendTreeView( const QHash<QString, QString> &content )
|
||||
{
|
||||
QStandardItemModel *model = new QStandardItemModel;
|
||||
model->setColumnCount( 2 );
|
||||
model->setHorizontalHeaderLabels( QStringList() << tr( "Symbol" ) << tr( "Text" ) );
|
||||
|
||||
const QgsLegendSymbolList lst = mLayer->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();
|
||||
}
|
||||
|
||||
|
||||
void QgsVectorLayerLegendWidget::labelsFromExpression()
|
||||
{
|
||||
QHash<QString, QString> content;
|
||||
QgsRenderContext context( QgsRenderContext::fromMapSettings( mCanvas->mapSettings() ) );
|
||||
|
||||
QgsExpressionBuilderDialog dlgExpression( mLayer );
|
||||
dlgExpression.setExpressionContext( context.expressionContext() );
|
||||
if ( !dlgExpression.exec() )
|
||||
return;
|
||||
|
||||
QgsExpression expr( dlgExpression.expressionText() );
|
||||
expr.prepare( &context.expressionContext() );
|
||||
|
||||
std::unique_ptr< QgsFeatureRenderer > r( mLayer->renderer()->clone() );
|
||||
|
||||
QgsFeature f;
|
||||
QgsFeatureRequest request;
|
||||
request.setSubsetOfAttributes( r->usedAttributes( context ), mLayer->fields() );
|
||||
QgsFeatureIterator fi = mLayer->getFeatures();
|
||||
|
||||
r->startRender( context, mLayer->fields() );
|
||||
while ( fi.nextFeature( f ) )
|
||||
{
|
||||
context.expressionContext().setFeature( f );
|
||||
const QSet<QString> keys = r->legendKeysForFeature( f, context );
|
||||
for ( const QString &key : keys )
|
||||
{
|
||||
if ( content.contains( key ) )
|
||||
continue;
|
||||
|
||||
QString label = expr.evaluate( &context.expressionContext() ).toString();
|
||||
if ( !label.isEmpty() )
|
||||
content[key] = label;
|
||||
}
|
||||
}
|
||||
r->stopRender( context );
|
||||
|
||||
populateLegendTreeView( content );
|
||||
}
|
71
src/app/qgsvectorlayerlegendwidget.h
Normal file
71
src/app/qgsvectorlayerlegendwidget.h
Normal file
@ -0,0 +1,71 @@
|
||||
/***************************************************************************
|
||||
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 QgsMapCanvas;
|
||||
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 );
|
||||
|
||||
//! Sets pointer to map canvas
|
||||
void setMapCanvas( QgsMapCanvas *canvas ) { mCanvas = canvas; }
|
||||
|
||||
//! Returns pointer to map canvas
|
||||
QgsMapCanvas *mapCanvas() const { return mCanvas; }
|
||||
|
||||
//! 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();
|
||||
void labelsFromExpression();
|
||||
|
||||
private:
|
||||
void populateLegendTreeView( const QHash<QString, QString> &content );
|
||||
|
||||
private:
|
||||
QTreeView *mLegendTreeView = nullptr;
|
||||
QPushButton *mTextOnSymbolFormatButton = nullptr;
|
||||
QPushButton *mTextOnSymbolFromExpressionButton = nullptr;
|
||||
QgsCollapsibleGroupBox *mTextOnSymbolGroupBox = nullptr;
|
||||
QLabel *mTextOnSymbolLabel = nullptr;
|
||||
|
||||
QgsMapCanvas *mCanvas = nullptr;
|
||||
QgsVectorLayer *mLayer = nullptr;
|
||||
QgsTextFormat mTextOnSymbolTextFormat;
|
||||
};
|
||||
|
||||
#endif // QGSVECTORLAYERLEGENDWIDGET_H
|
@ -302,6 +302,8 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(
|
||||
mDiagramFrame->setLayout( diagLayout );
|
||||
|
||||
// Legend tab
|
||||
mLegendWidget->setMapCanvas( QgisApp::instance()->mapCanvas() );
|
||||
mLegendWidget->setLayer( mLayer );
|
||||
mLegendConfigEmbeddedWidget->setLayer( mLayer );
|
||||
|
||||
// WMS Name as layer short name
|
||||
@ -572,6 +574,7 @@ void QgsVectorLayerProperties::apply()
|
||||
}
|
||||
|
||||
// apply legend settings
|
||||
mLegendWidget->applyToLayer();
|
||||
mLegendConfigEmbeddedWidget->applyToLayer();
|
||||
|
||||
// save metadata
|
||||
|
@ -178,6 +178,15 @@ QSize QgsSymbolLegendNode::minimumIconSize( QgsRenderContext *context ) const
|
||||
true ).size();
|
||||
}
|
||||
|
||||
if ( !mTextOnSymbolLabel.isEmpty() && context )
|
||||
{
|
||||
double w = QgsTextRenderer::textWidth( *context, mTextOnSymbolTextFormat, QStringList() << mTextOnSymbolLabel );
|
||||
double h = QgsTextRenderer::textHeight( *context, mTextOnSymbolTextFormat, QStringList() << mTextOnSymbolLabel, QgsTextRenderer::Point );
|
||||
int wInt = ceil( w ), hInt = ceil( h );
|
||||
if ( wInt > minSz.width() ) minSz.setWidth( wInt );
|
||||
if ( hInt > minSz.height() ) minSz.setHeight( hInt );
|
||||
}
|
||||
|
||||
if ( mItem.level() != 0 && !( model() && model()->testFlag( QgsLayerTreeModel::ShowLegendAsTree ) ) )
|
||||
minSz.setWidth( mItem.level() * INDENT_SIZE + minSz.width() );
|
||||
|
||||
@ -271,6 +280,17 @@ QVariant QgsSymbolLegendNode::data( int role ) const
|
||||
{
|
||||
std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
|
||||
pix = QgsSymbolLayerUtils::symbolPreviewPixmap( mItem.symbol(), mIconSize, 0, context.get() );
|
||||
|
||||
if ( !mTextOnSymbolLabel.isEmpty() && context )
|
||||
{
|
||||
QPainter painter( &pix );
|
||||
painter.setRenderHint( QPainter::Antialiasing );
|
||||
context->setPainter( &painter );
|
||||
QFontMetricsF fm( mTextOnSymbolTextFormat.scaledFont( *context.get() ) );
|
||||
qreal yBaselineVCenter = ( mIconSize.height() + fm.ascent() - fm.descent() ) / 2;
|
||||
QgsTextRenderer::drawText( QPointF( mIconSize.width() / 2, yBaselineVCenter ), 0, QgsTextRenderer::AlignCenter,
|
||||
QStringList() << mTextOnSymbolLabel, *context.get(), mTextOnSymbolTextFormat );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -418,6 +438,15 @@ QSizeF QgsSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemC
|
||||
{
|
||||
s->drawPreviewIcon( p, QSize( width * dotsPerMM, height * dotsPerMM ), &context );
|
||||
}
|
||||
|
||||
if ( !mTextOnSymbolLabel.isEmpty() )
|
||||
{
|
||||
QFontMetricsF fm( mTextOnSymbolTextFormat.scaledFont( context ) );
|
||||
qreal yBaselineVCenter = ( height * dotsPerMM + fm.ascent() - fm.descent() ) / 2;
|
||||
QgsTextRenderer::drawText( QPointF( width * dotsPerMM / 2, yBaselineVCenter ), 0, QgsTextRenderer::AlignCenter,
|
||||
QStringList() << mTextOnSymbolLabel, context, mTextOnSymbolTextFormat );
|
||||
}
|
||||
|
||||
p->restore();
|
||||
}
|
||||
|
||||
|
@ -144,6 +144,7 @@ class CORE_EXPORT QgsLayerTreeModelLegendNode : public QObject
|
||||
};
|
||||
|
||||
#include "qgslegendsymbolitem.h"
|
||||
#include "qgstextrenderer.h"
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
@ -221,6 +222,30 @@ class CORE_EXPORT QgsSymbolLegendNode : public QgsLayerTreeModelLegendNode
|
||||
*/
|
||||
void setSymbol( QgsSymbol *symbol );
|
||||
|
||||
/**
|
||||
* Returns label of text to be shown on top of the symbol.
|
||||
* \since QGIS 3.2
|
||||
*/
|
||||
QString textOnSymbolLabel() const { return mTextOnSymbolLabel; }
|
||||
|
||||
/**
|
||||
* Sets label of text to be shown on top of the symbol.
|
||||
* \since QGIS 3.2
|
||||
*/
|
||||
void setTextOnSymbolLabel( const QString &label ) { mTextOnSymbolLabel = label; }
|
||||
|
||||
/**
|
||||
* Returns text format of the label to be shown on top of the symbol.
|
||||
* \since QGIS 3.2
|
||||
*/
|
||||
QgsTextFormat textOnSymbolTextFormat() const { return mTextOnSymbolTextFormat; }
|
||||
|
||||
/**
|
||||
* Sets format of text to be shown on top of the symbol.
|
||||
* \since QGIS 3.2
|
||||
*/
|
||||
void setTextOnSymbolTextFormat( const QgsTextFormat &format ) { mTextOnSymbolTextFormat = format; }
|
||||
|
||||
public slots:
|
||||
|
||||
/**
|
||||
@ -247,6 +272,9 @@ class CORE_EXPORT QgsSymbolLegendNode : public QgsLayerTreeModelLegendNode
|
||||
bool mSymbolUsesMapUnits;
|
||||
QSize mIconSize;
|
||||
|
||||
QString mTextOnSymbolLabel;
|
||||
QgsTextFormat mTextOnSymbolTextFormat;
|
||||
|
||||
// ident the symbol icon to make it look like a tree structure
|
||||
static const int INDENT_SIZE = 20;
|
||||
|
||||
|
@ -30,6 +30,19 @@ QgsMapLayerLegend::QgsMapLayerLegend( QObject *parent )
|
||||
{
|
||||
}
|
||||
|
||||
void QgsMapLayerLegend::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
|
||||
{
|
||||
Q_UNUSED( elem );
|
||||
Q_UNUSED( context );
|
||||
}
|
||||
|
||||
QDomElement QgsMapLayerLegend::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
|
||||
{
|
||||
Q_UNUSED( doc );
|
||||
Q_UNUSED( context );
|
||||
return QDomElement();
|
||||
}
|
||||
|
||||
QgsMapLayerLegend *QgsMapLayerLegend::defaultVectorLegend( QgsVectorLayer *vl )
|
||||
{
|
||||
return new QgsDefaultVectorLayerLegend( vl );
|
||||
@ -199,7 +212,15 @@ QList<QgsLayerTreeModelLegendNode *> QgsDefaultVectorLayerLegend::createLayerTre
|
||||
if ( i.dataDefinedSizeLegendSettings() )
|
||||
nodes << new QgsDataDefinedSizeLegendNode( nodeLayer, *i.dataDefinedSizeLegendSettings() );
|
||||
else
|
||||
nodes << new QgsSymbolLegendNode( nodeLayer, i );
|
||||
{
|
||||
QgsSymbolLegendNode *legendNode = new QgsSymbolLegendNode( nodeLayer, i );
|
||||
if ( mTextOnSymbolEnabled && mTextOnSymbolContent.contains( i.ruleKey() ) )
|
||||
{
|
||||
legendNode->setTextOnSymbolLabel( mTextOnSymbolContent.value( i.ruleKey() ) );
|
||||
legendNode->setTextOnSymbolTextFormat( mTextOnSymbolTextFormat );
|
||||
}
|
||||
nodes << legendNode;
|
||||
}
|
||||
}
|
||||
|
||||
if ( nodes.count() == 1 && nodes[0]->data( Qt::EditRole ).toString().isEmpty() )
|
||||
@ -218,6 +239,52 @@ QList<QgsLayerTreeModelLegendNode *> QgsDefaultVectorLayerLegend::createLayerTre
|
||||
return nodes;
|
||||
}
|
||||
|
||||
void QgsDefaultVectorLayerLegend::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
|
||||
{
|
||||
mTextOnSymbolEnabled = false;
|
||||
mTextOnSymbolTextFormat = QgsTextFormat();
|
||||
mTextOnSymbolContent.clear();
|
||||
|
||||
QDomElement tosElem = elem.firstChildElement( QStringLiteral( "text-on-symbol" ) );
|
||||
if ( !tosElem.isNull() )
|
||||
{
|
||||
mTextOnSymbolEnabled = true;
|
||||
QDomElement tosFormatElem = tosElem.firstChildElement( QStringLiteral( "text-style" ) );
|
||||
mTextOnSymbolTextFormat.readXml( tosFormatElem, context );
|
||||
QDomElement tosContentElem = tosElem.firstChildElement( QStringLiteral( "content" ) );
|
||||
QDomElement tosContentItemElem = tosContentElem.firstChildElement( QStringLiteral( "item" ) );
|
||||
while ( !tosContentItemElem.isNull() )
|
||||
{
|
||||
mTextOnSymbolContent.insert( tosContentItemElem.attribute( QStringLiteral( "key" ) ), tosContentItemElem.attribute( QStringLiteral( "value" ) ) );
|
||||
tosContentItemElem = tosContentItemElem.nextSiblingElement( QStringLiteral( "item" ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QDomElement QgsDefaultVectorLayerLegend::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
|
||||
{
|
||||
QDomElement elem = doc.createElement( QStringLiteral( "legend" ) );
|
||||
elem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "default-vector" ) );
|
||||
|
||||
if ( mTextOnSymbolEnabled )
|
||||
{
|
||||
QDomElement tosElem = doc.createElement( QStringLiteral( "text-on-symbol" ) );
|
||||
QDomElement tosFormatElem = mTextOnSymbolTextFormat.writeXml( doc, context );
|
||||
tosElem.appendChild( tosFormatElem );
|
||||
QDomElement tosContentElem = doc.createElement( QStringLiteral( "content" ) );
|
||||
for ( auto it = mTextOnSymbolContent.constBegin(); it != mTextOnSymbolContent.constEnd(); ++it )
|
||||
{
|
||||
QDomElement tosContentItemElem = doc.createElement( QStringLiteral( "item" ) );
|
||||
tosContentItemElem.setAttribute( QStringLiteral( "key" ), it.key() );
|
||||
tosContentItemElem.setAttribute( QStringLiteral( "value" ), it.value() );
|
||||
tosContentElem.appendChild( tosContentItemElem );
|
||||
}
|
||||
tosElem.appendChild( tosContentElem );
|
||||
elem.appendChild( tosElem );
|
||||
}
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
@ -19,10 +19,14 @@
|
||||
#include <QObject>
|
||||
#include "qgis.h"
|
||||
|
||||
class QDomDocument;
|
||||
class QDomElement;
|
||||
|
||||
class QgsLayerTreeLayer;
|
||||
class QgsLayerTreeModelLegendNode;
|
||||
class QgsPluginLayer;
|
||||
class QgsRasterLayer;
|
||||
class QgsReadWriteContext;
|
||||
class QgsVectorLayer;
|
||||
|
||||
#include "qgis_core.h"
|
||||
@ -43,7 +47,19 @@ class CORE_EXPORT QgsMapLayerLegend : public QObject
|
||||
//! Constructor for QgsMapLayerLegend
|
||||
explicit QgsMapLayerLegend( QObject *parent SIP_TRANSFERTHIS = nullptr );
|
||||
|
||||
// TODO: type, load/save settings
|
||||
// TODO: type
|
||||
|
||||
/**
|
||||
* Reads configuration from a DOM element previously written by writeXml()
|
||||
* \since QGIS 3.2
|
||||
*/
|
||||
virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context );
|
||||
|
||||
/**
|
||||
* Writes configuration to a DOM element, to be used later with readXml()
|
||||
* \since QGIS 3.2
|
||||
*/
|
||||
virtual QDomElement writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const;
|
||||
|
||||
/**
|
||||
* Return list of legend nodes to be used for a particular layer tree layer node.
|
||||
@ -89,6 +105,8 @@ class CORE_EXPORT QgsMapLayerLegendUtils
|
||||
|
||||
#include <QHash>
|
||||
|
||||
#include "qgstextrenderer.h"
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
* Default legend implementation for vector layers
|
||||
@ -101,10 +119,59 @@ class CORE_EXPORT QgsDefaultVectorLayerLegend : public QgsMapLayerLegend
|
||||
public:
|
||||
explicit QgsDefaultVectorLayerLegend( QgsVectorLayer *vl );
|
||||
|
||||
/**
|
||||
* 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().
|
||||
* \since QGIS 3.2
|
||||
*/
|
||||
bool textOnSymbolEnabled() const { return mTextOnSymbolEnabled; }
|
||||
|
||||
/**
|
||||
* 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().
|
||||
* \since QGIS 3.2
|
||||
*/
|
||||
void setTextOnSymbolEnabled( bool enabled ) { mTextOnSymbolEnabled = enabled; }
|
||||
|
||||
/**
|
||||
* Returns text format of symbol labels for "text on symbol" functionality.
|
||||
* \since QGIS 3.2
|
||||
*/
|
||||
QgsTextFormat textOnSymbolTextFormat() const { return mTextOnSymbolTextFormat; }
|
||||
|
||||
/**
|
||||
* Sets text format of symbol labels for "text on symbol" functionality.
|
||||
* \since QGIS 3.2
|
||||
*/
|
||||
void setTextOnSymbolTextFormat( const QgsTextFormat &format ) { mTextOnSymbolTextFormat = format; }
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* \since QGIS 3.2
|
||||
*/
|
||||
QHash<QString, QString> textOnSymbolContent() const { return mTextOnSymbolContent; }
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* \since QGIS 3.2
|
||||
*/
|
||||
void setTextOnSymbolContent( const QHash<QString, QString> &content ) { mTextOnSymbolContent = content; }
|
||||
|
||||
QList<QgsLayerTreeModelLegendNode *> createLayerTreeModelLegendNodes( QgsLayerTreeLayer *nodeLayer ) SIP_FACTORY override;
|
||||
virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context ) override;
|
||||
virtual QDomElement writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const override;
|
||||
|
||||
private:
|
||||
QgsVectorLayer *mLayer = nullptr;
|
||||
|
||||
// text on symbol
|
||||
bool mTextOnSymbolEnabled = false;
|
||||
QgsTextFormat mTextOnSymbolTextFormat;
|
||||
QHash<QString, QString> mTextOnSymbolContent;
|
||||
};
|
||||
|
||||
|
||||
|
@ -1423,7 +1423,11 @@ bool QgsVectorLayer::readXml( const QDomNode &layer_node, QgsReadWriteContext &c
|
||||
}
|
||||
setDependencies( sources );
|
||||
|
||||
setLegend( QgsMapLayerLegend::defaultVectorLegend( this ) );
|
||||
QgsMapLayerLegend *legend = QgsMapLayerLegend::defaultVectorLegend( this );
|
||||
QDomElement legendElem = layer_node.firstChildElement( QStringLiteral( "legend" ) );
|
||||
if ( !legendElem.isNull() )
|
||||
legend->readXml( legendElem, context );
|
||||
setLegend( legend );
|
||||
|
||||
// read extent
|
||||
if ( mReadExtentFromXml )
|
||||
@ -1688,6 +1692,14 @@ bool QgsVectorLayer::writeXml( QDomNode &layer_node,
|
||||
}
|
||||
layer_node.appendChild( dataDependenciesElement );
|
||||
|
||||
// legend
|
||||
if ( legend() )
|
||||
{
|
||||
QDomElement legendElement = legend()->writeXml( document, context );
|
||||
if ( !legendElement.isNull() )
|
||||
layer_node.appendChild( legendElement );
|
||||
}
|
||||
|
||||
// save expression fields
|
||||
mExpressionFieldBuffer->writeXml( layer_node, document );
|
||||
|
||||
|
@ -828,6 +828,11 @@ QgsTextFormat QgsTextFormatWidget::format() const
|
||||
return format;
|
||||
}
|
||||
|
||||
void QgsTextFormatWidget::setFormat( const QgsTextFormat &format )
|
||||
{
|
||||
updateWidgetForFormat( format );
|
||||
}
|
||||
|
||||
void QgsTextFormatWidget::optionsStackedWidget_CurrentChanged( int indx )
|
||||
{
|
||||
mLabelingOptionsListWidget->blockSignals( true );
|
||||
|
@ -68,6 +68,12 @@ class GUI_EXPORT QgsTextFormatWidget : public QWidget, protected Ui::QgsTextForm
|
||||
*/
|
||||
QgsTextFormat format() const;
|
||||
|
||||
/**
|
||||
* Sets the current formatting settings
|
||||
* \since QGIS 3.2
|
||||
*/
|
||||
void setFormat( const QgsTextFormat &format );
|
||||
|
||||
public slots:
|
||||
|
||||
/**
|
||||
|
@ -1878,6 +1878,9 @@ border-radius: 2px;</string>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QgsVectorLayerLegendWidget" name="mLegendWidget" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="title">
|
||||
<string>Embedded widgets in legend</string>
|
||||
@ -2416,6 +2419,12 @@ border-radius: 2px;</string>
|
||||
<extends>QWidget</extends>
|
||||
<header>qgscodeeditorhtml.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsVectorLayerLegendWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>qgsvectorlayerlegendwidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>mSearchLineEdit</tabstop>
|
||||
|
@ -122,6 +122,7 @@ class TestQgsLegendRenderer : public QObject
|
||||
void testDiagramAttributeLegend();
|
||||
void testDiagramSizeLegend();
|
||||
void testDataDefinedSizeCollapsed();
|
||||
void testTextOnSymbol();
|
||||
|
||||
private:
|
||||
QgsLayerTree *mRoot = nullptr;
|
||||
@ -783,6 +784,50 @@ void TestQgsLegendRenderer::testDataDefinedSizeCollapsed()
|
||||
delete root;
|
||||
}
|
||||
|
||||
void TestQgsLegendRenderer::testTextOnSymbol()
|
||||
{
|
||||
QString testName = QStringLiteral( "legend_text_on_symbol" );
|
||||
|
||||
QgsVectorLayer *vl = new QgsVectorLayer( QStringLiteral( "Polygon" ), QStringLiteral( "Polygon Layer" ), QStringLiteral( "memory" ) );
|
||||
|
||||
QgsCategoryList cats;
|
||||
QgsFillSymbol *sym_1 = new QgsFillSymbol();
|
||||
sym_1->setColor( Qt::red );
|
||||
cats << QgsRendererCategory( 1, sym_1, QStringLiteral( "Red" ) );
|
||||
QgsFillSymbol *sym_2 = new QgsFillSymbol();
|
||||
sym_2->setColor( Qt::green );
|
||||
cats << QgsRendererCategory( 2, sym_2, QStringLiteral( "Green" ) );
|
||||
QgsFillSymbol *sym_3 = new QgsFillSymbol();
|
||||
sym_3->setColor( Qt::blue );
|
||||
cats << QgsRendererCategory( 3, sym_3, QStringLiteral( "Blue" ) );
|
||||
QgsCategorizedSymbolRenderer *r = new QgsCategorizedSymbolRenderer( QStringLiteral( "test_attr" ), cats );
|
||||
vl->setRenderer( r );
|
||||
|
||||
QgsDefaultVectorLayerLegend *legend = new QgsDefaultVectorLayerLegend( vl );
|
||||
legend->setTextOnSymbolEnabled( true );
|
||||
QHash<QString, QString> content;
|
||||
content["0"] = "Rd";
|
||||
content["2"] = "Bl";
|
||||
legend->setTextOnSymbolContent( content );
|
||||
QgsTextFormat textFormat;
|
||||
textFormat.setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Roman" ) ) );
|
||||
textFormat.setSize( 9 );
|
||||
legend->setTextOnSymbolTextFormat( textFormat );
|
||||
vl->setLegend( legend );
|
||||
|
||||
QgsLayerTree *root = new QgsLayerTree();
|
||||
root->addLayer( vl );
|
||||
|
||||
QgsLayerTreeModel legendModel( root );
|
||||
|
||||
QgsLegendSettings settings;
|
||||
_setStandardTestFont( settings );
|
||||
_renderLegend( testName, &legendModel, settings );
|
||||
QVERIFY( _verifyImage( testName, mReport ) );
|
||||
|
||||
delete root;
|
||||
}
|
||||
|
||||
|
||||
QGSTEST_MAIN( TestQgsLegendRenderer )
|
||||
#include "testqgslegendrenderer.moc"
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
Loading…
x
Reference in New Issue
Block a user