QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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->expressionString = expression;
374  d->expression = QgsExpression( expression );
375  d->expressionPrepared = false;
376  d->expressionIsInvalid = false;
377 
378  if ( d->expressionString.isEmpty() )
379  {
380  d->active = false;
381  d->type = InvalidProperty;
382  }
383  else
384  {
385  d->type = ExpressionBasedProperty;
386  }
387 }
388 
390 {
391  if ( d->type != ExpressionBasedProperty )
392  return QString();
393 
394  return d->expressionString;
395 }
396 
397 
399 {
400  QString exp;
401  switch ( d->type )
402  {
403  case StaticProperty:
404  exp = QgsExpression::quotedValue( d->staticValue );
405  break;
406 
407  case FieldBasedProperty:
408  exp = QgsExpression::quotedColumnRef( d->fieldName );
409  break;
410 
412  exp = d->expressionString;
413  break;
414 
415  case InvalidProperty:
416  exp = QString();
417  break;
418  }
419  return d->transformer ? d->transformer->toExpression( exp ) : exp;
420 }
421 
422 bool QgsProperty::prepare( const QgsExpressionContext &context ) const
423 {
424  if ( !d->active )
425  return true;
426 
427  switch ( d->type )
428  {
429  case StaticProperty:
430  return true;
431 
432  case FieldBasedProperty:
433  {
434  d.detach();
435  // cache field index to avoid subsequent lookups
436  const QgsFields f = context.fields();
437  d->cachedFieldIdx = f.lookupField( d->fieldName );
438  return true;
439  }
440 
442  {
443  d.detach();
444  if ( !d->expression.prepare( &context ) )
445  {
446  d->expressionReferencedCols.clear();
447  d->expressionPrepared = false;
448  d->expressionIsInvalid = true;
449  return false;
450  }
451 
452  d->expressionPrepared = true;
453  d->expressionIsInvalid = false;
454  d->expressionReferencedCols = d->expression.referencedColumns();
455  return true;
456  }
457 
458  case InvalidProperty:
459  return true;
460 
461  }
462 
463  return false;
464 }
465 
466 QSet<QString> QgsProperty::referencedFields( const QgsExpressionContext &context, bool ignoreContext ) const
467 {
468  if ( !d->active )
469  return QSet<QString>();
470 
471  switch ( d->type )
472  {
473  case StaticProperty:
474  case InvalidProperty:
475  return QSet<QString>();
476 
477  case FieldBasedProperty:
478  {
479  QSet< QString > fields;
480  if ( !d->fieldName.isEmpty() )
481  fields.insert( d->fieldName );
482  return fields;
483  }
484 
486  {
487  if ( ignoreContext )
488  {
489  return d->expression.referencedColumns();
490  }
491 
492  if ( d->expressionIsInvalid )
493  return QSet< QString >();
494 
495  d.detach();
496  if ( !d->expressionPrepared && !prepare( context ) )
497  {
498  d->expressionIsInvalid = true;
499  return QSet< QString >();
500  }
501 
502  return d->expressionReferencedCols;
503  }
504 
505  }
506  return QSet<QString>();
507 }
508 
510 {
511  const QRegularExpression rx( QStringLiteral( "^project_color\\('.*'\\)$" ) );
512  return d->type == QgsProperty::ExpressionBasedProperty && !d->expressionString.isEmpty()
513  && rx.match( d->expressionString ).hasMatch();
514 }
515 
516 QVariant QgsProperty::propertyValue( const QgsExpressionContext &context, const QVariant &defaultValue, bool *ok ) const
517 {
518  if ( ok )
519  *ok = false;
520 
521  if ( !d->active )
522  return defaultValue;
523 
524  switch ( d->type )
525  {
526  case StaticProperty:
527  {
528  if ( ok )
529  *ok = true;
530  return d->staticValue;
531  }
532 
533  case FieldBasedProperty:
534  {
535  const QgsFeature f = context.feature();
536  if ( !f.isValid() )
537  return defaultValue;
538 
539  //shortcut the field lookup
540  if ( d->cachedFieldIdx >= 0 )
541  {
542  if ( ok )
543  *ok = true;
544  return f.attribute( d->cachedFieldIdx );
545  }
546  prepare( context );
547  if ( d->cachedFieldIdx < 0 )
548  return defaultValue;
549 
550  if ( ok )
551  *ok = true;
552  return f.attribute( d->cachedFieldIdx );
553  }
554 
556  {
557  if ( d->expressionIsInvalid )
558  return defaultValue;
559 
560  if ( !d->expressionPrepared && !prepare( context ) )
561  return defaultValue;
562 
563  QVariant result = d->expression.evaluate( &context );
564  if ( !result.isNull() )
565  {
566  if ( ok )
567  *ok = true;
568  return result;
569  }
570  else
571  {
572  return defaultValue;
573  }
574  }
575 
576  case InvalidProperty:
577  return defaultValue;
578 
579  }
580 
581  return QVariant();
582 }
583 
584 
585 QVariant QgsProperty::value( const QgsExpressionContext &context, const QVariant &defaultValue, bool *ok ) const
586 {
587  if ( ok )
588  *ok = false;
589 
590  bool valOk = false;
591  QVariant val = propertyValue( context, defaultValue, &valOk );
592  if ( !d->transformer && !valOk ) // if transformer present, let it handle null values
593  return defaultValue;
594 
595  if ( d->transformer )
596  {
597  if ( !valOk )
598  val = QVariant();
599  val = d->transformer->transform( context, val );
600  }
601 
602  if ( ok )
603  *ok = true;
604 
605  return val;
606 }
607 
608 QDateTime QgsProperty::valueAsDateTime( const QgsExpressionContext &context, const QDateTime &defaultDateTime, bool *ok ) const
609 {
610  bool valOk = false;
611  const QVariant val = value( context, defaultDateTime, &valOk );
612 
613  if ( !valOk || val.isNull() )
614  {
615  if ( ok )
616  *ok = false;
617  return defaultDateTime;
618  }
619 
620  QDateTime dateTime;
621  if ( val.type() == QVariant::DateTime )
622  {
623  dateTime = val.value<QDateTime>();
624  }
625  else
626  {
627  dateTime = val.toDateTime();
628  }
629 
630  if ( !dateTime.isValid() )
631  return defaultDateTime;
632  else
633  {
634  if ( ok )
635  *ok = true;
636  return dateTime;
637  }
638 }
639 
640 QString QgsProperty::valueAsString( const QgsExpressionContext &context, const QString &defaultString, bool *ok ) const
641 {
642  bool valOk = false;
643  const QVariant val = value( context, defaultString, &valOk );
644 
645  if ( !valOk || val.isNull() )
646  {
647  if ( ok )
648  *ok = false;
649  return defaultString;
650  }
651  else
652  {
653  if ( ok )
654  *ok = true;
655  return val.toString();
656  }
657 }
658 
659 QColor QgsProperty::valueAsColor( const QgsExpressionContext &context, const QColor &defaultColor, bool *ok ) const
660 {
661  if ( ok )
662  *ok = false;
663 
664  bool valOk = false;
665  const QVariant val = value( context, defaultColor, &valOk );
666 
667  if ( !valOk || val.isNull() )
668  return defaultColor;
669 
670  QColor color;
671  if ( val.type() == QVariant::Color )
672  {
673  color = val.value<QColor>();
674  }
675  else
676  {
677  color = QgsSymbolLayerUtils::decodeColor( val.toString() );
678  }
679 
680  if ( !color.isValid() )
681  return defaultColor;
682  else
683  {
684  if ( ok )
685  *ok = true;
686  return color;
687  }
688 }
689 
690 double QgsProperty::valueAsDouble( const QgsExpressionContext &context, double defaultValue, bool *ok ) const
691 {
692  if ( ok )
693  *ok = false;
694 
695  bool valOk = false;
696  const QVariant val = value( context, defaultValue, &valOk );
697 
698  if ( !valOk || val.isNull() )
699  return defaultValue;
700 
701  bool convertOk = false;
702  const double dbl = val.toDouble( &convertOk );
703  if ( !convertOk )
704  return defaultValue;
705  else
706  {
707  if ( ok )
708  *ok = true;
709  return dbl;
710  }
711 }
712 
713 int QgsProperty::valueAsInt( const QgsExpressionContext &context, int defaultValue, bool *ok ) const
714 {
715  if ( ok )
716  *ok = false;
717 
718  bool valOk = false;
719  const QVariant val = value( context, defaultValue, &valOk );
720 
721  if ( !valOk || val.isNull() )
722  return defaultValue;
723 
724  bool convertOk = false;
725  const int integer = val.toInt( &convertOk );
726  if ( !convertOk )
727  {
728  //one more option to try
729  const double dbl = val.toDouble( &convertOk );
730  if ( convertOk )
731  {
732  if ( ok )
733  *ok = true;
734  return std::round( dbl );
735  }
736  else
737  {
738  return defaultValue;
739  }
740  }
741  else
742  {
743  if ( ok )
744  *ok = true;
745  return integer;
746  }
747 }
748 
749 bool QgsProperty::valueAsBool( const QgsExpressionContext &context, bool defaultValue, bool *ok ) const
750 {
751  if ( ok )
752  *ok = false;
753 
754  bool valOk = false;
755  const QVariant val = value( context, defaultValue, &valOk );
756 
757  if ( !valOk || val.isNull() )
758  return defaultValue;
759 
760  if ( ok )
761  *ok = true;
762  return val.toBool();
763 }
764 
765 QVariant QgsProperty::toVariant() const
766 {
767  QVariantMap propertyMap;
768 
769  propertyMap.insert( QStringLiteral( "active" ), d->active );
770  propertyMap.insert( QStringLiteral( "type" ), d->type );
771 
772  switch ( d->type )
773  {
774  case StaticProperty:
775  // propertyMap.insert( QStringLiteral( "valType" ), d->staticValue.typeName() );
776  propertyMap.insert( QStringLiteral( "val" ), d->staticValue.toString() );
777  break;
778 
779  case FieldBasedProperty:
780  propertyMap.insert( QStringLiteral( "field" ), d->fieldName );
781  break;
782 
784  propertyMap.insert( QStringLiteral( "expression" ), d->expressionString );
785  break;
786 
787  case InvalidProperty:
788  break;
789  }
790 
791  if ( d->transformer )
792  {
793  QVariantMap transformer;
794  transformer.insert( QStringLiteral( "t" ), d->transformer->transformerType() );
795  transformer.insert( QStringLiteral( "d" ), d->transformer->toVariant() );
796 
797  propertyMap.insert( QStringLiteral( "transformer" ), transformer );
798  }
799 
800  return propertyMap;
801 }
802 
803 bool QgsProperty::loadVariant( const QVariant &property )
804 {
805  const QVariantMap propertyMap = property.toMap();
806 
807  d.detach();
808  d->active = propertyMap.value( QStringLiteral( "active" ) ).toBool();
809  d->type = static_cast< Type >( propertyMap.value( QStringLiteral( "type" ), InvalidProperty ).toInt() );
810 
811  switch ( d->type )
812  {
813  case StaticProperty:
814  d->staticValue = propertyMap.value( QStringLiteral( "val" ) );
815  // d->staticValue.convert( QVariant::nameToType( propertyElem.attribute( "valType", "QString" ).toLocal8Bit().constData() ) );
816  break;
817 
818  case FieldBasedProperty:
819  d->fieldName = propertyMap.value( QStringLiteral( "field" ) ).toString();
820  if ( d->fieldName.isEmpty() )
821  d->active = false;
822  break;
823 
825  d->expressionString = propertyMap.value( QStringLiteral( "expression" ) ).toString();
826  if ( d->expressionString.isEmpty() )
827  d->active = false;
828 
829  d->expression = QgsExpression( d->expressionString );
830  d->expressionPrepared = false;
831  d->expressionIsInvalid = false;
832  d->expressionReferencedCols.clear();
833  break;
834 
835  case InvalidProperty:
836  break;
837 
838  }
839 
840  //restore transformer if present
841  delete d->transformer;
842  d->transformer = nullptr;
843 
844 
845  const QVariant transform = propertyMap.value( QStringLiteral( "transformer" ) );
846 
847  if ( transform.isValid() )
848  {
849  const QVariantMap transformerMap = transform.toMap();
850 
851  const QgsPropertyTransformer::Type type = static_cast< QgsPropertyTransformer::Type >( transformerMap.value( QStringLiteral( "t" ), QgsPropertyTransformer::GenericNumericTransformer ).toInt() );
852  std::unique_ptr< QgsPropertyTransformer > transformer( QgsPropertyTransformer::create( type ) );
853 
854  if ( transformer )
855  {
856  if ( transformer->loadVariant( transformerMap.value( QStringLiteral( "d" ) ) ) )
857  d->transformer = transformer.release();
858  }
859  }
860 
861  return true;
862 }
863 
864 
866 {
867  d.detach();
868  d->transformer = transformer;
869 }
870 
872 {
873  return d->transformer;
874 }
875 
877 {
878  if ( d->type != ExpressionBasedProperty )
879  return false;
880 
881  if ( d->transformer )
882  return false; // already a transformer
883 
884  QString baseExpression;
885  QString fieldName;
886  std::unique_ptr< QgsPropertyTransformer > transformer( QgsPropertyTransformer::fromExpression( d->expressionString, baseExpression, fieldName ) );
887  if ( !transformer )
888  return false;
889 
890  d.detach();
891  d->transformer = transformer.release();
892  if ( !fieldName.isEmpty() )
893  setField( fieldName );
894  else
895  setExpressionString( baseExpression );
896  return true;
897 }
898 
899 
900 
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:209
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:320
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:349
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