QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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 
36 
37 // FTC = FieldTypeCombo
38 constexpr int FTC_TYPE_ROLE_IDX = 0;
39 constexpr int FTC_TYPE_NAME_IDX = 1;
40 constexpr int FTC_MINLEN_IDX = 2;
41 constexpr int FTC_MAXLEN_IDX = 3;
42 constexpr int FTC_MINPREC_IDX = 4;
43 constexpr int FTC_MAXPREC_IDX = 5;
44 constexpr int FTC_SUBTYPE_IDX = 6;
45 
47  : QDialog( parent )
48  , mVectorLayer( vl )
49  , mAttributeId( -1 )
50 {
51  setupUi( this );
52  connect( mNewFieldGroupBox, &QGroupBox::toggled, this, &QgsFieldCalculator::mNewFieldGroupBox_toggled );
53  connect( mUpdateExistingGroupBox, &QGroupBox::toggled, this, &QgsFieldCalculator::mUpdateExistingGroupBox_toggled );
54  connect( mCreateVirtualFieldCheckbox, &QCheckBox::stateChanged, this, &QgsFieldCalculator::mCreateVirtualFieldCheckbox_stateChanged );
55  connect( mOutputFieldNameLineEdit, &QLineEdit::textChanged, this, &QgsFieldCalculator::mOutputFieldNameLineEdit_textChanged );
56  connect( mOutputFieldTypeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::activated ), this, &QgsFieldCalculator::mOutputFieldTypeComboBox_activated );
57 
59 
60  if ( !vl )
61  return;
62  QgsVectorDataProvider *dataProvider = vl->dataProvider();
63  if ( !dataProvider )
64  return;
65 
66  const QgsVectorDataProvider::Capabilities caps = dataProvider->capabilities();
67  mCanAddAttribute = caps & QgsVectorDataProvider::AddAttributes;
68  mCanChangeAttributeValue = caps & QgsVectorDataProvider::ChangeAttributeValues;
69 
71 
72  expContext.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "row_number" ), 1, true ) );
73  expContext.setHighlightedVariables( QStringList() << QStringLiteral( "row_number" ) );
74 
75  populateFields();
76  populateOutputFieldTypes();
77 
78  connect( builder, &QgsExpressionBuilderWidget::expressionParsed, this, &QgsFieldCalculator::setOkButtonState );
79  connect( mOutputFieldWidthSpinBox, &QAbstractSpinBox::editingFinished, this, &QgsFieldCalculator::setPrecisionMinMax );
80  connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsFieldCalculator::showHelp );
81 
82  QgsDistanceArea myDa;
84  myDa.setEllipsoid( QgsProject::instance()->ellipsoid() );
85  builder->setGeomCalculator( myDa );
86 
87  //default values for field width and precision
88  mOutputFieldWidthSpinBox->setValue( 10 );
89  mOutputFieldWidthSpinBox->setClearValue( 10 );
90  mOutputFieldPrecisionSpinBox->setValue( 3 );
91  mOutputFieldPrecisionSpinBox->setClearValue( 3 );
92  setPrecisionMinMax();
93 
94  if ( vl->providerType() == QLatin1String( "ogr" ) && vl->storageType() == QLatin1String( "ESRI Shapefile" ) )
95  {
96  mOutputFieldNameLineEdit->setMaxLength( 10 );
97  }
98 
99  if ( !mCanAddAttribute )
100  {
101  mCreateVirtualFieldCheckbox->setChecked( true );
102  mCreateVirtualFieldCheckbox->setEnabled( false );
103  mOnlyVirtualFieldsInfoLabel->setVisible( true );
104  mInfoIcon->setVisible( true );
105  }
106  else
107  {
108  mOnlyVirtualFieldsInfoLabel->setVisible( false );
109  mInfoIcon->setVisible( false );
110  }
111 
112  if ( !mCanChangeAttributeValue )
113  {
114  mUpdateExistingGroupBox->setEnabled( false );
115  mCreateVirtualFieldCheckbox->setChecked( true );
116  mCreateVirtualFieldCheckbox->setEnabled( false );
117  }
118 
119  Q_ASSERT( mNewFieldGroupBox->isEnabled() || mUpdateExistingGroupBox->isEnabled() );
120 
121  if ( mNewFieldGroupBox->isEnabled() )
122  {
123  mNewFieldGroupBox->setChecked( true );
124  }
125  else
126  {
127  mNewFieldGroupBox->setToolTip( tr( "Not available for layer" ) );
128  mUpdateExistingGroupBox->setChecked( true );
129  mUpdateExistingGroupBox->setCheckable( false );
130  }
131 
132  if ( mUpdateExistingGroupBox->isEnabled() )
133  {
134  mUpdateExistingGroupBox->setChecked( !mNewFieldGroupBox->isEnabled() );
135  }
136  else
137  {
138  mUpdateExistingGroupBox->setToolTip( tr( "Not available for layer" ) );
139  mNewFieldGroupBox->setChecked( true );
140  mNewFieldGroupBox->setCheckable( false );
141  }
142 
143  if ( ( mNewFieldGroupBox->isChecked() && mCreateVirtualFieldCheckbox->isChecked() ) || mVectorLayer->isEditable() )
144  {
145  mEditModeAutoTurnOnLabel->setVisible( false );
146  mInfoIcon->setVisible( false );
147  }
148  else
149  {
150  mInfoIcon->setVisible( true );
151  }
152 
153  const bool hasselection = vl->selectedFeatureCount() > 0;
154  mOnlyUpdateSelectedCheckBox->setChecked( mCanChangeAttributeValue && hasselection );
155  mOnlyUpdateSelectedCheckBox->setEnabled( mCanChangeAttributeValue && hasselection );
156  mOnlyUpdateSelectedCheckBox->setText( tr( "Only update %1 selected features" ).arg( vl->selectedFeatureCount() ) );
157 
158  builder->initWithLayer( vl, expContext, QStringLiteral( "fieldcalc" ) );
159 
160  mInfoIcon->setPixmap( style()->standardPixmap( QStyle::SP_MessageBoxInformation ) );
161 
162  setWindowTitle( tr( "%1 — Field Calculator" ).arg( mVectorLayer->name() ) );
163 
164  setOkButtonState();
165 }
166 
168 {
169  builder->expressionTree()->saveToRecent( builder->expressionText(), QStringLiteral( "fieldcalc" ) );
170 
171  if ( !mVectorLayer )
172  return;
173 
174  // Set up QgsDistanceArea each time we (re-)calculate
175  QgsDistanceArea myDa;
176 
177  myDa.setSourceCrs( mVectorLayer->crs(), QgsProject::instance()->transformContext() );
178  myDa.setEllipsoid( QgsProject::instance()->ellipsoid() );
179 
180  const QString calcString = builder->expressionText();
181  QgsExpression exp( calcString );
182  exp.setGeomCalculator( &myDa );
183  exp.setDistanceUnits( QgsProject::instance()->distanceUnits() );
184  exp.setAreaUnits( QgsProject::instance()->areaUnits() );
185 
187 
188  if ( !exp.prepare( &expContext ) )
189  {
190  QMessageBox::critical( nullptr, tr( "Evaluation Error" ), exp.evalErrorString() );
191  return;
192  }
193 
194  bool updatingGeom = false;
195 
196  // Test for creating expression field based on ! mUpdateExistingGroupBox checked rather
197  // than on mNewFieldGroupBox checked, as if the provider does not support adding attributes
198  // then mUpdateExistingGroupBox is set to not checkable, and hence is not checked. This
199  // is a minimum fix to resolve this - better would be some GUI redesign...
200  if ( ! mUpdateExistingGroupBox->isChecked() && mCreateVirtualFieldCheckbox->isChecked() )
201  {
202  mVectorLayer->addExpressionField( calcString, fieldDefinition() );
203  }
204  else
205  {
206  if ( !mVectorLayer->isEditable() )
207  mVectorLayer->startEditing();
208 
209  QgsTemporaryCursorOverride cursorOverride( Qt::WaitCursor );
210 
211  mVectorLayer->beginEditCommand( QStringLiteral( "Field calculator" ) );
212 
213  //update existing field
214  if ( mUpdateExistingGroupBox->isChecked() || !mNewFieldGroupBox->isEnabled() )
215  {
216  if ( mExistingFieldComboBox->currentData().toString() == QLatin1String( "geom" ) )
217  {
218  //update geometry
219  mAttributeId = -1;
220  updatingGeom = true;
221  }
222  else
223  {
224  bool ok = false;
225  const int id = mExistingFieldComboBox->currentData().toInt( &ok );
226  if ( ok )
227  mAttributeId = id;
228  }
229  }
230  else
231  {
232  //create new field
233  const QgsField newField = fieldDefinition();
234 
235  if ( !mVectorLayer->addAttribute( newField ) )
236  {
237  cursorOverride.release();
238  QMessageBox::critical( nullptr, tr( "Create New Field" ), tr( "Could not add the new field to the provider." ) );
239  mVectorLayer->destroyEditCommand();
240  return;
241  }
242 
243  //get index of the new field
244  const QgsFields &fields = mVectorLayer->fields();
245 
246  for ( int idx = 0; idx < fields.count(); ++idx )
247  {
248  if ( fields.at( idx ).name() == mOutputFieldNameLineEdit->text() )
249  {
250  mAttributeId = idx;
251  break;
252  }
253  }
254 
255  //update expression context with new fields
256  expContext.setFields( mVectorLayer->fields() );
257  if ( ! exp.prepare( &expContext ) )
258  {
259  cursorOverride.release();
260  QMessageBox::critical( nullptr, tr( "Evaluation Error" ), exp.evalErrorString() );
261  return;
262  }
263  }
264 
265  if ( mAttributeId == -1 && !updatingGeom )
266  {
267  mVectorLayer->destroyEditCommand();
268  return;
269  }
270 
271  //go through all the features and change the new attribute
272  QgsFeature feature;
273  bool calculationSuccess = true;
274  QString error;
275 
276  const bool useGeometry = exp.needsGeometry();
277  int rownum = 1;
278 
279  const QgsField field = !updatingGeom ? mVectorLayer->fields().at( mAttributeId ) : QgsField();
280 
281  const bool newField = !mUpdateExistingGroupBox->isChecked();
282  QVariant emptyAttribute;
283  if ( newField )
284  emptyAttribute = QVariant( field.type() );
285 
287  QSet< QString > referencedColumns = exp.referencedColumns();
288  referencedColumns.insert( field.name() ); // need existing column value to store old attribute when changing field values
289  req.setSubsetOfAttributes( referencedColumns, mVectorLayer->fields() );
290  if ( mOnlyUpdateSelectedCheckBox->isChecked() )
291  {
292  req.setFilterFids( mVectorLayer->selectedFeatureIds() );
293  }
294  QgsFeatureIterator fit = mVectorLayer->getFeatures( req );
295 
296  std::unique_ptr< QgsScopedProxyProgressTask > task = std::make_unique< QgsScopedProxyProgressTask >( tr( "Calculating field" ) );
297  const long long count = mOnlyUpdateSelectedCheckBox->isChecked() ? mVectorLayer->selectedFeatureCount() : mVectorLayer->featureCount();
298  long long i = 0;
299  while ( fit.nextFeature( feature ) )
300  {
301  i++;
302  task->setProgress( i / static_cast< double >( count ) * 100 );
303 
304  expContext.setFeature( feature );
305  expContext.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "row_number" ), rownum, true ) );
306 
307  QVariant value = exp.evaluate( &expContext );
308  if ( exp.hasEvalError() )
309  {
310  calculationSuccess = false;
311  error = exp.evalErrorString();
312  break;
313  }
314  else if ( updatingGeom )
315  {
316  if ( value.canConvert< QgsGeometry >() )
317  {
318  QgsGeometry geom = value.value< QgsGeometry >();
319  mVectorLayer->changeGeometry( feature.id(), geom );
320  }
321  }
322  else
323  {
324  ( void )field.convertCompatible( value );
325  mVectorLayer->changeAttributeValue( feature.id(), mAttributeId, value, newField ? emptyAttribute : feature.attributes().value( mAttributeId ) );
326  }
327 
328  rownum++;
329  }
330 
331  if ( !calculationSuccess )
332  {
333  cursorOverride.release();
334  task.reset();
335  QMessageBox::critical( nullptr, tr( "Evaluation Error" ), tr( "An error occurred while evaluating the calculation string:\n%1" ).arg( error ) );
336  mVectorLayer->destroyEditCommand();
337  return;
338  }
339 
340  mVectorLayer->endEditCommand();
341  }
342  QDialog::accept();
343 }
344 
345 void QgsFieldCalculator::populateOutputFieldTypes()
346 {
347  if ( !mVectorLayer )
348  {
349  return;
350  }
351 
352  QgsVectorDataProvider *provider = mVectorLayer->dataProvider();
353  if ( !provider )
354  {
355  return;
356  }
357 
358  const int oldDataType = mOutputFieldTypeComboBox->currentData( Qt::UserRole + FTC_TYPE_ROLE_IDX ).toInt();
359 
360  mOutputFieldTypeComboBox->blockSignals( true );
361 
362  // Standard subset of fields in case of virtual
363  const QList< QgsVectorDataProvider::NativeType > &typelist = mCreateVirtualFieldCheckbox->isChecked() ?
364  ( QList< QgsVectorDataProvider::NativeType >()
365  << QgsVectorDataProvider::NativeType( tr( "Whole number (integer)" ), QStringLiteral( "integer" ), QVariant::Int, 0, 10 )
366  << QgsVectorDataProvider::NativeType( tr( "Decimal number (double)" ), QStringLiteral( "double precision" ), QVariant::Double, -1, -1, -1, -1 )
367  << QgsVectorDataProvider::NativeType( tr( "Text (string)" ), QStringLiteral( "string" ), QVariant::String )
368  // date time
369  << QgsVectorDataProvider::NativeType( tr( "Date" ), QStringLiteral( "date" ), QVariant::Date, -1, -1, -1, -1 )
370  << QgsVectorDataProvider::NativeType( tr( "Time" ), QStringLiteral( "time" ), QVariant::Time, -1, -1, -1, -1 )
371  << QgsVectorDataProvider::NativeType( tr( "Date & Time" ), QStringLiteral( "datetime" ), QVariant::DateTime, -1, -1, -1, -1 )
372  // string types
373  << QgsVectorDataProvider::NativeType( tr( "Text, unlimited length (text)" ), QStringLiteral( "text" ), QVariant::String, -1, -1, -1, -1 )
374  // boolean
375  << QgsVectorDataProvider::NativeType( tr( "Boolean" ), QStringLiteral( "bool" ), QVariant::Bool )
376  // blob
377  << QgsVectorDataProvider::NativeType( tr( "Binary object (BLOB)" ), QStringLiteral( "binary" ), QVariant::ByteArray ) ) :
378  provider->nativeTypes();
379 
380  mOutputFieldTypeComboBox->clear();
381  for ( int i = 0; i < typelist.size(); i++ )
382  {
383  mOutputFieldTypeComboBox->addItem( typelist[i].mTypeDesc );
384  mOutputFieldTypeComboBox->setItemData( i, static_cast<int>( typelist[i].mType ), Qt::UserRole + FTC_TYPE_ROLE_IDX );
385  mOutputFieldTypeComboBox->setItemData( i, typelist[i].mTypeName, Qt::UserRole + FTC_TYPE_NAME_IDX );
386  mOutputFieldTypeComboBox->setItemData( i, typelist[i].mMinLen, Qt::UserRole + FTC_MINLEN_IDX );
387  mOutputFieldTypeComboBox->setItemData( i, typelist[i].mMaxLen, Qt::UserRole + FTC_MAXLEN_IDX );
388  mOutputFieldTypeComboBox->setItemData( i, typelist[i].mMinPrec, Qt::UserRole + FTC_MINPREC_IDX );
389  mOutputFieldTypeComboBox->setItemData( i, typelist[i].mMaxPrec, Qt::UserRole + FTC_MAXPREC_IDX );
390  mOutputFieldTypeComboBox->setItemData( i, static_cast<int>( typelist[i].mSubType ), Qt::UserRole + FTC_SUBTYPE_IDX );
391  }
392  mOutputFieldTypeComboBox->blockSignals( false );
393 
394  const int idx = mOutputFieldTypeComboBox->findData( oldDataType, Qt::UserRole + FTC_TYPE_ROLE_IDX );
395  if ( idx != -1 )
396  {
397  mOutputFieldTypeComboBox->setCurrentIndex( idx );
399  }
400  else
401  {
402  mOutputFieldTypeComboBox->setCurrentIndex( 0 );
404  }
405 }
406 
408 {
409  mUpdateExistingGroupBox->setChecked( !on );
410  if ( on && !mCanAddAttribute )
411  {
412  mOnlyVirtualFieldsInfoLabel->setVisible( true );
413  }
414  else
415  {
416  mOnlyVirtualFieldsInfoLabel->setVisible( false );
417  }
418 
419  if ( ( mNewFieldGroupBox->isChecked() && mCreateVirtualFieldCheckbox->isChecked() ) || mVectorLayer->isEditable() )
420  {
421  mEditModeAutoTurnOnLabel->setVisible( false );
422  }
423  else
424  {
425  mEditModeAutoTurnOnLabel->setVisible( true );
426  }
427 
428  mInfoIcon->setVisible( mOnlyVirtualFieldsInfoLabel->isVisible() || mEditModeAutoTurnOnLabel->isVisible() );
429 }
430 
432 {
433  mNewFieldGroupBox->setChecked( !on );
434  setOkButtonState();
435 
436  if ( on )
437  {
438  mOnlyUpdateSelectedCheckBox->setEnabled( mVectorLayer->selectedFeatureCount() > 0 );
439  }
440  else
441  {
442  mCreateVirtualFieldCheckbox_stateChanged( mCreateVirtualFieldCheckbox->checkState() );
443  }
444 }
445 
447 {
448  mOnlyUpdateSelectedCheckBox->setChecked( false );
449  mOnlyUpdateSelectedCheckBox->setEnabled( state != Qt::Checked && mVectorLayer->selectedFeatureCount() > 0 );
450 
451  if ( ( mNewFieldGroupBox->isChecked() && mCreateVirtualFieldCheckbox->isChecked() ) || mVectorLayer->isEditable() )
452  {
453  mEditModeAutoTurnOnLabel->setVisible( false );
454  }
455  else
456  {
457  mEditModeAutoTurnOnLabel->setVisible( true );
458  }
459  populateOutputFieldTypes();
460  mInfoIcon->setVisible( mOnlyVirtualFieldsInfoLabel->isVisible() || mEditModeAutoTurnOnLabel->isVisible() );
461 }
462 
463 
465 {
466  Q_UNUSED( text )
467  setOkButtonState();
468 }
469 
470 
472 {
473  mOutputFieldWidthSpinBox->setMinimum( mOutputFieldTypeComboBox->itemData( index, Qt::UserRole + FTC_MINLEN_IDX ).toInt() );
474  mOutputFieldWidthSpinBox->setMaximum( mOutputFieldTypeComboBox->itemData( index, Qt::UserRole + FTC_MAXLEN_IDX ).toInt() );
475  mOutputFieldWidthSpinBox->setEnabled( mOutputFieldWidthSpinBox->minimum() < mOutputFieldWidthSpinBox->maximum() );
476  if ( mOutputFieldWidthSpinBox->value() < mOutputFieldWidthSpinBox->minimum() )
477  mOutputFieldWidthSpinBox->setValue( mOutputFieldWidthSpinBox->minimum() );
478  if ( mOutputFieldWidthSpinBox->value() > mOutputFieldWidthSpinBox->maximum() )
479  mOutputFieldWidthSpinBox->setValue( mOutputFieldWidthSpinBox->maximum() );
480 
481  setPrecisionMinMax();
482 }
483 
484 void QgsFieldCalculator::populateFields()
485 {
486  if ( !mVectorLayer )
487  return;
488 
489  const QgsFields &fields = mVectorLayer->fields();
490  for ( int idx = 0; idx < fields.count(); ++idx )
491  {
492  switch ( fields.fieldOrigin( idx ) )
493  {
496 
497  continue; // can't be edited
498 
501  break; // can always be edited
502 
504  {
505  // show joined fields (e.g. auxiliary fields) only if they have a non-hidden editor widget.
506  // This enables them to be bulk field-calculated when a user needs to, but hides them by default
507  // (since there's often MANY of these, e.g. after using the label properties tool on a layer)
508  if ( fields.at( idx ).editorWidgetSetup().type() == QLatin1String( "Hidden" ) )
509  continue;
510 
511  // only show editable joins
512  int srcFieldIndex;
513  const QgsVectorLayerJoinInfo *info = mVectorLayer->joinBuffer()->joinForFieldIndex( idx, fields, srcFieldIndex );
514 
515  if ( !info || !info->isEditable() )
516  continue; // join is not editable
517 
518  break;
519  }
520  }
521 
522  const QString fieldName = fields.at( idx ).name();
523 
524  //insert into field combo box
525  mExistingFieldComboBox->addItem( fields.iconForField( idx ), fieldName, idx );
526  }
527 
528  if ( mVectorLayer->geometryType() != QgsWkbTypes::NullGeometry )
529  {
530  mExistingFieldComboBox->addItem( tr( "<geometry>" ), "geom" );
531 
532  QFont font = mExistingFieldComboBox->itemData( mExistingFieldComboBox->count() - 1, Qt::FontRole ).value<QFont>();
533  font.setItalic( true );
534  mExistingFieldComboBox->setItemData( mExistingFieldComboBox->count() - 1, font, Qt::FontRole );
535  }
536  mExistingFieldComboBox->setCurrentIndex( -1 );
537 }
538 
539 void QgsFieldCalculator::setOkButtonState()
540 {
541  QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
542 
543  if ( ( mNewFieldGroupBox->isChecked() || !mUpdateExistingGroupBox->isEnabled() )
544  && mOutputFieldNameLineEdit->text().isEmpty() )
545  {
546  okButton->setToolTip( tr( "Please enter a field name" ) );
547  okButton->setEnabled( false );
548  return;
549  }
550 
551  if ( !builder->isExpressionValid() )
552  {
553  okButton->setToolTip( okButton->toolTip() + tr( "\n The expression is invalid see (more info) for details" ) );
554  okButton->setEnabled( false );
555  return;
556  }
557 
558  okButton->setToolTip( QString() );
559  okButton->setEnabled( true );
560 }
561 
562 void QgsFieldCalculator::setPrecisionMinMax()
563 {
564  const int idx = mOutputFieldTypeComboBox->currentIndex();
565  const int minPrecType = mOutputFieldTypeComboBox->itemData( idx, Qt::UserRole + FTC_MINPREC_IDX ).toInt();
566  const int maxPrecType = mOutputFieldTypeComboBox->itemData( idx, Qt::UserRole + FTC_MAXPREC_IDX ).toInt();
567  const bool precisionIsEnabled = minPrecType < maxPrecType;
568  mOutputFieldPrecisionSpinBox->setEnabled( precisionIsEnabled );
569  // Do not set min/max if it's disabled or we'll loose the default value,
570  // see https://github.com/qgis/QGIS/issues/26880 - QGIS saves integer field when
571  // I create a new real field through field calculator (Update field works as intended)
572  if ( precisionIsEnabled )
573  {
574  mOutputFieldPrecisionSpinBox->setMinimum( minPrecType );
575  mOutputFieldPrecisionSpinBox->setMaximum( std::max( minPrecType, std::min( maxPrecType, mOutputFieldWidthSpinBox->value() ) ) );
576  }
577 }
578 
579 void QgsFieldCalculator::showHelp()
580 {
581  QgsHelp::openHelp( QStringLiteral( "working_with_vector/attribute_table.html#editing-attribute-values" ) );
582 }
583 
584 QgsField QgsFieldCalculator::fieldDefinition()
585 {
586  return QgsField( mOutputFieldNameLineEdit->text(),
587  static_cast< QVariant::Type >( mOutputFieldTypeComboBox->currentData( Qt::UserRole + FTC_TYPE_ROLE_IDX ).toInt() ),
588  mOutputFieldTypeComboBox->currentData( Qt::UserRole + FTC_TYPE_NAME_IDX ).toString(),
589  mOutputFieldWidthSpinBox->value(),
590  mOutputFieldPrecisionSpinBox->isEnabled() ? mOutputFieldPrecisionSpinBox->value() : 0,
591  QString(),
592  static_cast< QVariant::Type >( mOutputFieldTypeComboBox->currentData( Qt::UserRole + FTC_SUBTYPE_IDX ).toInt() )
593  );
594 }
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)
void mOutputFieldNameLineEdit_textChanged(const QString &text)
void mCreateVirtualFieldCheckbox_stateChanged(int state)
void mOutputFieldTypeComboBox_activated(int index)
void mNewFieldGroupBox_toggled(bool on)
void mUpdateExistingGroupBox_toggled(bool on)
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:384
QVariant::Type type
Definition: qgsfield.h:58
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:572
Container of fields for a vector layer.
Definition: qgsfields.h:45
@ 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:168
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:467
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.
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.