Skip to content

Commit

Permalink
Fix #10912 (joined attributes are not correctly propagated in nested …
Browse files Browse the repository at this point in the history
…joins)

This commit makes QgsVectorLayerJoinBuffer listen to changes in fields
of joined vector layers in order to update the cache and inform parent layer
  • Loading branch information
wonder-sk committed Sep 9, 2014
1 parent 1df01c0 commit 071a5ec
Show file tree
Hide file tree
Showing 11 changed files with 298 additions and 6 deletions.
5 changes: 5 additions & 0 deletions python/core/qgsfield.sip
Expand Up @@ -193,6 +193,11 @@ class QgsFields
//! Utility function to return a list of QgsField instances
QList<QgsField> toList() const;

//! @note added in 2.6
bool operator==( const QgsFields& other ) const;
//! @note added in 2.6
bool operator!=( const QgsFields& other ) const;

/* SIP_PYOBJECT __getitem__(int key);
%MethodCode
if (a0 = sipConvertFromSequenceIndex(a0, sipCpp->count()) < 0)
Expand Down
10 changes: 9 additions & 1 deletion python/core/qgsvectorlayerjoinbuffer.sip
@@ -1,4 +1,4 @@
class QgsVectorLayerJoinBuffer
class QgsVectorLayerJoinBuffer : QObject
{
%TypeHeaderCode
#include <qgsvectorlayerjoinbuffer.h>
Expand Down Expand Up @@ -39,4 +39,12 @@ class QgsVectorLayerJoinBuffer
@param sourceFieldIndex Output: field's index in source layer */
const QgsVectorJoinInfo* joinForFieldIndex( int index, const QgsFields& fields, int& sourceFieldIndex /Out/ ) const;

//! Create a copy of the join buffer
//! @note added in 2.6
QgsVectorLayerJoinBuffer* clone() const /Factory/;

signals:
//! Emitted whenever the list of joined fields changes (e.g. added join or joined layer's fields change)
//! @note added in 2.6
void joinedFieldsChanged();
};
1 change: 1 addition & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -369,6 +369,7 @@ SET(QGIS_CORE_MOC_HDRS
qgsnetworkaccessmanager.h
qgsvectordataprovider.h
qgsvectorlayercache.h
qgsvectorlayerjoinbuffer.h
qgsgeometryvalidator.h

composer/qgsaddremoveitemcommand.h
Expand Down
10 changes: 10 additions & 0 deletions src/core/qgsfield.h
Expand Up @@ -178,6 +178,11 @@ class CORE_EXPORT QgsFields
Field(): origin( OriginUnknown ), originIndex( -1 ) {}
Field( const QgsField& f, FieldOrigin o, int oi ): field( f ), origin( o ), originIndex( oi ) {}

//! @note added in 2.6
bool operator==( const Field& other ) const { return field == other.field && origin == other.origin && originIndex == other.originIndex; }
//! @note added in 2.6
bool operator!=( const Field& other ) const { return !( *this == other ); }

QgsField field; //!< field
FieldOrigin origin; //!< origin of the field
int originIndex; //!< index specific to the origin
Expand Down Expand Up @@ -238,6 +243,11 @@ class CORE_EXPORT QgsFields
//! Utility function to return a list of QgsField instances
QList<QgsField> toList() const;

//! @note added in 2.6
bool operator==( const QgsFields& other ) const { return mFields == other.mFields; }
//! @note added in 2.6
bool operator!=( const QgsFields& other ) const { return ! ( *this == other ); }

protected:
//! internal storage of the container
QVector<Field> mFields;
Expand Down
13 changes: 12 additions & 1 deletion src/core/qgsvectorlayer.cpp
Expand Up @@ -1304,6 +1304,7 @@ bool QgsVectorLayer::readXml( const QDomNode& layer_node )
if ( !mJoinBuffer )
{
mJoinBuffer = new QgsVectorLayerJoinBuffer();
connect( mJoinBuffer, SIGNAL( joinedFieldsChanged() ), this, SLOT( onJoinedFieldsChanged() ) );
}
mJoinBuffer->readXml( layer_node );

Expand Down Expand Up @@ -1366,6 +1367,7 @@ bool QgsVectorLayer::setDataProvider( QString const & provider )
mWkbType = mDataProvider->geometryType();

mJoinBuffer = new QgsVectorLayerJoinBuffer();
connect( mJoinBuffer, SIGNAL( joinedFieldsChanged() ), this, SLOT( onJoinedFieldsChanged() ) );
mExpressionFieldBuffer = new QgsExpressionFieldBuffer();
updateFields();

Expand Down Expand Up @@ -2782,6 +2784,8 @@ void QgsVectorLayer::updateFields()
if ( !mDataProvider )
return;

QgsFields oldFields = mUpdatedFields;

mUpdatedFields = mDataProvider->fields();

// added / removed fields
Expand All @@ -2795,7 +2799,8 @@ void QgsVectorLayer::updateFields()
if ( mExpressionFieldBuffer )
mExpressionFieldBuffer->updateFields( mUpdatedFields );

emit updatedFields();
if ( oldFields != mUpdatedFields )
emit updatedFields();
}


Expand Down Expand Up @@ -3563,6 +3568,12 @@ void QgsVectorLayer::onRelationsLoaded()
}
}

void QgsVectorLayer::onJoinedFieldsChanged()
{
// some of the fields of joined layers have changed -> we need to update this layer's fields too
updateFields();
}

QgsVectorLayer::ValueRelationData QgsVectorLayer::valueRelation( int idx )
{
if ( editorWidgetV2( idx ) == "ValueRelation" )
Expand Down
1 change: 1 addition & 0 deletions src/core/qgsvectorlayer.h
Expand Up @@ -1641,6 +1641,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer

private slots:
void onRelationsLoaded();
void onJoinedFieldsChanged();

protected:
/** Set the extent */
Expand Down
2 changes: 1 addition & 1 deletion src/core/qgsvectorlayerfeatureiterator.cpp
Expand Up @@ -27,7 +27,7 @@ QgsVectorLayerFeatureSource::QgsVectorLayerFeatureSource( QgsVectorLayer *layer
{
mProviderFeatureSource = layer->dataProvider()->featureSource();
mFields = layer->pendingFields();
mJoinBuffer = new QgsVectorLayerJoinBuffer( *layer->mJoinBuffer );
mJoinBuffer = layer->mJoinBuffer->clone();
mExpressionFieldBuffer = new QgsExpressionFieldBuffer( *layer->mExpressionFieldBuffer );

mCanBeSimplified = layer->hasGeometryType() && layer->geometryType() != QGis::Point;
Expand Down
44 changes: 44 additions & 0 deletions src/core/qgsvectorlayerjoinbuffer.cpp
Expand Up @@ -39,8 +39,18 @@ void QgsVectorLayerJoinBuffer::addJoin( const QgsVectorJoinInfo& joinInfo )
{
cacheJoinLayer( mVectorJoins.last() );
}

// Wait for notifications about changed fields in joined layer to propagate them.
// During project load the joined layers possibly do not exist yet so the connection will not be created,
// but then QgsProject makes sure to call createJoinCaches() which will do the connection.
// Unique connection makes sure we do not respond to one layer's update more times (in case of multiple join)
if ( QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinInfo.joinLayerId ) ) )
connect( vl, SIGNAL( updatedFields() ), this, SLOT( joinedLayerUpdatedFields() ), Qt::UniqueConnection );

emit joinedFieldsChanged();
}


void QgsVectorLayerJoinBuffer::removeJoin( const QString& joinLayerId )
{
for ( int i = 0; i < mVectorJoins.size(); ++i )
Expand All @@ -50,6 +60,11 @@ void QgsVectorLayerJoinBuffer::removeJoin( const QString& joinLayerId )
mVectorJoins.removeAt( i );
}
}

if ( QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinLayerId ) ) )
disconnect( vl, SIGNAL( updatedFields() ), this, SLOT( joinedLayerUpdatedFields() ) );

emit joinedFieldsChanged();
}

void QgsVectorLayerJoinBuffer::cacheJoinLayer( QgsVectorJoinInfo& joinInfo )
Expand Down Expand Up @@ -121,6 +136,10 @@ void QgsVectorLayerJoinBuffer::createJoinCaches()
for ( ; joinIt != mVectorJoins.end(); ++joinIt )
{
cacheJoinLayer( *joinIt );

// make sure we are connected to the joined layer
if ( QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinIt->joinLayerId ) ) )
connect( vl, SIGNAL( updatedFields() ), this, SLOT( joinedLayerUpdatedFields() ), Qt::UniqueConnection );
}
}

Expand Down Expand Up @@ -188,3 +207,28 @@ const QgsVectorJoinInfo* QgsVectorLayerJoinBuffer::joinForFieldIndex( int index,

return &( mVectorJoins[sourceJoinIndex] );
}

QgsVectorLayerJoinBuffer* QgsVectorLayerJoinBuffer::clone() const
{
QgsVectorLayerJoinBuffer* cloned = new QgsVectorLayerJoinBuffer;
cloned->mVectorJoins = mVectorJoins;
return cloned;
}

void QgsVectorLayerJoinBuffer::joinedLayerUpdatedFields()
{
QgsVectorLayer* joinedLayer = qobject_cast<QgsVectorLayer*>( sender() );
Q_ASSERT( joinedLayer );

// recache the joined layer
for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
{
if ( joinedLayer->id() == it->joinLayerId )
{
it->cachedAttributes.clear();
cacheJoinLayer( *it );
}
}

emit joinedFieldsChanged();
}
20 changes: 17 additions & 3 deletions src/core/qgsvectorlayerjoinbuffer.h
Expand Up @@ -25,11 +25,13 @@
#include <QString>


typedef QList< QgsVectorJoinInfo > QgsVectorJoinList;


/**Manages joined fields for a vector layer*/
class CORE_EXPORT QgsVectorLayerJoinBuffer
class CORE_EXPORT QgsVectorLayerJoinBuffer : public QObject
{
Q_OBJECT
public:
QgsVectorLayerJoinBuffer();
~QgsVectorLayerJoinBuffer();
Expand Down Expand Up @@ -58,18 +60,30 @@ class CORE_EXPORT QgsVectorLayerJoinBuffer
/**Quick way to test if there is any join at all*/
bool containsJoins() const { return !mVectorJoins.isEmpty(); }

const QList< QgsVectorJoinInfo >& vectorJoins() const { return mVectorJoins; }
const QgsVectorJoinList& vectorJoins() const { return mVectorJoins; }

/**Finds the vector join for a layer field index.
@param index this layers attribute index
@param fields fields of the vector layer (including joined fields)
@param sourceFieldIndex Output: field's index in source layer */
const QgsVectorJoinInfo* joinForFieldIndex( int index, const QgsFields& fields, int& sourceFieldIndex ) const;

//! Create a copy of the join buffer
//! @note added in 2.6
QgsVectorLayerJoinBuffer* clone() const;

signals:
//! Emitted whenever the list of joined fields changes (e.g. added join or joined layer's fields change)
//! @note added in 2.6
void joinedFieldsChanged();

private slots:
void joinedLayerUpdatedFields();

private:

/**Joined vector layers*/
QList< QgsVectorJoinInfo > mVectorJoins;
QgsVectorJoinList mVectorJoins;

/**Caches attributes of join layer in memory if QgsVectorJoinInfo.memoryCache is true (and the cache is not already there)*/
void cacheJoinLayer( QgsVectorJoinInfo& joinInfo );
Expand Down
1 change: 1 addition & 0 deletions tests/src/core/CMakeLists.txt
Expand Up @@ -133,3 +133,4 @@ ADD_QGIS_TEST(colorschemeregistry testqgscolorschemeregistry.cpp)
ADD_QGIS_TEST(colorscheme testqgscolorscheme.cpp)
ADD_QGIS_TEST(networkcontentfetcher testqgsnetworkcontentfetcher.cpp )
ADD_QGIS_TEST(legendrenderertest testqgslegendrenderer.cpp )
ADD_QGIS_TEST(vectorlayerjoinbuffer testqgsvectorlayerjoinbuffer.cpp )

0 comments on commit 071a5ec

Please sign in to comment.