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