Skip to content

Commit

Permalink
[composer] Use weak layer reference matching when loading attribute t…
Browse files Browse the repository at this point in the history
…able from XML

Allows attribute tables in templates to reattach to matching layers
when loaded in a different project to the project the template was
created in.

On behalf of Faunalia, sponsored by ENEL
  • Loading branch information
nyalldawson committed Apr 18, 2017
1 parent 85a7327 commit eb1e820
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 27 deletions.
41 changes: 18 additions & 23 deletions src/core/composer/qgscomposerattributetablev2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ bool QgsComposerAttributeTableCompareV2::operator()( const QgsComposerTableRow &
QgsComposerAttributeTableV2::QgsComposerAttributeTableV2( QgsComposition *composition, bool createUndoCommands )
: QgsComposerTableV2( composition, createUndoCommands )
, mSource( LayerAttributes )
, mVectorLayer( nullptr )
, mCurrentAtlasLayer( nullptr )
, mComposerMap( nullptr )
, mMaximumNumberOfFeatures( 30 )
Expand All @@ -69,15 +68,15 @@ QgsComposerAttributeTableV2::QgsComposerAttributeTableV2( QgsComposition *compos
QgsVectorLayer *vl = dynamic_cast<QgsVectorLayer *>( mapIt.value() );
if ( vl )
{
mVectorLayer = vl;
mVectorLayer.setLayer( vl );
break;
}
}
if ( mVectorLayer )
{
resetColumns();
//listen for modifications to layer and refresh table when they occur
connect( mVectorLayer, &QgsVectorLayer::layerModified, this, &QgsComposerTableV2::refreshAttributes );
connect( &mVectorLayer, &QgsVectorLayer::layerModified, this, &QgsComposerTableV2::refreshAttributes );
}

if ( mComposition )
Expand All @@ -103,14 +102,14 @@ QString QgsComposerAttributeTableV2::displayName() const

void QgsComposerAttributeTableV2::setVectorLayer( QgsVectorLayer *layer )
{
if ( layer == mVectorLayer )
if ( layer == &mVectorLayer )
{
//no change
return;
}

QgsVectorLayer *prevLayer = sourceLayer();
mVectorLayer = layer;
mVectorLayer.setLayer( layer );

if ( mSource == QgsComposerAttributeTableV2::LayerAttributes && layer != prevLayer )
{
Expand All @@ -124,7 +123,7 @@ void QgsComposerAttributeTableV2::setVectorLayer( QgsVectorLayer *layer )
resetColumns();

//listen for modifications to layer and refresh table when they occur
connect( mVectorLayer, &QgsVectorLayer::layerModified, this, &QgsComposerTableV2::refreshAttributes );
connect( &mVectorLayer, &QgsVectorLayer::layerModified, this, &QgsComposerTableV2::refreshAttributes );
}

refreshAttributes();
Expand Down Expand Up @@ -539,7 +538,7 @@ QgsExpressionContext QgsComposerAttributeTableV2::createExpressionContext() cons

if ( mSource == LayerAttributes )
{
context.appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer ) );
context.appendScope( QgsExpressionContextUtils::layerScope( &mVectorLayer ) );
}

return context;
Expand All @@ -563,7 +562,7 @@ QgsVectorLayer *QgsComposerAttributeTableV2::sourceLayer()
case QgsComposerAttributeTableV2::AtlasFeature:
return mComposition->atlasComposition().coverageLayer();
case QgsComposerAttributeTableV2::LayerAttributes:
return mVectorLayer;
return &mVectorLayer;
case QgsComposerAttributeTableV2::RelationChildren:
{
QgsRelation relation = mComposition->project()->relationManager()->relation( mRelationId );
Expand All @@ -579,7 +578,7 @@ void QgsComposerAttributeTableV2::removeLayer( const QString &layerId )
{
if ( layerId == mVectorLayer->id() )
{
mVectorLayer = nullptr;
mVectorLayer.setLayer( nullptr );
//remove existing columns
qDeleteAll( mColumns );
mColumns.clear();
Expand Down Expand Up @@ -657,7 +656,10 @@ bool QgsComposerAttributeTableV2::writeXml( QDomElement &elem, QDomDocument &doc
}
if ( mVectorLayer )
{
composerTableElem.setAttribute( QStringLiteral( "vectorLayer" ), mVectorLayer->id() );
composerTableElem.setAttribute( QStringLiteral( "vectorLayer" ), mVectorLayer.layerId );
composerTableElem.setAttribute( QStringLiteral( "vectorLayerName" ), mVectorLayer.name );
composerTableElem.setAttribute( QStringLiteral( "vectorLayerSource" ), mVectorLayer.source );
composerTableElem.setAttribute( QStringLiteral( "vectorLayerProvider" ), mVectorLayer.provider );
}

bool ok = QgsComposerTableV2::writeXml( composerTableElem, doc, ignoreFrames );
Expand Down Expand Up @@ -726,19 +728,12 @@ bool QgsComposerAttributeTableV2::readXml( const QDomElement &itemElem, const QD
}

//vector layer
QString layerId = itemElem.attribute( QStringLiteral( "vectorLayer" ), QStringLiteral( "not_existing" ) );
if ( layerId == QLatin1String( "not_existing" ) )
{
mVectorLayer = nullptr;
}
else
{
QgsMapLayer *ml = mComposition->project()->mapLayer( layerId );
if ( ml )
{
mVectorLayer = dynamic_cast<QgsVectorLayer *>( ml );
}
}
QString layerId = itemElem.attribute( QStringLiteral( "vectorLayer" ) );
QString layerName = itemElem.attribute( QStringLiteral( "vectorLayerName" ) );
QString layerSource = itemElem.attribute( QStringLiteral( "vectorLayerSource" ) );
QString layerProvider = itemElem.attribute( QStringLiteral( "vectorLayerProvider" ) );
mVectorLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
mVectorLayer.resolveWeakly( mComposition->project() );

//connect to new layer
connect( sourceLayer(), &QgsVectorLayer::layerModified, this, &QgsComposerTableV2::refreshAttributes );
Expand Down
5 changes: 3 additions & 2 deletions src/core/composer/qgscomposerattributetablev2.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include "qgis_core.h"
#include "qgscomposertablev2.h"
#include "qgsvectorlayerref.h"

class QgsComposerMap;
class QgsVectorLayer;
Expand Down Expand Up @@ -119,7 +120,7 @@ class CORE_EXPORT QgsComposerAttributeTableV2: public QgsComposerTableV2
* \returns attribute table's current vector layer
* \see setVectorLayer
*/
QgsVectorLayer *vectorLayer() const { return mVectorLayer; }
QgsVectorLayer *vectorLayer() const { return &mVectorLayer; }

/** Sets the relation id from which to display child features
* \param relationId id for relation to display child features from
Expand Down Expand Up @@ -303,7 +304,7 @@ class CORE_EXPORT QgsComposerAttributeTableV2: public QgsComposerTableV2
//! Attribute source
ContentSource mSource;
//! Associated vector layer
QgsVectorLayer *mVectorLayer = nullptr;
QgsVectorLayerRef mVectorLayer;
//! Relation id, if in relation children mode
QString mRelationId;

Expand Down
62 changes: 60 additions & 2 deletions tests/src/core/testqgscomposition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "qgsapplication.h"
#include "qgscomposition.h"
#include "qgscomposerattributetablev2.h"
#include "qgscomposerlabel.h"
#include "qgscomposershape.h"
#include "qgscomposerarrow.h"
Expand Down Expand Up @@ -65,6 +66,7 @@ class TestQgsComposition : public QObject
void referenceMap();
void legendRestoredFromTemplate();
void legendRestoredFromTemplateAutoUpdate();
void attributeTableRestoredFromTemplate();

private:
QgsComposition *mComposition = nullptr;
Expand Down Expand Up @@ -783,9 +785,15 @@ void TestQgsComposition::legendRestoredFromTemplateAutoUpdate()
c.writeXml( composerElem, doc );
c.atlasComposition().writeXml( composerElem, doc );

//new project
QgsVectorLayer *layer2 = new QgsVectorLayer( vectorFileInfo.filePath(),
vectorFileInfo.completeBaseName(),
"ogr" );
QgsProject p2;
p2.addMapLayer( layer2 );

// make a new composition from template
QgsComposition c2( &p );
QgsComposition c2( &p2 );
QVERIFY( c2.loadFromTemplate( doc ) );
// get legend from new composition
QList< QgsComposerLegend * > legends2;
Expand All @@ -797,9 +805,59 @@ void TestQgsComposition::legendRestoredFromTemplateAutoUpdate()
QgsLayerTreeNode *node2 = model2->rootGroup()->children().at( 0 );
QgsLayerTreeLayer *layerNode2 = dynamic_cast< QgsLayerTreeLayer * >( node2 );
QVERIFY( layerNode2 );
QCOMPARE( layerNode2->layer(), layer );
QCOMPARE( layerNode2->layer(), layer2 );
QCOMPARE( model2->data( model->node2index( layerNode2 ), Qt::DisplayRole ).toString(), QString( "points" ) );
}

void TestQgsComposition::attributeTableRestoredFromTemplate()
{
// load some layers
QFileInfo vectorFileInfo( QString( TEST_DATA_DIR ) + "/points.shp" );
QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(),
vectorFileInfo.completeBaseName(),
"ogr" );
QgsVectorLayer *layer2 = new QgsVectorLayer( "Point", "memory", "memory" );
QgsProject p;
p.addMapLayer( layer2 );
p.addMapLayer( layer );

// create composition
QgsComposition c( &p );
// add an attribute table
QgsComposerAttributeTableV2 *table = new QgsComposerAttributeTableV2( &c, false );
c.addMultiFrame( table );
table->setVectorLayer( layer );
QgsComposerFrame *frame = new QgsComposerFrame( &c, table, 1, 1, 10, 10 );
c.addComposerTableFrame( table, frame );
table->addFrame( frame );

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

// new project
QgsProject p2;
QgsVectorLayer *layer3 = new QgsVectorLayer( vectorFileInfo.filePath(),
vectorFileInfo.completeBaseName(),
"ogr" );
QgsVectorLayer *layer4 = new QgsVectorLayer( "Point", "memory", "memory" );
p2.addMapLayer( layer4 );
p2.addMapLayer( layer3 );

// make a new composition from template
QgsComposition c2( &p2 );
QVERIFY( c2.loadFromTemplate( doc ) );
// get table from new composition
QList< QgsComposerFrame * > frames2;
c2.composerItems( frames2 );
QgsComposerAttributeTableV2 *table2 = static_cast< QgsComposerAttributeTableV2 *>( frames2.at( 0 )->multiFrame() );
QVERIFY( table2 );

QCOMPARE( table2->vectorLayer(), layer3 );
}

QGSTEST_MAIN( TestQgsComposition )
#include "testqgscomposition.moc"

0 comments on commit eb1e820

Please sign in to comment.