QGIS API Documentation  3.6.0-Noosa (5873452)
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 #include "qgsapplication.h"
24 
25 #include <QSettings>
26 
28 {
29  return qgsVariantLessThan( p1.key, p2.key );
30 }
31 
33 {
34  return qgsVariantLessThan( p1.value, p2.value );
35 }
36 
38 {
39  return QStringLiteral( "ValueRelation" );
40 }
41 
42 QString QgsValueRelationFieldFormatter::representValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const
43 {
44  ValueRelationCache vrCache;
45 
46  if ( cache.isValid() )
47  {
49  }
50  else
51  {
53  }
54 
55  if ( config.value( QStringLiteral( "AllowMulti" ) ).toBool() )
56  {
57  QStringList keyList;
58 
59  if ( layer->fields().at( fieldIndex ).type() == QVariant::Map )
60  {
61  //because of json it's stored as QVariantList
62  keyList = value.toStringList();
63  }
64  else
65  {
66  keyList = valueToStringList( value );
67  }
68 
69  QStringList valueList;
70 
71  for ( const QgsValueRelationFieldFormatter::ValueRelationItem &item : qgis::as_const( vrCache ) )
72  {
73  if ( keyList.contains( item.key.toString() ) )
74  {
75  valueList << item.value;
76  }
77  }
78 
79  return valueList.join( QStringLiteral( ", " ) ).prepend( '{' ).append( '}' );
80  }
81  else
82  {
83  if ( value.isNull() )
84  {
86  }
87 
88  for ( const QgsValueRelationFieldFormatter::ValueRelationItem &item : qgis::as_const( vrCache ) )
89  {
90  if ( item.key == value )
91  {
92  return item.value;
93  }
94  }
95  }
96 
97  return QStringLiteral( "(%1)" ).arg( value.toString() );
98 }
99 
100 QVariant QgsValueRelationFieldFormatter::sortValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const
101 {
102  return representValue( layer, fieldIndex, config, cache, value );
103 }
104 
105 QVariant QgsValueRelationFieldFormatter::createCache( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config ) const
106 {
107  Q_UNUSED( layer )
108  Q_UNUSED( fieldIndex )
109  return QVariant::fromValue<ValueRelationCache>( createCache( config ) );
110 
111 }
112 
114 {
115  ValueRelationCache cache;
116 
117  QgsVectorLayer *layer = QgsProject::instance()->mapLayer<QgsVectorLayer *>( config.value( QStringLiteral( "Layer" ) ).toString() );
118 
119  if ( !layer )
120  return cache;
121 
122  QgsFields fields = layer->fields();
123  int ki = fields.indexOf( config.value( QStringLiteral( "Key" ) ).toString() );
124  int vi = fields.indexOf( config.value( QStringLiteral( "Value" ) ).toString() );
125 
126  QgsFeatureRequest request;
127 
129  request.setSubsetOfAttributes( QgsAttributeList() << ki << vi );
130 
131  const QString expression = config.value( QStringLiteral( "FilterExpression" ) ).toString();
132 
133  // Skip the filter and build a full cache if the form scope is required and the feature
134  // is not valid or the attributes required for the filter have no valid value
135  if ( ! expression.isEmpty() && ( ! expressionRequiresFormScope( expression )
136  || expressionIsUsable( expression, formFeature ) ) )
137  {
139  if ( formFeature.isValid( ) )
140  context.appendScope( QgsExpressionContextUtils::formScope( formFeature ) );
141  request.setExpressionContext( context );
142  request.setFilterExpression( expression );
143  }
144 
145  QgsFeatureIterator fit = layer->getFeatures( request );
146 
147  QgsFeature f;
148  while ( fit.nextFeature( f ) )
149  {
150  cache.append( ValueRelationItem( f.attribute( ki ), f.attribute( vi ).toString() ) );
151  }
152 
153  if ( config.value( QStringLiteral( "OrderByValue" ) ).toBool() )
154  {
155  std::sort( cache.begin(), cache.end(), orderByValueLessThan );
156  }
157  else
158  {
159  std::sort( cache.begin(), cache.end(), orderByKeyLessThan );
160  }
161 
162  return cache;
163 }
164 
165 QStringList QgsValueRelationFieldFormatter::valueToStringList( const QVariant &value )
166 {
167  QStringList checkList;
168  if ( value.type() == QVariant::StringList )
169  checkList = value.toStringList();
170  else if ( value.type() == QVariant::String )
171  checkList = value.toString().remove( QChar( '{' ) ).remove( QChar( '}' ) ).split( ',' );
172  else if ( value.type() == QVariant::List )
173  {
174  QVariantList valuesList( value.toList( ) );
175  for ( const QVariant &listItem : qgis::as_const( valuesList ) )
176  {
177  QString v( listItem.toString( ) );
178  if ( ! v.isEmpty() )
179  checkList.append( v );
180  }
181  }
182  return checkList;
183 }
184 
185 
186 QSet<QString> QgsValueRelationFieldFormatter::expressionFormVariables( const QString &expression )
187 {
188  std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::formScope() );
189  QSet< QString > formVariables = scope->variableNames().toSet();
190  const QSet< QString > usedVariables = QgsExpression( expression ).referencedVariables();
191  formVariables.intersect( usedVariables );
192  return formVariables;
193 }
194 
196 {
197  return !( expressionFormAttributes( expression ).isEmpty() && expressionFormVariables( expression ).isEmpty() );
198 }
199 
200 QSet<QString> QgsValueRelationFieldFormatter::expressionFormAttributes( const QString &expression )
201 {
202  QSet<QString> attributes;
203  QgsExpression exp( expression );
204  std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::formScope() );
205  // List of form function names used in the expression
206  const QSet<QString> formFunctions( scope->functionNames()
207  .toSet()
208  .intersect( exp.referencedFunctions( ) ) );
209  const QList<const QgsExpressionNodeFunction *> expFunctions( exp.findNodes<QgsExpressionNodeFunction>() );
210  QgsExpressionContext context;
211  for ( const auto &f : expFunctions )
212  {
213  QgsExpressionFunction *fd = QgsExpression::QgsExpression::Functions()[f->fnIndex()];
214  if ( formFunctions.contains( fd->name( ) ) )
215  {
216  for ( const auto &param : f->args( )->list() )
217  {
218  attributes.insert( param->eval( &exp, &context ).toString() );
219  }
220  }
221  }
222  return attributes;
223 }
224 
225 bool QgsValueRelationFieldFormatter::expressionIsUsable( const QString &expression, const QgsFeature &feature )
226 {
227  const QSet<QString> attrs = expressionFormAttributes( expression );
228  for ( auto it = attrs.constBegin() ; it != attrs.constEnd(); it++ )
229  {
230  if ( ! feature.attribute( *it ).isValid() )
231  return false;
232  }
233  if ( ! expressionFormVariables( expression ).isEmpty() && feature.geometry().isEmpty( ) )
234  return false;
235  return true;
236 }
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:183
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.
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...
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
QSet< QString > referencedVariables() const
Returns a list of all variables which are used in this expression.
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
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
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)...
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
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.
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)
QString name() const
The name of the function.
bool orderByValueLessThan(const QgsValueRelationFieldFormatter::ValueRelationItem &p1, const QgsValueRelationFieldFormatter::ValueRelationItem &p2)
A abstract base class for defining QgsExpression functions.
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.
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:207
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:430
static bool expressionRequiresFormScope(const QString &expression)
Check if the expression requires a form scope (i.e.
QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
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.
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.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:262
QList< const T * > findNodes() const
Returns a list of all nodes of the given class which are used in this expression. ...
QVariant::Type type
Definition: qgsfield.h:56
QSet< QString > referencedFunctions() const
Returns a list of the names of all functions which are used in this expression.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.