Skip to content

Commit eb1e820

Browse files
committedApr 18, 2017
[composer] Use weak layer reference matching when loading attribute table 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
1 parent 85a7327 commit eb1e820

File tree

3 files changed

+81
-27
lines changed

3 files changed

+81
-27
lines changed
 

‎src/core/composer/qgscomposerattributetablev2.cpp

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ bool QgsComposerAttributeTableCompareV2::operator()( const QgsComposerTableRow &
5151
QgsComposerAttributeTableV2::QgsComposerAttributeTableV2( QgsComposition *composition, bool createUndoCommands )
5252
: QgsComposerTableV2( composition, createUndoCommands )
5353
, mSource( LayerAttributes )
54-
, mVectorLayer( nullptr )
5554
, mCurrentAtlasLayer( nullptr )
5655
, mComposerMap( nullptr )
5756
, mMaximumNumberOfFeatures( 30 )
@@ -69,15 +68,15 @@ QgsComposerAttributeTableV2::QgsComposerAttributeTableV2( QgsComposition *compos
6968
QgsVectorLayer *vl = dynamic_cast<QgsVectorLayer *>( mapIt.value() );
7069
if ( vl )
7170
{
72-
mVectorLayer = vl;
71+
mVectorLayer.setLayer( vl );
7372
break;
7473
}
7574
}
7675
if ( mVectorLayer )
7776
{
7877
resetColumns();
7978
//listen for modifications to layer and refresh table when they occur
80-
connect( mVectorLayer, &QgsVectorLayer::layerModified, this, &QgsComposerTableV2::refreshAttributes );
79+
connect( &mVectorLayer, &QgsVectorLayer::layerModified, this, &QgsComposerTableV2::refreshAttributes );
8180
}
8281

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

104103
void QgsComposerAttributeTableV2::setVectorLayer( QgsVectorLayer *layer )
105104
{
106-
if ( layer == mVectorLayer )
105+
if ( layer == &mVectorLayer )
107106
{
108107
//no change
109108
return;
110109
}
111110

112111
QgsVectorLayer *prevLayer = sourceLayer();
113-
mVectorLayer = layer;
112+
mVectorLayer.setLayer( layer );
114113

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

126125
//listen for modifications to layer and refresh table when they occur
127-
connect( mVectorLayer, &QgsVectorLayer::layerModified, this, &QgsComposerTableV2::refreshAttributes );
126+
connect( &mVectorLayer, &QgsVectorLayer::layerModified, this, &QgsComposerTableV2::refreshAttributes );
128127
}
129128

130129
refreshAttributes();
@@ -539,7 +538,7 @@ QgsExpressionContext QgsComposerAttributeTableV2::createExpressionContext() cons
539538

540539
if ( mSource == LayerAttributes )
541540
{
542-
context.appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer ) );
541+
context.appendScope( QgsExpressionContextUtils::layerScope( &mVectorLayer ) );
543542
}
544543

545544
return context;
@@ -563,7 +562,7 @@ QgsVectorLayer *QgsComposerAttributeTableV2::sourceLayer()
563562
case QgsComposerAttributeTableV2::AtlasFeature:
564563
return mComposition->atlasComposition().coverageLayer();
565564
case QgsComposerAttributeTableV2::LayerAttributes:
566-
return mVectorLayer;
565+
return &mVectorLayer;
567566
case QgsComposerAttributeTableV2::RelationChildren:
568567
{
569568
QgsRelation relation = mComposition->project()->relationManager()->relation( mRelationId );
@@ -579,7 +578,7 @@ void QgsComposerAttributeTableV2::removeLayer( const QString &layerId )
579578
{
580579
if ( layerId == mVectorLayer->id() )
581580
{
582-
mVectorLayer = nullptr;
581+
mVectorLayer.setLayer( nullptr );
583582
//remove existing columns
584583
qDeleteAll( mColumns );
585584
mColumns.clear();
@@ -657,7 +656,10 @@ bool QgsComposerAttributeTableV2::writeXml( QDomElement &elem, QDomDocument &doc
657656
}
658657
if ( mVectorLayer )
659658
{
660-
composerTableElem.setAttribute( QStringLiteral( "vectorLayer" ), mVectorLayer->id() );
659+
composerTableElem.setAttribute( QStringLiteral( "vectorLayer" ), mVectorLayer.layerId );
660+
composerTableElem.setAttribute( QStringLiteral( "vectorLayerName" ), mVectorLayer.name );
661+
composerTableElem.setAttribute( QStringLiteral( "vectorLayerSource" ), mVectorLayer.source );
662+
composerTableElem.setAttribute( QStringLiteral( "vectorLayerProvider" ), mVectorLayer.provider );
661663
}
662664

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

728730
//vector layer
729-
QString layerId = itemElem.attribute( QStringLiteral( "vectorLayer" ), QStringLiteral( "not_existing" ) );
730-
if ( layerId == QLatin1String( "not_existing" ) )
731-
{
732-
mVectorLayer = nullptr;
733-
}
734-
else
735-
{
736-
QgsMapLayer *ml = mComposition->project()->mapLayer( layerId );
737-
if ( ml )
738-
{
739-
mVectorLayer = dynamic_cast<QgsVectorLayer *>( ml );
740-
}
741-
}
731+
QString layerId = itemElem.attribute( QStringLiteral( "vectorLayer" ) );
732+
QString layerName = itemElem.attribute( QStringLiteral( "vectorLayerName" ) );
733+
QString layerSource = itemElem.attribute( QStringLiteral( "vectorLayerSource" ) );
734+
QString layerProvider = itemElem.attribute( QStringLiteral( "vectorLayerProvider" ) );
735+
mVectorLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
736+
mVectorLayer.resolveWeakly( mComposition->project() );
742737

743738
//connect to new layer
744739
connect( sourceLayer(), &QgsVectorLayer::layerModified, this, &QgsComposerTableV2::refreshAttributes );

‎src/core/composer/qgscomposerattributetablev2.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "qgis_core.h"
2222
#include "qgscomposertablev2.h"
23+
#include "qgsvectorlayerref.h"
2324

2425
class QgsComposerMap;
2526
class QgsVectorLayer;
@@ -119,7 +120,7 @@ class CORE_EXPORT QgsComposerAttributeTableV2: public QgsComposerTableV2
119120
* \returns attribute table's current vector layer
120121
* \see setVectorLayer
121122
*/
122-
QgsVectorLayer *vectorLayer() const { return mVectorLayer; }
123+
QgsVectorLayer *vectorLayer() const { return &mVectorLayer; }
123124

124125
/** Sets the relation id from which to display child features
125126
* \param relationId id for relation to display child features from
@@ -303,7 +304,7 @@ class CORE_EXPORT QgsComposerAttributeTableV2: public QgsComposerTableV2
303304
//! Attribute source
304305
ContentSource mSource;
305306
//! Associated vector layer
306-
QgsVectorLayer *mVectorLayer = nullptr;
307+
QgsVectorLayerRef mVectorLayer;
307308
//! Relation id, if in relation children mode
308309
QString mRelationId;
309310

‎tests/src/core/testqgscomposition.cpp

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "qgsapplication.h"
1919
#include "qgscomposition.h"
20+
#include "qgscomposerattributetablev2.h"
2021
#include "qgscomposerlabel.h"
2122
#include "qgscomposershape.h"
2223
#include "qgscomposerarrow.h"
@@ -65,6 +66,7 @@ class TestQgsComposition : public QObject
6566
void referenceMap();
6667
void legendRestoredFromTemplate();
6768
void legendRestoredFromTemplateAutoUpdate();
69+
void attributeTableRestoredFromTemplate();
6870

6971
private:
7072
QgsComposition *mComposition = nullptr;
@@ -783,9 +785,15 @@ void TestQgsComposition::legendRestoredFromTemplateAutoUpdate()
783785
c.writeXml( composerElem, doc );
784786
c.atlasComposition().writeXml( composerElem, doc );
785787

788+
//new project
789+
QgsVectorLayer *layer2 = new QgsVectorLayer( vectorFileInfo.filePath(),
790+
vectorFileInfo.completeBaseName(),
791+
"ogr" );
792+
QgsProject p2;
793+
p2.addMapLayer( layer2 );
786794

787795
// make a new composition from template
788-
QgsComposition c2( &p );
796+
QgsComposition c2( &p2 );
789797
QVERIFY( c2.loadFromTemplate( doc ) );
790798
// get legend from new composition
791799
QList< QgsComposerLegend * > legends2;
@@ -797,9 +805,59 @@ void TestQgsComposition::legendRestoredFromTemplateAutoUpdate()
797805
QgsLayerTreeNode *node2 = model2->rootGroup()->children().at( 0 );
798806
QgsLayerTreeLayer *layerNode2 = dynamic_cast< QgsLayerTreeLayer * >( node2 );
799807
QVERIFY( layerNode2 );
800-
QCOMPARE( layerNode2->layer(), layer );
808+
QCOMPARE( layerNode2->layer(), layer2 );
801809
QCOMPARE( model2->data( model->node2index( layerNode2 ), Qt::DisplayRole ).toString(), QString( "points" ) );
802810
}
803811

812+
void TestQgsComposition::attributeTableRestoredFromTemplate()
813+
{
814+
// load some layers
815+
QFileInfo vectorFileInfo( QString( TEST_DATA_DIR ) + "/points.shp" );
816+
QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(),
817+
vectorFileInfo.completeBaseName(),
818+
"ogr" );
819+
QgsVectorLayer *layer2 = new QgsVectorLayer( "Point", "memory", "memory" );
820+
QgsProject p;
821+
p.addMapLayer( layer2 );
822+
p.addMapLayer( layer );
823+
824+
// create composition
825+
QgsComposition c( &p );
826+
// add an attribute table
827+
QgsComposerAttributeTableV2 *table = new QgsComposerAttributeTableV2( &c, false );
828+
c.addMultiFrame( table );
829+
table->setVectorLayer( layer );
830+
QgsComposerFrame *frame = new QgsComposerFrame( &c, table, 1, 1, 10, 10 );
831+
c.addComposerTableFrame( table, frame );
832+
table->addFrame( frame );
833+
834+
// save composition to template
835+
QDomDocument doc;
836+
QDomElement composerElem = doc.createElement( "Composer" );
837+
doc.appendChild( composerElem );
838+
c.writeXml( composerElem, doc );
839+
c.atlasComposition().writeXml( composerElem, doc );
840+
841+
// new project
842+
QgsProject p2;
843+
QgsVectorLayer *layer3 = new QgsVectorLayer( vectorFileInfo.filePath(),
844+
vectorFileInfo.completeBaseName(),
845+
"ogr" );
846+
QgsVectorLayer *layer4 = new QgsVectorLayer( "Point", "memory", "memory" );
847+
p2.addMapLayer( layer4 );
848+
p2.addMapLayer( layer3 );
849+
850+
// make a new composition from template
851+
QgsComposition c2( &p2 );
852+
QVERIFY( c2.loadFromTemplate( doc ) );
853+
// get table from new composition
854+
QList< QgsComposerFrame * > frames2;
855+
c2.composerItems( frames2 );
856+
QgsComposerAttributeTableV2 *table2 = static_cast< QgsComposerAttributeTableV2 *>( frames2.at( 0 )->multiFrame() );
857+
QVERIFY( table2 );
858+
859+
QCOMPARE( table2->vectorLayer(), layer3 );
860+
}
861+
804862
QGSTEST_MAIN( TestQgsComposition )
805863
#include "testqgscomposition.moc"

0 commit comments

Comments
 (0)
Please sign in to comment.