20#include "moc_qgsfieldcalculator.cpp"
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 );
79 populateOutputFieldTypes();
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 );
89 builder->setGeomCalculator( myDa );
92 mOutputFieldWidthSpinBox->setValue( 10 );
93 mOutputFieldWidthSpinBox->setClearValue( 10 );
94 mOutputFieldPrecisionSpinBox->setValue( 3 );
95 mOutputFieldPrecisionSpinBox->setClearValue( 3 );
100 mOutputFieldNameLineEdit->setMaxLength( 10 );
103 if ( !mCanAddAttribute )
105 mCreateVirtualFieldCheckbox->setChecked(
true );
106 mCreateVirtualFieldCheckbox->setEnabled(
false );
107 mOnlyVirtualFieldsInfoLabel->setVisible(
true );
108 mInfoIcon->setVisible(
true );
112 mOnlyVirtualFieldsInfoLabel->setVisible(
false );
113 mInfoIcon->setVisible(
false );
116 if ( !mCanChangeAttributeValue )
118 mUpdateExistingGroupBox->setEnabled(
false );
119 mCreateVirtualFieldCheckbox->setChecked(
true );
120 mCreateVirtualFieldCheckbox->setEnabled(
false );
123 Q_ASSERT( mNewFieldGroupBox->isEnabled() || mUpdateExistingGroupBox->isEnabled() );
125 if ( mNewFieldGroupBox->isEnabled() )
127 mNewFieldGroupBox->setChecked(
true );
131 mNewFieldGroupBox->setToolTip( tr(
"Not available for layer" ) );
132 mUpdateExistingGroupBox->setChecked(
true );
133 mUpdateExistingGroupBox->setCheckable(
false );
136 if ( mUpdateExistingGroupBox->isEnabled() )
138 mUpdateExistingGroupBox->setChecked( !mNewFieldGroupBox->isEnabled() );
142 mUpdateExistingGroupBox->setToolTip( tr(
"Not available for layer" ) );
143 mNewFieldGroupBox->setChecked(
true );
144 mNewFieldGroupBox->setCheckable(
false );
147 if ( ( mNewFieldGroupBox->isChecked() && mCreateVirtualFieldCheckbox->isChecked() ) || mVectorLayer->
isEditable() )
149 mEditModeAutoTurnOnLabel->setVisible(
false );
150 mInfoIcon->setVisible(
false );
154 mInfoIcon->setVisible(
true );
158 mOnlyUpdateSelectedCheckBox->setChecked( mCanChangeAttributeValue && hasselection );
159 mOnlyUpdateSelectedCheckBox->setEnabled( mCanChangeAttributeValue && hasselection );
160 mOnlyUpdateSelectedCheckBox->setText( tr(
"Only update %n selected feature(s)",
nullptr, vl->
selectedFeatureCount() ) );
162 builder->initWithLayer( vl, expContext, QStringLiteral(
"fieldcalc" ) );
164 mInfoIcon->setPixmap( style()->standardPixmap( QStyle::SP_MessageBoxInformation ) );
166 setWindowTitle( tr(
"%1 — Field Calculator" ).arg( mVectorLayer->
name() ) );
170 mMsgBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
171 this->vLayout->insertWidget( 0, mMsgBar );
173 setDialogButtonState();
182void QgsFieldCalculator::calculate()
184 builder->expressionTree()->saveToRecent( builder->expressionText(), QStringLiteral(
"fieldcalc" ) );
195 const QString calcString = builder->expressionText();
197 exp.setGeomCalculator( &myDa );
203 if ( !exp.prepare( &expContext ) )
205 QMessageBox::critical(
nullptr, tr(
"Evaluation Error" ), exp.evalErrorString() );
209 bool updatingGeom =
false;
215 if ( !mUpdateExistingGroupBox->isChecked() && mCreateVirtualFieldCheckbox->isChecked() )
229 if ( mUpdateExistingGroupBox->isChecked() || !mNewFieldGroupBox->isEnabled() )
231 if ( mExistingFieldComboBox->currentData().toString() == QLatin1String(
"geom" ) )
240 const int id = mExistingFieldComboBox->currentData().toInt( &ok );
248 const QgsField newField = fieldDefinition();
252 cursorOverride.release();
253 QMessageBox::critical(
nullptr, tr(
"Create New Field" ), tr(
"Could not add the new field to the provider." ) );
261 for (
int idx = 0; idx < fields.
count(); ++idx )
263 if ( fields.
at( idx ).
name() == mOutputFieldNameLineEdit->text() )
271 expContext.setFields( mVectorLayer->
fields() );
272 if ( !exp.prepare( &expContext ) )
274 cursorOverride.release();
275 QMessageBox::critical(
nullptr, tr(
"Evaluation Error" ), exp.evalErrorString() );
280 if ( mAttributeId == -1 && !updatingGeom )
288 bool calculationSuccess =
true;
291 const bool useGeometry = exp.needsGeometry();
296 const bool newField = !mUpdateExistingGroupBox->isChecked();
297 QVariant emptyAttribute;
302 QSet<QString> referencedColumns = exp.referencedColumns();
303 referencedColumns.insert( field.
name() );
305 if ( mOnlyUpdateSelectedCheckBox->isChecked() )
311 std::unique_ptr<QgsScopedProxyProgressTask> task = std::make_unique<QgsScopedProxyProgressTask>( tr(
"Calculating field" ) );
317 task->setProgress( i /
static_cast<double>( count ) * 100 );
319 expContext.setFeature( feature );
322 QVariant value = exp.evaluate( &expContext );
323 if ( exp.hasEvalError() )
325 calculationSuccess =
false;
326 error = exp.evalErrorString();
329 else if ( updatingGeom )
331 if ( value.userType() == qMetaTypeId<QgsGeometry>() )
346 if ( !calculationSuccess )
348 cursorOverride.release();
350 QMessageBox::critical(
nullptr, tr(
"Evaluation Error" ), tr(
"An error occurred while evaluating the calculation string:\n%1" ).arg( error ) );
356 if ( mNewFieldGroupBox->isChecked() )
358 pushMessage( tr(
"Field \"%1\" created successfully" ).arg( mOutputFieldNameLineEdit->text() ) );
360 else if ( mUpdateExistingGroupBox->isChecked() )
362 pushMessage( tr(
"Field \"%1\" updated successfully" ).arg( mExistingFieldComboBox->currentText() ) );
367void QgsFieldCalculator::populateOutputFieldTypes()
380 const int oldDataType = mOutputFieldTypeComboBox->currentData( Qt::UserRole +
FTC_TYPE_ROLE_IDX ).toInt();
382 mOutputFieldTypeComboBox->blockSignals(
true );
385 const QList<QgsVectorDataProvider::NativeType> &typelist = mCreateVirtualFieldCheckbox->isChecked() ? ( QList<QgsVectorDataProvider::NativeType>()
399 : provider->nativeTypes();
401 mOutputFieldTypeComboBox->clear();
402 for (
int i = 0; i < typelist.size(); i++ )
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 );
413 mOutputFieldTypeComboBox->blockSignals(
false );
415 const int idx = mOutputFieldTypeComboBox->findData( oldDataType, Qt::UserRole +
FTC_TYPE_ROLE_IDX );
418 mOutputFieldTypeComboBox->setCurrentIndex( idx );
419 mOutputFieldTypeComboBox_activated( idx );
423 mOutputFieldTypeComboBox->setCurrentIndex( 0 );
424 mOutputFieldTypeComboBox_activated( 0 );
428void QgsFieldCalculator::mNewFieldGroupBox_toggled(
bool on )
430 mUpdateExistingGroupBox->setChecked( !on );
431 if ( on && !mCanAddAttribute )
433 mOnlyVirtualFieldsInfoLabel->setVisible(
true );
437 mOnlyVirtualFieldsInfoLabel->setVisible(
false );
440 if ( ( mNewFieldGroupBox->isChecked() && mCreateVirtualFieldCheckbox->isChecked() ) || mVectorLayer->
isEditable() )
442 mEditModeAutoTurnOnLabel->setVisible(
false );
446 mEditModeAutoTurnOnLabel->setVisible(
true );
449 mInfoIcon->setVisible( mOnlyVirtualFieldsInfoLabel->isVisible() || mEditModeAutoTurnOnLabel->isVisible() );
452void QgsFieldCalculator::mUpdateExistingGroupBox_toggled(
bool on )
454 mNewFieldGroupBox->setChecked( !on );
455 setDialogButtonState();
463 mCreateVirtualFieldCheckbox_stateChanged( mCreateVirtualFieldCheckbox->checkState() );
467void QgsFieldCalculator::mCreateVirtualFieldCheckbox_stateChanged(
int state )
469 mOnlyUpdateSelectedCheckBox->setChecked(
false );
470 mOnlyUpdateSelectedCheckBox->setEnabled( state != Qt::Checked && mVectorLayer->
selectedFeatureCount() > 0 );
472 if ( ( mNewFieldGroupBox->isChecked() && mCreateVirtualFieldCheckbox->isChecked() ) || mVectorLayer->
isEditable() )
474 mEditModeAutoTurnOnLabel->setVisible(
false );
478 mEditModeAutoTurnOnLabel->setVisible(
true );
480 populateOutputFieldTypes();
481 mInfoIcon->setVisible( mOnlyVirtualFieldsInfoLabel->isVisible() || mEditModeAutoTurnOnLabel->isVisible() );
485void QgsFieldCalculator::mOutputFieldNameLineEdit_textChanged(
const QString &text )
488 setDialogButtonState();
492void QgsFieldCalculator::mOutputFieldTypeComboBox_activated(
int index )
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() );
502 setPrecisionMinMax();
505void QgsFieldCalculator::mExistingFieldComboBox_currentIndexChanged(
const int index )
508 setDialogButtonState();
511void QgsFieldCalculator::populateFields()
517 for (
int idx = 0; idx < fields.
count(); ++idx )
549 const QString fieldName = fields.
at( idx ).
name();
552 mExistingFieldComboBox->addItem( fields.
iconForField( idx ), fieldName, idx );
557 mExistingFieldComboBox->addItem( tr(
"<geometry>" ),
"geom" );
559 QFont font = mExistingFieldComboBox->itemData( mExistingFieldComboBox->count() - 1, Qt::FontRole ).value<QFont>();
560 font.setItalic(
true );
561 mExistingFieldComboBox->setItemData( mExistingFieldComboBox->count() - 1, font, Qt::FontRole );
563 mExistingFieldComboBox->setCurrentIndex( -1 );
566void QgsFieldCalculator::setDialogButtonState()
568 QList<QPushButton *> buttons = {
569 mButtonBox->button( QDialogButtonBox::Ok ),
570 mButtonBox->button( QDialogButtonBox::Apply )
573 bool enableButtons =
true;
576 if ( ( mNewFieldGroupBox->isChecked() || !mUpdateExistingGroupBox->isEnabled() )
577 && mOutputFieldNameLineEdit->text().isEmpty() )
579 tooltip = tr(
"Please enter a field name" );
580 enableButtons =
false;
582 else if ( ( mUpdateExistingGroupBox->isChecked() || !mNewFieldGroupBox->isEnabled() )
583 && mExistingFieldComboBox->currentIndex() == -1 )
585 tooltip = tr(
"Please select a field" );
586 enableButtons =
false;
588 else if ( builder->expressionText().isEmpty() )
590 tooltip = tr(
"Please insert an expression" );
591 enableButtons =
false;
593 else if ( !builder->isExpressionValid() )
595 tooltip = tr(
"The expression is invalid. See \"(more info)\" for details" );
596 enableButtons =
false;
599 for ( QPushButton *button : buttons )
603 button->setEnabled( enableButtons );
604 button->setToolTip( tooltip );
609void QgsFieldCalculator::setPrecisionMinMax()
611 const int idx = mOutputFieldTypeComboBox->currentIndex();
612 const int minPrecType = mOutputFieldTypeComboBox->itemData( idx, Qt::UserRole +
FTC_MINPREC_IDX ).toInt();
613 const int maxPrecType = mOutputFieldTypeComboBox->itemData( idx, Qt::UserRole +
FTC_MAXPREC_IDX ).toInt();
614 const bool precisionIsEnabled = minPrecType < maxPrecType;
615 mOutputFieldPrecisionSpinBox->setEnabled( precisionIsEnabled );
619 if ( precisionIsEnabled )
621 mOutputFieldPrecisionSpinBox->setMinimum( minPrecType );
622 mOutputFieldPrecisionSpinBox->setMaximum( std::max( minPrecType, std::min( maxPrecType, mOutputFieldWidthSpinBox->value() ) ) );
626void QgsFieldCalculator::showHelp()
628 QgsHelp::openHelp( QStringLiteral(
"working_with_vector/attribute_table.html#editing-attribute-values" ) );
631QgsField QgsFieldCalculator::fieldDefinition()
633 return QgsField( mOutputFieldNameLineEdit->text(),
static_cast<QMetaType::Type
>( mOutputFieldTypeComboBox->currentData( Qt::UserRole +
FTC_TYPE_ROLE_IDX ).toInt() ), mOutputFieldTypeComboBox->currentData( Qt::UserRole +
FTC_TYPE_NAME_IDX ).toString(), mOutputFieldWidthSpinBox->value(), mOutputFieldPrecisionSpinBox->isEnabled() ? mOutputFieldPrecisionSpinBox->value() : 0, QString(), static_cast<QMetaType::Type>( mOutputFieldTypeComboBox->currentData( Qt::UserRole +
FTC_SUBTYPE_IDX ).toInt() ) );
636void QgsFieldCalculator::pushMessage(
const QString &text,
Qgis::MessageLevel level,
int duration )
The Qgis class provides global constants for use throughout the application.
@ 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.
QFlags< VectorProviderCapability > VectorProviderCapabilities
Vector data provider capabilities.
@ 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 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...
QgsFieldCalculator(QgsVectorLayer *vl, QWidget *parent=nullptr)
Encapsulate a field in an attribute table or data source.
bool convertCompatible(QVariant &v, QString *errorMessage=nullptr) const
Converts the provided variant to a compatible format.
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Container of fields for a vector layer.
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...
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsCoordinateReferenceSystem crs
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
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.