mirror of
https://github.com/qgis/QGIS.git
synced 2025-06-19 00:02:48 -04:00
[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:
parent
f58768c550
commit
c3ec4b9eb0
@ -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
|
||||
|
@ -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.
|
||||
*
|
||||
*/
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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 ) )
|
||||
|
@ -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.
|
||||
*
|
||||
*/
|
||||
|
@ -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>
|
||||
|
@ -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
BIN
tests/testdata/points_relations.dbf
vendored
Normal file
Binary file not shown.
1
tests/testdata/points_relations.prj
vendored
Normal file
1
tests/testdata/points_relations.prj
vendored
Normal 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
1
tests/testdata/points_relations.qpj
vendored
Normal 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
BIN
tests/testdata/points_relations.shp
vendored
Normal file
Binary file not shown.
BIN
tests/testdata/points_relations.shx
vendored
Normal file
BIN
tests/testdata/points_relations.shx
vendored
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user