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 QVariant data( const QModelIndex &index, int role ) const;
virtual bool setData( const QModelIndex &index, const QVariant &value, int role ); 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: public slots:
virtual void setFilterText( const QString &filterText = QString() ); virtual void setFilterText( const QString &filterText = QString() );
%Docstring %Docstring

View File

@ -31,6 +31,18 @@ All spacing and sizes are in millimeters.
%Docstring %Docstring
Constructor for QgsLegendRenderer. The ownership of the legend model is not changed, Constructor for QgsLegendRenderer. The ownership of the legend model is not changed,
and the model must exist for the lifetime of this renderer. 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 %End
QSizeF minimumSize( QgsRenderContext *renderContext = 0 ); QSizeF minimumSize( QgsRenderContext *renderContext = 0 );
@ -101,6 +113,8 @@ Returns the style for the given ``node``, within the specified ``model``.
.. seealso:: :py:func:`setNodeLegendStyle` .. seealso:: :py:func:`setNodeLegendStyle`
%End %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 QVariant data( const QModelIndex &index, int role ) const;
virtual bool setData( const QModelIndex &index, const QVariant &value, int role ); 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: public slots:
virtual void setFilterText( const QString &filterText = QString() ); virtual void setFilterText( const QString &filterText = QString() );
%Docstring %Docstring

View File

@ -31,6 +31,18 @@ All spacing and sizes are in millimeters.
%Docstring %Docstring
Constructor for QgsLegendRenderer. The ownership of the legend model is not changed, Constructor for QgsLegendRenderer. The ownership of the legend model is not changed,
and the model must exist for the lifetime of this renderer. 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 %End
QSizeF minimumSize( QgsRenderContext *renderContext = 0 ); QSizeF minimumSize( QgsRenderContext *renderContext = 0 );
@ -101,6 +113,8 @@ Returns the style for the given ``node``, within the specified ``model``.
.. seealso:: :py:func:`setNodeLegendStyle` .. seealso:: :py:func:`setNodeLegendStyle`
%End %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 QVariant data( const QModelIndex &index, int role ) const override;
virtual bool setData( const QModelIndex &index, const QVariant &value, int role ) 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: public slots:
//! Sets the filter text to search for a layer in the tree //! Sets the filter text to search for a layer in the tree
virtual void setFilterText( const QString &filterText = QString() ); virtual void setFilterText( const QString &filterText = QString() );
@ -97,8 +104,6 @@ class CORE_EXPORT QgsLayerTreeFilterProxyModel : public QSortFilterProxyModel
*/ */
virtual bool layerShown( QgsMapLayer *layer ) const; virtual bool layerShown( QgsMapLayer *layer ) const;
bool nodeShown( QgsLayerTreeNode *node ) const;
//! This will call the virtual method and takes care of emitting dataChanged signal //! This will call the virtual method and takes care of emitting dataChanged signal
void setLayerCheckedPrivate( QgsMapLayer *layer, bool checked ); void setLayerCheckedPrivate( QgsMapLayer *layer, bool checked );

View File

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

View File

@ -30,6 +30,7 @@ class QgsLayerTreeModelLegendNode;
class QgsLayerTreeNode; class QgsLayerTreeNode;
class QgsSymbol; class QgsSymbol;
class QgsRenderContext; class QgsRenderContext;
class QgsLayerTreeFilterProxyModel;
#include "qgslegendsettings.h" #include "qgslegendsettings.h"
@ -52,6 +53,22 @@ class CORE_EXPORT QgsLegendRenderer
* and the model must exist for the lifetime of this renderer. * and the model must exist for the lifetime of this renderer.
*/ */
QgsLegendRenderer( QgsLayerTreeModel *legendModel, const QgsLegendSettings &settings ); 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. * 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 ); QgsLegendStyle::Style nodeLegendStyle( QgsLayerTreeNode *node );
QgsLayerTreeModel *mLegendModel = nullptr; QgsLayerTreeModel *mLegendModel = nullptr;
std::unique_ptr< QgsLayerTreeFilterProxyModel >mProxyModel;
QgsLegendSettings mSettings; QgsLegendSettings mSettings;
@ -303,6 +321,10 @@ class CORE_EXPORT QgsLegendRenderer
#endif #endif
#ifdef SIP_RUN
QgsLegendRenderer( const QgsLegendRenderer &other );
#endif
void widthAndOffsetForTitleText( const Qt::AlignmentFlag halignment, double legendWidth, double &width, double &offset ) const; void widthAndOffsetForTitleText( const Qt::AlignmentFlag halignment, double legendWidth, double &width, double &offset ) const;
}; };

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB