diff --git a/python/core/layout/qgslayoutitemattributetable.sip.in b/python/core/layout/qgslayoutitemattributetable.sip.in
index 10a7f29f2be..d9235ae5556 100644
--- a/python/core/layout/qgslayoutitemattributetable.sip.in
+++ b/python/core/layout/qgslayoutitemattributetable.sip.in
@@ -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;
diff --git a/python/core/layout/qgslayoutobject.sip.in b/python/core/layout/qgslayoutobject.sip.in
index 1905b3b6048..788c8333e64 100644
--- a/python/core/layout/qgslayoutobject.sip.in
+++ b/python/core/layout/qgslayoutobject.sip.in
@@ -71,6 +71,8 @@ A base class for objects which belong to a layout.
ScalebarFillColor2,
ScalebarLineColor,
ScalebarLineWidth,
+ //table
+ AttributeTableSourceLayer,
};
enum PropertyValueType
diff --git a/src/app/layout/qgslayoutattributetablewidget.cpp b/src/app/layout/qgslayoutattributetablewidget.cpp
index 1cd843be903..1570ab8b87c 100644
--- a/src/app/layout/qgslayoutattributetablewidget.cpp
+++ b/src/app/layout/qgslayoutattributetablewidget.cpp
@@ -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 );
diff --git a/src/core/layout/qgslayoutitemattributetable.cpp b/src/core/layout/qgslayoutitemattributetable.cpp
index 6ad26f63356..a0825bfddc9 100644
--- a/src/core/layout/qgslayoutitemattributetable.cpp
+++ b/src/core/layout/qgslayoutitemattributetable.cpp
@@ -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 );
diff --git a/src/core/layout/qgslayoutitemattributetable.h b/src/core/layout/qgslayoutitemattributetable.h
index ec2d761affa..d2dd9b1909f 100644
--- a/src/core/layout/qgslayoutitemattributetable.h
+++ b/src/core/layout/qgslayoutitemattributetable.h
@@ -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;
diff --git a/src/core/layout/qgslayoutobject.cpp b/src/core/layout/qgslayoutobject.cpp
index 255b1c27158..637866632f2 100644
--- a/src/core/layout/qgslayoutobject.cpp
+++ b/src/core/layout/qgslayoutobject.cpp
@@ -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 ) },
};
}
diff --git a/src/core/layout/qgslayoutobject.h b/src/core/layout/qgslayoutobject.h
index bfc04d0b7d5..08bc21ff4cb 100644
--- a/src/core/layout/qgslayoutobject.h
+++ b/src/core/layout/qgslayoutobject.h
@@ -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
};
/**
diff --git a/src/ui/layout/qgslayoutattributetablewidgetbase.ui b/src/ui/layout/qgslayoutattributetablewidgetbase.ui
index a9ac77b7dc2..8f58f20760e 100644
--- a/src/ui/layout/qgslayoutattributetablewidgetbase.ui
+++ b/src/ui/layout/qgslayoutattributetablewidgetbase.ui
@@ -55,8 +55,8 @@
0
0
- 689
- 2574
+ 394
+ 1487
@@ -85,6 +85,30 @@
-
+ -
+
+
+ Relation
+
+
+
+ -
+
+
+ -
+
+
+ Refresh table data
+
+
+
+ -
+
+
+ Attributes...
+
+
+
-
@@ -96,38 +120,25 @@
-
-
-
-
- 0
- 0
-
-
-
-
- -
-
-
- Relation
-
-
-
- -
-
-
- -
-
-
- Refresh table data
-
-
-
- -
-
-
- Attributes...
-
-
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ …
+
+
+
+
@@ -832,12 +843,18 @@
QComboBox
+
+ QgsPropertyOverrideButton
+ QToolButton
+ qgspropertyoverridebutton.h
+
scrollArea
groupBox
mSourceComboBox
mLayerComboBox
+ mLayerSourceDDBtn
mRelationsComboBox
mRefreshPushButton
mAttributesPushButton
@@ -905,6 +922,8 @@
+
+
diff --git a/tests/src/core/testqgslayouttable.cpp b/tests/src/core/testqgslayouttable.cpp
index af5b188b71f..961aa2f8f59 100644
--- a/tests/src/core/testqgslayouttable.cpp
+++ b/tests/src/core/testqgslayouttable.cpp
@@ -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"