18#include <nlohmann/json.hpp>
33using namespace Qt::StringLiterals;
35using namespace nlohmann;
41 setFlags(
flags() | QgsFieldFormatter::CanProvideAvailableValues );
46 return u
"ValueRelation"_s;
53 if ( cache.isValid() )
62 if ( config.value( u
"AllowMulti"_s ).toBool() )
66 if ( layer->
fields().
at( fieldIndex ).
type() == QMetaType::Type::QVariantMap )
69 keyList = value.toStringList();
76 QStringList valueList;
80 if ( keyList.contains( item.key.toString() ) )
82 valueList << item.value;
86 return valueList.join(
", "_L1 ).prepend(
'{' ).append(
'}' );
97 if ( item.key == value )
104 return u
"(%1)"_s.arg( value.toString() );
115 Q_UNUSED( fieldIndex )
116 return QVariant::fromValue<ValueRelationCache>(
createCache( config ) );
121 const QVariantMap &config,
133 const int keyIdx = fields.
indexOf( config.value( u
"Key"_s ).toString() );
134 const int valueIdx = fields.
indexOf( config.value( u
"Value"_s ).toString() );
141 const int groupIdx = fields.
lookupField( config.value( u
"Group"_s ).toString() );
144 subsetOfAttributes << groupIdx;
147 const bool orderByField { config.value( u
"OrderByField"_s ).toBool() };
148 const int fieldIdx { orderByField ? layer->
fields().
lookupField( config.value( u
"OrderByFieldName"_s ).toString() ) : -1 };
149 const bool reverseSort { config.value( u
"OrderByDescending"_s ).toBool() };
150 if ( fieldIdx != -1 )
152 subsetOfAttributes << fieldIdx;
155 const QString descriptionExpressionString = config.value(
"Description" ).toString();
156 QgsExpression descriptionExpression( descriptionExpressionString );
158 descriptionExpression.
prepare( &context );
162 const QString filterExpression = config.value( u
"FilterExpression"_s ).toString();
182 QMap<QVariant, QVariant> orderByFieldValues;
188 if ( descriptionExpression.
isValid() )
191 description = descriptionExpression.
evaluate( &context ).toString();
193 const QVariant group = groupIdx > -1 ? f.
attribute( groupIdx ) : QVariant();
194 const QVariant keyValue = f.
attribute( keyIdx );
195 if ( fieldIdx != -1 )
197 orderByFieldValues.insert( keyValue, f.
attribute( fieldIdx ) );
203 if ( config.value( u
"OrderByValue"_s ).toBool() )
208 return p1.group == p2.group ? qgsVariantGreaterThan( p1.value, p2.value ) : qgsVariantGreaterThan( p1.group, p2.group );
210 return p1.group == p2.group ? qgsVariantLessThan( p1.value, p2.value ) : qgsVariantLessThan( p1.group, p2.group );
214 else if ( fieldIdx != -1 )
219 return p1.group == p2.group ? qgsVariantGreaterThan( orderByFieldValues.value( p1.key ), orderByFieldValues.value( p2.key ) ) : qgsVariantGreaterThan( p1.group, p2.group );
221 return p1.group == p2.group ? qgsVariantLessThan( orderByFieldValues.value( p1.key ), orderByFieldValues.value( p2.key ) ) : qgsVariantLessThan( p1.group, p2.group );
231 return p1.group == p2.group ? qgsVariantGreaterThan( p1.key, p2.key ) : qgsVariantGreaterThan( p1.group, p2.group );
233 return p1.group == p2.group ? qgsVariantLessThan( p1.key, p2.key ) : qgsVariantLessThan( p1.group, p2.group );
243 QList<QgsVectorLayerRef> result;
244 const QString layerId { config.value( u
"Layer"_s ).toString() };
245 const QString layerName { config.value( u
"LayerName"_s ).toString() };
246 const QString providerName { config.value( u
"LayerProviderName"_s ).toString() };
247 const QString layerSource { config.value( u
"LayerSource"_s ).toString() };
248 if ( ! layerId.isEmpty() && ! layerName.isEmpty() && ! providerName.isEmpty() && ! layerSource.isEmpty() )
250 result.append(
QgsVectorLayerRef( layerId, layerName, layerSource, providerName ) );
259 if (
auto *lProject = context.
project() )
261 const QgsVectorLayer *referencedLayer = qobject_cast<QgsVectorLayer *>( lProject->mapLayer( config[u
"Layer"_s].toString() ) );
262 if ( referencedLayer )
264 int fieldIndex = referencedLayer->
fields().
indexOf( config.value( u
"Key"_s ).toString() );
265 values = qgis::setToList( referencedLayer->
uniqueValues( fieldIndex, countLimit ) );
273 QStringList checkList;
274 if ( value.userType() == QMetaType::Type::QStringList )
276 checkList = value.toStringList();
280 QVariantList valuesList;
281 if ( value.userType() == QMetaType::Type::QString )
284 auto newVal { value };
285 if ( newVal.toString().trimmed().startsWith(
'{' ) )
290 else if ( newVal.toString().trimmed().startsWith(
'[' ) )
295 for (
auto &element : json::parse( newVal.toString().toStdString() ) )
297 if ( element.is_number_integer() )
299 valuesList.push_back( element.get<
int>() );
301 else if ( element.is_number_unsigned() )
303 valuesList.push_back( element.get<
unsigned>() );
305 else if ( element.is_string() )
307 valuesList.push_back( QString::fromStdString( element.get<std::string>() ) );
311 catch ( json::parse_error &ex )
313 QgsMessageLog::logMessage( QObject::tr(
"Cannot parse JSON like string '%1' Error: %2" ).arg( newVal.toString(), ex.what() ) );
317 else if ( value.userType() == QMetaType::Type::QVariantList )
319 valuesList = value.toList( );
322 checkList.reserve( valuesList.size() );
323 for (
const QVariant &listItem : std::as_const( valuesList ) )
325 QString v( listItem.toString( ) );
327 checkList.append( v );
337 QSet< QString > formVariables = qgis::listToSet( scope->variableNames() );
339 formVariables.intersect( usedVariables );
340 return formVariables;
346 QSet< QString > formVariables = qgis::listToSet( scope->variableNames() );
348 formVariables.intersect( usedVariables );
349 return formVariables;
364 QSet<QString> attributes;
368 const QSet<QString> formFunctions( qgis::listToSet( scope->functionNames() )
372 for (
const auto &f : expFunctions )
375 if ( formFunctions.contains( fd->
name( ) ) )
377 const QList<QgsExpressionNode *> cExpressionNodes { f->args( )->list() };
378 for (
const auto ¶m : std::as_const( cExpressionNodes ) )
380 attributes.insert( param->eval( &exp, &context ).toString() );
389 QSet<QString> attributes;
393 const QSet<QString> formFunctions( qgis::listToSet( scope->functionNames() )
397 for (
const auto &f : expFunctions )
400 if ( formFunctions.contains( fd->
name( ) ) )
402 const QList<QgsExpressionNode *> cExpressionNodes { f->args( )->list() };
403 for (
const auto ¶m : std::as_const( cExpressionNodes ) )
405 attributes.insert( param->eval( &exp, &context ).toString() );
417 for (
auto it = attrs.constBegin() ; it != attrs.constEnd(); it++ )
429 for (
auto it = parentAttrs.constBegin() ; it != parentAttrs.constEnd(); it++ )
431 if ( ! parentFeature.
attribute( *it ).isValid() )
443 config.value( u
"LayerName"_s ).toString(),
444 config.value( u
"LayerSource"_s ).toString(),
445 config.value( u
"LayerProviderName"_s ).toString() };
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
static QString nullRepresentation()
Returns the string used to represent the value NULL throughout QGIS.
static QgsExpressionContextScope * parentFormScope(const QgsFeature &formFeature=QgsFeature(), const QString &formMode=QString())
Creates a new scope which contains functions and variables from the current parent attribute form/tab...
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 form...
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
An abstract base class for defining QgsExpression functions.
QString name() const
The name of the function.
An expression node for expression functions.
Handles parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
QList< const T * > findNodes() const
Returns a list of all nodes of the given class which are used in this expression.
QSet< QString > referencedVariables() const
Returns a list of all variables which are used in this expression.
QSet< QString > referencedFunctions() const
Returns a list of the names of all functions which are used in this expression.
QVariant evaluate()
Evaluate the feature and return the result.
bool isValid() const
Checks if this expression is valid.
QSet< int > referencedAttributeIndexes(const QgsFields &fields) const
Returns a list of field name indexes obtained from the provided fields.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
int fieldNameIndex(const QString &fieldName) const
Utility method to get attribute index from name.
bool isValid() const
Returns the validity of this feature.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
A context for field formatter containing information like the project.
QgsProject * project() const
Returns the project used in field formatter.
Container of fields for a vector layer.
Q_INVOKABLE int indexOf(const QString &fieldName) const
Gets the field index from the field name.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
static QVariantList parseArray(const QString &string)
Returns a QVariantList created out of a string containing an array in postgres array format {1,...
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
static QgsProject * instance()
Returns the QgsProject singleton instance.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
Represents a vector layer which manages a vector based dataset.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const final
Calculates a list of unique values contained within an attribute in the layer.
QSet< int > QgsAttributeIds
_LayerRef< QgsVectorLayer > QgsVectorLayerRef
TYPE * resolveByIdOrNameOnly(const QgsProject *project)
Resolves the map layer by attempting to find a matching layer in a project using a weak match.