QGIS API Documentation  3.4.15-Madeira (e83d02e274)
qgsvaluerelationfieldformatter.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvaluerelationfieldformatter.cpp - QgsValueRelationFieldFormatter
3 
4  ---------------------
5  begin : 3.12.2016
6  copyright : (C) 2016 by Matthias Kuhn
7  email : [email protected]
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
17 
18 #include "qgis.h"
19 #include "qgsproject.h"
20 #include "qgsvectorlayer.h"
21 #include "qgsexpressionnodeimpl.h"
22 
23 #include <QSettings>
24 
26 {
27  return qgsVariantLessThan( p1.key, p2.key );
28 }
29 
31 {
32  return qgsVariantLessThan( p1.value, p2.value );
33 }
34 
36 {
37  return QStringLiteral( "ValueRelation" );
38 }
39 
40 QString QgsValueRelationFieldFormatter::representValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const
41 {
42  Q_UNUSED( layer )
43  Q_UNUSED( fieldIndex )
44 
45  ValueRelationCache vrCache;
46 
47  if ( cache.isValid() )
48  {
50  }
51  else
52  {
54  }
55 
56  if ( config.value( QStringLiteral( "AllowMulti" ) ).toBool() )
57  {
58  QStringList keyList = valueToStringList( value );
59  QStringList valueList;
60 
61  for ( const QgsValueRelationFieldFormatter::ValueRelationItem &item : qgis::as_const( vrCache ) )
62  {
63  if ( keyList.contains( item.key.toString() ) )
64  {
65  valueList << item.value;
66  }
67  }
68 
69  return valueList.join( QStringLiteral( ", " ) ).prepend( '{' ).append( '}' );
70  }
71  else
72  {
73  if ( value.isNull() )
74  {
76  }
77 
78  for ( const QgsValueRelationFieldFormatter::ValueRelationItem &item : qgis::as_const( vrCache ) )
79  {
80  if ( item.key == value )
81  {
82  return item.value;
83  }
84  }
85  }
86 
87  return QStringLiteral( "(%1)" ).arg( value.toString() );
88 }
89 
90 QVariant QgsValueRelationFieldFormatter::sortValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const
91 {
92  return representValue( layer, fieldIndex, config, cache, value );
93 }
94 
95 QVariant QgsValueRelationFieldFormatter::createCache( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config ) const
96 {
97  Q_UNUSED( layer )
98  Q_UNUSED( fieldIndex )
99  return QVariant::fromValue<ValueRelationCache>( createCache( config ) );
100 
101 }
102 
104 {
105  ValueRelationCache cache;
106 
107  QgsVectorLayer *layer = qobject_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( config.value( QStringLiteral( "Layer" ) ).toString() ) );
108 
109  if ( !layer )
110  return cache;
111 
112  QgsFields fields = layer->fields();
113  int ki = fields.indexOf( config.value( QStringLiteral( "Key" ) ).toString() );
114  int vi = fields.indexOf( config.value( QStringLiteral( "Value" ) ).toString() );
115 
116  QgsFeatureRequest request;
117 
119  request.setSubsetOfAttributes( QgsAttributeList() << ki << vi );
120 
121  const QString expression = config.value( QStringLiteral( "FilterExpression" ) ).toString();
122 
123  // Skip the filter and build a full cache if the form scope is required and the feature
124  // is not valid or the attributes required for the filter have no valid value
125  if ( ! expression.isEmpty() && ( ! expressionRequiresFormScope( expression )
126  || expressionIsUsable( expression, formFeature ) ) )
127  {
129  if ( formFeature.isValid( ) )
130  context.appendScope( QgsExpressionContextUtils::formScope( formFeature ) );
131  request.setExpressionContext( context );
132  request.setFilterExpression( expression );
133  }
134 
135  QgsFeatureIterator fit = layer->getFeatures( request );
136 
137  QgsFeature f;
138  while ( fit.nextFeature( f ) )
139  {
140  cache.append( ValueRelationItem( f.attribute( ki ), f.attribute( vi ).toString() ) );
141  }
142 
143  if ( config.value( QStringLiteral( "OrderByValue" ) ).toBool() )
144  {
145  std::sort( cache.begin(), cache.end(), orderByValueLessThan );
146  }
147  else
148  {
149  std::sort( cache.begin(), cache.end(), orderByKeyLessThan );
150  }
151 
152  return cache;
153 }
154 
155 QStringList QgsValueRelationFieldFormatter::valueToStringList( const QVariant &value )
156 {
157  QStringList checkList;
158  if ( value.type() == QVariant::StringList )
159  checkList = value.toStringList();
160  else if ( value.type() == QVariant::String )
161  checkList = value.toString().remove( QChar( '{' ) ).remove( QChar( '}' ) ).split( ',' );
162  else if ( value.type() == QVariant::List )
163  {
164  QVariantList valuesList( value.toList( ) );
165  for ( const QVariant &listItem : qgis::as_const( valuesList ) )
166  {
167  QString v( listItem.toString( ) );
168  if ( ! v.isEmpty() )
169  checkList.append( v );
170  }
171  }
172  return checkList;
173 }
174 
175 
176 QSet<QString> QgsValueRelationFieldFormatter::expressionFormVariables( const QString &expression )
177 {
178  std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::formScope() );
179  QSet< QString > formVariables = scope->variableNames().toSet();
180  const QSet< QString > usedVariables = QgsExpression( expression ).referencedVariables();
181  formVariables.intersect( usedVariables );
182  return formVariables;
183 }
184 
186 {
187  return !( expressionFormAttributes( expression ).isEmpty() && expressionFormVariables( expression ).isEmpty() );
188 }
189 
190 QSet<QString> QgsValueRelationFieldFormatter::expressionFormAttributes( const QString &expression )
191 {
192  QSet<QString> attributes;
193  QgsExpression exp( expression );
194  std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::formScope() );
195  // List of form function names used in the expression
196  const QSet<QString> formFunctions( scope->functionNames()
197  .toSet()
198  .intersect( exp.referencedFunctions( ) ) );
199  const QList<const QgsExpressionNodeFunction *> expFunctions( exp.findNodes<QgsExpressionNodeFunction>() );
200  QgsExpressionContext context;
201  for ( const auto &f : expFunctions )
202  {
203  QgsExpressionFunction *fd = QgsExpression::QgsExpression::Functions()[f->fnIndex()];
204  if ( formFunctions.contains( fd->name( ) ) )
205  {
206  for ( const auto &param : f->args( )->list() )
207  {
208  attributes.insert( param->eval( &exp, &context ).toString() );
209  }
210  }
211  }
212  return attributes;
213 }
214 
215 bool QgsValueRelationFieldFormatter::expressionIsUsable( const QString &expression, const QgsFeature &feature )
216 {
217  const QSet<QString> attrs = expressionFormAttributes( expression );
218  for ( auto it = attrs.constBegin() ; it != attrs.constEnd(); it++ )
219  {
220  if ( ! feature.attribute( *it ).isValid() )
221  return false;
222  }
223  if ( ! expressionFormVariables( expression ).isEmpty() && feature.geometry().isEmpty( ) )
224  return false;
225  return true;
226 }
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
static QSet< QString > expressionFormVariables(const QString &expression)
Returns a list of variables required by the form context expression.
QSet< QString > referencedVariables() const
Returns a list of all variables which are used in this expression.
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:183
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
Container of fields for a vector layer.
Definition: qgsfields.h:42
QVariant sortValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const override
If the default sort order should be overwritten for this widget, you can transform the value in here...
QSet< QString > referencedFunctions() const
Returns a list of the names of all functions which are used in this expression.
QString name() const
The name of the function.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
static QgsExpressionContextScope * formScope(const QgsFeature &formFeature=QgsFeature(), const QString &formMode=QString())
Creates a new scope which contains functions and variables from the current attribute form/table feat...
QVector< QgsValueRelationFieldFormatter::ValueRelationItem > ValueRelationCache
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition: qgis.cpp:153
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
This class wraps a request for features to a vector layer (or directly its vector data provider)...
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer&#39;s project and layer.
An expression node for expression functions.
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:207
QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const override
Create a cache for a given field.
static QSet< QString > expressionFormAttributes(const QString &expression)
Returns a list of attributes required by the form context expression.
bool orderByKeyLessThan(const QgsValueRelationFieldFormatter::ValueRelationItem &p1, const QgsValueRelationFieldFormatter::ValueRelationItem &p2)
bool orderByValueLessThan(const QgsValueRelationFieldFormatter::ValueRelationItem &p1, const QgsValueRelationFieldFormatter::ValueRelationItem &p2)
A abstract base class for defining QgsExpression functions.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:262
QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const override
Create a pretty String representation of the value.
QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
static QStringList valueToStringList(const QVariant &value)
Utility to convert an array or a string representation of an array value to a string list...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:411
static bool expressionRequiresFormScope(const QString &expression)
Check if the expression requires a form scope (i.e.
QString id() const override
Returns a unique id for this field formatter.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Query the layer for features specified in request.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
static bool expressionIsUsable(const QString &expression, const QgsFeature &feature)
Check whether the feature has all values required by the expression.
QgsGeometry geometry
Definition: qgsfeature.h:67
QList< int > QgsAttributeList
Definition: qgsfield.h:27
bool nextFeature(QgsFeature &f)
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Represents a vector layer which manages a vector based data sets.
QList< const T * > findNodes() const
Returns a list of all nodes of the given class which are used in this expression. ...
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.