Skip to content

Commit

Permalink
Allow configurable icon sizes for legend tree nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
vmora committed May 12, 2015
1 parent c8aa777 commit d13ace5
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 19 deletions.
10 changes: 10 additions & 0 deletions python/core/layertree/qgslayertreemodellegendnode.sip
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,16 @@ class QgsSymbolV2LegendNode : QgsLayerTreeModelLegendNode
virtual bool isScaleOK( double scale ) const;

virtual void invalidateMapBasedData();

//! Set the icon size
//! @note added in 2.10
void setIconSize( const QSize& sz );
//! @note added in 2.10
QSize iconSize() const;

//! Get the minimum icon size to prevent cropping
//! @note added in 2.10
QSize minimumIconSize() const;
};


Expand Down
76 changes: 58 additions & 18 deletions src/core/layertree/qgslayertreemodellegendnode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "qgsrasterlayer.h"
#include "qgsrendererv2.h"
#include "qgssymbollayerv2utils.h"
#include "qgsimageoperation.h"
#include "qgsvectorlayer.h"


Expand Down Expand Up @@ -135,6 +136,7 @@ QgsSymbolV2LegendNode::QgsSymbolV2LegendNode( QgsLayerTreeLayer* nodeLayer, cons
: QgsLayerTreeModelLegendNode( nodeLayer, parent )
, mItem( item )
, mSymbolUsesMapUnits( false )
, mIconSize( 16, 16 )
{
updateLabel();

Expand All @@ -155,6 +157,58 @@ Qt::ItemFlags QgsSymbolV2LegendNode::flags() const
}


QSize QgsSymbolV2LegendNode::minimumIconSize() const
{
QSize minSz;
if ( mItem.symbol() && mItem.symbol()->type() == QgsSymbolV2::Marker )
{
QScopedPointer<QgsRenderContext> context( createTemporaryRenderContext() );
QPixmap pix = QPixmap::fromImage( QgsImageOperation::cropTransparent(
QgsSymbolLayerV2Utils::symbolPreviewPixmap( mItem.symbol(),
QSize( 512, 512 ),
context.data()
).toImage(), mIconSize ) );
minSz = pix.size();
}
else if ( mItem.symbol() && mItem.symbol()->type() == QgsSymbolV2::Line )
{
QScopedPointer<QgsRenderContext> context( createTemporaryRenderContext() );
QPixmap pix = QPixmap::fromImage( QgsImageOperation::cropTransparent(
QgsSymbolLayerV2Utils::symbolPreviewPixmap( mItem.symbol(),
QSize( mIconSize.width(), 512 ),
context.data()
).toImage(), mIconSize ) );
minSz = pix.size();
}
else
{
minSz = mIconSize;
}

if ( mItem.level() != 0 && !( model() && model()->testFlag( QgsLayerTreeModel::ShowLegendAsTree ) ) )
minSz.setWidth( indentSize + minSz.width() );

return minSz;
}

inline
QgsRenderContext * QgsSymbolV2LegendNode::createTemporaryRenderContext() const
{
double scale = 0.0;
double mupp = 0.0;
int dpi = 0;
if ( model() )
model()->legendMapViewData( &mupp, &dpi, &scale );
bool validData = mupp != 0 && dpi != 0 && scale != 0;

// setup temporary render context
QScopedPointer<QgsRenderContext> context( new QgsRenderContext );
context->setScaleFactor( dpi / 25.4 );
context->setRendererScale( scale );
context->setMapToPixel( QgsMapToPixel( mupp ) ); // hope it's ok to leave out other params
return validData ? context.take() : 0;
}

QVariant QgsSymbolV2LegendNode::data( int role ) const
{
if ( role == Qt::DisplayRole )
Expand All @@ -167,31 +221,17 @@ QVariant QgsSymbolV2LegendNode::data( int role ) const
}
else if ( role == Qt::DecorationRole )
{
QSize iconSize( 16, 16 ); // TODO: configurable
const int indentSize = 20;
if ( mPixmap.isNull() )
if ( mPixmap.isNull() || mPixmap.size() != mIconSize )
{
QPixmap pix;
if ( mItem.symbol() )
{
double scale = 0.0;
double mupp = 0.0;
int dpi = 0;
if ( model() )
model()->legendMapViewData( &mupp, &dpi, &scale );
bool validData = mupp != 0 && dpi != 0 && scale != 0;

// setup temporary render context
QgsRenderContext context;
context.setScaleFactor( dpi / 25.4 );
context.setRendererScale( scale );
context.setMapToPixel( QgsMapToPixel( mupp ) ); // hope it's ok to leave out other params

pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( mItem.symbol(), iconSize, validData ? &context : 0 );
QScopedPointer<QgsRenderContext> context( createTemporaryRenderContext() );
pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( mItem.symbol(), mIconSize, context.data() );
}
else
{
pix = QPixmap( iconSize );
pix = QPixmap( mIconSize );
pix.fill( Qt::transparent );
}

Expand Down
19 changes: 19 additions & 0 deletions src/core/layertree/qgslayertreemodellegendnode.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class QgsLayerTreeModel;
class QgsLegendSettings;
class QgsMapSettings;
class QgsSymbolV2;
class QgsRenderContext;

/**
* The QgsLegendRendererItem class is abstract interface for legend items
Expand Down Expand Up @@ -162,6 +163,16 @@ class CORE_EXPORT QgsSymbolV2LegendNode : public QgsLayerTreeModelLegendNode

virtual void invalidateMapBasedData() override;

//! Set the icon size
//! @note added in 2.10
void setIconSize( const QSize& sz ) { mIconSize = sz; }
//! @note added in 2.10
QSize iconSize() const { return mIconSize; }

//! Get the minimum icon size to prevent cropping
//! @note added in 2.10
QSize minimumIconSize() const;

private:
void updateLabel();

Expand All @@ -170,6 +181,14 @@ class CORE_EXPORT QgsSymbolV2LegendNode : public QgsLayerTreeModelLegendNode
mutable QPixmap mPixmap; // cached symbol preview
QString mLabel;
bool mSymbolUsesMapUnits;
QSize mIconSize;

// ident the symbol icon to make it look like a tree structure
static const int indentSize = 20;

// return a temporary context or null if legendMapViewData are not valid
QgsRenderContext * createTemporaryRenderContext() const;

};


Expand Down
26 changes: 25 additions & 1 deletion src/core/qgsmaplayerlegend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,33 @@ QList<QgsLayerTreeModelLegendNode*> QgsDefaultVectorLayerLegend::createLayerTree
nodes.append( new QgsSimpleLegendNode( nodeLayer, r->legendClassificationAttribute() ) );
}

// we have varying icon sizes, and we want icon to be centered and
// text to be left aligned, so we have to compute the max width of icons
//
// we do that for nodes who share a common parent

QList<QgsSymbolV2LegendNode*> symbolNodes;
QMap<QString, int> widthMax;
foreach ( const QgsLegendSymbolItemV2& i, r->legendSymbolItemsV2() )
{
nodes.append( new QgsSymbolV2LegendNode( nodeLayer, i ) );
QgsSymbolV2LegendNode * n = new QgsSymbolV2LegendNode( nodeLayer, i );
nodes.append( n );
if ( i.symbol() )
{
const QSize sz( n->minimumIconSize() );
const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() );
widthMax[parentKey] = qMax( sz.width(), widthMax.contains( parentKey ) ? widthMax[parentKey] : 0 );
n->setIconSize( sz );
symbolNodes.append( n );
}
}

foreach ( QgsSymbolV2LegendNode* n, symbolNodes )
{
const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() );
Q_ASSERT( widthMax[parentKey] > 0 );
const int twiceMarginWidth = 2; // a one pixel margin avoids hugly rendering of icon
n->setIconSize( QSize( widthMax[parentKey] + twiceMarginWidth, n->iconSize().rheight() + twiceMarginWidth ) );
}

if ( nodes.count() == 1 && nodes[0]->data( Qt::EditRole ).toString().isEmpty() )
Expand Down

0 comments on commit d13ace5

Please sign in to comment.