Skip to content

Commit 89d54ca

Browse files
authoredJan 4, 2022
Merge pull request #46502 from elpaso/formatted_attributes
New represent_attributes function
2 parents e8a9711 + 7c356eb commit 89d54ca

File tree

3 files changed

+183
-0
lines changed

3 files changed

+183
-0
lines changed
 
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "represent_attributes",
3+
"type": "function",
4+
"groups": ["Record and Attributes"],
5+
"description": "Returns a map with the attribute names as keys and the configured representation values as values. The representation value for the attributes depends on the configured widget type for each attribute. Can be used with zero, one or more arguments, see below for details.",
6+
"variants": [
7+
{
8+
"variant": "No parameters",
9+
"variant_description": "If called with no parameters, the function will return the representation of the attributes of the current feature in the current layer.",
10+
"arguments": [],
11+
"examples": [ { "expression": "represent_attributes()", "returns" : "The representation of the attributes for the current feature." } ]
12+
},
13+
{
14+
"variant": "One 'feature' parameter",
15+
"variant_description": "If called with a 'feature' parameter only, the function will return the representation of the attributes of the specified feature from the current layer.",
16+
"arguments": [ { "arg": "feature", "description": "The feature which should be evaluated." } ],
17+
"examples": [ { "expression": "represent_attributes(@atlas_feature)", "returns" : "The representation of the attributes for the specified feature from the current layer." } ]
18+
},
19+
{
20+
"variant": "Layer and feature parameters",
21+
"variant_description": "If called with a 'layer' and a 'feature' parameter, the function will return the representation of the attributes of the specified feature from the specified layer.",
22+
"arguments": [ { "arg": "layer", "description": "The layer (or its ID or name)." }, { "arg": "feature", "description": "The feature which should be evaluated." } ],
23+
"examples": [ { "expression": "represent_attributes('atlas_layer', @atlas_feature)", "returns" : "The representation of the attributes for the specified feature from the specified layer." } ]
24+
}
25+
]
26+
}

‎src/core/expression/qgsexpressionfunction.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1729,6 +1729,88 @@ static QVariant fcnAttributes( const QVariantList &values, const QgsExpressionCo
17291729
return result;
17301730
}
17311731

1732+
static QVariant fcnRepresentAttributes( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction * )
1733+
{
1734+
QgsVectorLayer *layer = nullptr;
1735+
QgsFeature feature;
1736+
1737+
if ( values.isEmpty() )
1738+
{
1739+
feature = context->feature();
1740+
layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1741+
}
1742+
else if ( values.size() == 1 )
1743+
{
1744+
layer = QgsExpressionUtils::getVectorLayer( context->variable( QStringLiteral( "layer" ) ), parent );
1745+
feature = QgsExpressionUtils::getFeature( values.at( 0 ), parent );
1746+
}
1747+
else if ( values.size() == 2 )
1748+
{
1749+
layer = QgsExpressionUtils::getVectorLayer( values.at( 0 ), parent );
1750+
feature = QgsExpressionUtils::getFeature( values.at( 1 ), parent );
1751+
}
1752+
else
1753+
{
1754+
parent->setEvalErrorString( QObject::tr( "Function `represent_attributes` requires no more than two parameters. %1 given." ).arg( values.length() ) );
1755+
return QVariant();
1756+
}
1757+
1758+
if ( !layer )
1759+
{
1760+
parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: layer could not be resolved." ) );
1761+
return QVariant();
1762+
}
1763+
1764+
if ( !feature.isValid() )
1765+
{
1766+
parent->setEvalErrorString( QObject::tr( "Cannot use represent attributes function: feature could not be resolved." ) );
1767+
return QVariant();
1768+
}
1769+
1770+
const QgsFields fields = feature.fields();
1771+
QVariantMap result;
1772+
for ( int fieldIndex = 0; fieldIndex < fields.count(); ++fieldIndex )
1773+
{
1774+
const QString fieldName { fields.at( fieldIndex ).name() };
1775+
const QVariant attributeVal = feature.attribute( fieldIndex );
1776+
const QString cacheValueKey = QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer->id(), fieldName, attributeVal.toString() );
1777+
if ( context && context->hasCachedValue( cacheValueKey ) )
1778+
{
1779+
result.insert( fieldName, context->cachedValue( cacheValueKey ) );
1780+
}
1781+
else
1782+
{
1783+
const QgsEditorWidgetSetup setup = layer->editorWidgetSetup( fieldIndex );
1784+
QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
1785+
QVariant cache;
1786+
if ( context )
1787+
{
1788+
const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer->id(), fieldName );
1789+
1790+
if ( !context->hasCachedValue( cacheKey ) )
1791+
{
1792+
cache = fieldFormatter->createCache( layer, fieldIndex, setup.config() );
1793+
context->setCachedValue( cacheKey, cache );
1794+
}
1795+
else
1796+
{
1797+
cache = context->cachedValue( cacheKey );
1798+
}
1799+
}
1800+
QString value( fieldFormatter->representValue( layer, fieldIndex, setup.config(), cache, attributeVal ) );
1801+
1802+
result.insert( fields.at( fieldIndex ).name(), value );
1803+
1804+
if ( context )
1805+
{
1806+
context->setCachedValue( cacheValueKey, value );
1807+
}
1808+
1809+
}
1810+
}
1811+
return result;
1812+
}
1813+
17321814
static QVariant fcnCoreFeatureMaptipDisplay( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const bool isMaptip )
17331815
{
17341816
QgsVectorLayer *layer = nullptr;
@@ -7809,6 +7891,10 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
78097891
fcnAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
78107892
attributesFunc->setIsStatic( false );
78117893
functions << attributesFunc;
7894+
QgsStaticExpressionFunction *representAttributesFunc = new QgsStaticExpressionFunction( QStringLiteral( "represent_attributes" ), -1,
7895+
fcnRepresentAttributes, QStringLiteral( "Record and Attributes" ), QString(), false, QSet<QString>() << QgsFeatureRequest::ALL_ATTRIBUTES );
7896+
representAttributesFunc->setIsStatic( false );
7897+
functions << representAttributesFunc;
78127898

78137899
QgsStaticExpressionFunction *maptipFunc = new QgsStaticExpressionFunction(
78147900
QStringLiteral( "maptip" ),

‎tests/src/core/testqgsexpression.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,77 @@ class TestQgsExpression: public QObject
520520
QCOMPARE( exp.dump(), dump );
521521
}
522522

523+
void represent_attributes()
524+
{
525+
526+
QgsVectorLayer layer { QStringLiteral( "Point?field=col1:integer&field=col2:string" ), QStringLiteral( "test_represent_attributes" ), QStringLiteral( "memory" ) };
527+
QVERIFY( layer.isValid() );
528+
QgsFeature f1( layer.dataProvider()->fields(), 1 );
529+
f1.setAttribute( QStringLiteral( "col1" ), 1 );
530+
f1.setAttribute( QStringLiteral( "col2" ), "test1" );
531+
QgsFeature f2( layer.dataProvider()->fields(), 2 );
532+
f2.setAttribute( QStringLiteral( "col1" ), 2 );
533+
f2.setAttribute( QStringLiteral( "col2" ), "test2" );
534+
layer.dataProvider()->addFeatures( QgsFeatureList() << f1 << f2 );
535+
536+
QVariantMap config;
537+
QVariantMap map;
538+
map.insert( QStringLiteral( "one" ), QStringLiteral( "1" ) );
539+
map.insert( QStringLiteral( "two" ), QStringLiteral( "2" ) );
540+
541+
config.insert( QStringLiteral( "map" ), map );
542+
layer.setEditorWidgetSetup( 0, QgsEditorWidgetSetup( QStringLiteral( "ValueMap" ), config ) );
543+
544+
QgsExpressionContext context( { QgsExpressionContextUtils::layerScope( &layer ) } );
545+
context.setFeature( f2 );
546+
QgsExpression expression( "represent_attributes()" );
547+
548+
if ( expression.hasParserError() )
549+
qDebug() << expression.parserErrorString();
550+
QVERIFY( !expression.hasParserError() );
551+
552+
expression.prepare( &context );
553+
554+
QVariantMap result { expression.evaluate( &context ).toMap() };
555+
556+
QCOMPARE( result.value( QStringLiteral( "col1" ) ).toString(), QStringLiteral( "two" ) );
557+
QCOMPARE( result.value( QStringLiteral( "col2" ) ).toString(), QStringLiteral( "test2" ) );
558+
559+
QgsExpressionContext context2( { QgsExpressionContextUtils::layerScope( &layer ) } );
560+
context2.setFeature( f2 );
561+
expression = QgsExpression( "represent_attributes($currentfeature)" );
562+
563+
result = expression.evaluate( &context2 ).toMap();
564+
565+
QVERIFY( !expression.hasEvalError() );
566+
567+
QCOMPARE( result.value( QStringLiteral( "col1" ) ).toString(), QStringLiteral( "two" ) );
568+
QCOMPARE( result.value( QStringLiteral( "col2" ) ).toString(), QStringLiteral( "test2" ) );
569+
570+
QgsProject::instance()->addMapLayer( &layer, false, false );
571+
QgsExpressionContext context3;
572+
context3.setFeature( f2 );
573+
expression = QgsExpression( "represent_attributes('test_represent_attributes', $currentfeature)" );
574+
575+
result = expression.evaluate( &context3 ).toMap();
576+
577+
QVERIFY( !expression.hasEvalError() );
578+
579+
QCOMPARE( result.value( QStringLiteral( "col1" ) ).toString(), QStringLiteral( "two" ) );
580+
QCOMPARE( result.value( QStringLiteral( "col2" ) ).toString(), QStringLiteral( "test2" ) );
581+
582+
// Test the cached value
583+
QCOMPARE( context3.cachedValue( QStringLiteral( "repvalfcnval:%1:%2:%3" ).arg( layer.id(), QStringLiteral( "col1" ), QStringLiteral( "2" ) ) ).toString(), QStringLiteral( "two" ) );
584+
585+
// Test errors
586+
QgsProject::instance()->removeMapLayer( layer.id() );
587+
expression = QgsExpression( "represent_attributes('test_represent_attributes', $currentfeature)" );
588+
QgsExpressionContext context4;
589+
result = expression.evaluate( &context4 ).toMap();
590+
QVERIFY( expression.hasEvalError() );
591+
592+
};
593+
523594
void represent_value()
524595
{
525596
QVariantMap config;

0 commit comments

Comments
 (0)
Please sign in to comment.