QGIS API Documentation 3.39.0-Master (be2050b798e)
Loading...
Searching...
No Matches
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 "qgsproject.h"
25#include "qgsvectorlayer.h"
27#include "qgsgeometry.h"
28#include "qgsgui.h"
29#include "qgsguiutils.h"
33#include "qgsvariantutils.h"
34#include "qgsfields.h"
35
36
37// FTC = FieldTypeCombo
38constexpr int FTC_TYPE_ROLE_IDX = 0;
39constexpr int FTC_TYPE_NAME_IDX = 1;
40constexpr int FTC_MINLEN_IDX = 2;
41constexpr int FTC_MAXLEN_IDX = 3;
42constexpr int FTC_MINPREC_IDX = 4;
43constexpr int FTC_MAXPREC_IDX = 5;
44constexpr 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 Qgis::VectorProviderCapabilities caps = dataProvider->capabilities();
67 mCanAddAttribute = caps & Qgis::VectorProviderCapability::AddAttributes;
68 mCanChangeAttributeValue = caps & Qgis::VectorProviderCapability::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 %n selected feature(s)", nullptr, 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 = QgsVariantUtils::createNullVariant( 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.userType() == qMetaTypeId< 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
345void 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( QgsVariantUtils::typeToDisplayString( QMetaType::Type::Int ), QStringLiteral( "integer" ), QMetaType::Type::Int, 0, 10 )
366 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::Double ), QStringLiteral( "double precision" ), QMetaType::Type::Double, -1, -1, -1, -1 )
367 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QString ), QStringLiteral( "string" ), QMetaType::Type::QString )
368 // date time
369 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QDate ), QStringLiteral( "date" ), QMetaType::Type::QDate, -1, -1, -1, -1 )
370 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QTime ), QStringLiteral( "time" ), QMetaType::Type::QTime, -1, -1, -1, -1 )
371 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QDateTime ), QStringLiteral( "datetime" ), QMetaType::Type::QDateTime, -1, -1, -1, -1 )
372 // string types
373 << QgsVectorDataProvider::NativeType( tr( "Text, unlimited length (text)" ), QStringLiteral( "text" ), QMetaType::Type::QString, -1, -1, -1, -1 )
374 // boolean
375 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::Bool ), QStringLiteral( "bool" ), QMetaType::Type::Bool )
376 // blob
377 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QByteArray ), QStringLiteral( "binary" ), QMetaType::Type::QByteArray ) ) :
378 provider->nativeTypes();
379
380 mOutputFieldTypeComboBox->clear();
381 for ( int i = 0; i < typelist.size(); i++ )
382 {
383 mOutputFieldTypeComboBox->addItem( QgsFields::iconForFieldType( typelist[i].mType, typelist[i].mSubType, typelist[i].mTypeName ), 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 );
398 mOutputFieldTypeComboBox_activated( idx );
399 }
400 else
401 {
402 mOutputFieldTypeComboBox->setCurrentIndex( 0 );
403 mOutputFieldTypeComboBox_activated( 0 );
404 }
405}
406
407void QgsFieldCalculator::mNewFieldGroupBox_toggled( bool on )
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
431void QgsFieldCalculator::mUpdateExistingGroupBox_toggled( bool on )
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
446void QgsFieldCalculator::mCreateVirtualFieldCheckbox_stateChanged( int state )
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
464void QgsFieldCalculator::mOutputFieldNameLineEdit_textChanged( const QString &text )
465{
466 Q_UNUSED( text )
467 setOkButtonState();
468}
469
470
471void QgsFieldCalculator::mOutputFieldTypeComboBox_activated( int index )
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
484void 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() != Qgis::GeometryType::Null )
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
539void 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
562void 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
579void QgsFieldCalculator::showHelp()
580{
581 QgsHelp::openHelp( QStringLiteral( "working_with_vector/attribute_table.html#editing-attribute-values" ) );
582}
583
584QgsField QgsFieldCalculator::fieldDefinition()
585{
586 return QgsField( mOutputFieldNameLineEdit->text(),
587 static_cast< QMetaType::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< QMetaType::Type >( mOutputFieldTypeComboBox->currentData( Qt::UserRole + FTC_SUBTYPE_IDX ).toInt() )
593 );
594}
@ AddAttributes
Allows addition of new attributes (fields)
@ ChangeAttributeValues
Allows modification of attribute values.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
@ NoFlags
No flags are set.
@ Null
No geometry.
QFlags< VectorProviderCapability > VectorProviderCapabilities
Vector data provider capabilities.
Definition qgis.h:426
@ Provider
Field originates from the underlying data provider of the vector layer.
@ Edit
Field has been temporarily added in editing mode.
@ Unknown
The field origin has not been specified.
@ Expression
Field is calculated from an expression.
@ Join
Field originates from a joined layer.
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.
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
void setDistanceUnits(Qgis::DistanceUnit unit)
Sets the desired distance units for calculations involving geomCalculator(), e.g.,...
void setAreaUnits(Qgis::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)
Fetch next feature and stores in f, returns true on success.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets the feature IDs that should be fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsAttributes attributes
Definition qgsfeature.h:67
QgsFeatureId id
Definition qgsfeature.h:66
QgsFieldCalculator(QgsVectorLayer *vl, QWidget *parent=nullptr)
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
QMetaType::Type type
Definition qgsfield.h:60
QString name
Definition qgsfield.h:62
bool convertCompatible(QVariant &v, QString *errorMessage=nullptr) const
Converts the provided variant to a compatible format.
Definition qgsfield.cpp:472
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:739
Container of fields for a vector layer.
Definition qgsfields.h:46
int count
Definition qgsfields.h:50
static QIcon iconForFieldType(QMetaType::Type type, QMetaType::Type subType=QMetaType::Type::UnknownType, const QString &typeString=QString())
Returns an icon corresponding to a field type.
Qgis::FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
QIcon iconForField(int fieldIdx, bool considerOrigin=false) const
Returns an icon corresponding to a field index, based on the field's type and source.
A geometry is the spatial representation of a feature.
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:200
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:39
QString name
Definition qgsmaplayer.h:79
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:82
static QgsProject * instance()
Returns the QgsProject singleton instance.
QgsCoordinateTransformContext transformContext
Definition qgsproject.h:113
Temporarily sets a cursor override for the QApplication for the lifetime of the object.
void release()
Releases the cursor override early (i.e.
static QString typeToDisplayString(QMetaType::Type type, QMetaType::Type subType=QMetaType::Type::UnknownType)
Returns a user-friendly translated string representing a QVariant type.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
This is the base class for vector data providers.
virtual Q_INVOKABLE Qgis::VectorProviderCapabilities 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 bool startEditing()
Makes the layer editable.
Q_INVOKABLE bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant(), bool skipDefaultValues=false, QgsVectorLayerToolsContext *context=nullptr)
Changes an attribute value for a feature (but does not immediately commit the changes).
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.
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.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
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...
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.