Skip to content

Commit

Permalink
[FEATURE] Expression field (Virtual field)
Browse files Browse the repository at this point in the history
  • Loading branch information
m-kuhn committed Jul 24, 2014
1 parent 4e50f54 commit 1395367
Show file tree
Hide file tree
Showing 16 changed files with 574 additions and 115 deletions.
18 changes: 18 additions & 0 deletions python/core/qgsvectorlayer.sip
Expand Up @@ -280,6 +280,24 @@ class QgsVectorLayer : QgsMapLayer
/** @note added in 1.7 */
const QList< QgsVectorJoinInfo >& vectorJoins() const;

/**
* Add a new field which is calculated by the expression specified
*
* @param exp The expression which calculates the field
*
* @note added in 2.6
*/
void addExpressionField( const QString& exp, const QgsField& fld );

/**
* Remove an expression field
*
* @param exp The expression which calculates the field
*
* @note added in 2.6
*/
void removeExpressionField( int index );

/** Get the label object associated with this layer */
QgsLabel *label();

Expand Down
32 changes: 32 additions & 0 deletions src/app/qgsaddattrdialog.cpp
Expand Up @@ -19,12 +19,14 @@
#include "qgsvectorlayer.h"
#include "qgsvectordataprovider.h"
#include "qgslogger.h"
#include "qgsexpressionbuilderdialog.h"

#include <QMessageBox>

QgsAddAttrDialog::QgsAddAttrDialog( QgsVectorLayer *vlayer, QWidget *parent, Qt::WindowFlags fl )
: QDialog( parent, fl )
, mIsShapeFile( vlayer && vlayer->providerType() == "ogr" && vlayer->storageType() == "ESRI Shapefile" )
, mLayer( vlayer )
{
setupUi( this );

Expand All @@ -50,9 +52,12 @@ QgsAddAttrDialog::QgsAddAttrDialog( QgsVectorLayer *vlayer, QWidget *parent, Qt:
}

on_mTypeBox_currentIndexChanged( 0 );
on_mFieldModeButtonGroup_buttonClicked( mButtonProviderField );

if ( mIsShapeFile )
mNameEdit->setMaxLength( 10 );

mExpressionWidget->setLayer( vlayer );
}

void QgsAddAttrDialog::on_mTypeBox_currentIndexChanged( int idx )
Expand All @@ -70,6 +75,20 @@ void QgsAddAttrDialog::on_mTypeBox_currentIndexChanged( int idx )
setPrecisionMinMax();
}

void QgsAddAttrDialog::on_mFieldModeButtonGroup_buttonClicked( QAbstractButton* button )
{
if ( button == mButtonProviderField )
{
mExpressionWidget->hide();
mExpressionLabel->hide();
}
else
{
mExpressionWidget->show();
mExpressionLabel->show();
}
}

void QgsAddAttrDialog::on_mLength_editingFinished()
{
setPrecisionMinMax();
Expand Down Expand Up @@ -116,3 +135,16 @@ QgsField QgsAddAttrDialog::field() const
mPrec->value(),
mCommentEdit->text() );
}

const QString QgsAddAttrDialog::expression() const
{
return mExpressionWidget->currentField();
}

QgsAddAttrDialog::AttributeMode QgsAddAttrDialog::mode() const
{
if ( mButtonVirtualField->isChecked() )
return VirtualField;
else
return ProviderField;
}
12 changes: 11 additions & 1 deletion src/app/qgsaddattrdialog.h
Expand Up @@ -28,20 +28,30 @@ class APP_EXPORT QgsAddAttrDialog: public QDialog, private Ui::QgsAddAttrDialogB
{
Q_OBJECT
public:
enum AttributeMode
{
ProviderField,
VirtualField
};

QgsAddAttrDialog( QgsVectorLayer *vlayer,
QWidget *parent = 0, Qt::WindowFlags fl = QgisGui::ModalDialogFlags );
QgsAddAttrDialog( const std::list<QString>& typelist,
QWidget *parent = 0, Qt::WindowFlags fl = QgisGui::ModalDialogFlags );

QgsField field() const;
const QString expression() const;
AttributeMode mode() const;

public slots:
private slots:
void on_mTypeBox_currentIndexChanged( int idx );
void on_mFieldModeButtonGroup_buttonClicked( QAbstractButton* button );
void on_mLength_editingFinished();
void accept();

private:
bool mIsShapeFile;
QgsVectorLayer* mLayer;

void setPrecisionMinMax();
};
Expand Down
18 changes: 13 additions & 5 deletions src/app/qgsattributetabledialog.cpp
Expand Up @@ -591,16 +591,24 @@ void QgsAttributeTableDialog::on_mAddAttribute_clicked()
QgsAddAttrDialog dialog( mLayer, this );
if ( dialog.exec() == QDialog::Accepted )
{
mLayer->beginEditCommand( tr( "Attribute added" ) );
if ( mLayer->addAttribute( dialog.field() ) )
if ( dialog.mode() == QgsAddAttrDialog::VirtualField )
{
mLayer->endEditCommand();
mLayer->addExpressionField( dialog.expression(), dialog.field() );
}
else
{
QMessageBox::critical( 0, tr( "Attribute Error" ), tr( "The attribute could not be added to the layer" ) );
mLayer->destroyEditCommand();
mLayer->beginEditCommand( tr( "Attribute added" ) );
if ( mLayer->addAttribute( dialog.field() ) )
{
mLayer->endEditCommand();
}
else
{
QMessageBox::critical( 0, tr( "Attribute Error" ), tr( "The attribute could not be added to the layer" ) );
mLayer->destroyEditCommand();
}
}

// update model - a field has been added or updated
masterModel->reload( masterModel->index( 0, 0 ), masterModel->index( masterModel->rowCount() - 1, masterModel->columnCount() - 1 ) );
columnBoxInit();
Expand Down
20 changes: 14 additions & 6 deletions src/app/qgsfieldsproperties.cpp
Expand Up @@ -516,15 +516,22 @@ void QgsFieldsProperties::on_mAddAttributeButton_clicked()
QgsAddAttrDialog dialog( mLayer, this );
if ( dialog.exec() == QDialog::Accepted )
{
mLayer->beginEditCommand( "Attribute added" );
if ( !addAttribute( dialog.field() ) )
if ( dialog.mode() == QgsAddAttrDialog::VirtualField )
{
mLayer->destroyEditCommand();
QMessageBox::information( this, tr( "Name conflict" ), tr( "The attribute could not be inserted. The name already exists in the table." ) );
mLayer->addExpressionField( dialog.expression(), dialog.field() );
}
else
{
mLayer->endEditCommand();
mLayer->beginEditCommand( "Attribute added" );
if ( !addAttribute( dialog.field() ) )
{
mLayer->destroyEditCommand();
QMessageBox::information( this, tr( "Name conflict" ), tr( "The attribute could not be inserted. The name already exists in the table." ) );
}
else
{
mLayer->endEditCommand();
}
}
}
}
Expand Down Expand Up @@ -766,7 +773,8 @@ QgsFieldsProperties::FieldConfig::FieldConfig()
QgsFieldsProperties::FieldConfig::FieldConfig( QgsVectorLayer* layer, int idx )
{
mEditable = layer->fieldEditable( idx );
mEditableEnabled = layer->pendingFields().fieldOrigin( idx ) != QgsFields::OriginJoin;
mEditableEnabled = layer->pendingFields().fieldOrigin( idx ) != QgsFields::OriginJoin
&& layer->pendingFields().fieldOrigin( idx ) != QgsFields::OriginExpression;
mLabelOnTop = layer->labelOnTop( idx );
mEditorWidgetV2Type = layer->editorWidgetV2( idx );
mEditorWidgetV2Config = layer->editorWidgetV2Config( idx );
Expand Down
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -75,6 +75,7 @@ SET(QGIS_CORE_SRCS
qgserror.cpp
qgsexpression.cpp
qgsexpression_texts.cpp
qgsexpressionfieldbuffer.cpp
qgsfeature.cpp
qgsfeatureiterator.cpp
qgsfeaturerequest.cpp
Expand Down Expand Up @@ -454,6 +455,7 @@ SET(QGIS_CORE_HDRS
qgserror.h
qgsexception.h
qgsexpression.h
qgsexpressionfieldbuffer.h
qgsfeature.h
qgsfeatureiterator.h
qgsfeaturerequest.h
Expand Down
91 changes: 91 additions & 0 deletions src/core/qgsexpressionfieldbuffer.cpp
@@ -0,0 +1,91 @@
/***************************************************************************
qgsexpressionfieldbuffer.cpp
---------------------------
begin : May 27, 2014
copyright : (C) 2014 by Matthias Kuhn
email : matthias dot kuhn at gmx dot ch
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsexpressionfieldbuffer.h"

#include "qgsvectorlayer.h"

QgsExpressionFieldBuffer::QgsExpressionFieldBuffer()
{
}

void QgsExpressionFieldBuffer::addExpression( const QString& exp, const QgsField& fld )
{
mExpressions << ExpressionField( exp, fld );
}

void QgsExpressionFieldBuffer::removeExpression( int index )
{
mExpressions.removeAt( index );
}

void QgsExpressionFieldBuffer::writeXml( QDomNode& layerNode, QDomDocument& document ) const
{
QDomElement expressionFieldsElem = document.createElement( "expressionfields" );
layerNode.appendChild( expressionFieldsElem );

Q_FOREACH( const ExpressionField& fld, mExpressions )
{
QDomElement fldElem = document.createElement( "field" );

fldElem.setAttribute( "expression", fld.expression );
fldElem.setAttribute( "name", fld.field.name() );
fldElem.setAttribute( "precision", fld.field.precision() );
fldElem.setAttribute( "comment", fld.field.comment() );
fldElem.setAttribute( "length", fld.field.length() );
fldElem.setAttribute( "type", fld.field.type() );
fldElem.setAttribute( "typeName", fld.field.typeName() );

expressionFieldsElem.appendChild( fldElem );
}
}

void QgsExpressionFieldBuffer::readXml( const QDomNode& layerNode )
{
mExpressions.clear();

const QDomElement expressionFieldsElem = layerNode.firstChildElement( "expressionfields" );

if ( !expressionFieldsElem.isNull() )
{
QDomNodeList fields = expressionFieldsElem.elementsByTagName( "field" );

for ( unsigned int i = 0; i < fields.length(); ++i )
{
QDomElement field = fields.at( i ).toElement();
QString exp = field.attribute( "expression" );
QString name = field.attribute( "name" );
QString comment = field.attribute( "comment" );
int precision = field.attribute( "precision" ).toInt();
int length = field.attribute( "length" ).toInt();
QVariant::Type type = ( QVariant::Type )( field.attribute( "type" ).toInt() );
QString typeName = field.attribute( "typeName" );

mExpressions.append( ExpressionField( exp, QgsField( name, type, typeName, length, precision, comment ) ) );
}
}
}

void QgsExpressionFieldBuffer::updateFields( QgsFields& flds )
{
int index = 0;
Q_FOREACH( const ExpressionField& fld, mExpressions )
{
flds.appendExpressionField( fld.field, index );
++index;
}
}
83 changes: 83 additions & 0 deletions src/core/qgsexpressionfieldbuffer.h
@@ -0,0 +1,83 @@
/***************************************************************************
qgsexpressionfieldbuffer.h
---------------------------
begin : May 27, 2014
copyright : (C) 2014 by Matthias Kuhn
email : matthias dot kuhn at gmx dot ch
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSEXPRESSIONFIELDBUFFER_H
#define QGSEXPRESSIONFIELDBUFFER_H

#include <QString>
#include <QList>
#include <QDomNode>

#include "qgsfield.h"

/**
* Buffers information about expression fields for a vector layer.
*
* @note added in 2.6
*/
class CORE_EXPORT QgsExpressionFieldBuffer
{
public:
typedef struct ExpressionField
{
ExpressionField() {}
ExpressionField( QString exp, QgsField fld ) : expression( exp ), field( fld ) {}

QString expression;
QgsField field;
} ExpressionField;

QgsExpressionFieldBuffer();

/**
* Add an expression to the buffer
*
* @param exp Expression to add
*/
void addExpression( const QString& exp, const QgsField& fld );

/**
* Remove an expression from the buffer
*
* @param exp Expression to remove
*/
void removeExpression( int index );

/**
* Saves expressions to xml under the layer node
*/
void writeXml( QDomNode& layer_node, QDomDocument& document ) const;

/**
* Reads expressions from project file
*/
void readXml( const QDomNode& layer_node );

/**
* Adds fields with the expressions buffered in this object to a QgsFields object
*
* @param flds The fields to be updated
*/
void updateFields( QgsFields& flds );

const QList<ExpressionField> expressions() const { return mExpressions; }

private:
QList<ExpressionField> mExpressions;
};

#endif // QGSEXPRESSIONFIELDBUFFER_H

0 comments on commit 1395367

Please sign in to comment.