mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
[FEATURE][layouts] Data defined table source for attribute table items
When an attribute table is set to a "Layer features" source, this allows the underlying vector layer from which to source features to be data defined. (All existing table attributes (column settings) are left intact, so setting a data defined table to a layer with different fields will result in empty columns in the table.) Sponsored by Kartoza/Inasafe
This commit is contained in:
parent
97324661c7
commit
0c1ceb3900
@ -295,6 +295,9 @@ be replaced by a line break.
|
||||
virtual void finalizeRestoreFromXml();
|
||||
|
||||
|
||||
virtual void refreshDataDefinedProperty( const QgsLayoutObject::DataDefinedProperty property = QgsLayoutObject::AllProperties );
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
virtual bool writePropertiesToElement( QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context ) const;
|
||||
|
@ -71,6 +71,8 @@ A base class for objects which belong to a layout.
|
||||
ScalebarFillColor2,
|
||||
ScalebarLineColor,
|
||||
ScalebarLineWidth,
|
||||
//table
|
||||
AttributeTableSourceLayer,
|
||||
};
|
||||
|
||||
enum PropertyValueType
|
||||
|
@ -131,8 +131,12 @@ QgsLayoutAttributeTableWidget::QgsLayoutAttributeTableWidget( QgsLayoutFrame *fr
|
||||
{
|
||||
connect( atlas, &QgsLayoutAtlas::toggled, this, &QgsLayoutAttributeTableWidget::atlasToggled );
|
||||
}
|
||||
|
||||
mLayerSourceDDBtn->registerExpressionContextGenerator( mTable );
|
||||
}
|
||||
|
||||
registerDataDefinedButton( mLayerSourceDDBtn, QgsLayoutObject::AttributeTableSourceLayer );
|
||||
|
||||
//embed widget for general options
|
||||
if ( mFrame )
|
||||
{
|
||||
@ -925,6 +929,7 @@ void QgsLayoutAttributeTableWidget::toggleSourceControls()
|
||||
case QgsLayoutItemAttributeTable::LayerAttributes:
|
||||
mLayerComboBox->setEnabled( true );
|
||||
mLayerComboBox->setVisible( true );
|
||||
mLayerSourceDDBtn->setVisible( true );
|
||||
mLayerLabel->setVisible( true );
|
||||
mRelationsComboBox->setEnabled( false );
|
||||
mRelationsComboBox->setVisible( false );
|
||||
@ -938,6 +943,7 @@ void QgsLayoutAttributeTableWidget::toggleSourceControls()
|
||||
case QgsLayoutItemAttributeTable::AtlasFeature:
|
||||
mLayerComboBox->setEnabled( false );
|
||||
mLayerComboBox->setVisible( false );
|
||||
mLayerSourceDDBtn->setVisible( false );
|
||||
mLayerLabel->setVisible( false );
|
||||
mRelationsComboBox->setEnabled( false );
|
||||
mRelationsComboBox->setVisible( false );
|
||||
@ -952,6 +958,7 @@ void QgsLayoutAttributeTableWidget::toggleSourceControls()
|
||||
mLayerComboBox->setEnabled( false );
|
||||
mLayerComboBox->setVisible( false );
|
||||
mLayerLabel->setVisible( false );
|
||||
mLayerSourceDDBtn->setVisible( false );
|
||||
mRelationsComboBox->setEnabled( true );
|
||||
mRelationsComboBox->setVisible( true );
|
||||
mRelationLabel->setVisible( true );
|
||||
|
@ -552,6 +552,28 @@ void QgsLayoutItemAttributeTable::finalizeRestoreFromXml()
|
||||
}
|
||||
}
|
||||
|
||||
void QgsLayoutItemAttributeTable::refreshDataDefinedProperty( const QgsLayoutObject::DataDefinedProperty property )
|
||||
{
|
||||
QgsExpressionContext context = createExpressionContext();
|
||||
|
||||
if ( mSource == QgsLayoutItemAttributeTable::LayerAttributes &&
|
||||
( property == QgsLayoutObject::AttributeTableSourceLayer || property == QgsLayoutObject::AllProperties ) )
|
||||
{
|
||||
mDataDefinedVectorLayer = nullptr;
|
||||
|
||||
QString currentLayerIdentifier;
|
||||
if ( QgsVectorLayer *currentLayer = mVectorLayer.get() )
|
||||
currentLayerIdentifier = currentLayer->id();
|
||||
|
||||
const QString layerIdentifier = mDataDefinedProperties.valueAsString( QgsLayoutObject::AttributeTableSourceLayer, context, currentLayerIdentifier );
|
||||
QgsVectorLayer *ddLayer = qobject_cast< QgsVectorLayer * >( QgsLayoutUtils::mapLayerFromString( layerIdentifier, mLayout->project() ) );
|
||||
if ( ddLayer )
|
||||
mDataDefinedVectorLayer = ddLayer;
|
||||
}
|
||||
|
||||
QgsLayoutMultiFrame::refreshDataDefinedProperty( property );
|
||||
}
|
||||
|
||||
QVariant QgsLayoutItemAttributeTable::replaceWrapChar( const QVariant &variant ) const
|
||||
{
|
||||
//avoid converting variants to string if not required (try to maintain original type for sorting)
|
||||
@ -570,7 +592,12 @@ QgsVectorLayer *QgsLayoutItemAttributeTable::sourceLayer() const
|
||||
case QgsLayoutItemAttributeTable::AtlasFeature:
|
||||
return mLayout->reportContext().layer();
|
||||
case QgsLayoutItemAttributeTable::LayerAttributes:
|
||||
return mVectorLayer.get();
|
||||
{
|
||||
if ( mDataDefinedVectorLayer )
|
||||
return mDataDefinedVectorLayer;
|
||||
else
|
||||
return mVectorLayer.get();
|
||||
}
|
||||
case QgsLayoutItemAttributeTable::RelationChildren:
|
||||
{
|
||||
QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
|
||||
|
@ -292,6 +292,8 @@ class CORE_EXPORT QgsLayoutItemAttributeTable: public QgsLayoutTable
|
||||
QgsExpressionContext createExpressionContext() const override;
|
||||
void finalizeRestoreFromXml() override;
|
||||
|
||||
void refreshDataDefinedProperty( const QgsLayoutObject::DataDefinedProperty property = QgsLayoutObject::AllProperties ) override;
|
||||
|
||||
protected:
|
||||
|
||||
bool writePropertiesToElement( QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context ) const override;
|
||||
@ -303,6 +305,10 @@ class CORE_EXPORT QgsLayoutItemAttributeTable: public QgsLayoutTable
|
||||
ContentSource mSource = LayerAttributes;
|
||||
//! Associated vector layer
|
||||
QgsVectorLayerRef mVectorLayer;
|
||||
|
||||
//! Data defined vector layer - only
|
||||
QPointer< QgsVectorLayer > mDataDefinedVectorLayer;
|
||||
|
||||
//! Relation id, if in relation children mode
|
||||
QString mRelationId;
|
||||
|
||||
|
@ -76,6 +76,7 @@ void QgsLayoutObject::initPropertyDefinitions()
|
||||
{ QgsLayoutObject::ScalebarFillColor2, QgsPropertyDefinition( "dataDefinedScalebarFill2", QObject::tr( "Secondary fill color" ), QgsPropertyDefinition::ColorWithAlpha ) },
|
||||
{ QgsLayoutObject::ScalebarLineColor, QgsPropertyDefinition( "dataDefinedScalebarLineColor", QObject::tr( "Line color" ), QgsPropertyDefinition::ColorWithAlpha ) },
|
||||
{ QgsLayoutObject::ScalebarLineWidth, QgsPropertyDefinition( "dataDefinedScalebarLineWidth", QObject::tr( "Line width" ), QgsPropertyDefinition::StrokeWidth ) },
|
||||
{ QgsLayoutObject::AttributeTableSourceLayer, QgsPropertyDefinition( "dataDefinedAttributeTableSourceLayer", QObject::tr( "Table source layer" ), QgsPropertyDefinition::String ) },
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,9 @@ class CORE_EXPORT QgsLayoutObject: public QObject, public QgsExpressionContextGe
|
||||
ScalebarFillColor, //!< Scalebar fill color
|
||||
ScalebarFillColor2, //!< Scalebar secondary fill color
|
||||
ScalebarLineColor, //!< Scalebar line color
|
||||
ScalebarLineWidth, //!< Scalebar line width
|
||||
ScalebarLineWidth, //!< Scalebar line width,
|
||||
//table item
|
||||
AttributeTableSourceLayer, //!< Attribute table source layer
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -55,8 +55,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>689</width>
|
||||
<height>2574</height>
|
||||
<width>394</width>
|
||||
<height>1487</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="mainLayout">
|
||||
@ -85,6 +85,30 @@
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="mSourceComboBox"/>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="mRelationLabel">
|
||||
<property name="text">
|
||||
<string>Relation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QComboBox" name="mRelationsComboBox"/>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="mRefreshPushButton">
|
||||
<property name="text">
|
||||
<string>Refresh table data</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="mAttributesPushButton">
|
||||
<property name="text">
|
||||
<string>Attributes...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="mLayerLabel">
|
||||
<property name="text">
|
||||
@ -96,38 +120,25 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QgsMapLayerComboBox" name="mLayerComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="mRelationLabel">
|
||||
<property name="text">
|
||||
<string>Relation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="mRelationsComboBox"/>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="mRefreshPushButton">
|
||||
<property name="text">
|
||||
<string>Refresh table data</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="mAttributesPushButton">
|
||||
<property name="text">
|
||||
<string>Attributes...</string>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QgsMapLayerComboBox" name="mLayerComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QgsPropertyOverrideButton" name="mLayerSourceDDBtn">
|
||||
<property name="text">
|
||||
<string>…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@ -832,12 +843,18 @@
|
||||
<extends>QComboBox</extends>
|
||||
<header>qgslayoutitemcombobox.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsPropertyOverrideButton</class>
|
||||
<extends>QToolButton</extends>
|
||||
<header>qgspropertyoverridebutton.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>scrollArea</tabstop>
|
||||
<tabstop>groupBox</tabstop>
|
||||
<tabstop>mSourceComboBox</tabstop>
|
||||
<tabstop>mLayerComboBox</tabstop>
|
||||
<tabstop>mLayerSourceDDBtn</tabstop>
|
||||
<tabstop>mRelationsComboBox</tabstop>
|
||||
<tabstop>mRefreshPushButton</tabstop>
|
||||
<tabstop>mAttributesPushButton</tabstop>
|
||||
@ -905,6 +922,8 @@
|
||||
<include location="../../../images/images.qrc"/>
|
||||
<include location="../../../images/images.qrc"/>
|
||||
<include location="../../../images/images.qrc"/>
|
||||
<include location="../../../images/images.qrc"/>
|
||||
<include location="../../../images/images.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
@ -70,6 +70,7 @@ class TestQgsLayoutTable : public QObject
|
||||
void autoWrap(); //test auto word wrap
|
||||
void cellStyles(); //test cell styles
|
||||
void cellStylesRender(); //test rendering cell styles
|
||||
void dataDefinedSource();
|
||||
|
||||
private:
|
||||
QgsVectorLayer *mVectorLayer = nullptr;
|
||||
@ -1323,5 +1324,69 @@ void TestQgsLayoutTable::cellStylesRender()
|
||||
QVERIFY( checker.testLayout( mReport, 0 ) );
|
||||
}
|
||||
|
||||
void TestQgsLayoutTable::dataDefinedSource()
|
||||
{
|
||||
// add a couple of layers
|
||||
QgsVectorLayer *layer1 = new QgsVectorLayer( QStringLiteral( "Point?field=col1:integer&field=col2:integer&field=col3:integer" ), QStringLiteral( "l1" ), QStringLiteral( "memory" ) );
|
||||
QVERIFY( layer1->isValid() );
|
||||
QgsFeature f( layer1->fields() );
|
||||
f.setAttributes( QgsAttributes() << 1 << 2 << 3 );
|
||||
layer1->dataProvider()->addFeature( f );
|
||||
|
||||
// different field order
|
||||
QgsVectorLayer *layer2 = new QgsVectorLayer( QStringLiteral( "Point?field=col1:integer&field=col3:integer&field=col2:integer" ), QStringLiteral( "l2" ), QStringLiteral( "memory" ) );
|
||||
QVERIFY( layer2->isValid() );
|
||||
QgsFeature f2( layer2->fields() );
|
||||
f2.setAttributes( QgsAttributes() << 11 << 13 << 12 );
|
||||
layer2->dataProvider()->addFeature( f2 );
|
||||
|
||||
// missing fields
|
||||
QgsVectorLayer *layer3 = new QgsVectorLayer( QStringLiteral( "Point?field=col1:integer&field=col3:integer" ), QStringLiteral( "l3" ), QStringLiteral( "memory" ) );
|
||||
QVERIFY( layer3->isValid() );
|
||||
QgsFeature f3( layer3->fields() );
|
||||
f3.setAttributes( QgsAttributes() << 21 << 23 );
|
||||
layer3->dataProvider()->addFeature( f3 );
|
||||
|
||||
QgsProject p;
|
||||
p.addMapLayer( layer1 );
|
||||
p.addMapLayer( layer2 );
|
||||
p.addMapLayer( layer3 );
|
||||
|
||||
QgsLayout l( &p );
|
||||
l.initializeDefaults();
|
||||
QgsLayoutItemAttributeTable *table = new QgsLayoutItemAttributeTable( &l );
|
||||
table->setSource( QgsLayoutItemAttributeTable::LayerAttributes );
|
||||
table->setVectorLayer( layer1 );
|
||||
table->setMaximumNumberOfFeatures( 50 );
|
||||
QCOMPARE( table->contents().length(), 1 );
|
||||
QCOMPARE( table->contents().at( 0 ), QVector< QVariant >() << 1 << 2 << 3 );
|
||||
|
||||
// data defined table name, by layer id
|
||||
table->dataDefinedProperties().setProperty( QgsLayoutObject::AttributeTableSourceLayer, layer1->id() );
|
||||
table->refresh();
|
||||
QCOMPARE( table->contents().length(), 1 );
|
||||
QCOMPARE( table->contents().at( 0 ), QVector< QVariant >() << 1 << 2 << 3 );
|
||||
|
||||
// by layer name
|
||||
table->dataDefinedProperties().setProperty( QgsLayoutObject::AttributeTableSourceLayer, QStringLiteral( "l2" ) );
|
||||
table->refresh();
|
||||
QCOMPARE( table->contents().length(), 1 );
|
||||
QCOMPARE( table->contents().at( 0 ), QVector< QVariant >() << 11 << 12 << 13 );
|
||||
|
||||
// by layer name (case insensitive)
|
||||
table->dataDefinedProperties().setProperty( QgsLayoutObject::AttributeTableSourceLayer, QStringLiteral( "L3" ) );
|
||||
table->refresh();
|
||||
QCOMPARE( table->contents().length(), 1 );
|
||||
QCOMPARE( table->contents().at( 0 ), QVector< QVariant >() << 21 << QVariant() << 23 );
|
||||
|
||||
// delete current data defined layer match
|
||||
p.removeMapLayer( layer3->id() );
|
||||
QApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
|
||||
// expect table to return to preset layer
|
||||
table->refreshAttributes();
|
||||
QCOMPARE( table->contents().length(), 1 );
|
||||
QCOMPARE( table->contents().at( 0 ), QVector< QVariant >() << 1 << 2 << 3 );
|
||||
}
|
||||
|
||||
QGSTEST_MAIN( TestQgsLayoutTable )
|
||||
#include "testqgslayouttable.moc"
|
||||
|
Loading…
x
Reference in New Issue
Block a user