QGIS API Documentation  3.24.2-Tisler (13c1a02865)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
qgsfieldcalculator.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsfieldcalculator.cpp
3  ---------------------
4  begin : September 2009
5  copyright : (C) 2009 by Marco Hugentobler
6  email : marco at hugis dot net
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 <QMessageBox>
17 
18 
19 #include "qgsfieldcalculator.h"
20 #include "qgsdistancearea.h"
21 #include "qgsexpression.h"
22 #include "qgsfeatureiterator.h"
23 #include "qgsmapcanvas.h"
24 #include "qgsproject.h"
25 #include "qgsvectordataprovider.h"
26 #include "qgsvectorlayer.h"
27 #include "qgsexpressioncontext.h"
28 #include "qgsgeometry.h"
29 #include "qgssettings.h"
30 #include "qgsgui.h"
31 #include "qgsguiutils.h"
32 #include "qgsproxyprogresstask.h"
35 #include "qgsvariantutils.h"
36 #include "qgsfields.h"
37 
38 
39 // FTC = FieldTypeCombo
40 constexpr int FTC_TYPE_ROLE_IDX = 0;
41 constexpr int FTC_TYPE_NAME_IDX = 1;
42 constexpr int FTC_MINLEN_IDX = 2;
43 constexpr int FTC_MAXLEN_IDX = 3;
44 constexpr int FTC_MINPREC_IDX = 4;
45 constexpr int FTC_MAXPREC_IDX = 5;
46 constexpr int FTC_SUBTYPE_IDX = 6;
47 
49  : QDialog( parent )
50  , mVectorLayer( vl )
51  , mAttributeId( -1 )
52 {
53  setupUi( this );
54  connect( mNewFieldGroupBox, &QGroupBox::toggled, this, &QgsFieldCalculator::mNewFieldGroupBox_toggled );
55  connect( mUpdateExistingGroupBox, &QGroupBox::toggled, this, &QgsFieldCalculator::mUpdateExistingGroupBox_toggled );
56  connect( mCreateVirtualFieldCheckbox, &QCheckBox::stateChanged, this, &QgsFieldCalculator::mCreateVirtualFieldCheckbox_stateChanged );
57  connect( mOutputFieldNameLineEdit, &QLineEdit::textChanged, this, &QgsFieldCalculator::mOutputFieldNameLineEdit_textChanged );
58  connect( mOutputFieldTypeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::activated ), this, &QgsFieldCalculator::mOutputFieldTypeComboBox_activated );
59 
61 
62  if ( !vl )
63  return;
64  QgsVectorDataProvider *dataProvider = vl->dataProvider();
65  if ( !dataProvider )
66  return;
67 
68  const QgsVectorDataProvider::Capabilities caps = dataProvider->capabilities();
69  mCanAddAttribute = caps & QgsVectorDataProvider::AddAttributes;
70  mCanChangeAttributeValue = caps & QgsVectorDataProvider::ChangeAttributeValues;
71 
73 
74  expContext.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "row_number" ), 1, true ) );
75  expContext.setHighlightedVariables( QStringList() << QStringLiteral( "row_number" ) );
76 
77  populateFields();
78  populateOutputFieldTypes();
79 
80  connect( builder, &QgsExpressionBuilderWidget::expressionParsed, this, &QgsFieldCalculator::setOkButtonState );
81  connect( mOutputFieldWidthSpinBox, &QAbstractSpinBox::editingFinished, this, &QgsFieldCalculator::setPrecisionMinMax );
82  connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsFieldCalculator::showHelp );
83 
84  QgsDistanceArea myDa;
86  myDa.setEllipsoid( QgsProject::instance()->ellipsoid() );
87  builder->setGeomCalculator( myDa );
88 
89  //default values for field width and precision
90  mOutputFieldWidthSpinBox->setValue( 10 );
91  mOutputFieldWidthSpinBox->setClearValue( 10 );
92  mOutputFieldPrecisionSpinBox->setValue( 3 );
93  mOutputFieldPrecisionSpinBox->setClearValue( 3 );
94  setPrecisionMinMax();
95 
96  if ( vl->providerType() == QLatin1String( "ogr" ) && vl->storageType() == QLatin1String( "ESRI Shapefile" ) )
97  {
98  mOutputFieldNameLineEdit->setMaxLength( 10 );
99  }
100 
101  if ( !mCanAddAttribute )
102  {
103  mCreateVirtualFieldCheckbox->setChecked( true );
104  mCreateVirtualFieldCheckbox->setEnabled( false );
105  mOnlyVirtualFieldsInfoLabel->setVisible( true );
106  mInfoIcon->setVisible( true );
107  }
108  else
109  {
110  mOnlyVirtualFieldsInfoLabel->setVisible( false );
111  mInfoIcon->setVisible( false );
112  }
113 
114  if ( !mCanChangeAttributeValue )
115  {
116  mUpdateExistingGroupBox->setEnabled( false );
117  mCreateVirtualFieldCheckbox->setChecked( true );
118  mCreateVirtualFieldCheckbox->setEnabled( false );
119  }
120 
121  Q_ASSERT( mNewFieldGroupBox->isEnabled() || mUpdateExistingGroupBox->isEnabled() );
122 
123  if ( mNewFieldGroupBox->isEnabled() )
124  {
125  mNewFieldGroupBox->setChecked( true );
126  }
127  else
128  {
129  mNewFieldGroupBox->setToolTip( tr( "Not available for layer" ) );
130  mUpdateExistingGroupBox->setChecked( true );
131  mUpdateExistingGroupBox->setCheckable( false );
132  }
133 
134  if ( mUpdateExistingGroupBox->isEnabled() )
135  {
136  mUpdateExistingGroupBox->setChecked( !mNewFieldGroupBox->isEnabled() );
137  }
138  else
139  {
140  mUpdateExistingGroupBox->setToolTip( tr( "Not available for layer" ) );
141  mNewFieldGroupBox->setChecked( true );
142  mNewFieldGroupBox->setCheckable( false );
143  }
144 
145  if ( ( mNewFieldGroupBox->isChecked() && mCreateVirtualFieldCheckbox->isChecked() ) || mVectorLayer->isEditable() )
146  {
147  mEditModeAutoTurnOnLabel->setVisible( false );
148  mInfoIcon->setVisible( false );
149  }
150  else
151  {
152  mInfoIcon->setVisible( true );
153  }
154 
155  const bool hasselection = vl->selectedFeatureCount() > 0;
156  mOnlyUpdateSelectedCheckBox->setChecked( mCanChangeAttributeValue && hasselection );
157  mOnlyUpdateSelectedCheckBox->setEnabled( mCanChangeAttributeValue && hasselection );
158  mOnlyUpdateSelectedCheckBox->setText( tr( "Only update %n selected feature(s)", nullptr, vl->selectedFeatureCount() ) );
159 
160  builder->initWithLayer( vl, expContext, QStringLiteral( "fieldcalc" ) );
161 
162  mInfoIcon->setPixmap( style()->standardPixmap( QStyle::SP_MessageBoxInformation ) );
163 
164  setWindowTitle( tr( "%1 — Field Calculator" ).arg( mVectorLayer->name() ) );
165 
166  setOkButtonState();
167 }
168 
170 {
171  builder->expressionTree()->saveToRecent( builder->expressionText(), QStringLiteral( "fieldcalc" ) );
172 
173  if ( !mVectorLayer )
174  return;
175 
176  // Set up QgsDistanceArea each time we (re-)calculate
177  QgsDistanceArea myDa;
178 
179  myDa.setSourceCrs( mVectorLayer->crs(), QgsProject::instance()->transformContext() );
180  myDa.setEllipsoid( QgsProject::instance()->ellipsoid() );
181 
182  const QString calcString = builder->expressionText();
183  QgsExpression exp( calcString );
184  exp.setGeomCalculator( &myDa );
185  exp.setDistanceUnits( QgsProject::instance()->distanceUnits() );
186  exp.setAreaUnits( QgsProject::instance()->areaUnits() );
187 
189 
190  if ( !exp.prepare( &expContext ) )
191  {
192  QMessageBox::critical( nullptr, tr( "Evaluation Error" ), exp.evalErrorString() );
193  return;
194  }
195 
196  bool updatingGeom = false;
197 
198  // Test for creating expression field based on ! mUpdateExistingGroupBox checked rather
199  // than on mNewFieldGroupBox checked, as if the provider does not support adding attributes
200  // then mUpdateExistingGroupBox is set to not checkable, and hence is not checked. This
201  // is a minimum fix to resolve this - better would be some GUI redesign...
202  if ( ! mUpdateExistingGroupBox->isChecked() && mCreateVirtualFieldCheckbox->isChecked() )
203  {
204  mVectorLayer->addExpressionField( calcString, fieldDefinition() );
205  }
206  else
207  {
208  if ( !mVectorLayer->isEditable() )
209  mVectorLayer->startEditing();
210 
211  QgsTemporaryCursorOverride cursorOverride( Qt::WaitCursor );
212 
213  mVectorLayer->beginEditCommand( QStringLiteral( "Field calculator" ) );
214 
215  //update existing field
216  if ( mUpdateExistingGroupBox->isChecked() || !mNewFieldGroupBox->isEnabled() )
217  {
218  if ( mExistingFieldComboBox->currentData().toString() == QLatin1String( "geom" ) )
219  {
220  //update geometry
221  mAttributeId = -1;
222  updatingGeom = true;
223  }
224  else
225  {
226  bool ok = false;
227  const int id = mExistingFieldComboBox->currentData().toInt( &ok );
228  if ( ok )
229  mAttributeId = id;
230  }
231  }
232  else
233  {
234  //create new field
235  const QgsField newField = fieldDefinition();
236 
237  if ( !mVectorLayer->addAttribute( newField ) )
238  {
239  cursorOverride.release();
240  QMessageBox::critical( nullptr, tr( "Create New Field" ), tr( "Could not add the new field to the provider." ) );
241  mVectorLayer->destroyEditCommand();
242  return;
243  }
244 
245  //get index of the new field
246  const QgsFields &fields = mVectorLayer->fields();
247 
248  for ( int idx = 0; idx < fields.count(); ++idx )
249  {
250  if ( fields.at( idx ).name() == mOutputFieldNameLineEdit->text() )
251  {
252  mAttributeId = idx;
253  break;
254  }
255  }
256 
257  //update expression context with new fields
258  expContext.setFields( mVectorLayer->fields() );
259  if ( ! exp.prepare( &expContext ) )
260  {
261  cursorOverride.release();
262  QMessageBox::critical( nullptr, tr( "Evaluation Error" ), exp.evalErrorString() );
263  return;
264  }
265  }
266 
267  if ( mAttributeId == -1 && !updatingGeom )
268  {
269  mVectorLayer->destroyEditCommand();
270  return;
271  }
272 
273  //go through all the features and change the new attribute
274  QgsFeature feature;
275  bool calculationSuccess = true;
276  QString error;
277 
278  const bool useGeometry = exp.needsGeometry();
279  int rownum = 1;
280 
281  const QgsField field = !updatingGeom ? mVectorLayer->fields().at( mAttributeId ) : QgsField();
282 
283  const bool newField = !mUpdateExistingGroupBox->isChecked();
284  QVariant emptyAttribute;
285  if ( newField )
286  emptyAttribute = QVariant( field.type() );
287 
289  QSet< QString > referencedColumns = exp.referencedColumns();
290  referencedColumns.insert( field.name() ); // need existing column value to store old attribute when changing field values
291  req.setSubsetOfAttributes( referencedColumns, mVectorLayer->fields() );
292  if ( mOnlyUpdateSelectedCheckBox->isChecked() )
293  {
294  req.setFilterFids( mVectorLayer->selectedFeatureIds() );
295  }
296  QgsFeatureIterator fit = mVectorLayer->getFeatures( req );
297 
298  std::unique_ptr< QgsScopedProxyProgressTask > task = std::make_unique< QgsScopedProxyProgressTask >( tr( "Calculating field" ) );
299  const long long count = mOnlyUpdateSelectedCheckBox->isChecked() ? mVectorLayer->selectedFeatureCount() : mVectorLayer->featureCount();
300  long long i = 0;
301  while ( fit.nextFeature( feature ) )
302  {
303  i++;
304  task->setProgress( i / static_cast< double >( count ) * 100 );
305 
306  expContext.setFeature( feature );
307  expContext.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "row_number" ), rownum, true ) );
308 
309  QVariant value = exp.evaluate( &expContext );
310  if ( exp.hasEvalError() )
311  {
312  calculationSuccess = false;
313  error = exp.evalErrorString();
314  break;
315  }
316  else if ( updatingGeom )
317  {
318  if ( value.canConvert< QgsGeometry >() )
319  {
320  QgsGeometry geom = value.value< QgsGeometry >();
321  mVectorLayer->changeGeometry( feature.id(), geom );
322  }
323  }
324  else
325  {
326  ( void )field.convertCompatible( value );
327  mVectorLayer->changeAttributeValue( feature.id(), mAttributeId, value, newField ? emptyAttribute : feature.attributes().value( mAttributeId ) );
328  }
329 
330  rownum++;
331  }
332 
333  if ( !calculationSuccess )
334  {
335  cursorOverride.release();
336  task.reset();
337  QMessageBox::critical( nullptr, tr( "Evaluation Error" ), tr( "An error occurred while evaluating the calculation string:\n%1" ).arg( error ) );
338  mVectorLayer->destroyEditCommand();
339  return;
340  }
341 
342  mVectorLayer->endEditCommand();
343  }
344  QDialog::accept();
345 }
346 
347 void QgsFieldCalculator::populateOutputFieldTypes()
348 {
349  if ( !mVectorLayer )
350  {
351  return;
352  }
353 
354  QgsVectorDataProvider *provider = mVectorLayer->dataProvider();
355  if ( !provider )
356  {
357  return;
358  }
359 
360  const int oldDataType = mOutputFieldTypeComboBox->currentData( Qt::UserRole + FTC_TYPE_ROLE_IDX ).toInt();
361 
362  mOutputFieldTypeComboBox->blockSignals( true );
363 
364  // Standard subset of fields in case of virtual
365  const QList< QgsVectorDataProvider::NativeType > &typelist = mCreateVirtualFieldCheckbox->isChecked() ?
366  ( QList< QgsVectorDataProvider::NativeType >()
367  << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::Int ), QStringLiteral( "integer" ), QVariant::Int, 0, 10 )
368  << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::Double ), QStringLiteral( "double precision" ), QVariant::Double, -1, -1, -1, -1 )
369  << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::String ), QStringLiteral( "string" ), QVariant::String )
370  // date time
371  << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::Date ), QStringLiteral( "date" ), QVariant::Date, -1, -1, -1, -1 )
372  << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::Time ), QStringLiteral( "time" ), QVariant::Time, -1, -1, -1, -1 )
373  << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::DateTime ), QStringLiteral( "datetime" ), QVariant::DateTime, -1, -1, -1, -1 )
374  // string types
375  << QgsVectorDataProvider::NativeType( tr( "Text, unlimited length (text)" ), QStringLiteral( "text" ), QVariant::String, -1, -1, -1, -1 )
376  // boolean
377  << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::Bool ), QStringLiteral( "bool" ), QVariant::Bool )
378  // blob
379  << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QVariant::ByteArray ), QStringLiteral( "binary" ), QVariant::ByteArray ) ) :
380  provider->nativeTypes();
381 
382  mOutputFieldTypeComboBox->clear();
383  for ( int i = 0; i < typelist.size(); i++ )
384  {
385  mOutputFieldTypeComboBox->addItem( QgsFields::iconForFieldType( typelist[i].mType, typelist[i].mSubType ), typelist[i].mTypeDesc );
386  mOutputFieldTypeComboBox->setItemData( i, static_cast<int>( typelist[i].mType ), Qt::UserRole + FTC_TYPE_ROLE_IDX );
387  mOutputFieldTypeComboBox->setItemData( i, typelist[i].mTypeName, Qt::UserRole + FTC_TYPE_NAME_IDX );
388  mOutputFieldTypeComboBox->setItemData( i, typelist[i].mMinLen, Qt::UserRole + FTC_MINLEN_IDX );
389  mOutputFieldTypeComboBox->setItemData( i, typelist[i].mMaxLen, Qt::UserRole + FTC_MAXLEN_IDX );
390  mOutputFieldTypeComboBox->setItemData( i, typelist[i].mMinPrec, Qt::UserRole + FTC_MINPREC_IDX );
391  mOutputFieldTypeComboBox->setItemData( i, typelist[i].mMaxPrec, Qt::UserRole + FTC_MAXPREC_IDX );
392  mOutputFieldTypeComboBox->setItemData( i, static_cast<int>( typelist[i].mSubType ), Qt::UserRole + FTC_SUBTYPE_IDX );
393  }
394  mOutputFieldTypeComboBox->blockSignals( false );
395 
396  const int idx = mOutputFieldTypeComboBox->findData( oldDataType, Qt::UserRole + FTC_TYPE_ROLE_IDX );
397  if ( idx != -1 )
398  {
399  mOutputFieldTypeComboBox->setCurrentIndex( idx );
400  mOutputFieldTypeComboBox_activated( idx );
401  }
402  else
403  {
404  mOutputFieldTypeComboBox->setCurrentIndex( 0 );
405  mOutputFieldTypeComboBox_activated( 0 );
406  }
407 }
408 
409 void QgsFieldCalculator::mNewFieldGroupBox_toggled( bool on )
410 {
411  mUpdateExistingGroupBox->setChecked( !on );
412  if ( on && !mCanAddAttribute )
413  {
414  mOnlyVirtualFieldsInfoLabel->setVisible( true );
415  }
416  else
417  {
418  mOnlyVirtualFieldsInfoLabel->setVisible( false );
419  }
420 
421  if ( ( mNewFieldGroupBox->isChecked() && mCreateVirtualFieldCheckbox->isChecked() ) || mVectorLayer->isEditable() )
422  {
423  mEditModeAutoTurnOnLabel->setVisible( false );
424  }
425  else
426  {
427  mEditModeAutoTurnOnLabel->setVisible( true );
428  }
429 
430  mInfoIcon->setVisible( mOnlyVirtualFieldsInfoLabel->isVisible() || mEditModeAutoTurnOnLabel->isVisible() );
431 }
432 
433 void QgsFieldCalculator::mUpdateExistingGroupBox_toggled( bool on )
434 {
435  mNewFieldGroupBox->setChecked( !on );
436  setOkButtonState();
437 
438  if ( on )
439  {
440  mOnlyUpdateSelectedCheckBox->setEnabled( mVectorLayer->selectedFeatureCount() > 0 );
441  }
442  else
443  {
444  mCreateVirtualFieldCheckbox_stateChanged( mCreateVirtualFieldCheckbox->checkState() );
445  }
446 }
447 
448 void QgsFieldCalculator::mCreateVirtualFieldCheckbox_stateChanged( int state )
449 {
450  mOnlyUpdateSelectedCheckBox->setChecked( false );
451  mOnlyUpdateSelectedCheckBox->setEnabled( state != Qt::Checked && mVectorLayer->selectedFeatureCount() > 0 );
452 
453  if ( ( mNewFieldGroupBox->isChecked() && mCreateVirtualFieldCheckbox->isChecked() ) || mVectorLayer->isEditable() )
454  {
455  mEditModeAutoTurnOnLabel->setVisible( false );
456  }
457  else
458  {
459  mEditModeAutoTurnOnLabel->setVisible( true );
460  }
461  populateOutputFieldTypes();
462  mInfoIcon->setVisible( mOnlyVirtualFieldsInfoLabel->isVisible() || mEditModeAutoTurnOnLabel->isVisible() );
463 }
464 
465 
466 void QgsFieldCalculator::mOutputFieldNameLineEdit_textChanged( const QString &text )
467 {
468  Q_UNUSED( text )
469  setOkButtonState();
470 }
471 
472 
473 void QgsFieldCalculator::mOutputFieldTypeComboBox_activated( int index )
474 {
475  mOutputFieldWidthSpinBox->setMinimum( mOutputFieldTypeComboBox->itemData( index, Qt::UserRole + FTC_MINLEN_IDX ).toInt() );
476  mOutputFieldWidthSpinBox->setMaximum( mOutputFieldTypeComboBox->itemData( index, Qt::UserRole + FTC_MAXLEN_IDX ).toInt() );
477  mOutputFieldWidthSpinBox->setEnabled( mOutputFieldWidthSpinBox->minimum() < mOutputFieldWidthSpinBox->maximum() );
478  if ( mOutputFieldWidthSpinBox->value() < mOutputFieldWidthSpinBox->minimum() )
479  mOutputFieldWidthSpinBox->setValue( mOutputFieldWidthSpinBox->minimum() );
480  if ( mOutputFieldWidthSpinBox->value() > mOutputFieldWidthSpinBox->maximum() )
481  mOutputFieldWidthSpinBox->setValue( mOutputFieldWidthSpinBox->maximum() );
482 
483  setPrecisionMinMax();
484 }
485 
486 void QgsFieldCalculator::populateFields()
487 {
488  if ( !mVectorLayer )
489  return;
490 
491  const QgsFields &fields = mVectorLayer->fields();
492  for ( int idx = 0; idx < fields.count(); ++idx )
493  {
494  switch ( fields.fieldOrigin( idx ) )
495  {
498 
499  continue; // can't be edited
500 
503  break; // can always be edited
504 
506  {
507  // show joined fields (e.g. auxiliary fields) only if they have a non-hidden editor widget.
508  // This enables them to be bulk field-calculated when a user needs to, but hides them by default
509  // (since there's often MANY of these, e.g. after using the label properties tool on a layer)
510  if ( fields.at( idx ).editorWidgetSetup().type() == QLatin1String( "Hidden" ) )
511  continue;
512 
513  // only show editable joins
514  int srcFieldIndex;
515  const QgsVectorLayerJoinInfo *info = mVectorLayer->joinBuffer()->joinForFieldIndex( idx, fields, srcFieldIndex );
516 
517  if ( !info || !info->isEditable() )
518  continue; // join is not editable
519 
520  break;
521  }
522  }
523 
524  const QString fieldName = fields.at( idx ).name();
525 
526  //insert into field combo box
527  mExistingFieldComboBox->addItem( fields.iconForField( idx ), fieldName, idx );
528  }
529 
530  if ( mVectorLayer->geometryType() != QgsWkbTypes::NullGeometry )
531  {
532  mExistingFieldComboBox->addItem( tr( "<geometry>" ), "geom" );
533 
534  QFont font = mExistingFieldComboBox->itemData( mExistingFieldComboBox->count() - 1, Qt::FontRole ).value<QFont>();
535  font.setItalic( true );
536  mExistingFieldComboBox->setItemData( mExistingFieldComboBox->count() - 1, font, Qt::FontRole );
537  }
538  mExistingFieldComboBox->setCurrentIndex( -1 );
539 }
540 
541 void QgsFieldCalculator::setOkButtonState()
542 {
543  QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
544 
545  if ( ( mNewFieldGroupBox->isChecked() || !mUpdateExistingGroupBox->isEnabled() )
546  && mOutputFieldNameLineEdit->text().isEmpty() )
547  {
548  okButton->setToolTip( tr( "Please enter a field name" ) );
549  okButton->setEnabled( false );
550  return;
551  }
552 
553  if ( !builder->isExpressionValid() )
554  {
555  okButton->setToolTip( okButton->toolTip() + tr( "\n The expression is invalid see (more info) for details" ) );
556  okButton->setEnabled( false );
557  return;
558  }
559 
560  okButton->setToolTip( QString() );
561  okButton->setEnabled( true );
562 }
563 
564 void QgsFieldCalculator::setPrecisionMinMax()
565 {
566  const int idx = mOutputFieldTypeComboBox->currentIndex();
567  const int minPrecType = mOutputFieldTypeComboBox->itemData( idx, Qt::UserRole + FTC_MINPREC_IDX ).toInt();
568  const int maxPrecType = mOutputFieldTypeComboBox->itemData( idx, Qt::UserRole + FTC_MAXPREC_IDX ).toInt();
569  const bool precisionIsEnabled = minPrecType < maxPrecType;
570  mOutputFieldPrecisionSpinBox->setEnabled( precisionIsEnabled );
571  // Do not set min/max if it's disabled or we'll loose the default value,
572  // see https://github.com/qgis/QGIS/issues/26880 - QGIS saves integer field when
573  // I create a new real field through field calculator (Update field works as intended)
574  if ( precisionIsEnabled )
575  {
576  mOutputFieldPrecisionSpinBox->setMinimum( minPrecType );
577  mOutputFieldPrecisionSpinBox->setMaximum( std::max( minPrecType, std::min( maxPrecType, mOutputFieldWidthSpinBox->value() ) ) );
578  }
579 }
580 
581 void QgsFieldCalculator::showHelp()
582 {
583  QgsHelp::openHelp( QStringLiteral( "working_with_vector/attribute_table.html#editing-attribute-values" ) );
584 }
585 
586 QgsField QgsFieldCalculator::fieldDefinition()
587 {
588  return QgsField( mOutputFieldNameLineEdit->text(),
589  static_cast< QVariant::Type >( mOutputFieldTypeComboBox->currentData( Qt::UserRole + FTC_TYPE_ROLE_IDX ).toInt() ),
590  mOutputFieldTypeComboBox->currentData( Qt::UserRole + FTC_TYPE_NAME_IDX ).toString(),
591  mOutputFieldWidthSpinBox->value(),
592  mOutputFieldPrecisionSpinBox->isEnabled() ? mOutputFieldPrecisionSpinBox->value() : 0,
593  QString(),
594  static_cast< QVariant::Type >( mOutputFieldTypeComboBox->currentData( Qt::UserRole + FTC_SUBTYPE_IDX ).toInt() )
595  );
596 }
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
void expressionParsed(bool isValid)
Emitted when the user changes the expression in the widget.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
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...
QgsExpressionContextScope * lastScope()
Returns the last scope added to the context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
void setHighlightedVariables(const QStringList &variableNames)
Sets the list of variable names within the context intended to be highlighted to the user.
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
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.
QString evalErrorString() const
Returns evaluation error.
void setDistanceUnits(QgsUnitTypes::DistanceUnit unit)
Sets the desired distance units for calculations involving geomCalculator(), e.g.,...
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
void setAreaUnits(QgsUnitTypes::AreaUnit unit)
Sets the desired areal units for calculations involving geomCalculator(), e.g., "$area".
void setGeomCalculator(const QgsDistanceArea *calc)
Sets the geometry calculator used for distance and area calculations in expressions.
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QVariant evaluate()
Evaluate the feature and return the result.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets the feature IDs that should be fetched.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsAttributes attributes
Definition: qgsfeature.h:65
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
QgsFieldCalculator(QgsVectorLayer *vl, QWidget *parent=nullptr)
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QString name
Definition: qgsfield.h:60
bool convertCompatible(QVariant &v, QString *errorMessage=nullptr) const
Converts the provided variant to a compatible format.
Definition: qgsfield.cpp:402
QVariant::Type type
Definition: qgsfield.h:58
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:602
Container of fields for a vector layer.
Definition: qgsfields.h:45
static QIcon iconForFieldType(QVariant::Type type, QVariant::Type subType=QVariant::Type::Invalid)
Returns an icon corresponding to a field type.
Definition: qgsfields.cpp:294
@ OriginExpression
Field is calculated from an expression.
Definition: qgsfields.h:54
@ OriginEdit
Field has been temporarily added in editing mode (originIndex = index in the list of added attributes...
Definition: qgsfields.h:53
@ OriginUnknown
It has not been specified where the field comes from.
Definition: qgsfields.h:50
@ OriginJoin
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
Definition: qgsfields.h:52
@ OriginProvider
Field comes from the underlying data provider of the vector layer (originIndex = index in provider's ...
Definition: qgsfields.h:51
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
Definition: qgsfields.cpp:189
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
QIcon iconForField(int fieldIdx, bool considerOrigin=false) const
Returns an icon corresponding to a field index, based on the field's type and source.
Definition: qgsfields.cpp:275
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition: qgsgui.cpp:174
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:36
QString name
Definition: qgsmaplayer.h:76
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:470
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:107
Temporarily sets a cursor override for the QApplication for the lifetime of the object.
Definition: qgsguiutils.h:221
void release()
Releases the cursor override early (i.e.
static QString typeToDisplayString(QVariant::Type type, QVariant::Type subType=QVariant::Type::Invalid)
Returns a user-friendly translated string representing a QVariant type.
This is the base class for vector data providers.
@ AddAttributes
Allows addition of new attributes (fields)
@ ChangeAttributeValues
Allows modification of attribute values.
QList< QgsVectorDataProvider::NativeType > nativeTypes() const
Returns the names of the supported types.
virtual Q_INVOKABLE QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
const QgsVectorLayerJoinInfo * joinForFieldIndex(int index, const QgsFields &fields, int &sourceFieldIndex) const
Finds the vector join for a layer field index.
Defines left outer join from our vector layer to some other vector layer.
bool isEditable() const
Returns whether joined fields may be edited through the form of the target layer.
Represents a vector layer which manages a vector based data sets.
int addExpressionField(const QString &exp, const QgsField &fld)
Add a new field which is calculated by the expression specified.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
Q_INVOKABLE bool startEditing()
Makes the layer editable.
bool addAttribute(const QgsField &field)
Add an attribute field (but does not commit it) returns true if the field was added.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
void endEditCommand()
Finish edit command and add it to undo/redo stack.
void destroyEditCommand()
Destroy active command and reverts all changes in it.
QgsVectorLayerJoinBuffer * joinBuffer()
Returns the join buffer object.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant(), bool skipDefaultValues=false)
Changes an attribute value for a feature (but does not immediately commit the changes).
void beginEditCommand(const QString &text)
Create edit command for undo/redo operations.
bool changeGeometry(QgsFeatureId fid, QgsGeometry &geometry, bool skipDefaultValue=false)
Changes a feature's geometry within the layer's edit buffer (but does not immediately commit the chan...
const QgsField & field
Definition: qgsfield.h:463
constexpr int FTC_TYPE_NAME_IDX
constexpr int FTC_MINLEN_IDX
constexpr int FTC_TYPE_ROLE_IDX
constexpr int FTC_MAXLEN_IDX
constexpr int FTC_MAXPREC_IDX
constexpr int FTC_MINPREC_IDX
constexpr int FTC_SUBTYPE_IDX
Single variable definition for use within a QgsExpressionContextScope.