QGIS API Documentation 3.27.0-Master (75dc696944)
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"
26#include "qgsvectorlayer.h"
28#include "qgsgeometry.h"
29#include "qgssettings.h"
30#include "qgsgui.h"
31#include "qgsguiutils.h"
35#include "qgsvariantutils.h"
36#include "qgsfields.h"
37
38
39// FTC = FieldTypeCombo
40constexpr int FTC_TYPE_ROLE_IDX = 0;
41constexpr int FTC_TYPE_NAME_IDX = 1;
42constexpr int FTC_MINLEN_IDX = 2;
43constexpr int FTC_MAXLEN_IDX = 3;
44constexpr int FTC_MINPREC_IDX = 4;
45constexpr int FTC_MAXPREC_IDX = 5;
46constexpr 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
347void 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
409void 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
433void 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
448void 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
466void QgsFieldCalculator::mOutputFieldNameLineEdit_textChanged( const QString &text )
467{
468 Q_UNUSED( text )
469 setOkButtonState();
470}
471
472
473void 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
486void 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
541void 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
564void 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
581void QgsFieldCalculator::showHelp()
582{
583 QgsHelp::openHelp( QStringLiteral( "working_with_vector/attribute_table.html#editing-attribute-values" ) );
584}
585
586QgsField 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:180
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:478
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:110
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.
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.
QgsVectorLayerJoinBuffer * joinBuffer()
Returns the join buffer object.
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.