Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Fix #10912 (joined attributes are not correctly propagated in nested …
…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.