QGIS API Documentation  3.23.0-Master (eb871beae0)
qgsproperty.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsproperty.cpp
3  ---------------
4  Date : January 2017
5  Copyright : (C) 2017 by Nyall Dawson
6  Email : nyall dot dawson at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsproperty.h"
17 #include "qgsproperty_p.h"
18 
19 #include "qgslogger.h"
20 #include "qgsexpression.h"
21 #include "qgsfeature.h"
22 #include "qgssymbollayerutils.h"
23 #include "qgscolorramp.h"
24 #include "qgsexpressionnodeimpl.h"
25 
26 #include <QRegularExpression>
27 
28 QgsPropertyDefinition::QgsPropertyDefinition( const QString &name, const QString &description, QgsPropertyDefinition::StandardPropertyTemplate type, const QString &origin, const QString &comment )
29  : mName( name )
30  , mDescription( description )
31  , mStandardType( type )
32  , mOrigin( origin )
33  , mComment( comment )
34 {
35  switch ( mStandardType )
36  {
37  case Boolean:
38  mTypes = DataTypeBoolean;
39  mHelpText = QObject::tr( "bool [<b>1</b>=True|<b>0</b>=False]" );
40  break;
41 
42  case Integer:
43  mTypes = DataTypeNumeric;
44  mHelpText = QObject::tr( "int [≤ 0 ≥]" );
45  break;
46 
47  case IntegerPositive:
48  mTypes = DataTypeNumeric;
49  mHelpText = QObject::tr( "int [≥ 0]" );
50  break;
51 
53  mTypes = DataTypeNumeric;
54  mHelpText = QObject::tr( "int [≥ 1]" );
55  break;
56 
57  case Double:
58  mTypes = DataTypeNumeric;
59  mHelpText = QObject::tr( "double [≤ 0.0 ≥]" );
60  break;
61 
62  case DoublePositive:
63  mTypes = DataTypeNumeric;
64  mHelpText = QObject::tr( "double [≥ 0.0]" );
65  break;
66 
67  case Double0To1:
68  mTypes = DataTypeNumeric;
69  mHelpText = QObject::tr( "double [0.0-1.0]" );
70  break;
71 
72  case Rotation:
73  mTypes = DataTypeNumeric;
74  mHelpText = QObject::tr( "double [0.0-360.0]" );
75  break;
76 
77  case String:
78  mTypes = DataTypeString;
79  mHelpText = QObject::tr( "string of variable length" );
80  break;
81 
82  case Opacity:
83  mTypes = DataTypeNumeric;
84  mHelpText = QObject::tr( "int [0-100]" );
85  break;
86 
87  case RenderUnits:
88  mTypes = DataTypeString;
89  mHelpText = trString() + QStringLiteral( "[<b>MM</b>|<b>MapUnit</b>|<b>Pixel</b>|<b>Point</b>]" );
90  break;
91 
92  case ColorWithAlpha:
93  mTypes = DataTypeString;
94  mHelpText = QObject::tr( "string [<b>r,g,b,a</b>] as int 0-255 or #<b>AARRGGBB</b> as hex or <b>color</b> as color's name" );
95  break;
96 
97  case ColorNoAlpha:
98  mTypes = DataTypeString;
99  mHelpText = QObject::tr( "string [<b>r,g,b</b>] as int 0-255 or #<b>RRGGBB</b> as hex or <b>color</b> as color's name" );
100  break;
101 
102  case PenJoinStyle:
103  mTypes = DataTypeString;
104  mHelpText = trString() + QStringLiteral( "[<b>bevel</b>|<b>miter</b>|<b>round</b>]" );
105  break;
106 
107  case BlendMode:
108  mTypes = DataTypeString;
109  mHelpText = trString() + QStringLiteral( "[<b>Normal</b>|<b>Lighten</b>|<b>Screen</b>|<b>Dodge</b>|<br>"
110  "<b>Addition</b>|<b>Darken</b>|<b>Multiply</b>|<b>Burn</b>|<b>Overlay</b>|<br>"
111  "<b>SoftLight</b>|<b>HardLight</b>|<b>Difference</b>|<b>Subtract</b>]" );
112  break;
113 
114  case Point:
115  mTypes = DataTypeString;
116  mHelpText = QObject::tr( "double coord [<b>X,Y</b>]" );
117  break;
118 
119  case Size:
120  mTypes = DataTypeNumeric;
121  mHelpText = QObject::tr( "double [≥ 0.0]" );
122  break;
123 
124  case Size2D:
125  mTypes = DataTypeString;
126  mHelpText = QObject::tr( "string of doubles '<b>width,height</b>' or array of doubles <b>[width, height]</b>" );
127  break;
128 
129  case LineStyle:
130  mTypes = DataTypeString;
131  mHelpText = trString() + QStringLiteral( "[<b>no</b>|<b>solid</b>|<b>dash</b>|<b>dot</b>|<b>dash dot</b>|<b>dash dot dot</b>]" );
132  break;
133 
134  case StrokeWidth:
135  mTypes = DataTypeNumeric;
136  mHelpText = QObject::tr( "double [≥ 0.0]" );
137  break;
138 
139  case FillStyle:
140  mTypes = DataTypeString;
141  mHelpText = trString() + QStringLiteral( "[<b>solid</b>|<b>horizontal</b>|<b>vertical</b>|<b>cross</b>|<b>b_diagonal</b>|<b>f_diagonal"
142  "</b>|<b>diagonal_x</b>|<b>dense1</b>|<b>dense2</b>|<b>dense3</b>|<b>dense4</b>|<b>dense5"
143  "</b>|<b>dense6</b>|<b>dense7</b>|<b>no]" );
144  break;
145 
146  case CapStyle:
147  mTypes = DataTypeString;
148  mHelpText = trString() + QStringLiteral( "[<b>square</b>|<b>flat</b>|<b>round</b>]" );
149  break;
150 
151  case HorizontalAnchor:
152  mTypes = DataTypeString;
153  mHelpText = trString() + QStringLiteral( "[<b>left</b>|<b>center</b>|<b>right</b>]" );
154  break;
155 
156  case VerticalAnchor:
157  mTypes = DataTypeString;
158  mHelpText = trString() + QStringLiteral( "[<b>top</b>|<b>center</b>|<b>bottom</b>]" );
159  break;
160 
161  case SvgPath:
162  mTypes = DataTypeString;
163  mHelpText = trString() + QStringLiteral( "[<b>filepath</b>] as<br>"
164  "<b>''</b>=empty|absolute|search-paths-relative|<br>"
165  "project-relative|URL" );
166  break;
167 
168  case Offset:
169  mTypes = DataTypeString;
170  mHelpText = QObject::tr( "string of doubles '<b>x,y</b>' or array of doubles <b>[x, y]</b>" );
171  break;
172 
173  case DateTime:
174  mTypes = DataTypeString;
175  mHelpText = QObject::tr( "DateTime or string representation of a DateTime" );
176  break;
177 
178  case Custom:
179  mTypes = DataTypeString;
180  }
181 }
182 
183 QgsPropertyDefinition::QgsPropertyDefinition( const QString &name, DataType dataType, const QString &description, const QString &helpText, const QString &origin, const QString &comment )
184  : mName( name )
185  , mDescription( description )
186  , mTypes( dataType )
187  , mHelpText( helpText )
188  , mOrigin( origin )
189  , mComment( comment )
190 {}
191 
193 {
194  return mTypes == DataTypeNumeric || mStandardType == Size || mStandardType == StrokeWidth || mStandardType == ColorNoAlpha || mStandardType == ColorWithAlpha
195  || mStandardType == Rotation;
196 }
197 
198 QString QgsPropertyDefinition::trString()
199 {
200  // just something to reduce translation redundancy
201  return QObject::tr( "string " );
202 }
203 
204 //
205 // QgsProperty
206 //
207 
208 QVariantMap QgsProperty::propertyMapToVariantMap( const QMap<QString, QgsProperty> &propertyMap )
209 {
210  QVariantMap variantMap;
211  QMap<QString, QgsProperty>::const_iterator it = propertyMap.constBegin();
212  for ( ; it != propertyMap.constEnd(); ++it )
213  variantMap.insert( it.key(), it.value().toVariant() );
214  return variantMap;
215 }
216 
217 QMap<QString, QgsProperty> QgsProperty::variantMapToPropertyMap( const QVariantMap &variantMap )
218 {
219  QMap<QString, QgsProperty> propertyMap;
220  QVariantMap::const_iterator it = variantMap.constBegin();
221  for ( ; it != variantMap.constEnd(); ++it )
222  {
223  QgsProperty property;
224  if ( property.loadVariant( it.value() ) )
225  propertyMap.insert( it.key(), property );
226  }
227  return propertyMap;
228 }
229 
231 {
232  d = new QgsPropertyPrivate();
233 }
234 
235 QgsProperty::~QgsProperty() = default;
236 
237 QgsProperty QgsProperty::fromExpression( const QString &expression, bool isActive )
238 {
239  QgsProperty p;
240  p.setExpressionString( expression );
241  p.setActive( isActive );
242  return p;
243 }
244 
245 QgsProperty QgsProperty::fromField( const QString &fieldName, bool isActive )
246 {
247  QgsProperty p;
248  p.setField( fieldName );
249  p.setActive( isActive );
250  return p;
251 }
252 
253 QgsProperty QgsProperty::fromValue( const QVariant &value, bool isActive )
254 {
255  QgsProperty p;
256  p.setStaticValue( value );
257  p.setActive( isActive );
258  return p;
259 }
260 
261 QgsProperty::QgsProperty( const QgsProperty &other ) //NOLINT
262  : d( other.d )
263 {}
264 
266 {
267  d = other.d;
268  return *this;
269 }
270 
271 bool QgsProperty::operator==( const QgsProperty &other ) const
272 {
273  return d->active == other.d->active
274  && d->type == other.d->type
275  && ( d->type != StaticProperty || d->staticValue == other.d->staticValue )
276  && ( d->type != FieldBasedProperty || d->fieldName == other.d->fieldName )
277  && ( d->type != ExpressionBasedProperty || d->expressionString == other.d->expressionString )
278  && ( ( !d->transformer && !other.d->transformer ) || ( d->transformer && other.d->transformer && d->transformer->toExpression( QString() ) == other.d->transformer->toExpression( QString() ) ) );
279 }
280 
281 bool QgsProperty::operator!=( const QgsProperty &other ) const
282 {
283  return ( !( ( *this ) == other ) );
284 }
285 
287 {
288  return static_cast< Type >( d->type );
289 }
290 
292 {
293  return d->type != InvalidProperty && d->active;
294 }
295 
296 bool QgsProperty::isStaticValueInContext( const QgsExpressionContext &context, QVariant &staticValue ) const
297 {
298  staticValue = QVariant();
299  switch ( d->type )
300  {
301  case InvalidProperty:
302  return true;
303 
304  case StaticProperty:
305  staticValue = d->staticValue;
306  return true;
307 
308  case FieldBasedProperty:
309  return false;
310 
312  {
313  QgsExpression exp = d->expression;
314  if ( exp.prepare( &context ) && exp.rootNode() )
315  {
316  if ( exp.rootNode()->hasCachedStaticValue() )
317  {
319  return true;
320  }
321  }
322  return false;
323  }
324  }
325  return false;
326 }
327 
328 void QgsProperty::setActive( bool active )
329 {
330  d.detach();
331  d->active = active;
332 }
333 
334 void QgsProperty::setStaticValue( const QVariant &value )
335 {
336  d.detach();
337  d->type = StaticProperty;
338  d->staticValue = value;
339 }
340 
341 QVariant QgsProperty::staticValue() const
342 {
343  if ( d->type != StaticProperty )
344  return QVariant();
345 
346  return d->staticValue;
347 }
348 
349 void QgsProperty::setField( const QString &field )
350 {
351  d.detach();
352  d->type = FieldBasedProperty;
353  d->fieldName = field;
354  d->cachedFieldIdx = -1;
355 }
356 
357 QString QgsProperty::field() const
358 {
359  if ( d->type != FieldBasedProperty )
360  return QString();
361 
362  return d->fieldName;
363 }
364 
365 QgsProperty::operator bool() const
366 {
367  return d->type != InvalidProperty;
368 }
369 
370 void QgsProperty::setExpressionString( const QString &expression )
371 {
372  d.detach();
373  d->type = ExpressionBasedProperty;
374  d->expressionString = expression;
375  d->expression = QgsExpression( expression );
376  d->expressionPrepared = false;
377  d->expressionIsInvalid = false;
378 }
379 
381 {
382  if ( d->type != ExpressionBasedProperty )
383  return QString();
384 
385  return d->expressionString;
386 }
387 
388 
390 {
391  QString exp;
392  switch ( d->type )
393  {
394  case StaticProperty:
395  exp = QgsExpression::quotedValue( d->staticValue );
396  break;
397 
398  case FieldBasedProperty:
399  exp = QgsExpression::quotedColumnRef( d->fieldName );
400  break;
401 
403  exp = d->expressionString;
404  break;
405 
406  case InvalidProperty:
407  exp = QString();
408  break;
409  }
410  return d->transformer ? d->transformer->toExpression( exp ) : exp;
411 }
412 
413 bool QgsProperty::prepare( const QgsExpressionContext &context ) const
414 {
415  if ( !d->active )
416  return true;
417 
418  switch ( d->type )
419  {
420  case StaticProperty:
421  return true;
422 
423  case FieldBasedProperty:
424  {
425  d.detach();
426  // cache field index to avoid subsequent lookups
427  const QgsFields f = context.fields();
428  d->cachedFieldIdx = f.lookupField( d->fieldName );
429  return true;
430  }
431 
433  {
434  d.detach();
435  if ( !d->expression.prepare( &context ) )
436  {
437  d->expressionReferencedCols.clear();
438  d->expressionPrepared = false;
439  d->expressionIsInvalid = true;
440  return false;
441  }
442 
443  d->expressionPrepared = true;
444  d->expressionIsInvalid = false;
445  d->expressionReferencedCols = d->expression.referencedColumns();
446  return true;
447  }
448 
449  case InvalidProperty:
450  return true;
451 
452  }
453 
454  return false;
455 }
456 
457 QSet<QString> QgsProperty::referencedFields( const QgsExpressionContext &context, bool ignoreContext ) const
458 {
459  if ( !d->active )
460  return QSet<QString>();
461 
462  switch ( d->type )
463  {
464  case StaticProperty:
465  case InvalidProperty:
466  return QSet<QString>();
467 
468  case FieldBasedProperty:
469  {
470  QSet< QString > fields;
471  if ( !d->fieldName.isEmpty() )
472  fields.insert( d->fieldName );
473  return fields;
474  }
475 
477  {
478  if ( ignoreContext )
479  {
480  return d->expression.referencedColumns();
481  }
482 
483  if ( d->expressionIsInvalid )
484  return QSet< QString >();
485 
486  d.detach();
487  if ( !d->expressionPrepared && !prepare( context ) )
488  {
489  d->expressionIsInvalid = true;
490  return QSet< QString >();
491  }
492 
493  return d->expressionReferencedCols;
494  }
495 
496  }
497  return QSet<QString>();
498 }
499 
501 {
502  const QRegularExpression rx( QStringLiteral( "^project_color\\('.*'\\)$" ) );
503  return d->type == QgsProperty::ExpressionBasedProperty && !d->expressionString.isEmpty()
504  && rx.match( d->expressionString ).hasMatch();
505 }
506 
507 QVariant QgsProperty::propertyValue( const QgsExpressionContext &context, const QVariant &defaultValue, bool *ok ) const
508 {
509  if ( ok )
510  *ok = false;
511 
512  if ( !d->active )
513  return defaultValue;
514 
515  switch ( d->type )
516  {
517  case StaticProperty:
518  {
519  if ( ok )
520  *ok = true;
521  return d->staticValue;
522  }
523 
524  case FieldBasedProperty:
525  {
526  const QgsFeature f = context.feature();
527  if ( !f.isValid() )
528  return defaultValue;
529 
530  //shortcut the field lookup
531  if ( d->cachedFieldIdx >= 0 )
532  {
533  if ( ok )
534  *ok = true;
535  return f.attribute( d->cachedFieldIdx );
536  }
537  prepare( context );
538  if ( d->cachedFieldIdx < 0 )
539  return defaultValue;
540 
541  if ( ok )
542  *ok = true;
543  return f.attribute( d->cachedFieldIdx );
544  }
545 
547  {
548  if ( d->expressionIsInvalid )
549  return defaultValue;
550 
551  if ( !d->expressionPrepared && !prepare( context ) )
552  return defaultValue;
553 
554  QVariant result = d->expression.evaluate( &context );
555  if ( !result.isNull() )
556  {
557  if ( ok )
558  *ok = true;
559  return result;
560  }
561  else
562  {
563  return defaultValue;
564  }
565  }
566 
567  case InvalidProperty:
568  return defaultValue;
569 
570  }
571 
572  return QVariant();
573 }
574 
575 
576 QVariant QgsProperty::value( const QgsExpressionContext &context, const QVariant &defaultValue, bool *ok ) const
577 {
578  if ( ok )
579  *ok = false;
580 
581  bool valOk = false;
582  QVariant val = propertyValue( context, defaultValue, &valOk );
583  if ( !d->transformer && !valOk ) // if transformer present, let it handle null values
584  return defaultValue;
585 
586  if ( d->transformer )
587  {
588  if ( !valOk )
589  val = QVariant();
590  val = d->transformer->transform( context, val );
591  }
592 
593  if ( ok )
594  *ok = true;
595 
596  return val;
597 }
598 
599 QDateTime QgsProperty::valueAsDateTime( const QgsExpressionContext &context, const QDateTime &defaultDateTime, bool *ok ) const
600 {
601  bool valOk = false;
602  const QVariant val = value( context, defaultDateTime, &valOk );
603 
604  if ( !valOk || val.isNull() )
605  {
606  if ( ok )
607  *ok = false;
608  return defaultDateTime;
609  }
610 
611  QDateTime dateTime;
612  if ( val.type() == QVariant::DateTime )
613  {
614  dateTime = val.value<QDateTime>();
615  }
616  else
617  {
618  dateTime = val.toDateTime();
619  }
620 
621  if ( !dateTime.isValid() )
622  return defaultDateTime;
623  else
624  {
625  if ( ok )
626  *ok = true;
627  return dateTime;
628  }
629 }
630 
631 QString QgsProperty::valueAsString( const QgsExpressionContext &context, const QString &defaultString, bool *ok ) const
632 {
633  bool valOk = false;
634  const QVariant val = value( context, defaultString, &valOk );
635 
636  if ( !valOk || val.isNull() )
637  {
638  if ( ok )
639  *ok = false;
640  return defaultString;
641  }
642  else
643  {
644  if ( ok )
645  *ok = true;
646  return val.toString();
647  }
648 }
649 
650 QColor QgsProperty::valueAsColor( const QgsExpressionContext &context, const QColor &defaultColor, bool *ok ) const
651 {
652  if ( ok )
653  *ok = false;
654 
655  bool valOk = false;
656  const QVariant val = value( context, defaultColor, &valOk );
657 
658  if ( !valOk || val.isNull() )
659  return defaultColor;
660 
661  QColor color;
662  if ( val.type() == QVariant::Color )
663  {
664  color = val.value<QColor>();
665  }
666  else
667  {
668  color = QgsSymbolLayerUtils::decodeColor( val.toString() );
669  }
670 
671  if ( !color.isValid() )
672  return defaultColor;
673  else
674  {
675  if ( ok )
676  *ok = true;
677  return color;
678  }
679 }
680 
681 double QgsProperty::valueAsDouble( const QgsExpressionContext &context, double defaultValue, bool *ok ) const
682 {
683  if ( ok )
684  *ok = false;
685 
686  bool valOk = false;
687  const QVariant val = value( context, defaultValue, &valOk );
688 
689  if ( !valOk || val.isNull() )
690  return defaultValue;
691 
692  bool convertOk = false;
693  const double dbl = val.toDouble( &convertOk );
694  if ( !convertOk )
695  return defaultValue;
696  else
697  {
698  if ( ok )
699  *ok = true;
700  return dbl;
701  }
702 }
703 
704 int QgsProperty::valueAsInt( const QgsExpressionContext &context, int defaultValue, bool *ok ) const
705 {
706  if ( ok )
707  *ok = false;
708 
709  bool valOk = false;
710  const QVariant val = value( context, defaultValue, &valOk );
711 
712  if ( !valOk || val.isNull() )
713  return defaultValue;
714 
715  bool convertOk = false;
716  const int integer = val.toInt( &convertOk );
717  if ( !convertOk )
718  {
719  //one more option to try
720  const double dbl = val.toDouble( &convertOk );
721  if ( convertOk )
722  {
723  if ( ok )
724  *ok = true;
725  return std::round( dbl );
726  }
727  else
728  {
729  return defaultValue;
730  }
731  }
732  else
733  {
734  if ( ok )
735  *ok = true;
736  return integer;
737  }
738 }
739 
740 bool QgsProperty::valueAsBool( const QgsExpressionContext &context, bool defaultValue, bool *ok ) const
741 {
742  if ( ok )
743  *ok = false;
744 
745  bool valOk = false;
746  const QVariant val = value( context, defaultValue, &valOk );
747 
748  if ( !valOk || val.isNull() )
749  return defaultValue;
750 
751  if ( ok )
752  *ok = true;
753  return val.toBool();
754 }
755 
756 QVariant QgsProperty::toVariant() const
757 {
758  QVariantMap propertyMap;
759 
760  propertyMap.insert( QStringLiteral( "active" ), d->active );
761  propertyMap.insert( QStringLiteral( "type" ), d->type );
762 
763  switch ( d->type )
764  {
765  case StaticProperty:
766  // propertyMap.insert( QStringLiteral( "valType" ), d->staticValue.typeName() );
767  propertyMap.insert( QStringLiteral( "val" ), d->staticValue.toString() );
768  break;
769 
770  case FieldBasedProperty:
771  propertyMap.insert( QStringLiteral( "field" ), d->fieldName );
772  break;
773 
775  propertyMap.insert( QStringLiteral( "expression" ), d->expressionString );
776  break;
777 
778  case InvalidProperty:
779  break;
780  }
781 
782  if ( d->transformer )
783  {
784  QVariantMap transformer;
785  transformer.insert( QStringLiteral( "t" ), d->transformer->transformerType() );
786  transformer.insert( QStringLiteral( "d" ), d->transformer->toVariant() );
787 
788  propertyMap.insert( QStringLiteral( "transformer" ), transformer );
789  }
790 
791  return propertyMap;
792 }
793 
794 bool QgsProperty::loadVariant( const QVariant &property )
795 {
796  const QVariantMap propertyMap = property.toMap();
797 
798  d.detach();
799  d->active = propertyMap.value( QStringLiteral( "active" ) ).toBool();
800  d->type = static_cast< Type >( propertyMap.value( QStringLiteral( "type" ), InvalidProperty ).toInt() );
801 
802  switch ( d->type )
803  {
804  case StaticProperty:
805  d->staticValue = propertyMap.value( QStringLiteral( "val" ) );
806  // d->staticValue.convert( QVariant::nameToType( propertyElem.attribute( "valType", "QString" ).toLocal8Bit().constData() ) );
807  break;
808 
809  case FieldBasedProperty:
810  d->fieldName = propertyMap.value( QStringLiteral( "field" ) ).toString();
811  if ( d->fieldName.isEmpty() )
812  d->active = false;
813  break;
814 
816  d->expressionString = propertyMap.value( QStringLiteral( "expression" ) ).toString();
817  if ( d->expressionString.isEmpty() )
818  d->active = false;
819 
820  d->expression = QgsExpression( d->expressionString );
821  d->expressionPrepared = false;
822  d->expressionIsInvalid = false;
823  d->expressionReferencedCols.clear();
824  break;
825 
826  case InvalidProperty:
827  break;
828 
829  }
830 
831  //restore transformer if present
832  delete d->transformer;
833  d->transformer = nullptr;
834 
835 
836  const QVariant transform = propertyMap.value( QStringLiteral( "transformer" ) );
837 
838  if ( transform.isValid() )
839  {
840  const QVariantMap transformerMap = transform.toMap();
841 
842  const QgsPropertyTransformer::Type type = static_cast< QgsPropertyTransformer::Type >( transformerMap.value( QStringLiteral( "t" ), QgsPropertyTransformer::GenericNumericTransformer ).toInt() );
843  std::unique_ptr< QgsPropertyTransformer > transformer( QgsPropertyTransformer::create( type ) );
844 
845  if ( transformer )
846  {
847  if ( transformer->loadVariant( transformerMap.value( QStringLiteral( "d" ) ) ) )
848  d->transformer = transformer.release();
849  }
850  }
851 
852  return true;
853 }
854 
855 
857 {
858  d.detach();
859  d->transformer = transformer;
860 }
861 
863 {
864  return d->transformer;
865 }
866 
868 {
869  if ( d->type != ExpressionBasedProperty )
870  return false;
871 
872  if ( d->transformer )
873  return false; // already a transformer
874 
875  QString baseExpression;
876  QString fieldName;
877  std::unique_ptr< QgsPropertyTransformer > transformer( QgsPropertyTransformer::fromExpression( d->expressionString, baseExpression, fieldName ) );
878  if ( !transformer )
879  return false;
880 
881  d.detach();
882  d->transformer = transformer.release();
883  if ( !fieldName.isEmpty() )
884  setField( fieldName );
885  else
886  setExpressionString( baseExpression );
887  return true;
888 }
889 
890 
891 
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
QgsFields fields() const
Convenience function for retrieving the fields for the context, if set.
bool hasCachedStaticValue() const
Returns true if the node can be replaced by a static cached value.
QVariant cachedStaticValue() const
Returns the node's static cached value.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
const QgsExpressionNode * rootNode() const
Returns the root node of the expression.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:191
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:302
Container of fields for a vector layer.
Definition: qgsfields.h:45
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:344
StandardPropertyTemplate
Predefined standard property templates.
Definition: qgsproperty.h:52
@ HorizontalAnchor
Horizontal anchor point.
Definition: qgsproperty.h:75
@ Double
Double value (including negative values)
Definition: qgsproperty.h:57
@ VerticalAnchor
Vertical anchor point.
Definition: qgsproperty.h:76
@ Double0To1
Double value between 0-1 (inclusive)
Definition: qgsproperty.h:59
@ FillStyle
Fill style (eg solid, lines)
Definition: qgsproperty.h:73
@ StrokeWidth
Line stroke width.
Definition: qgsproperty.h:72
@ LineStyle
Line style (eg solid/dashed)
Definition: qgsproperty.h:71
@ Integer
Integer value (including negative values)
Definition: qgsproperty.h:54
@ String
Any string value.
Definition: qgsproperty.h:61
@ DateTime
DateTime value.
Definition: qgsproperty.h:79
@ BlendMode
Blend mode.
Definition: qgsproperty.h:67
@ Boolean
Boolean value.
Definition: qgsproperty.h:53
@ RenderUnits
Render units (eg mm/pixels/map units)
Definition: qgsproperty.h:63
@ PenJoinStyle
Pen join style.
Definition: qgsproperty.h:66
@ SvgPath
Path to an SVG file.
Definition: qgsproperty.h:77
@ IntegerPositiveGreaterZero
Non-zero positive integer values.
Definition: qgsproperty.h:56
@ IntegerPositive
Positive integer values (including 0)
Definition: qgsproperty.h:55
@ Opacity
Opacity (0-100)
Definition: qgsproperty.h:62
@ CapStyle
Line cap style (eg round)
Definition: qgsproperty.h:74
@ ColorNoAlpha
Color with no alpha channel.
Definition: qgsproperty.h:65
@ Rotation
Rotation (value between 0-360 degrees)
Definition: qgsproperty.h:60
@ Custom
Custom property types.
Definition: qgsproperty.h:80
@ Size
1D size (eg marker radius, or square marker height/width)
Definition: qgsproperty.h:69
@ ColorWithAlpha
Color with alpha channel.
Definition: qgsproperty.h:64
@ DoublePositive
Positive double value (including 0)
Definition: qgsproperty.h:58
@ Size2D
2D size (width/height different)
Definition: qgsproperty.h:70
bool supportsAssistant() const
Returns true if the property is of a type which is compatible with property override assistants.
QgsPropertyDefinition()=default
Constructs an empty property.
DataType
Valid data types required by property.
Definition: qgsproperty.h:85
@ DataTypeString
Property requires a string value.
Definition: qgsproperty.h:92
@ DataTypeBoolean
Property requires a boolean value.
Definition: qgsproperty.h:106
@ DataTypeNumeric
Property requires a numeric value.
Definition: qgsproperty.h:99
Abstract base class for objects which transform the calculated value of a property.
static QgsPropertyTransformer * fromExpression(const QString &expression, QString &baseExpression, QString &fieldName)
Attempts to parse an expression into a corresponding property transformer.
virtual bool loadVariant(const QVariant &transformer)
Loads this transformer from a QVariantMap, wrapped in a QVariant.
@ GenericNumericTransformer
Generic transformer for numeric values (QgsGenericNumericTransformer)
static QgsPropertyTransformer * create(Type type)
Factory method for creating a new property transformer of the specified type.
A store for object properties.
Definition: qgsproperty.h:231
QDateTime valueAsDateTime(const QgsExpressionContext &context, const QDateTime &defaultDateTime=QDateTime(), bool *ok=nullptr) const
Calculates the current value of the property and interprets it as a datetime.
Type
Property types.
Definition: qgsproperty.h:236
@ ExpressionBasedProperty
Expression based property (QgsExpressionBasedProperty)
Definition: qgsproperty.h:240
@ StaticProperty
Static property (QgsStaticProperty)
Definition: qgsproperty.h:238
@ FieldBasedProperty
Field based property (QgsFieldBasedProperty)
Definition: qgsproperty.h:239
@ InvalidProperty
Invalid (not set) property.
Definition: qgsproperty.h:237
QColor valueAsColor(const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property and interprets it as a color.
bool isProjectColor() const
Returns true if the property is set to a linked project color.
QSet< QString > referencedFields(const QgsExpressionContext &context=QgsExpressionContext(), bool ignoreContext=false) const
Returns the set of any fields referenced by the property for a specified expression context.
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
bool operator==(const QgsProperty &other) const
static QVariantMap propertyMapToVariantMap(const QMap< QString, QgsProperty > &propertyMap)
Convert a map of QgsProperty to a map of QVariant This is useful to save a map of properties.
QString expressionString() const
Returns the expression used for the property value.
bool convertToTransformer()
Attempts to convert an existing expression based property to a base expression with corresponding tra...
void setTransformer(QgsPropertyTransformer *transformer)
Sets an optional transformer to use for manipulating the calculated values for the property.
void setStaticValue(const QVariant &value)
Sets the static value for the property.
QString valueAsString(const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property and interprets it as a string.
QString field() const
Returns the current field name the property references.
const QgsPropertyTransformer * transformer() const
Returns the existing transformer used for manipulating the calculated values for the property,...
QVariant value(const QgsExpressionContext &context, const QVariant &defaultValue=QVariant(), bool *ok=nullptr) const
Calculates the current value of the property, including any transforms which are set for the property...
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
QVariant toVariant() const
Saves this property to a QVariantMap, wrapped in a QVariant.
bool isActive() const
Returns whether the property is currently active.
double valueAsDouble(const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property and interprets it as a double.
static QMap< QString, QgsProperty > variantMapToPropertyMap(const QVariantMap &variantMap)
Convert a map of QVariant to a map of QgsProperty This is useful to restore a map of properties.
void setField(const QString &field)
Sets the field name the property references.
int valueAsInt(const QgsExpressionContext &context, int defaultValue=0, bool *ok=nullptr) const
Calculates the current value of the property and interprets it as an integer.
bool valueAsBool(const QgsExpressionContext &context, bool defaultValue=false, bool *ok=nullptr) const
Calculates the current value of the property and interprets it as an boolean.
bool prepare(const QgsExpressionContext &context=QgsExpressionContext()) const
Prepares the property against a specified expression context.
bool loadVariant(const QVariant &property)
Loads this property from a QVariantMap, wrapped in a QVariant.
QVariant staticValue() const
Returns the current static value for the property.
Type propertyType() const
Returns the property type.
bool operator!=(const QgsProperty &other) const
bool isStaticValueInContext(const QgsExpressionContext &context, QVariant &staticValue) const
Returns true if the property is effectively a static value in the specified context.
void setExpressionString(const QString &expression)
Sets the expression to use for the property value.
QgsProperty & operator=(const QgsProperty &other)
static QgsProperty fromField(const QString &fieldName, bool isActive=true)
Returns a new FieldBasedProperty created from the specified field name.
QgsProperty()
Constructor for a QgsProperty.
static QgsProperty fromValue(const QVariant &value, bool isActive=true)
Returns a new StaticProperty created from the specified value.
void setActive(bool active)
Sets whether the property is currently active.
static QColor decodeColor(const QString &str)
const QgsField & field
Definition: qgsfield.h:463