Expose proxy model for QgsLegendRenderer to allow filtering rendered content

This commit is contained in:
Nyall Dawson 2024-08-19 10:05:29 +10:00
parent a24fcef948
commit d55b4ea066
10 changed files with 133 additions and 5 deletions

View File

@ -78,6 +78,13 @@ If the list is empty, all types are shown.
virtual QVariant data( const QModelIndex &index, int role ) const;
virtual bool setData( const QModelIndex &index, const QVariant &value, int role );
bool nodeShown( QgsLayerTreeNode *node ) const;
%Docstring
Returns ``True`` if the specified ``node`` will be shown in the model.
.. versionadded:: 3.40
%End
public slots:
virtual void setFilterText( const QString &filterText = QString() );
%Docstring

View File

@ -31,6 +31,18 @@ All spacing and sizes are in millimeters.
%Docstring
Constructor for QgsLegendRenderer. The ownership of the legend model is not changed,
and the model must exist for the lifetime of this renderer.
%End
~QgsLegendRenderer();
QgsLayerTreeFilterProxyModel *proxyModel();
%Docstring
Returns the filter proxy model used for filtering the legend model content during
rendering.
Filters can be set on the proxy model to filter rendered legend content.
.. versionadded:: 3.40
%End
QSizeF minimumSize( QgsRenderContext *renderContext = 0 );
@ -101,6 +113,8 @@ Returns the style for the given ``node``, within the specified ``model``.
.. seealso:: :py:func:`setNodeLegendStyle`
%End
private:
QgsLegendRenderer( const QgsLegendRenderer &other );
};
/************************************************************************

View File

@ -78,6 +78,13 @@ If the list is empty, all types are shown.
virtual QVariant data( const QModelIndex &index, int role ) const;
virtual bool setData( const QModelIndex &index, const QVariant &value, int role );
bool nodeShown( QgsLayerTreeNode *node ) const;
%Docstring
Returns ``True`` if the specified ``node`` will be shown in the model.
.. versionadded:: 3.40
%End
public slots:
virtual void setFilterText( const QString &filterText = QString() );
%Docstring

View File

@ -31,6 +31,18 @@ All spacing and sizes are in millimeters.
%Docstring
Constructor for QgsLegendRenderer. The ownership of the legend model is not changed,
and the model must exist for the lifetime of this renderer.
%End
~QgsLegendRenderer();
QgsLayerTreeFilterProxyModel *proxyModel();
%Docstring
Returns the filter proxy model used for filtering the legend model content during
rendering.
Filters can be set on the proxy model to filter rendered legend content.
.. versionadded:: 3.40
%End
QSizeF minimumSize( QgsRenderContext *renderContext = 0 );
@ -101,6 +113,8 @@ Returns the style for the given ``node``, within the specified ``model``.
.. seealso:: :py:func:`setNodeLegendStyle`
%End
private:
QgsLegendRenderer( const QgsLegendRenderer &other );
};
/************************************************************************

View File

@ -76,6 +76,13 @@ class CORE_EXPORT QgsLayerTreeFilterProxyModel : public QSortFilterProxyModel
virtual QVariant data( const QModelIndex &index, int role ) const override;
virtual bool setData( const QModelIndex &index, const QVariant &value, int role ) override;
/**
* Returns TRUE if the specified \a node will be shown in the model.
*
* \since QGIS 3.40
*/
bool nodeShown( QgsLayerTreeNode *node ) const;
public slots:
//! Sets the filter text to search for a layer in the tree
virtual void setFilterText( const QString &filterText = QString() );
@ -97,8 +104,6 @@ class CORE_EXPORT QgsLayerTreeFilterProxyModel : public QSortFilterProxyModel
*/
virtual bool layerShown( QgsMapLayer *layer ) const;
bool nodeShown( QgsLayerTreeNode *node ) const;
//! This will call the virtual method and takes care of emitting dataChanged signal
void setLayerCheckedPrivate( QgsMapLayer *layer, bool checked );

View File

@ -22,6 +22,8 @@
#include "qgsrendercontext.h"
#include "qgsexpressioncontextutils.h"
#include "qgstextrenderer.h"
#include "qgslayertreefilterproxymodel.h"
#include <QJsonObject>
#include <QPainter>
@ -29,10 +31,14 @@
QgsLegendRenderer::QgsLegendRenderer( QgsLayerTreeModel *legendModel, const QgsLegendSettings &settings )
: mLegendModel( legendModel )
, mProxyModel( std::make_unique< QgsLayerTreeFilterProxyModel >() )
, mSettings( settings )
{
mProxyModel->setLayerTreeModel( mLegendModel );
}
QgsLegendRenderer::~QgsLegendRenderer() = default;
QSizeF QgsLegendRenderer::minimumSize( QgsRenderContext *renderContext )
{
std::unique_ptr< QgsRenderContext > tmpContext;
@ -86,6 +92,9 @@ QJsonObject QgsLegendRenderer::exportLegendToJson( const QgsRenderContext &conte
const QList<QgsLayerTreeNode *> childNodes = nodeGroup->children();
for ( QgsLayerTreeNode *node : childNodes )
{
if ( !mProxyModel->nodeShown( node ) )
continue;
if ( QgsLayerTree::isGroup( node ) )
{
QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
@ -316,6 +325,9 @@ QList<QgsLegendRenderer::LegendComponentGroup> QgsLegendRenderer::createComponen
const QList<QgsLayerTreeNode *> childNodes = parentGroup->children();
for ( QgsLayerTreeNode *node : childNodes )
{
if ( !mProxyModel->nodeShown( node ) )
continue;
if ( QgsLayerTree::isGroup( node ) )
{
QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
@ -1087,6 +1099,11 @@ QgsLegendStyle::Style QgsLegendRenderer::nodeLegendStyle( QgsLayerTreeNode *node
return nodeLegendStyle( node, mLegendModel );
}
QgsLayerTreeFilterProxyModel *QgsLegendRenderer::proxyModel()
{
return mProxyModel.get();
}
void QgsLegendRenderer::setNodeLegendStyle( QgsLayerTreeNode *node, QgsLegendStyle::Style style )
{
QString str;

View File

@ -30,6 +30,7 @@ class QgsLayerTreeModelLegendNode;
class QgsLayerTreeNode;
class QgsSymbol;
class QgsRenderContext;
class QgsLayerTreeFilterProxyModel;
#include "qgslegendsettings.h"
@ -52,6 +53,22 @@ class CORE_EXPORT QgsLegendRenderer
* and the model must exist for the lifetime of this renderer.
*/
QgsLegendRenderer( QgsLayerTreeModel *legendModel, const QgsLegendSettings &settings );
~QgsLegendRenderer();
#ifndef SIP_RUN
QgsLegendRenderer( const QgsLegendRenderer &other ) = delete;
QgsLegendRenderer &operator=( const QgsLegendRenderer &other ) = delete;
#endif
/**
* Returns the filter proxy model used for filtering the legend model content during
* rendering.
*
* Filters can be set on the proxy model to filter rendered legend content.
*
* \since QGIS 3.40
*/
QgsLayerTreeFilterProxyModel *proxyModel();
/**
* Runs the layout algorithm and returns the minimum size required for the legend.
@ -296,6 +313,7 @@ class CORE_EXPORT QgsLegendRenderer
QgsLegendStyle::Style nodeLegendStyle( QgsLayerTreeNode *node );
QgsLayerTreeModel *mLegendModel = nullptr;
std::unique_ptr< QgsLayerTreeFilterProxyModel >mProxyModel;
QgsLegendSettings mSettings;
@ -303,6 +321,10 @@ class CORE_EXPORT QgsLegendRenderer
#endif
#ifdef SIP_RUN
QgsLegendRenderer( const QgsLegendRenderer &other );
#endif
void widthAndOffsetForTitleText( const Qt::AlignmentFlag halignment, double legendWidth, double &width, double &offset ) const;
};

View File

@ -33,6 +33,7 @@
#include "qgsmaplayerlegend.h"
#include "qgspainteffect.h"
#include "qgsproject.h"
#include "qgslayertreefilterproxymodel.h"
#include "qgslegendrenderer.h"
#include "qgsrasterlayer.h"
#include "qgsshadoweffect.h"
@ -73,10 +74,8 @@ static QImage _base64ToImage( const QString &base64 )
return QImage::fromData( bytearray, "PNG" );
}
static QImage _renderLegend( QgsLayerTreeModel *legendModel, QgsLegendSettings &settings )
static QImage _renderLegend( QgsLegendRenderer &legendRenderer )
{
settings.setTitle( QStringLiteral( "Legend" ) );
QgsLegendRenderer legendRenderer( legendModel, settings );
const QSizeF size = legendRenderer.minimumSize();
const int dpi = 96;
@ -104,6 +103,14 @@ static QImage _renderLegend( QgsLayerTreeModel *legendModel, QgsLegendSettings &
return img;
}
static QImage _renderLegend( QgsLayerTreeModel *legendModel, QgsLegendSettings &settings )
{
settings.setTitle( QStringLiteral( "Legend" ) );
QgsLegendRenderer legendRenderer( legendModel, settings );
return _renderLegend( legendRenderer );
}
static QJsonObject _renderJsonLegend( QgsLayerTreeModel *legendModel, const QgsLegendSettings &settings )
{
QgsLegendRenderer legendRenderer( legendModel, settings );
@ -212,6 +219,9 @@ class TestQgsLegendRenderer : public QgsTest
void testLabelLegend();
void testHeatmap();
void testFilteredVector();
void testFilteredRaster();
private:
QgsLayerTree *mRoot = nullptr;
QgsVectorLayer *mVL1 = nullptr ; // line
@ -2045,6 +2055,38 @@ void TestQgsLegendRenderer::testHeatmap()
QVERIFY( _verifyImage( res, QStringLiteral( "heatmap" ) ) );
}
void TestQgsLegendRenderer::testFilteredVector()
{
const QString testName = QStringLiteral( "legend_filtered_vector" );
QgsLayerTreeModel legendModel( mRoot );
QgsLegendSettings settings;
settings.setTitle( QStringLiteral( "Legend" ) );
_setStandardTestFont( settings, QStringLiteral( "Bold" ) );
QgsLegendRenderer legendRenderer( &legendModel, settings );
legendRenderer.proxyModel()->setFilters( Qgis::LayerFilter::VectorLayer );
const QImage res = _renderLegend( legendRenderer );
QVERIFY( _verifyImage( res, testName ) );
}
void TestQgsLegendRenderer::testFilteredRaster()
{
const QString testName = QStringLiteral( "legend_filtered_raster" );
QgsLayerTreeModel legendModel( mRoot );
QgsLegendSettings settings;
settings.setTitle( QStringLiteral( "Legend" ) );
_setStandardTestFont( settings, QStringLiteral( "Bold" ) );
QgsLegendRenderer legendRenderer( &legendModel, settings );
legendRenderer.proxyModel()->setFilters( Qgis::LayerFilter::RasterLayer );
const QImage res = _renderLegend( legendRenderer );
QVERIFY( _verifyImage( res, testName ) );
}
QGSTEST_MAIN( TestQgsLegendRenderer )
#include "testqgslegendrenderer.moc"

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB