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