Skip to content
Permalink
Browse files
Fix composer legends are not restored from XML
Also add unit test to protect this and switch to unique_ptrs
instead of raw pointers
  • Loading branch information
nyalldawson committed Apr 12, 2017
1 parent 3bb3f59 commit da5c0ed2cd220dec0f55bbf036423c816bd5bddd
Showing with 163 additions and 19 deletions.
  1. +15 −15 src/core/composer/qgscomposerlegend.cpp
  2. +4 −4 src/core/composer/qgscomposerlegend.h
  3. +144 −0 tests/src/core/testqgscomposition.cpp
@@ -35,6 +35,7 @@

QgsComposerLegend::QgsComposerLegend( QgsComposition *composition )
: QgsComposerItem( composition )
, mLegendModel( new QgsLegendModel( mComposition->project()->layerTreeRoot() ) )
, mCustomLayerTree( nullptr )
, mComposerMap( nullptr )
, mLegendFilterByMap( false )
@@ -46,8 +47,6 @@ QgsComposerLegend::QgsComposerLegend( QgsComposition *composition )
, mForceResize( false )
, mSizeToContents( true )
{
mLegendModel = new QgsLegendModel( mComposition->project()->layerTreeRoot() );

connect( &composition->atlasComposition(), &QgsAtlasComposition::renderEnded, this, &QgsComposerLegend::onAtlasEnded );
connect( &composition->atlasComposition(), &QgsAtlasComposition::featureChanged, this, &QgsComposerLegend::onAtlasFeature );

@@ -73,12 +72,6 @@ QgsComposerLegend::QgsComposerLegend()

}

QgsComposerLegend::~QgsComposerLegend()
{
delete mLegendModel;
delete mCustomLayerTree;
}

void QgsComposerLegend::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
{
Q_UNUSED( itemStyle );
@@ -119,7 +112,7 @@ void QgsComposerLegend::paint( QPainter *painter, const QStyleOptionGraphicsItem
}
mInitialMapScaleCalculated = true;

QgsLegendRenderer legendRenderer( mLegendModel, mSettings );
QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
legendRenderer.setLegendSize( mForceResize && mSizeToContents ? QSize() : rect().size() );

//adjust box if width or height is too small
@@ -180,7 +173,7 @@ QSizeF QgsComposerLegend::paintAndDetermineSize( QPainter *painter )
doUpdateFilterByMap();
}

QgsLegendRenderer legendRenderer( mLegendModel, mSettings );
QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
QSizeF size = legendRenderer.minimumSize();
if ( painter )
legendRenderer.drawLegend( painter );
@@ -202,7 +195,7 @@ void QgsComposerLegend::adjustBoxSize()
return;
}

QgsLegendRenderer legendRenderer( mLegendModel, mSettings );
QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
QSizeF size = legendRenderer.minimumSize();
QgsDebugMsg( QString( "width = %1 height = %2" ).arg( size.width() ).arg( size.height() ) );
if ( size.isValid() )
@@ -227,8 +220,7 @@ void QgsComposerLegend::setCustomLayerTree( QgsLayerTree *rootGroup )
{
mLegendModel->setRootGroup( rootGroup ? rootGroup : mComposition->project()->layerTreeRoot() );

delete mCustomLayerTree;
mCustomLayerTree = rootGroup;
mCustomLayerTree.reset( rootGroup );
}


@@ -493,7 +485,15 @@ bool QgsComposerLegend::readXml( const QDomElement &itemElem, const QDomDocument
if ( layerTreeElem.isNull() )
layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree-group" ) );

setCustomLayerTree( QgsLayerTree::readXml( layerTreeElem ) );
if ( !layerTreeElem.isNull() )
{
std::unique_ptr< QgsLayerTree > tree( QgsLayerTree::readXml( layerTreeElem ) );
if ( mComposition )
tree->resolveReferences( mComposition->project() );
setCustomLayerTree( tree.release() );
}
else
setCustomLayerTree( nullptr );

//restore general composer item properties
QDomNodeList composerItemList = itemElem.elementsByTagName( QStringLiteral( "ComposerItem" ) );
@@ -686,7 +686,7 @@ void QgsComposerLegend::doUpdateFilterByMap()
mLegendModel->setLayerStyleOverrides( QMap<QString, QString>() );


bool filterByExpression = QgsLayerTreeUtils::hasLegendFilterExpression( *( mCustomLayerTree ? mCustomLayerTree : mComposition->project()->layerTreeRoot() ) );
bool filterByExpression = QgsLayerTreeUtils::hasLegendFilterExpression( *( mCustomLayerTree ? mCustomLayerTree.get() : mComposition->project()->layerTreeRoot() ) );

if ( mComposerMap && ( mLegendFilterByMap || filterByExpression || mInAtlas ) )
{
@@ -22,6 +22,7 @@
#include "qgscomposeritem.h"
#include "qgslayertreemodel.h"
#include "qgslegendsettings.h"
#include "qgslayertreegroup.h"

class QgsLayerTreeModel;
class QgsSymbol;
@@ -58,7 +59,6 @@ class CORE_EXPORT QgsComposerLegend : public QgsComposerItem

public:
QgsComposerLegend( QgsComposition *composition );
~QgsComposerLegend();

//! Return correct graphics item type.
virtual int type() const override { return ComposerLegend; }
@@ -90,7 +90,7 @@ class CORE_EXPORT QgsComposerLegend : public QgsComposerItem
/**
* Returns the legend model
*/
QgsLegendModel *model() { return mLegendModel; }
QgsLegendModel *model() { return mLegendModel.get(); }

//! \since QGIS 2.6
void setAutoUpdateModel( bool autoUpdate );
@@ -308,8 +308,8 @@ class CORE_EXPORT QgsComposerLegend : public QgsComposerItem
//! use new custom layer tree and update model. if new root is null pointer, will use project's tree
void setCustomLayerTree( QgsLayerTree *rootGroup );

QgsLegendModel *mLegendModel = nullptr;
QgsLayerTreeGroup *mCustomLayerTree = nullptr;
std::unique_ptr< QgsLegendModel > mLegendModel;
std::unique_ptr< QgsLayerTreeGroup > mCustomLayerTree;

QgsLegendSettings mSettings;

@@ -27,6 +27,11 @@
#include "qgsmultirenderchecker.h"
#include "qgsfillsymbollayer.h"
#include "qgsproject.h"
#include "qgscomposerlegend.h"
#include "qgsvectorlayer.h"
#include "qgslayertreegroup.h"
#include "qgslayertreelayer.h"
#include "qgslayertree.h"

#include <QObject>
#include "qgstest.h"
@@ -58,6 +63,8 @@ class TestQgsComposition : public QObject
void variablesEdited();
void itemVariablesFunction();
void referenceMap();
void legendRestoredFromTemplate();
void legendRestoredFromTemplateAutoUpdate();

private:
QgsComposition *mComposition = nullptr;
@@ -659,5 +666,142 @@ void TestQgsComposition::referenceMap()
delete composition;
}

void TestQgsComposition::legendRestoredFromTemplate()
{
// load a layer

QFileInfo vectorFileInfo( QString( TEST_DATA_DIR ) + "/points.shp" );
QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(),
vectorFileInfo.completeBaseName(),
"ogr" );
QgsProject p;
p.addMapLayer( layer );

// create composition
QgsComposition c( &p );
// add a legend
QgsComposerLegend *legend = new QgsComposerLegend( &c );
c.addComposerLegend( legend );
legend->setAutoUpdateModel( false );

QgsLegendModel *model = legend->model();
QgsLayerTreeNode *node = model->rootGroup()->children().at( 0 );
// make sure we've got right node
QgsLayerTreeLayer *layerNode = dynamic_cast< QgsLayerTreeLayer * >( node );
QVERIFY( layerNode );
QCOMPARE( layerNode->layer(), layer );

// got it!
layerNode->setCustomProperty( "legend/title-label", QString( "new title!" ) );
// make sure new title stuck
QCOMPARE( model->data( model->node2index( layerNode ), Qt::DisplayRole ).toString(), QString( "new title!" ) );

// save composition to template
QDomDocument doc;
QDomElement composerElem = doc.createElement( "Composer" );
doc.appendChild( composerElem );
c.writeXml( composerElem, doc );
c.atlasComposition().writeXml( composerElem, doc );


// make a new composition from template
QgsComposition c2( &p );
QVERIFY( c2.loadFromTemplate( doc ) );
// get legend from new composition
QList< QgsComposerLegend * > legends2;
c2.composerItems( legends2 );
QgsComposerLegend *legend2 = legends2.at( 0 );
QVERIFY( legend2 );

QgsLegendModel *model2 = legend2->model();
QgsLayerTreeNode *node2 = model2->rootGroup()->children().at( 0 );
QgsLayerTreeLayer *layerNode2 = dynamic_cast< QgsLayerTreeLayer * >( node2 );
QVERIFY( layerNode2 );
QCOMPARE( layerNode2->layer(), layer );
QCOMPARE( model2->data( model->node2index( layerNode2 ), Qt::DisplayRole ).toString(), QString( "new title!" ) );

#if 0 //expected failure (#2738)
QString oldId = layer->id();
// new test
// remove existing layer
p.removeMapLayer( layer );

// reload it, with a new id
QgsVectorLayer *layer2 = new QgsVectorLayer( vectorFileInfo.filePath(),
vectorFileInfo.completeBaseName(),
"ogr" );
p.addMapLayer( layer2 );
QVERIFY( oldId != layer2->id() );

// load composition from template
QgsComposition c3( &p );
QVERIFY( c3.loadFromTemplate( doc ) );
// get legend from new composition
QList< QgsComposerLegend * > legends3;
c3.composerItems( legends3 );
QgsComposerLegend *legend3 = legends3.at( 0 );
QVERIFY( legend3 );

//make sure customisation remains intact
QgsLegendModel *model3 = legend3->model();
QgsLayerTreeNode *node3 = model3->rootGroup()->children().at( 0 );
QgsLayerTreeLayer *layerNode3 = dynamic_cast< QgsLayerTreeLayer * >( node3 );
QVERIFY( layerNode3 );
QCOMPARE( layerNode3->layer(), layer2 );
QCOMPARE( model3->data( model->node2index( layerNode3 ), Qt::DisplayRole ).toString(), QString( "new title!" ) );
#endif
}

void TestQgsComposition::legendRestoredFromTemplateAutoUpdate()
{
// load a layer

QFileInfo vectorFileInfo( QString( TEST_DATA_DIR ) + "/points.shp" );
QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(),
vectorFileInfo.completeBaseName(),
"ogr" );
QgsProject p;
p.addMapLayer( layer );

// create composition
QgsComposition c( &p );
// add a legend
QgsComposerLegend *legend = new QgsComposerLegend( &c );
c.addComposerLegend( legend );
legend->setAutoUpdateModel( true );

QgsLegendModel *model = legend->model();
QgsLayerTreeNode *node = model->rootGroup()->children().at( 0 );
// make sure we've got right node
QgsLayerTreeLayer *layerNode = dynamic_cast< QgsLayerTreeLayer * >( node );
QVERIFY( layerNode );
QCOMPARE( layerNode->layer(), layer );
QCOMPARE( model->data( model->node2index( layerNode ), Qt::DisplayRole ).toString(), QString( "points" ) );

// save composition to template
QDomDocument doc;
QDomElement composerElem = doc.createElement( "Composer" );
doc.appendChild( composerElem );
c.writeXml( composerElem, doc );
c.atlasComposition().writeXml( composerElem, doc );


// make a new composition from template
QgsComposition c2( &p );
QVERIFY( c2.loadFromTemplate( doc ) );
// get legend from new composition
QList< QgsComposerLegend * > legends2;
c2.composerItems( legends2 );
QgsComposerLegend *legend2 = legends2.at( 0 );
QVERIFY( legend2 );

QgsLegendModel *model2 = legend2->model();
QgsLayerTreeNode *node2 = model2->rootGroup()->children().at( 0 );
QgsLayerTreeLayer *layerNode2 = dynamic_cast< QgsLayerTreeLayer * >( node2 );
QVERIFY( layerNode2 );
QCOMPARE( layerNode2->layer(), layer );
QCOMPARE( model2->data( model->node2index( layerNode2 ), Qt::DisplayRole ).toString(), QString( "points" ) );
}

QGSTEST_MAIN( TestQgsComposition )
#include "testqgscomposition.moc"

0 comments on commit da5c0ed

Please sign in to comment.