QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 }
QgsFields::OriginProvider
@ OriginProvider
Field comes from the underlying data provider of the vector layer (originIndex = index in provider's ...
Definition: qgsfields.h:51
QgsFeatureRequest::NoGeometry
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition: qgsfeaturerequest.h:115
QgsVectorLayer::getFeatures
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Definition: qgsvectorlayer.cpp:1052
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:406
QgsMapLayer::crs
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
FTC_MINPREC_IDX
constexpr int FTC_MINPREC_IDX
Definition: qgsfieldcalculator.cpp:44
qgsfields.h
qgsexpressioncontextutils.h
QgsFields::iconForField
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
qgsproxyprogresstask.h
QgsFields::OriginEdit
@ OriginEdit
Field has been temporarily added in editing mode (originIndex = index in the list of added attributes...
Definition: qgsfields.h:53
qgsfieldcalculator.h
QgsExpression::setDistanceUnits
void setDistanceUnits(QgsUnitTypes::DistanceUnit unit)
Sets the desired distance units for calculations involving geomCalculator(), e.g.,...
Definition: qgsexpression.cpp:419
QgsVectorLayer::dataProvider
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
Definition: qgsvectorlayer.cpp:676
QgsExpression::evalErrorString
QString evalErrorString() const
Returns evaluation error.
Definition: qgsexpression.cpp:383
qgsmapcanvas.h
QgsWkbTypes::NullGeometry
@ NullGeometry
Definition: qgswkbtypes.h:146
QgsExpressionContextScope::addVariable
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
Definition: qgsexpressioncontext.cpp:97
QgsExpression::referencedColumns
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
Definition: qgsexpression.cpp:221
qgsexpression.h
QgsVectorLayer::featureCount
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
Definition: qgsvectorlayer.cpp:812
qgsgui.h
FTC_MINLEN_IDX
constexpr int FTC_MINLEN_IDX
Definition: qgsfieldcalculator.cpp:42
QgsExpressionContext::lastScope
QgsExpressionContextScope * lastScope()
Returns the last scope added to the context.
Definition: qgsexpressioncontext.cpp:377
QgsVectorLayer::addExpressionField
int addExpressionField(const QString &exp, const QgsField &fld)
Add a new field which is calculated by the expression specified.
Definition: qgsvectorlayer.cpp:3926
qgsfeatureiterator.h
QgsFields::count
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsFields
Container of fields for a vector layer.
Definition: qgsfields.h:44
QgsProject::transformContext
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:110
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:480
QgsVectorLayerJoinBuffer::joinForFieldIndex
const QgsVectorLayerJoinInfo * joinForFieldIndex(int index, const QgsFields &fields, int &sourceFieldIndex) const
Finds the vector join for a layer field index.
Definition: qgsvectorlayerjoinbuffer.cpp:425
QgsVectorDataProvider::AddAttributes
@ AddAttributes
Allows addition of new attributes (fields)
Definition: qgsvectordataprovider.h:78
QgsExpressionContextUtils::globalProjectLayerScopes
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Definition: qgsexpressioncontextutils.cpp:377
QgsExpressionContext::setFields
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
Definition: qgsexpressioncontext.cpp:587
QgsVectorLayer::startEditing
Q_INVOKABLE bool startEditing()
Makes the layer editable.
Definition: qgsvectorlayer.cpp:1491
QgsFeatureRequest::setSubsetOfAttributes
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
Definition: qgsfeaturerequest.cpp:228
field
const QgsField & field
Definition: qgsfield.h:463
QgsVectorDataProvider::NativeType
Definition: qgsvectordataprovider.h:471
FTC_MAXPREC_IDX
constexpr int FTC_MAXPREC_IDX
Definition: qgsfieldcalculator.cpp:45
QgsField::name
QString name
Definition: qgsfield.h:60
QgsVectorLayer::beginEditCommand
void beginEditCommand(const QString &text)
Create edit command for undo/redo operations.
Definition: qgsvectorlayer.cpp:3852
FTC_TYPE_NAME_IDX
constexpr int FTC_TYPE_NAME_IDX
Definition: qgsfieldcalculator.cpp:41
QgsVectorLayer::changeAttributeValue
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).
Definition: qgsvectorlayer.cpp:3072
QgsVectorLayer::isEditable
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
Definition: qgsvectorlayer.cpp:3728
QgsExpression::setAreaUnits
void setAreaUnits(QgsUnitTypes::AreaUnit unit)
Sets the desired areal units for calculations involving geomCalculator(), e.g., "$area".
Definition: qgsexpression.cpp:429
QgsMapLayer::providerType
QString providerType() const
Returns the provider type (provider key) for this layer.
Definition: qgsmaplayer.cpp:1864
QgsExpression::setGeomCalculator
void setGeomCalculator(const QgsDistanceArea *calc)
Sets the geometry calculator used for distance and area calculations in expressions.
Definition: qgsexpression.cpp:318
QgsGui::enableAutoGeometryRestore
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:180
QgsVectorLayer::fields
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Definition: qgsvectorlayer.cpp:3436
QgsVectorLayer::changeGeometry
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...
Definition: qgsvectorlayer.cpp:3048
QgsFeature::id
QgsFeatureId id
Definition: qgsfeature.h:68
QgsEditorWidgetSetup::type
QString type() const
Definition: qgseditorwidgetsetup.h:59
QgsDistanceArea::setEllipsoid
bool setEllipsoid(const QString &ellipsoid)
Sets the ellipsoid by its acronym.
Definition: qgsdistancearea.cpp:89
QgsFeatureRequest
This class wraps a request for features to a vector layer (or directly its vector data provider).
Definition: qgsfeaturerequest.h:83
QgsVectorDataProvider::nativeTypes
QList< QgsVectorDataProvider::NativeType > nativeTypes() const
Returns the names of the supported types.
Definition: qgsvectordataprovider.cpp:371
QgsVectorDataProvider::capabilities
virtual Q_INVOKABLE QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
Definition: qgsvectordataprovider.cpp:208
QgsExpression::hasEvalError
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
Definition: qgsexpression.cpp:378
QgsFeatureRequest::NoFlags
@ NoFlags
Definition: qgsfeaturerequest.h:114
qgsexpressioncontext.h
QgsVectorLayer::endEditCommand
void endEditCommand()
Finish edit command and add it to undo/redo stack.
Definition: qgsvectorlayer.cpp:3868
qgsvectorlayerjoinbuffer.h
QgsFieldCalculator::accept
void accept() override
Definition: qgsfieldcalculator.cpp:169
QgsFields::OriginUnknown
@ OriginUnknown
It has not been specified where the field comes from.
Definition: qgsfields.h:50
QgsVectorLayer::selectedFeatureIds
const Q_INVOKABLE QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
Definition: qgsvectorlayer.cpp:3621
QgsFieldCalculator::QgsFieldCalculator
QgsFieldCalculator(QgsVectorLayer *vl, QWidget *parent=nullptr)
Definition: qgsfieldcalculator.cpp:48
QgsFields::fieldOrigin
FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
Definition: qgsfields.cpp:189
qgsvectordataprovider.h
qgsvariantutils.h
QgsVectorLayerJoinInfo::isEditable
bool isEditable() const
Returns whether joined fields may be edited through the form of the target layer.
Definition: qgsvectorlayerjoininfo.h:108
QgsFeatureRequest::setFilterFids
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets the feature IDs that should be fetched.
Definition: qgsfeaturerequest.cpp:148
QgsVectorLayerJoinInfo
Defines left outer join from our vector layer to some other vector layer. The join is done based on [...
Definition: qgsvectorlayerjoininfo.h:33
QgsDistanceArea::setSourceCrs
void setSourceCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets source spatial reference system crs.
Definition: qgsdistancearea.cpp:83
FTC_MAXLEN_IDX
constexpr int FTC_MAXLEN_IDX
Definition: qgsfieldcalculator.cpp:43
QgsExpression::prepare
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
Definition: qgsexpression.cpp:327
QgsFeature::attributes
QgsAttributes attributes
Definition: qgsfeature.h:69
FTC_SUBTYPE_IDX
constexpr int FTC_SUBTYPE_IDX
Definition: qgsfieldcalculator.cpp:46
QgsExpression::evaluate
QVariant evaluate()
Evaluate the feature and return the result.
Definition: qgsexpression.cpp:350
QgsFields::iconForFieldType
static QIcon iconForFieldType(QVariant::Type type, QVariant::Type subType=QVariant::Type::Invalid)
Returns an icon corresponding to a field type.
Definition: qgsfields.cpp:294
qgsvectorlayer.h
QgsVariantUtils::typeToDisplayString
static QString typeToDisplayString(QVariant::Type type, QVariant::Type subType=QVariant::Type::Invalid)
Returns a user-friendly translated string representing a QVariant type.
Definition: qgsvariantutils.cpp:19
QgsTemporaryCursorOverride::release
void release()
Releases the cursor override early (i.e.
Definition: qgsguiutils.cpp:348
qgsgeometry.h
QgsFields::OriginExpression
@ OriginExpression
Field is calculated from an expression.
Definition: qgsfields.h:54
QgsFeatureIterator::nextFeature
bool nextFeature(QgsFeature &f)
Definition: qgsfeatureiterator.h:399
QgsGeometry
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
QgsVectorLayer::destroyEditCommand
void destroyEditCommand()
Destroy active command and reverts all changes in it.
Definition: qgsvectorlayer.cpp:3888
QgsHelp::openHelp
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition: qgshelp.cpp:36
QgsVectorDataProvider::ChangeAttributeValues
@ ChangeAttributeValues
Allows modification of attribute values.
Definition: qgsvectordataprovider.h:77
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
FTC_TYPE_ROLE_IDX
constexpr int FTC_TYPE_ROLE_IDX
Definition: qgsfieldcalculator.cpp:40
qgssettings.h
QgsMapLayer::name
QString name
Definition: qgsmaplayer.h:76
QgsExpression::needsGeometry
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
Definition: qgsexpression.cpp:270
QgsExpressionContext::setHighlightedVariables
void setHighlightedVariables(const QStringList &variableNames)
Sets the list of variable names within the context intended to be highlighted to the user.
Definition: qgsexpressioncontext.cpp:328
QgsVectorLayer::storageType
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
Definition: qgsvectorlayer.cpp:369
QgsDistanceArea
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
Definition: qgsdistancearea.h:52
QgsVectorDataProvider
This is the base class for vector data providers.
Definition: qgsvectordataprovider.h:58
qgsdistancearea.h
QgsFeature
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:55
QgsVectorLayer::selectedFeatureCount
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.
Definition: qgsvectorlayer.cpp:3616
qgsguiutils.h
QgsExpressionContextScope::StaticVariable
Single variable definition for use within a QgsExpressionContextScope.
Definition: qgsexpressioncontext.h:120
QgsField::editorWidgetSetup
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:602
QgsFields::at
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
QgsExpression
Class for parsing and evaluation of expressions (formerly called "search strings")....
Definition: qgsexpression.h:102
QgsFeatureIterator
Wrapper for iterator of features from vector data provider or vector layer.
Definition: qgsfeatureiterator.h:289
QgsVectorLayer::geometryType
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
Definition: qgsvectorlayer.cpp:720
QgsFeatureRequest::setFlags
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
Definition: qgsfeaturerequest.cpp:222
qgsproject.h
QgsField::convertCompatible
bool convertCompatible(QVariant &v, QString *errorMessage=nullptr) const
Converts the provided variant to a compatible format.
Definition: qgsfield.cpp:402
QgsField::type
QVariant::Type type
Definition: qgsfield.h:58
QgsFields::OriginJoin
@ OriginJoin
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
Definition: qgsfields.h:52
QgsTemporaryCursorOverride
Temporarily sets a cursor override for the QApplication for the lifetime of the object.
Definition: qgsguiutils.h:220
QgsVectorLayer::joinBuffer
QgsVectorLayerJoinBuffer * joinBuffer()
Returns the join buffer object.
Definition: qgsvectorlayer.h:663
QgsExpressionBuilderWidget::expressionParsed
void expressionParsed(bool isValid)
Emitted when the user changes the expression in the widget.
QgsExpressionContext::setFeature
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Definition: qgsexpressioncontext.cpp:525
QgsVectorLayer::addAttribute
bool addAttribute(const QgsField &field)
Add an attribute field (but does not commit it) returns true if the field was added.
Definition: qgsvectorlayer.cpp:3161
QgsField
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:50