QGIS API Documentation 3.39.0-Master (d85f3c2a281)
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#include "qgsmessagebar.h"
36
37
38// FTC = FieldTypeCombo
39constexpr int FTC_TYPE_ROLE_IDX = 0;
40constexpr int FTC_TYPE_NAME_IDX = 1;
41constexpr int FTC_MINLEN_IDX = 2;
42constexpr int FTC_MAXLEN_IDX = 3;
43constexpr int FTC_MINPREC_IDX = 4;
44constexpr int FTC_MAXPREC_IDX = 5;
45constexpr int FTC_SUBTYPE_IDX = 6;
46
48 : QDialog( parent )
49 , mVectorLayer( vl )
50 , mAttributeId( -1 )
51{
52 setupUi( this );
53 connect( mNewFieldGroupBox, &QGroupBox::toggled, this, &QgsFieldCalculator::mNewFieldGroupBox_toggled );
54 connect( mUpdateExistingGroupBox, &QGroupBox::toggled, this, &QgsFieldCalculator::mUpdateExistingGroupBox_toggled );
55 connect( mCreateVirtualFieldCheckbox, &QCheckBox::stateChanged, this, &QgsFieldCalculator::mCreateVirtualFieldCheckbox_stateChanged );
56 connect( mOutputFieldNameLineEdit, &QLineEdit::textChanged, this, &QgsFieldCalculator::mOutputFieldNameLineEdit_textChanged );
57 connect( mOutputFieldTypeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::activated ), this, &QgsFieldCalculator::mOutputFieldTypeComboBox_activated );
58
60
61 if ( !vl )
62 return;
63 QgsVectorDataProvider *dataProvider = vl->dataProvider();
64 if ( !dataProvider )
65 return;
66
67 const Qgis::VectorProviderCapabilities caps = dataProvider->capabilities();
68 mCanAddAttribute = caps & Qgis::VectorProviderCapability::AddAttributes;
69 mCanChangeAttributeValue = caps & Qgis::VectorProviderCapability::ChangeAttributeValues;
70
72
73 expContext.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "row_number" ), 1, true ) );
74 expContext.setHighlightedVariables( QStringList() << QStringLiteral( "row_number" ) );
75
76 populateFields();
77 populateOutputFieldTypes();
78
79 connect( builder, &QgsExpressionBuilderWidget::expressionParsed, this, &QgsFieldCalculator::setDialogButtonState );
80 connect( mOutputFieldWidthSpinBox, &QAbstractSpinBox::editingFinished, this, &QgsFieldCalculator::setPrecisionMinMax );
81 connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsFieldCalculator::showHelp );
82 connect( mButtonBox->button( QDialogButtonBox::Apply ), &QAbstractButton::clicked, this, &QgsFieldCalculator::calculate );
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 // Init the message bar instance
167 mMsgBar = new QgsMessageBar( this );
168 mMsgBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
169 this->vLayout->insertWidget( 0, mMsgBar );
170
171 setDialogButtonState();
172}
173
175{
176 calculate();
177 QDialog::accept();
178}
179
180void QgsFieldCalculator::calculate()
181{
182 builder->expressionTree()->saveToRecent( builder->expressionText(), QStringLiteral( "fieldcalc" ) );
183
184 if ( !mVectorLayer )
185 return;
186
187 // Set up QgsDistanceArea each time we (re-)calculate
188 QgsDistanceArea myDa;
189
190 myDa.setSourceCrs( mVectorLayer->crs(), QgsProject::instance()->transformContext() );
191 myDa.setEllipsoid( QgsProject::instance()->ellipsoid() );
192
193 const QString calcString = builder->expressionText();
194 QgsExpression exp( calcString );
195 exp.setGeomCalculator( &myDa );
196 exp.setDistanceUnits( QgsProject::instance()->distanceUnits() );
197 exp.setAreaUnits( QgsProject::instance()->areaUnits() );
198
200
201 if ( !exp.prepare( &expContext ) )
202 {
203 QMessageBox::critical( nullptr, tr( "Evaluation Error" ), exp.evalErrorString() );
204 return;
205 }
206
207 bool updatingGeom = false;
208
209 // Test for creating expression field based on ! mUpdateExistingGroupBox checked rather
210 // than on mNewFieldGroupBox checked, as if the provider does not support adding attributes
211 // then mUpdateExistingGroupBox is set to not checkable, and hence is not checked. This
212 // is a minimum fix to resolve this - better would be some GUI redesign...
213 if ( ! mUpdateExistingGroupBox->isChecked() && mCreateVirtualFieldCheckbox->isChecked() )
214 {
215 mVectorLayer->addExpressionField( calcString, fieldDefinition() );
216 }
217 else
218 {
219 if ( !mVectorLayer->isEditable() )
220 mVectorLayer->startEditing();
221
222 QgsTemporaryCursorOverride cursorOverride( Qt::WaitCursor );
223
224 mVectorLayer->beginEditCommand( QStringLiteral( "Field calculator" ) );
225
226 //update existing field
227 if ( mUpdateExistingGroupBox->isChecked() || !mNewFieldGroupBox->isEnabled() )
228 {
229 if ( mExistingFieldComboBox->currentData().toString() == QLatin1String( "geom" ) )
230 {
231 //update geometry
232 mAttributeId = -1;
233 updatingGeom = true;
234 }
235 else
236 {
237 bool ok = false;
238 const int id = mExistingFieldComboBox->currentData().toInt( &ok );
239 if ( ok )
240 mAttributeId = id;
241 }
242 }
243 else
244 {
245 //create new field
246 const QgsField newField = fieldDefinition();
247
248 if ( !mVectorLayer->addAttribute( newField ) )
249 {
250 cursorOverride.release();
251 QMessageBox::critical( nullptr, tr( "Create New Field" ), tr( "Could not add the new field to the provider." ) );
252 mVectorLayer->destroyEditCommand();
253 return;
254 }
255
256 //get index of the new field
257 const QgsFields &fields = mVectorLayer->fields();
258
259 for ( int idx = 0; idx < fields.count(); ++idx )
260 {
261 if ( fields.at( idx ).name() == mOutputFieldNameLineEdit->text() )
262 {
263 mAttributeId = idx;
264 break;
265 }
266 }
267
268 //update expression context with new fields
269 expContext.setFields( mVectorLayer->fields() );
270 if ( ! exp.prepare( &expContext ) )
271 {
272 cursorOverride.release();
273 QMessageBox::critical( nullptr, tr( "Evaluation Error" ), exp.evalErrorString() );
274 return;
275 }
276 }
277
278 if ( mAttributeId == -1 && !updatingGeom )
279 {
280 mVectorLayer->destroyEditCommand();
281 return;
282 }
283
284 //go through all the features and change the new attribute
285 QgsFeature feature;
286 bool calculationSuccess = true;
287 QString error;
288
289 const bool useGeometry = exp.needsGeometry();
290 int rownum = 1;
291
292 const QgsField field = !updatingGeom ? mVectorLayer->fields().at( mAttributeId ) : QgsField();
293
294 const bool newField = !mUpdateExistingGroupBox->isChecked();
295 QVariant emptyAttribute;
296 if ( newField )
297 emptyAttribute = QgsVariantUtils::createNullVariant( field.type() );
298
299 QgsFeatureRequest req = QgsFeatureRequest().setFlags( useGeometry ? Qgis::FeatureRequestFlag::NoFlags : Qgis::FeatureRequestFlag::NoGeometry );
300 QSet< QString > referencedColumns = exp.referencedColumns();
301 referencedColumns.insert( field.name() ); // need existing column value to store old attribute when changing field values
302 req.setSubsetOfAttributes( referencedColumns, mVectorLayer->fields() );
303 if ( mOnlyUpdateSelectedCheckBox->isChecked() )
304 {
305 req.setFilterFids( mVectorLayer->selectedFeatureIds() );
306 }
307 QgsFeatureIterator fit = mVectorLayer->getFeatures( req );
308
309 std::unique_ptr< QgsScopedProxyProgressTask > task = std::make_unique< QgsScopedProxyProgressTask >( tr( "Calculating field" ) );
310 const long long count = mOnlyUpdateSelectedCheckBox->isChecked() ? mVectorLayer->selectedFeatureCount() : mVectorLayer->featureCount();
311 long long i = 0;
312 while ( fit.nextFeature( feature ) )
313 {
314 i++;
315 task->setProgress( i / static_cast< double >( count ) * 100 );
316
317 expContext.setFeature( feature );
318 expContext.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "row_number" ), rownum, true ) );
319
320 QVariant value = exp.evaluate( &expContext );
321 if ( exp.hasEvalError() )
322 {
323 calculationSuccess = false;
324 error = exp.evalErrorString();
325 break;
326 }
327 else if ( updatingGeom )
328 {
329 if ( value.userType() == qMetaTypeId< QgsGeometry>() )
330 {
331 QgsGeometry geom = value.value< QgsGeometry >();
332 mVectorLayer->changeGeometry( feature.id(), geom );
333 }
334 }
335 else
336 {
337 ( void )field.convertCompatible( value );
338 mVectorLayer->changeAttributeValue( feature.id(), mAttributeId, value, newField ? emptyAttribute : feature.attributes().value( mAttributeId ) );
339 }
340
341 rownum++;
342 }
343
344 if ( !calculationSuccess )
345 {
346 cursorOverride.release();
347 task.reset();
348 QMessageBox::critical( nullptr, tr( "Evaluation Error" ), tr( "An error occurred while evaluating the calculation string:\n%1" ).arg( error ) );
349 mVectorLayer->destroyEditCommand();
350 return;
351 }
352
353 mVectorLayer->endEditCommand();
354 if ( mNewFieldGroupBox->isChecked() )
355 {
356 pushMessage( tr( "Field \"%1\" created successfully" ).arg( mOutputFieldNameLineEdit->text() ) );
357 }
358 else if ( mUpdateExistingGroupBox->isChecked() )
359 {
360 pushMessage( tr( "Field \"%1\" updated successfully" ).arg( mExistingFieldComboBox->currentText() ) ) ;
361 }
362 }
363}
364
365void QgsFieldCalculator::populateOutputFieldTypes()
366{
367 if ( !mVectorLayer )
368 {
369 return;
370 }
371
372 QgsVectorDataProvider *provider = mVectorLayer->dataProvider();
373 if ( !provider )
374 {
375 return;
376 }
377
378 const int oldDataType = mOutputFieldTypeComboBox->currentData( Qt::UserRole + FTC_TYPE_ROLE_IDX ).toInt();
379
380 mOutputFieldTypeComboBox->blockSignals( true );
381
382 // Standard subset of fields in case of virtual
383 const QList< QgsVectorDataProvider::NativeType > &typelist = mCreateVirtualFieldCheckbox->isChecked() ?
384 ( QList< QgsVectorDataProvider::NativeType >()
385 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::Int ), QStringLiteral( "integer" ), QMetaType::Type::Int, 0, 10 )
386 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::Double ), QStringLiteral( "double precision" ), QMetaType::Type::Double, -1, -1, -1, -1 )
387 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QString ), QStringLiteral( "string" ), QMetaType::Type::QString )
388 // date time
389 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QDate ), QStringLiteral( "date" ), QMetaType::Type::QDate, -1, -1, -1, -1 )
390 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QTime ), QStringLiteral( "time" ), QMetaType::Type::QTime, -1, -1, -1, -1 )
391 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QDateTime ), QStringLiteral( "datetime" ), QMetaType::Type::QDateTime, -1, -1, -1, -1 )
392 // string types
393 << QgsVectorDataProvider::NativeType( tr( "Text, unlimited length (text)" ), QStringLiteral( "text" ), QMetaType::Type::QString, -1, -1, -1, -1 )
394 // boolean
395 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::Bool ), QStringLiteral( "bool" ), QMetaType::Type::Bool )
396 // blob
397 << QgsVectorDataProvider::NativeType( QgsVariantUtils::typeToDisplayString( QMetaType::Type::QByteArray ), QStringLiteral( "binary" ), QMetaType::Type::QByteArray ) ) :
398 provider->nativeTypes();
399
400 mOutputFieldTypeComboBox->clear();
401 for ( int i = 0; i < typelist.size(); i++ )
402 {
403 mOutputFieldTypeComboBox->addItem( QgsFields::iconForFieldType( typelist[i].mType, typelist[i].mSubType, typelist[i].mTypeName ), typelist[i].mTypeDesc );
404 mOutputFieldTypeComboBox->setItemData( i, static_cast<int>( typelist[i].mType ), Qt::UserRole + FTC_TYPE_ROLE_IDX );
405 mOutputFieldTypeComboBox->setItemData( i, typelist[i].mTypeName, Qt::UserRole + FTC_TYPE_NAME_IDX );
406 mOutputFieldTypeComboBox->setItemData( i, typelist[i].mMinLen, Qt::UserRole + FTC_MINLEN_IDX );
407 mOutputFieldTypeComboBox->setItemData( i, typelist[i].mMaxLen, Qt::UserRole + FTC_MAXLEN_IDX );
408 mOutputFieldTypeComboBox->setItemData( i, typelist[i].mMinPrec, Qt::UserRole + FTC_MINPREC_IDX );
409 mOutputFieldTypeComboBox->setItemData( i, typelist[i].mMaxPrec, Qt::UserRole + FTC_MAXPREC_IDX );
410 mOutputFieldTypeComboBox->setItemData( i, static_cast<int>( typelist[i].mSubType ), Qt::UserRole + FTC_SUBTYPE_IDX );
411 }
412 mOutputFieldTypeComboBox->blockSignals( false );
413
414 const int idx = mOutputFieldTypeComboBox->findData( oldDataType, Qt::UserRole + FTC_TYPE_ROLE_IDX );
415 if ( idx != -1 )
416 {
417 mOutputFieldTypeComboBox->setCurrentIndex( idx );
418 mOutputFieldTypeComboBox_activated( idx );
419 }
420 else
421 {
422 mOutputFieldTypeComboBox->setCurrentIndex( 0 );
423 mOutputFieldTypeComboBox_activated( 0 );
424 }
425}
426
427void QgsFieldCalculator::mNewFieldGroupBox_toggled( bool on )
428{
429 mUpdateExistingGroupBox->setChecked( !on );
430 if ( on && !mCanAddAttribute )
431 {
432 mOnlyVirtualFieldsInfoLabel->setVisible( true );
433 }
434 else
435 {
436 mOnlyVirtualFieldsInfoLabel->setVisible( false );
437 }
438
439 if ( ( mNewFieldGroupBox->isChecked() && mCreateVirtualFieldCheckbox->isChecked() ) || mVectorLayer->isEditable() )
440 {
441 mEditModeAutoTurnOnLabel->setVisible( false );
442 }
443 else
444 {
445 mEditModeAutoTurnOnLabel->setVisible( true );
446 }
447
448 mInfoIcon->setVisible( mOnlyVirtualFieldsInfoLabel->isVisible() || mEditModeAutoTurnOnLabel->isVisible() );
449}
450
451void QgsFieldCalculator::mUpdateExistingGroupBox_toggled( bool on )
452{
453 mNewFieldGroupBox->setChecked( !on );
454 setDialogButtonState();
455
456 if ( on )
457 {
458 mOnlyUpdateSelectedCheckBox->setEnabled( mVectorLayer->selectedFeatureCount() > 0 );
459 }
460 else
461 {
462 mCreateVirtualFieldCheckbox_stateChanged( mCreateVirtualFieldCheckbox->checkState() );
463 }
464}
465
466void QgsFieldCalculator::mCreateVirtualFieldCheckbox_stateChanged( int state )
467{
468 mOnlyUpdateSelectedCheckBox->setChecked( false );
469 mOnlyUpdateSelectedCheckBox->setEnabled( state != Qt::Checked && mVectorLayer->selectedFeatureCount() > 0 );
470
471 if ( ( mNewFieldGroupBox->isChecked() && mCreateVirtualFieldCheckbox->isChecked() ) || mVectorLayer->isEditable() )
472 {
473 mEditModeAutoTurnOnLabel->setVisible( false );
474 }
475 else
476 {
477 mEditModeAutoTurnOnLabel->setVisible( true );
478 }
479 populateOutputFieldTypes();
480 mInfoIcon->setVisible( mOnlyVirtualFieldsInfoLabel->isVisible() || mEditModeAutoTurnOnLabel->isVisible() );
481}
482
483
484void QgsFieldCalculator::mOutputFieldNameLineEdit_textChanged( const QString &text )
485{
486 Q_UNUSED( text )
487 setDialogButtonState();
488}
489
490
491void QgsFieldCalculator::mOutputFieldTypeComboBox_activated( int index )
492{
493 mOutputFieldWidthSpinBox->setMinimum( mOutputFieldTypeComboBox->itemData( index, Qt::UserRole + FTC_MINLEN_IDX ).toInt() );
494 mOutputFieldWidthSpinBox->setMaximum( mOutputFieldTypeComboBox->itemData( index, Qt::UserRole + FTC_MAXLEN_IDX ).toInt() );
495 mOutputFieldWidthSpinBox->setEnabled( mOutputFieldWidthSpinBox->minimum() < mOutputFieldWidthSpinBox->maximum() );
496 if ( mOutputFieldWidthSpinBox->value() < mOutputFieldWidthSpinBox->minimum() )
497 mOutputFieldWidthSpinBox->setValue( mOutputFieldWidthSpinBox->minimum() );
498 if ( mOutputFieldWidthSpinBox->value() > mOutputFieldWidthSpinBox->maximum() )
499 mOutputFieldWidthSpinBox->setValue( mOutputFieldWidthSpinBox->maximum() );
500
501 setPrecisionMinMax();
502}
503
504void QgsFieldCalculator::populateFields()
505{
506 if ( !mVectorLayer )
507 return;
508
509 const QgsFields &fields = mVectorLayer->fields();
510 for ( int idx = 0; idx < fields.count(); ++idx )
511 {
512 switch ( fields.fieldOrigin( idx ) )
513 {
516
517 continue; // can't be edited
518
521 break; // can always be edited
522
524 {
525 // show joined fields (e.g. auxiliary fields) only if they have a non-hidden editor widget.
526 // This enables them to be bulk field-calculated when a user needs to, but hides them by default
527 // (since there's often MANY of these, e.g. after using the label properties tool on a layer)
528 if ( fields.at( idx ).editorWidgetSetup().type() == QLatin1String( "Hidden" ) )
529 continue;
530
531 // only show editable joins
532 int srcFieldIndex;
533 const QgsVectorLayerJoinInfo *info = mVectorLayer->joinBuffer()->joinForFieldIndex( idx, fields, srcFieldIndex );
534
535 if ( !info || !info->isEditable() )
536 continue; // join is not editable
537
538 break;
539 }
540 }
541
542 const QString fieldName = fields.at( idx ).name();
543
544 //insert into field combo box
545 mExistingFieldComboBox->addItem( fields.iconForField( idx ), fieldName, idx );
546 }
547
548 if ( mVectorLayer->geometryType() != Qgis::GeometryType::Null )
549 {
550 mExistingFieldComboBox->addItem( tr( "<geometry>" ), "geom" );
551
552 QFont font = mExistingFieldComboBox->itemData( mExistingFieldComboBox->count() - 1, Qt::FontRole ).value<QFont>();
553 font.setItalic( true );
554 mExistingFieldComboBox->setItemData( mExistingFieldComboBox->count() - 1, font, Qt::FontRole );
555 }
556 mExistingFieldComboBox->setCurrentIndex( -1 );
557}
558
559void QgsFieldCalculator::setDialogButtonState()
560{
561 QList<QPushButton *> buttons =
562 {
563 mButtonBox->button( QDialogButtonBox::Ok ),
564 mButtonBox->button( QDialogButtonBox::Apply )
565 };
566
567 bool enableButtons = true;
568 QString tooltip;
569
570 if ( ( mNewFieldGroupBox->isChecked() || !mUpdateExistingGroupBox->isEnabled() )
571 && mOutputFieldNameLineEdit->text().isEmpty() )
572 {
573 tooltip = tr( "Please enter a field name" );
574 enableButtons = false;
575 }
576 else if ( !builder->isExpressionValid() )
577 {
578 tooltip = tr( "The expression is invalid see \"(more info)\" for details" );
579 enableButtons = false;
580 }
581
582 for ( QPushButton *button : buttons )
583 {
584 if ( button )
585 {
586 button->setEnabled( enableButtons );
587 button->setToolTip( tooltip );
588 }
589 }
590}
591
592void QgsFieldCalculator::setPrecisionMinMax()
593{
594 const int idx = mOutputFieldTypeComboBox->currentIndex();
595 const int minPrecType = mOutputFieldTypeComboBox->itemData( idx, Qt::UserRole + FTC_MINPREC_IDX ).toInt();
596 const int maxPrecType = mOutputFieldTypeComboBox->itemData( idx, Qt::UserRole + FTC_MAXPREC_IDX ).toInt();
597 const bool precisionIsEnabled = minPrecType < maxPrecType;
598 mOutputFieldPrecisionSpinBox->setEnabled( precisionIsEnabled );
599 // Do not set min/max if it's disabled or we'll loose the default value,
600 // see https://github.com/qgis/QGIS/issues/26880 - QGIS saves integer field when
601 // I create a new real field through field calculator (Update field works as intended)
602 if ( precisionIsEnabled )
603 {
604 mOutputFieldPrecisionSpinBox->setMinimum( minPrecType );
605 mOutputFieldPrecisionSpinBox->setMaximum( std::max( minPrecType, std::min( maxPrecType, mOutputFieldWidthSpinBox->value() ) ) );
606 }
607}
608
609void QgsFieldCalculator::showHelp()
610{
611 QgsHelp::openHelp( QStringLiteral( "working_with_vector/attribute_table.html#editing-attribute-values" ) );
612}
613
614QgsField QgsFieldCalculator::fieldDefinition()
615{
616 return QgsField( mOutputFieldNameLineEdit->text(),
617 static_cast< QMetaType::Type >( mOutputFieldTypeComboBox->currentData( Qt::UserRole + FTC_TYPE_ROLE_IDX ).toInt() ),
618 mOutputFieldTypeComboBox->currentData( Qt::UserRole + FTC_TYPE_NAME_IDX ).toString(),
619 mOutputFieldWidthSpinBox->value(),
620 mOutputFieldPrecisionSpinBox->isEnabled() ? mOutputFieldPrecisionSpinBox->value() : 0,
621 QString(),
622 static_cast< QMetaType::Type >( mOutputFieldTypeComboBox->currentData( Qt::UserRole + FTC_SUBTYPE_IDX ).toInt() )
623 );
624}
625
626void QgsFieldCalculator::pushMessage( const QString &text, Qgis::MessageLevel level, int duration )
627{
628 mMsgBar->pushMessage( text, level, duration );
629}
The Qgis class provides global constants for use throughout the application.
Definition qgis.h:54
@ AddAttributes
Allows addition of new attributes (fields)
@ ChangeAttributeValues
Allows modification of attribute values.
@ NoFlags
No flags are set.
MessageLevel
Level for messages This will be used both for message log and message bar in application.
Definition qgis.h:154
@ Null
No geometry.
QFlags< VectorProviderCapability > VectorProviderCapabilities
Vector data provider capabilities.
Definition qgis.h:500
@ 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 setHighlightedVariables(const QStringList &variableNames)
Sets the list of variable names within the context intended to be highlighted to the user.
Class for parsing and evaluation of expressions (formerly called "search strings").
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:208
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:80
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:83
A bar for displaying non-blocking messages to the user.
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::MessageLevel::Info, int duration=-1)
A convenience method for pushing a message with the specified text to the bar.
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.
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.