QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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 #include "qgsvectorlayerref.h"
25 
26 #include <nlohmann/json.hpp>
28 
29 #include <QSettings>
30 
32 {
33  return qgsVariantLessThan( p1.key, p2.key );
34 }
35 
37 {
38  return qgsVariantLessThan( p1.value, p2.value );
39 }
40 
42 {
43  return QStringLiteral( "ValueRelation" );
44 }
45 
46 QString QgsValueRelationFieldFormatter::representValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const
47 {
48  ValueRelationCache vrCache;
49 
50  if ( cache.isValid() )
51  {
53  }
54  else
55  {
57  }
58 
59  if ( config.value( QStringLiteral( "AllowMulti" ) ).toBool() )
60  {
61  QStringList keyList;
62 
63  if ( layer->fields().at( fieldIndex ).type() == QVariant::Map )
64  {
65  //because of json it's stored as QVariantList
66  keyList = value.toStringList();
67  }
68  else
69  {
70  keyList = valueToStringList( value );
71  }
72 
73  QStringList valueList;
74 
75  for ( const QgsValueRelationFieldFormatter::ValueRelationItem &item : qgis::as_const( vrCache ) )
76  {
77  if ( keyList.contains( item.key.toString() ) )
78  {
79  valueList << item.value;
80  }
81  }
82 
83  return valueList.join( QStringLiteral( ", " ) ).prepend( '{' ).append( '}' );
84  }
85  else
86  {
87  if ( value.isNull() )
88  {
90  }
91 
92  for ( const QgsValueRelationFieldFormatter::ValueRelationItem &item : qgis::as_const( vrCache ) )
93  {
94  if ( item.key == value )
95  {
96  return item.value;
97  }
98  }
99  }
100 
101  return QStringLiteral( "(%1)" ).arg( value.toString() );
102 }
103 
104 QVariant QgsValueRelationFieldFormatter::sortValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const
105 {
106  return representValue( layer, fieldIndex, config, cache, value );
107 }
108 
109 QVariant QgsValueRelationFieldFormatter::createCache( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config ) const
110 {
111  Q_UNUSED( layer )
112  Q_UNUSED( fieldIndex )
113  return QVariant::fromValue<ValueRelationCache>( createCache( config ) );
114 
115 }
116 
118 {
119  ValueRelationCache cache;
120 
121  const QgsVectorLayer *layer = resolveLayer( config, QgsProject::instance() );
122 
123  if ( !layer )
124  return cache;
125 
126  QgsFields fields = layer->fields();
127  int ki = fields.indexOf( config.value( QStringLiteral( "Key" ) ).toString() );
128  int vi = fields.indexOf( config.value( QStringLiteral( "Value" ) ).toString() );
129 
130  QgsFeatureRequest request;
131 
133  request.setSubsetOfAttributes( QgsAttributeList() << ki << vi );
134 
135  const QString expression = config.value( QStringLiteral( "FilterExpression" ) ).toString();
136 
137  // Skip the filter and build a full cache if the form scope is required and the feature
138  // is not valid or the attributes required for the filter have no valid value
139  if ( ! expression.isEmpty() && ( ! expressionRequiresFormScope( expression )
140  || expressionIsUsable( expression, formFeature ) ) )
141  {
143  if ( formFeature.isValid( ) )
144  context.appendScope( QgsExpressionContextUtils::formScope( formFeature ) );
145  request.setExpressionContext( context );
146  request.setFilterExpression( expression );
147  }
148 
149  QgsFeatureIterator fit = layer->getFeatures( request );
150 
151  QgsFeature f;
152  while ( fit.nextFeature( f ) )
153  {
154  cache.append( ValueRelationItem( f.attribute( ki ), f.attribute( vi ).toString() ) );
155  }
156 
157  if ( config.value( QStringLiteral( "OrderByValue" ) ).toBool() )
158  {
159  std::sort( cache.begin(), cache.end(), orderByValueLessThan );
160  }
161  else
162  {
163  std::sort( cache.begin(), cache.end(), orderByKeyLessThan );
164  }
165 
166  return cache;
167 }
168 
169 QStringList QgsValueRelationFieldFormatter::valueToStringList( const QVariant &value )
170 {
171  QStringList checkList;
172  if ( value.type() == QVariant::StringList )
173  {
174  checkList = value.toStringList();
175  }
176  else if ( value.type() == QVariant::String )
177  {
178  // This must be an array representation
179  auto newVal { value };
180  if ( newVal.toString().trimmed().startsWith( '{' ) )
181  {
182  newVal = QVariant( newVal.toString().trimmed().mid( 1 ).mid( 0, newVal.toString().length() - 2 ).prepend( '[' ).append( ']' ) );
183  }
184  if ( newVal.toString().trimmed().startsWith( '[' ) )
185  {
186  try
187  {
188  for ( auto &element : json::parse( newVal.toString().toStdString() ) )
189  {
190  if ( element.is_number_integer() )
191  {
192  checkList << QString::number( element.get<int>() );
193  }
194  else if ( element.is_number_unsigned() )
195  {
196  checkList << QString::number( element.get<unsigned>() );
197  }
198  else if ( element.is_string() )
199  {
200  checkList << QString::fromStdString( element.get<std::string>() );
201  }
202  }
203  }
204  catch ( json::parse_error &ex )
205  {
206  qDebug() << QString::fromStdString( ex.what() );
207  }
208  }
209  }
210  else if ( value.type() == QVariant::List )
211  {
212  QVariantList valuesList( value.toList( ) );
213  checkList.reserve( valuesList.size() );
214  for ( const QVariant &listItem : qgis::as_const( valuesList ) )
215  {
216  QString v( listItem.toString( ) );
217  if ( ! v.isEmpty() )
218  checkList.append( v );
219  }
220  }
221  return checkList;
222 }
223 
224 
225 QSet<QString> QgsValueRelationFieldFormatter::expressionFormVariables( const QString &expression )
226 {
227  std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::formScope() );
228  QSet< QString > formVariables = scope->variableNames().toSet();
229  const QSet< QString > usedVariables = QgsExpression( expression ).referencedVariables();
230  formVariables.intersect( usedVariables );
231  return formVariables;
232 }
233 
235 {
236  return !( expressionFormAttributes( expression ).isEmpty() && expressionFormVariables( expression ).isEmpty() );
237 }
238 
239 QSet<QString> QgsValueRelationFieldFormatter::expressionFormAttributes( const QString &expression )
240 {
241  QSet<QString> attributes;
242  QgsExpression exp( expression );
243  std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::formScope() );
244  // List of form function names used in the expression
245  const QSet<QString> formFunctions( scope->functionNames()
246  .toSet()
247  .intersect( exp.referencedFunctions( ) ) );
248  const QList<const QgsExpressionNodeFunction *> expFunctions( exp.findNodes<QgsExpressionNodeFunction>() );
249  QgsExpressionContext context;
250  for ( const auto &f : expFunctions )
251  {
252  QgsExpressionFunction *fd = QgsExpression::QgsExpression::Functions()[f->fnIndex()];
253  if ( formFunctions.contains( fd->name( ) ) )
254  {
255  for ( const auto &param : f->args( )->list() )
256  {
257  attributes.insert( param->eval( &exp, &context ).toString() );
258  }
259  }
260  }
261  return attributes;
262 }
263 
264 bool QgsValueRelationFieldFormatter::expressionIsUsable( const QString &expression, const QgsFeature &feature )
265 {
266  const QSet<QString> attrs = expressionFormAttributes( expression );
267  for ( auto it = attrs.constBegin() ; it != attrs.constEnd(); it++ )
268  {
269  if ( ! feature.attribute( *it ).isValid() )
270  return false;
271  }
272  if ( ! expressionFormVariables( expression ).isEmpty() && feature.geometry().isEmpty( ) )
273  return false;
274  return true;
275 }
276 
277 QgsVectorLayer *QgsValueRelationFieldFormatter::resolveLayer( const QVariantMap &config, const QgsProject *project )
278 {
279  QgsVectorLayerRef ref { config.value( QStringLiteral( "Layer" ) ).toString(),
280  config.value( QStringLiteral( "LayerName" ) ).toString(),
281  config.value( QStringLiteral( "LayerSource" ) ).toString(),
282  config.value( QStringLiteral( "LayerProviderName" ) ).toString() };
283  return ref.resolveByIdOrNameOnly( project );
284 }
285 
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.
TYPE * resolveByIdOrNameOnly(const QgsProject *project)
Resolves the map layer by attempting to find a matching layer in a project using a weak match...
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
nlohmann::json json
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.
Reads and writes project states.
Definition: qgsproject.h:89
static QgsVectorLayer * resolveLayer(const QVariantMap &config, const QgsProject *project)
Returns the (possibly NULL) layer from the widget&#39;s config and project.
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.
nlohmann::json json
Definition: qgsjsonutils.h:27
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 a list or a string representation of an (hstore style: {1,2...}) list in value to ...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:438
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
Queries 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.
QgsSQLStatement::Node * parse(const QString &str, QString &parserErrorMsg)
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.