[FEATURE][composer] Add option for showing child features from a relation

for attribute tables in the composer. If selected, the attribute table
will show all related features to the current atlas feature within
the table body. Sponsored by City of Uster, Switzerland.
This commit is contained in:
Nyall Dawson 2014-09-22 22:59:20 +10:00
parent f58768c550
commit c3ec4b9eb0
15 changed files with 379 additions and 138 deletions

View File

@ -84,6 +84,22 @@ class QgsComposerAttributeTableV2 : QgsComposerTableV2
* @see setVectorLayer
*/
QgsVectorLayer* vectorLayer() const;
/**Sets the relation id from which to display child features
* @param relationId id for relation to display child features from
* @see relationId
* @see setSource
* @note only used if table source is set to RelationChildren
*/
void setRelationId( const QString relationId );
/**Returns the relation id which the table displays child features from
* @returns relation id
* @see setRelationId
* @see source
* @note only used if table source is set to RelationChildren
*/
QString relationId() const;
/**Resets the attribute table's columns to match the vector layer's fields
* @see setVectorLayer

View File

@ -22,7 +22,7 @@ class QgsRelation
/**
* Defines a relation between matchin fields of the two involved tables of a relation.
* Often, a relation is only defined by just one FieldPair with the name of the foreign key
* column of the referencing table as first element and the name of the primkary key column
* column of the referencing table as first element and the name of the primary key column
* of the referenced table as the second element.
*
*/

View File

@ -26,6 +26,8 @@
#include "qgsmaplayerregistry.h"
#include "qgsvectorlayer.h"
#include "qgsexpressionbuilderdialog.h"
#include "qgsproject.h"
#include "qgsrelationmanager.h"
#include <QColorDialog>
#include <QFontDialog>
@ -47,10 +49,10 @@ QgsComposerAttributeTableWidget::QgsComposerAttributeTableWidget( QgsComposerAtt
bool atlasEnabled = atlasComposition() && atlasComposition()->enabled();
mSourceComboBox->addItem( tr( "Layer features" ), QgsComposerAttributeTableV2::LayerAttributes );
if ( atlasEnabled )
{
mSourceComboBox->addItem( tr( "Current atlas feature" ), QgsComposerAttributeTableV2::AtlasFeature );
}
toggleAtlasSpecificControls( atlasEnabled );
//update relations combo when relations modified in project
connect( QgsProject::instance()->relationManager(), SIGNAL( changed() ), this, SLOT( updateRelationsCombo() ) );
mLayerComboBox->setFilters( QgsMapLayerProxyModel::VectorLayer );
connect( mLayerComboBox, SIGNAL( layerChanged( QgsMapLayer* ) ), this, SLOT( changeLayer( QgsMapLayer* ) ) );
@ -77,9 +79,9 @@ QgsComposerAttributeTableWidget::QgsComposerAttributeTableWidget( QgsComposerAtt
QgsAtlasComposition* atlas = atlasComposition();
if ( atlas )
{
// repopulate table source combo box if atlas properties change
// connect( atlas, SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ),
// this, SLOT( populateDataDefinedButtons() ) );
// repopulate relations combo box if atlas layer changes
connect( atlas, SIGNAL( coverageLayerChanged( QgsVectorLayer* ) ),
this, SLOT( updateRelationsCombo() ) );
connect( atlas, SIGNAL( toggled( bool ) ), this, SLOT( atlasToggled() ) );
}
}
@ -162,7 +164,7 @@ void QgsComposerAttributeTableWidget::on_mAttributesPushButton_clicked()
composition->beginMultiFrameCommand( mComposerTable, tr( "Table attribute settings" ) );
}
QgsAttributeSelectionDialog d( mComposerTable, mComposerTable->vectorLayer(), 0 );
QgsAttributeSelectionDialog d( mComposerTable, mComposerTable->sourceLayer(), 0 );
if ( d.exec() == QDialog::Accepted )
{
mComposerTable->refreshAttributes();
@ -427,6 +429,7 @@ void QgsComposerAttributeTableWidget::updateGuiElements()
blockAllSignals( true );
mSourceComboBox->setCurrentIndex( mSourceComboBox->findData( mComposerTable->source() ) );
mRelationsComboBox->setCurrentIndex( mRelationsComboBox->findData( mComposerTable->relationId() ) );
//layer combo box
if ( mComposerTable->vectorLayer() )
@ -503,6 +506,34 @@ void QgsComposerAttributeTableWidget::atlasToggled()
{
//display/hide atlas options in source combobox depending on atlas status
bool atlasEnabled = atlasComposition() && atlasComposition()->enabled();
toggleAtlasSpecificControls( atlasEnabled );
mSourceComboBox->blockSignals( true );
mSourceComboBox->setCurrentIndex( mSourceComboBox->findData( mComposerTable->source() ) );
mSourceComboBox->blockSignals( false );
}
void QgsComposerAttributeTableWidget::updateRelationsCombo()
{
mRelationsComboBox->blockSignals( true );
mRelationsComboBox->clear();
QgsVectorLayer* atlasLayer = atlasCoverageLayer();
if ( atlasLayer )
{
QList<QgsRelation> relations = QgsProject::instance()->relationManager()->referencedRelations( mComposerTable->composition()->atlasComposition().coverageLayer() );
Q_FOREACH( const QgsRelation& relation, relations )
{
mRelationsComboBox->addItem( relation.name(), relation.id() );
}
}
mRelationsComboBox->setCurrentIndex( mRelationsComboBox->findData( mComposerTable->relationId() ) );
mRelationsComboBox->blockSignals( false );
}
void QgsComposerAttributeTableWidget::toggleAtlasSpecificControls( const bool atlasEnabled )
{
if ( !atlasEnabled )
{
if ( mComposerTable->source() == QgsComposerAttributeTableV2::AtlasFeature )
@ -510,6 +541,11 @@ void QgsComposerAttributeTableWidget::atlasToggled()
mComposerTable->setSource( QgsComposerAttributeTableV2::LayerAttributes );
}
mSourceComboBox->removeItem( mSourceComboBox->findData( QgsComposerAttributeTableV2::AtlasFeature ) );
mSourceComboBox->removeItem( mSourceComboBox->findData( QgsComposerAttributeTableV2::RelationChildren ) );
mRelationsComboBox->blockSignals( true );
mRelationsComboBox->setEnabled( false );
mRelationsComboBox->clear();
mRelationsComboBox->blockSignals( false );
}
else
{
@ -518,10 +554,16 @@ void QgsComposerAttributeTableWidget::atlasToggled()
//add missing atlasfeature option to combobox
mSourceComboBox->addItem( tr( "Current atlas feature" ), QgsComposerAttributeTableV2::AtlasFeature );
}
if ( mSourceComboBox->findData( QgsComposerAttributeTableV2::RelationChildren ) == -1 )
{
//add missing relation children option to combobox
mSourceComboBox->addItem( tr( "Relation children" ), QgsComposerAttributeTableV2::RelationChildren );
}
//add relations for coverage layer
updateRelationsCombo();
mRelationsComboBox->setEnabled( true );
}
mSourceComboBox->blockSignals( true );
mSourceComboBox->setCurrentIndex( mSourceComboBox->findData( mComposerTable->source() ) );
mSourceComboBox->blockSignals( false );
}
void QgsComposerAttributeTableWidget::blockAllSignals( bool b )
@ -542,6 +584,7 @@ void QgsComposerAttributeTableWidget::blockAllSignals( bool b )
mHeaderFontColorButton->blockSignals( b );
mContentFontColorButton->blockSignals( b );
mResizeModeComboBox->blockSignals( b );
mRelationsComboBox->blockSignals( b );
}
void QgsComposerAttributeTableWidget::setMaximumNumberOfFeatures( int n )
@ -634,7 +677,7 @@ void QgsComposerAttributeTableWidget::on_mFeatureFilterButton_clicked()
return;
}
QgsExpressionBuilderDialog exprDlg( mComposerTable->vectorLayer(), mFeatureFilterEdit->text(), this );
QgsExpressionBuilderDialog exprDlg( mComposerTable->sourceLayer(), mFeatureFilterEdit->text(), this );
exprDlg.setWindowTitle( tr( "Expression based filter" ) );
if ( exprDlg.exec() == QDialog::Accepted )
{
@ -791,12 +834,33 @@ void QgsComposerAttributeTableWidget::on_mSourceComboBox_currentIndexChanged( in
toggleSourceControls();
}
void QgsComposerAttributeTableWidget::on_mRelationsComboBox_currentIndexChanged( int index )
{
if ( !mComposerTable )
{
return;
}
QgsComposition* composition = mComposerTable->composition();
if ( composition )
{
composition->beginMultiFrameCommand( mComposerTable, tr( "Change table source relation" ) );
mComposerTable->setRelationId( mRelationsComboBox->itemData( index ).toString() );
composition->endMultiFrameCommand();
}
}
void QgsComposerAttributeTableWidget::toggleSourceControls()
{
switch ( mComposerTable->source() )
{
case QgsComposerAttributeTableV2::LayerAttributes:
mLayerComboBox->setEnabled( true );
mLayerComboBox->setVisible( true );
mLayerLabel->setVisible( true );
mRelationsComboBox->setEnabled( false );
mRelationsComboBox->setVisible( false );
mRelationLabel->setVisible( false );
mMaximumRowsSpinBox->setEnabled( true );
mMaxNumFeaturesLabel->setEnabled( true );
mShowOnlyVisibleFeaturesCheckBox->setEnabled( true );
@ -805,13 +869,29 @@ void QgsComposerAttributeTableWidget::toggleSourceControls()
break;
case QgsComposerAttributeTableV2::AtlasFeature:
mLayerComboBox->setEnabled( false );
mLayerComboBox->setVisible( false );
mLayerLabel->setVisible( false );
mRelationsComboBox->setEnabled( false );
mRelationsComboBox->setVisible( false );
mRelationLabel->setVisible( false );
mMaximumRowsSpinBox->setEnabled( false );
mMaxNumFeaturesLabel->setEnabled( false );
mShowOnlyVisibleFeaturesCheckBox->setEnabled( false );
mComposerMapComboBox->setEnabled( false );
mComposerMapLabel->setEnabled( false );
break;
default:
case QgsComposerAttributeTableV2::RelationChildren:
mLayerComboBox->setEnabled( false );
mLayerComboBox->setVisible( false );
mLayerLabel->setVisible( false );
mRelationsComboBox->setEnabled( true );
mRelationsComboBox->setVisible( true );
mRelationLabel->setVisible( true );
mMaximumRowsSpinBox->setEnabled( true );
mMaxNumFeaturesLabel->setEnabled( true );
mShowOnlyVisibleFeaturesCheckBox->setEnabled( true );
mComposerMapComboBox->setEnabled( true );
mComposerMapLabel->setEnabled( true );
break;
}
}

View File

@ -44,6 +44,8 @@ class QgsComposerAttributeTableWidget: public QgsComposerItemBaseWidget, private
void toggleSourceControls();
void toggleAtlasSpecificControls( const bool atlasEnabled );
private slots:
void on_mRefreshPushButton_clicked();
void on_mAttributesPushButton_clicked();
@ -67,6 +69,7 @@ class QgsComposerAttributeTableWidget: public QgsComposerItemBaseWidget, private
void on_mAddFramePushButton_clicked();
void on_mResizeModeComboBox_currentIndexChanged( int index );
void on_mSourceComboBox_currentIndexChanged( int index );
void on_mRelationsComboBox_currentIndexChanged( int index );
/**Inserts a new maximum number of features into the spin box (without the spinbox emitting a signal)*/
void setMaximumNumberOfFeatures( int n );
@ -76,6 +79,7 @@ class QgsComposerAttributeTableWidget: public QgsComposerItemBaseWidget, private
void atlasToggled();
void updateRelationsCombo();
};
#endif // QGSCOMPOSERATTRIBUTETABLEWIDGET_H

View File

@ -23,6 +23,8 @@
#include "qgsvectorlayer.h"
#include "qgscomposerframe.h"
#include "qgsatlascomposition.h"
#include "qgsproject.h"
#include "qgsrelationmanager.h"
//QgsComposerAttributeTableCompareV2
@ -120,6 +122,8 @@ QgsComposerAttributeTableV2::QgsComposerAttributeTableV2( QgsComposition* compos
if ( mVectorLayer )
{
resetColumns();
//listen for modifications to layer and refresh table when they occur
connect( mVectorLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( removeLayer( const QString& ) ) );
@ -149,22 +153,83 @@ void QgsComposerAttributeTableV2::setVectorLayer( QgsVectorLayer* layer )
return;
}
if ( mVectorLayer )
QgsVectorLayer* prevLayer = sourceLayer();
mVectorLayer = layer;
if ( mSource == QgsComposerAttributeTableV2::LayerAttributes && layer != prevLayer )
{
//disconnect from previous layer
QObject::disconnect( mVectorLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
if ( prevLayer )
{
//disconnect from previous layer
disconnect( prevLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
//rebuild column list to match all columns from layer
resetColumns();
//listen for modifications to layer and refresh table when they occur
connect( mVectorLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
mVectorLayer = layer;
refreshAttributes();
emit changed();
}
void QgsComposerAttributeTableV2::setRelationId( const QString relationId )
{
if ( relationId == mRelationId )
{
//no change
return;
}
QgsVectorLayer* prevLayer = sourceLayer();
mRelationId = relationId;
QgsRelation relation = QgsProject::instance()->relationManager()->relation( mRelationId );
QgsVectorLayer* newLayer = relation.referencingLayer();
if ( mSource == QgsComposerAttributeTableV2::RelationChildren && newLayer != prevLayer )
{
if ( prevLayer )
{
//disconnect from previous layer
disconnect( prevLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
//rebuild column list to match all columns from layer
resetColumns();
//listen for modifications to layer and refresh table when they occur
connect( newLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
refreshAttributes();
emit changed();
}
void QgsComposerAttributeTableV2::atlasLayerChanged( QgsVectorLayer *layer )
{
if ( mSource != QgsComposerAttributeTableV2::AtlasFeature || layer == mCurrentAtlasLayer )
{
//nothing to do
return;
}
//atlas feature mode, atlas layer changed, so we need to reset columns
if ( mCurrentAtlasLayer )
{
//disconnect from previous layer
disconnect( mCurrentAtlasLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
mCurrentAtlasLayer = layer;
//rebuild column list to match all columns from layer
resetColumns();
refreshAttributes();
//listen for modifications to layer and refresh table when they occur
QObject::connect( mVectorLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
emit changed();
connect( layer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
void QgsComposerAttributeTableV2::resetColumns()
@ -202,13 +267,13 @@ void QgsComposerAttributeTableV2::setComposerMap( const QgsComposerMap* map )
if ( mComposerMap )
{
//disconnect from previous map
QObject::disconnect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( refreshAttributes() ) );
disconnect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( refreshAttributes() ) );
}
mComposerMap = map;
if ( mComposerMap )
{
//listen out for extent changes in linked map
QObject::connect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( refreshAttributes() ) );
connect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( refreshAttributes() ) );
}
refreshAttributes();
emit changed();
@ -339,10 +404,10 @@ bool QgsComposerAttributeTableV2::getTableContents( QgsComposerTableContents &co
{
contents.clear();
if ( mSource == QgsComposerAttributeTableV2::AtlasFeature &&
!mComposition->atlasComposition().enabled() )
if (( mSource == QgsComposerAttributeTableV2::AtlasFeature || mSource == QgsComposerAttributeTableV2::RelationChildren )
&& !mComposition->atlasComposition().enabled() )
{
//atlas source mode, but atlas disabled
//source mode requires atlas, but atlas disabled
return false;
}
@ -387,6 +452,22 @@ bool QgsComposerAttributeTableV2::getTableContents( QgsComposerTableContents &co
}
QgsFeatureRequest req;
if ( mSource == QgsComposerAttributeTableV2::RelationChildren )
{
QgsRelation relation = QgsProject::instance()->relationManager()->relation( mRelationId );
QgsFeature* atlasFeature = mComposition->atlasComposition().currentFeature();
if ( atlasFeature )
{
req = relation.getRelatedFeaturesRequest( *atlasFeature );
}
else
{
//no atlas feature, so empty table
return true;
}
}
if ( !selectionRect.isEmpty() )
req.setFilterRect( selectionRect );
@ -471,14 +552,18 @@ QgsVectorLayer *QgsComposerAttributeTableV2::sourceLayer()
return mComposition->atlasComposition().coverageLayer();
case QgsComposerAttributeTableV2::LayerAttributes:
return mVectorLayer;
default:
return 0;
case QgsComposerAttributeTableV2::RelationChildren:
{
QgsRelation relation = QgsProject::instance()->relationManager()->relation( mRelationId );
return relation.referencingLayer();
}
}
return 0;
}
void QgsComposerAttributeTableV2::removeLayer( QString layerId )
{
if ( mVectorLayer )
if ( mVectorLayer && mSource == QgsComposerAttributeTableV2::LayerAttributes )
{
if ( layerId == mVectorLayer->id() )
{
@ -490,31 +575,6 @@ void QgsComposerAttributeTableV2::removeLayer( QString layerId )
}
}
void QgsComposerAttributeTableV2::atlasLayerChanged( QgsVectorLayer *layer )
{
if ( mSource != QgsComposerAttributeTableV2::AtlasFeature )
{
//nothing to do
return;
}
//atlas feature mode, atlas layer changed, so we need to reset columns
if ( mCurrentAtlasLayer )
{
//disconnect from previous layer
QObject::disconnect( mCurrentAtlasLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
mCurrentAtlasLayer = layer;
//rebuild column list to match all columns from layer
resetColumns();
refreshAttributes();
//listen for modifications to layer and refresh table when they occur
QObject::connect( layer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
static bool columnsBySortRank( QPair<int, QgsComposerTableColumn* > a, QPair<int, QgsComposerTableColumn* > b )
{
return a.second->sortByRank() < b.second->sortByRank();
@ -554,6 +614,7 @@ bool QgsComposerAttributeTableV2::writeXML( QDomElement& elem, QDomDocument & do
{
QDomElement composerTableElem = doc.createElement( "ComposerAttributeTableV2" );
composerTableElem.setAttribute( "source", QString::number(( int )mSource ) );
composerTableElem.setAttribute( "relationId", mRelationId );
composerTableElem.setAttribute( "showOnlyVisibleFeatures", mShowOnlyVisibleFeatures );
composerTableElem.setAttribute( "maxFeatures", mMaximumNumberOfFeatures );
composerTableElem.setAttribute( "filterFeatures", mFilterFeatures ? "true" : "false" );
@ -592,17 +653,19 @@ bool QgsComposerAttributeTableV2::readXML( const QDomElement& itemElem, const QD
return false;
}
QgsVectorLayer* prevLayer = sourceLayer();
if ( prevLayer )
{
//disconnect from previous layer
disconnect( prevLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
mSource = QgsComposerAttributeTableV2::ContentSource( itemElem.attribute( "source", "0" ).toInt() );
mRelationId = itemElem.attribute( "relationId", "" );
if ( mSource == QgsComposerAttributeTableV2::AtlasFeature )
{
if ( mCurrentAtlasLayer )
{
//disconnect from previous layer
QObject::disconnect( mCurrentAtlasLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
mCurrentAtlasLayer = mComposition->atlasComposition().coverageLayer();
//listen for modifications to layer and refresh table when they occur
QObject::connect( mCurrentAtlasLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
mShowOnlyVisibleFeatures = itemElem.attribute( "showOnlyVisibleFeatures", "1" ).toInt();
@ -629,7 +692,7 @@ bool QgsComposerAttributeTableV2::readXML( const QDomElement& itemElem, const QD
if ( mComposerMap )
{
//if we have found a valid map item, listen out to extent changes on it and refresh the table
QObject::connect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( refreshAttributes() ) );
connect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( refreshAttributes() ) );
}
//vector layer
@ -644,14 +707,12 @@ bool QgsComposerAttributeTableV2::readXML( const QDomElement& itemElem, const QD
if ( ml )
{
mVectorLayer = dynamic_cast<QgsVectorLayer*>( ml );
if ( mVectorLayer && mSource == QgsComposerAttributeTableV2::LayerAttributes )
{
//if we have found a valid vector layer, listen for modifications on it and refresh the table
QObject::connect( mVectorLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
}
}
//connect to new layer
connect( sourceLayer(), SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
refreshAttributes();
emit changed();
@ -681,64 +742,25 @@ void QgsComposerAttributeTableV2::setSource( const QgsComposerAttributeTableV2::
return;
}
QgsVectorLayer* prevLayer = 0;
//disconnect from previous layer
switch ( mSource )
{
case QgsComposerAttributeTableV2::AtlasFeature:
{
prevLayer = mComposition->atlasComposition().coverageLayer();
if ( mCurrentAtlasLayer )
{
QObject::disconnect( mCurrentAtlasLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
break;
}
case QgsComposerAttributeTableV2::LayerAttributes:
{
if ( mVectorLayer )
{
prevLayer = mVectorLayer;
QObject::disconnect( mVectorLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
break;
}
default:
break;
}
QgsVectorLayer* prevLayer = sourceLayer();
mSource = source;
//connect to new layer
QgsVectorLayer* newLayer = 0;
switch ( mSource )
{
case QgsComposerAttributeTableV2::AtlasFeature:
{
mCurrentAtlasLayer = mComposition->atlasComposition().coverageLayer();
newLayer = mCurrentAtlasLayer;
if ( mCurrentAtlasLayer )
{
QObject::connect( mCurrentAtlasLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
break;
}
case QgsComposerAttributeTableV2::LayerAttributes:
{
if ( mVectorLayer )
{
newLayer = mVectorLayer;
QObject::connect( mVectorLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
break;
}
default:
break;
}
QgsVectorLayer* newLayer = sourceLayer();
if ( newLayer != prevLayer )
{
//disconnect from previous layer
if ( prevLayer )
{
disconnect( prevLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
}
//connect to new layer
connect( newLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
if ( mSource == QgsComposerAttributeTableV2::AtlasFeature )
{
mCurrentAtlasLayer = newLayer;
}
//layer has changed as a result of the source change, so reset column list
resetColumns();
}

View File

@ -96,6 +96,14 @@ class CORE_EXPORT QgsComposerAttributeTableV2: public QgsComposerTableV2
*/
ContentSource source() const { return mSource; }
/**Returns the source layer for the table, considering the table source mode. Eg,
* if the table is set to atlas feature mode, then the source layer will be the
* atlas coverage layer. If the table is set to layer attributes mode, then
* the source layer will be the user specified vector layer.
* @returns actual source layer
*/
QgsVectorLayer* sourceLayer();
/**Sets the vector layer from which to display feature attributes
* @param layer Vector layer for attribute table
* @see vectorLayer
@ -108,6 +116,22 @@ class CORE_EXPORT QgsComposerAttributeTableV2: public QgsComposerTableV2
*/
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
* @see relationId
* @see setSource
* @note only used if table source is set to RelationChildren
*/
void setRelationId( const QString relationId );
/**Returns the relation id which the table displays child features from
* @returns relation id
* @see setRelationId
* @see source
* @note only used if table source is set to RelationChildren
*/
QString relationId() const { return mRelationId; }
/**Resets the attribute table's columns to match the vector layer's fields
* @see setVectorLayer
*/
@ -228,6 +252,8 @@ class CORE_EXPORT QgsComposerAttributeTableV2: public QgsComposerTableV2
ContentSource mSource;
/**Associated vector layer*/
QgsVectorLayer* mVectorLayer;
/**Relation id, if in relation children mode*/
QString mRelationId;
/**Current vector layer, if in atlas feature mode*/
QgsVectorLayer* mCurrentAtlasLayer;
@ -245,14 +271,6 @@ class CORE_EXPORT QgsComposerAttributeTableV2: public QgsComposerTableV2
/**Feature filter expression*/
QString mFeatureFilter;
/**Returns the source layer for the table, considering the table source mode. Eg,
* if the table is set to atlas feature mode, then the source layer will be the
* atlas coverage layer. If the table is set to layer attributes mode, then
* the source layer will be the user specified vector layer.
* @returns actual source layer
*/
QgsVectorLayer* sourceLayer();
/**Returns a list of attribute indices corresponding to displayed fields in the table.
* @note kept for compatibility with 2.0 api only
*/

View File

@ -241,7 +241,7 @@ void QgsRelation::updateRelationStatus()
mValid = false;
}
Q_FOREACH ( const FieldPair& fieldPair, mFieldPairs )
Q_FOREACH( const FieldPair& fieldPair, mFieldPairs )
{
if ( -1 == mReferencingLayer->fieldNameIndex( fieldPair.first )
|| -1 == mReferencedLayer->fieldNameIndex( fieldPair.second ) )

View File

@ -31,7 +31,7 @@ class CORE_EXPORT QgsRelation
/**
* Defines a relation between matchin fields of the two involved tables of a relation.
* Often, a relation is only defined by just one FieldPair with the name of the foreign key
* column of the referencing table as first element and the name of the primkary key column
* column of the referencing table as first element and the name of the primary key column
* of the referenced table as the second element.
*
*/

View File

@ -47,7 +47,7 @@
<x>0</x>
<y>0</y>
<width>392</width>
<height>879</height>
<height>918</height>
</rect>
</property>
<layout class="QVBoxLayout" name="mainLayout">
@ -99,21 +99,21 @@
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<item row="4" column="0" colspan="2">
<widget class="QPushButton" name="mRefreshPushButton">
<property name="text">
<string>Refresh table data</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<item row="5" column="0" colspan="2">
<widget class="QPushButton" name="mAttributesPushButton">
<property name="text">
<string>Attributes...</string>
</property>
</widget>
</item>
<item row="4" column="0">
<item row="6" column="0">
<widget class="QLabel" name="mMarginLabel">
<property name="text">
<string>Margin</string>
@ -126,13 +126,23 @@
</property>
</widget>
</item>
<item row="4" column="1">
<item row="6" column="1">
<widget class="QDoubleSpinBox" name="mMarginSpinBox">
<property name="suffix">
<string> mm</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="mRelationsComboBox"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="mRelationLabel">
<property name="text">
<string>Relation</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -28,6 +28,8 @@
#include "qgscompositionchecker.h"
#include "qgsfontutils.h"
#include "qgsmaplayerregistry.h"
#include "qgsproject.h"
#include "qgsrelationmanager.h"
#include <QObject>
#include <QtTest>
@ -54,8 +56,7 @@ class TestQgsComposerTableV2: public QObject
void attributeTableRepeat();
void attributeTableAtlasSource(); //test attribute table in atlas feature mode
void attributeTableRelationSource(); //test attribute table in relation mode
private:
QgsComposition* mComposition;
@ -82,6 +83,7 @@ void TestQgsComposerTableV2::initTestCase()
mVectorLayer = new QgsVectorLayer( vectorFileInfo.filePath(),
vectorFileInfo.completeBaseName(),
"ogr" );
QgsMapLayerRegistry::instance()->addMapLayer( mVectorLayer );
mVectorLayer2 = new QgsVectorLayer( vectorFileInfo.filePath(),
vectorFileInfo.completeBaseName(),
"ogr" );
@ -421,6 +423,93 @@ void TestQgsComposerTableV2::attributeTableAtlasSource()
delete table;
}
void TestQgsComposerTableV2::attributeTableRelationSource()
{
QFileInfo vectorFileInfo( QString( TEST_DATA_DIR ) + QDir::separator() + "points_relations.shp" );
QgsVectorLayer* atlasLayer = new QgsVectorLayer( vectorFileInfo.filePath(),
vectorFileInfo.completeBaseName(),
"ogr" );
QgsMapLayerRegistry::instance()->addMapLayer( atlasLayer );
//setup atlas
mComposition->atlasComposition().setCoverageLayer( atlasLayer );
mComposition->atlasComposition().setEnabled( true );
//create a relation
QgsRelation relation;
relation.setRelationId( "testrelation" );
relation.setReferencedLayer( atlasLayer->id() );
relation.setReferencingLayer( mVectorLayer->id() );
relation.addFieldPair( "Class", "Class" );
QgsProject::instance()->relationManager()->addRelation( relation );
QgsComposerAttributeTableV2* table = new QgsComposerAttributeTableV2( mComposition, false );
table->setMaximumNumberOfFeatures( 50 );
table->setSource( QgsComposerAttributeTableV2::RelationChildren );
table->setRelationId( relation.id() );
QVERIFY( mComposition->atlasComposition().beginRender() );
QVERIFY( mComposition->atlasComposition().prepareForFeature( 0 ) );
QCOMPARE( mComposition->atlasComposition().currentFeature()->attribute( "Class" ).toString(), QString( "Jet" ) );
QCOMPARE( table->contents()->length(), 8 );
QgsComposerTableRow row = table->contents()->at( 0 );
//check a couple of results
QCOMPARE( row.at( 0 ), QVariant( "Jet" ) );
QCOMPARE( row.at( 1 ), QVariant( 90 ) );
QCOMPARE( row.at( 2 ), QVariant( 3 ) );
QCOMPARE( row.at( 3 ), QVariant( 2 ) );
QCOMPARE( row.at( 4 ), QVariant( 0 ) );
QCOMPARE( row.at( 5 ), QVariant( 2 ) );
row = table->contents()->at( 1 );
QCOMPARE( row.at( 0 ), QVariant( "Jet" ) );
QCOMPARE( row.at( 1 ), QVariant( 85 ) );
QCOMPARE( row.at( 2 ), QVariant( 3 ) );
QCOMPARE( row.at( 3 ), QVariant( 1 ) );
QCOMPARE( row.at( 4 ), QVariant( 1 ) );
QCOMPARE( row.at( 5 ), QVariant( 2 ) );
row = table->contents()->at( 2 );
QCOMPARE( row.at( 0 ), QVariant( "Jet" ) );
QCOMPARE( row.at( 1 ), QVariant( 95 ) );
QCOMPARE( row.at( 2 ), QVariant( 3 ) );
QCOMPARE( row.at( 3 ), QVariant( 1 ) );
QCOMPARE( row.at( 4 ), QVariant( 1 ) );
QCOMPARE( row.at( 5 ), QVariant( 2 ) );
//next atlas feature
QVERIFY( mComposition->atlasComposition().prepareForFeature( 1 ) );
QCOMPARE( mComposition->atlasComposition().currentFeature()->attribute( "Class" ).toString(), QString( "Biplane" ) );
QCOMPARE( table->contents()->length(), 5 );
row = table->contents()->at( 0 );
QCOMPARE( row.at( 0 ), QVariant( "Biplane" ) );
QCOMPARE( row.at( 1 ), QVariant( 0 ) );
QCOMPARE( row.at( 2 ), QVariant( 1 ) );
QCOMPARE( row.at( 3 ), QVariant( 3 ) );
QCOMPARE( row.at( 4 ), QVariant( 3 ) );
QCOMPARE( row.at( 5 ), QVariant( 6 ) );
row = table->contents()->at( 1 );
QCOMPARE( row.at( 0 ), QVariant( "Biplane" ) );
QCOMPARE( row.at( 1 ), QVariant( 340 ) );
QCOMPARE( row.at( 2 ), QVariant( 1 ) );
QCOMPARE( row.at( 3 ), QVariant( 3 ) );
QCOMPARE( row.at( 4 ), QVariant( 3 ) );
QCOMPARE( row.at( 5 ), QVariant( 6 ) );
mComposition->atlasComposition().endRender();
//try for a crash when removing current atlas layer
QgsMapLayerRegistry::instance()->removeMapLayer( atlasLayer->id() );
table->refreshAttributes();
mComposition->removeMultiFrame( table );
delete table;
}
QTEST_MAIN( TestQgsComposerTableV2 )
#include "moc_testqgscomposertablev2.cxx"

BIN
tests/testdata/points_relations.dbf vendored Normal file

Binary file not shown.

1
tests/testdata/points_relations.prj vendored Normal file
View File

@ -0,0 +1 @@
GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]

1
tests/testdata/points_relations.qpj vendored Normal file
View File

@ -0,0 +1 @@
GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]

BIN
tests/testdata/points_relations.shp vendored Normal file

Binary file not shown.

BIN
tests/testdata/points_relations.shx vendored Normal file

Binary file not shown.