Skip to content
Permalink
Browse files
[FEATURE] Quick field field calc bar (like Excel) in attribute table
  • Loading branch information
NathanW2 committed May 22, 2014
1 parent 9873782 commit ee9e17ca5289a14a57f7a635421c651a320539a9
@@ -41,6 +41,7 @@
#include "qgsmessagebar.h"
#include "qgsexpressionselectiondialog.h"
#include "qgsfeaturelistmodel.h"
#include "qgsexpressionbuilderdialog.h"

class QgsAttributeTableDock : public QDockWidget
{
@@ -199,6 +200,13 @@ QgsAttributeTableDialog::QgsAttributeTableDialog( QgsVectorLayer *theLayer, QWid
filterShowAll();
break;
}

mFieldModel = new QgsFieldModel();
mFieldModel->setLayer( mLayer );
mFieldComboButton->setModel( mFieldModel );

This comment has been minimized.

Copy link
@3nids

3nids May 23, 2014

Member

You can use QgsFieldCombo directly here.
That doesn't change a lot, but it's a bit faster and would use native QGIS widgets.
It has the advantage of providing useful signal/slot with layers.

This comment has been minimized.

Copy link
@NathanW2

NathanW2 via email May 23, 2014

Author Member

This comment has been minimized.

Copy link
@3nids

3nids May 23, 2014

Member

I don't think it's worth a change, as the field combo is really straightforward.
Just a remark for next time ;)

connect( mOpenExpressionWidget, SIGNAL( clicked() ), this, SLOT( openExpressionBuilder() ) );
connect( mRunFieldCalc, SIGNAL( clicked() ), this, SLOT( updateFieldFromExpression() ) );
editingToggled();
}

QgsAttributeTableDialog::~QgsAttributeTableDialog()
@@ -214,6 +222,11 @@ void QgsAttributeTableDialog::updateTitle()
.arg( mMainView->filteredFeatureCount() )
.arg( mLayer->selectedFeatureCount() )
);

if ( mMainView->filterMode() == QgsAttributeTableFilterModel::ShowAll )
mRunFieldCalc->setText( tr( "Update All") );
else
mRunFieldCalc->setText( tr( "Update Filtered") );
}

void QgsAttributeTableDialog::closeEvent( QCloseEvent* event )
@@ -275,6 +288,88 @@ void QgsAttributeTableDialog::columnBoxInit()
}
}

void QgsAttributeTableDialog::updateFieldFromExpression()
{
QApplication::setOverrideCursor( Qt::WaitCursor );

mLayer->beginEditCommand( "Field calculator" );

QModelIndex modelindex = mFieldModel->indexFromName( mFieldComboButton->currentText() );
int fieldindex = modelindex.data( QgsFieldModel::FieldIndexRole ).toInt();

bool calculationSuccess = true;
QString error;

QgsExpression exp( mUpdateExpressionText->text() );
bool useGeometry = exp.needsGeometry();

QgsFeatureRequest request;
request.setFlags( useGeometry ? QgsFeatureRequest::NoFlags : QgsFeatureRequest::NoGeometry );
QgsFeatureIds filteredIds = mMainView->filteredFeatures();
QgsDebugMsg( QString( filteredIds.size() ) );

// This would be nice but doesn't work on all providers
// if ( mMainView->filterMode() != QgsAttributeTableFilterModel::ShowAll )
// {
// QgsDebugMsg( " Updating only selected features " );
// request.setFilterFids( mMainView->filteredFeatures() );
// }

bool filtered = mMainView->filterMode() != QgsAttributeTableFilterModel::ShowAll;
int rownum = 1;

//go through all the features and change the new attributes
QgsFeatureIterator fit = mLayer->getFeatures( request );
QgsFeature feature;
while ( fit.nextFeature( feature ) )
{
if ( filtered )
{
if ( !filteredIds.contains( feature.id() ) )
{
continue;
}
}

exp.setCurrentRowNumber( rownum );
QVariant value = exp.evaluate( &feature );
// Bail if we have a update error
if ( exp.hasEvalError() )
{
calculationSuccess = false;
error = exp.evalErrorString();
break;
}
else
{
QVariant oldvalue = feature.attributes().value( fieldindex );
mLayer->changeAttributeValue( feature.id(), fieldindex, value, oldvalue );
}

rownum++;
}

QApplication::restoreOverrideCursor();

if ( !calculationSuccess )
{
QMessageBox::critical( 0, tr( "Error" ), tr( "An error occured while evaluating the calculation string:\n%1" ).arg( error ) );
mLayer->destroyEditCommand();
return;
}

mLayer->endEditCommand();
}

void QgsAttributeTableDialog::openExpressionBuilder()
{
QgsExpressionBuilderDialog dlg( mLayer, mUpdateExpressionText->text(), this );
if ( dlg.exec() )
{
mUpdateExpressionText->setText( dlg.expressionText() );
}
}

void QgsAttributeTableDialog::filterColumnChanged( QObject* filterAction )
{
mFilterButton->setDefaultAction( qobject_cast<QAction *>( filterAction ) );
@@ -475,6 +570,7 @@ void QgsAttributeTableDialog::editingToggled()
mRemoveAttribute->setEnabled( canDeleteAttributes && mLayer->isEditable() );
mAddFeature->setEnabled( canAddFeatures && mLayer->isEditable() && mLayer->geometryType() == QGis::NoGeometry );

mUpdateExpressionBox->setVisible( mLayer->isEditable() );
// not necessary to set table read only if layer is not editable
// because model always reflects actual state when returning item flags
}
@@ -28,6 +28,7 @@

#include "qgsattributedialog.h"
#include "qgsvectorlayer.h" //QgsFeatureIds
#include "qgsfieldmodel.h"

class QDialogButtonBox;
class QPushButton;
@@ -184,6 +185,10 @@ class APP_EXPORT QgsAttributeTableDialog : public QDialog, private Ui::QgsAttrib
*/
void columnBoxInit();

void openExpressionBuilder();

void updateFieldFromExpression();

private:
QMenu* mMenuActions;
QAction* mActionToggleEditing;
@@ -194,6 +199,7 @@ class APP_EXPORT QgsAttributeTableDialog : public QDialog, private Ui::QgsAttrib
QSignalMapper* mFilterActionMapper;

QgsVectorLayer* mLayer;
QgsFieldModel* mFieldModel;
};

#endif
@@ -130,6 +130,17 @@ void QgsAttributeTableFilterModel::setFilteredFeatures( QgsFeatureIds ids )
invalidateFilter();
}

QgsFeatureIds QgsAttributeTableFilterModel::filteredFeatures()
{
QgsFeatureIds ids;
for (int i = 0; i < rowCount(); ++i)
{
QModelIndex row = index( i, 0 );
ids << rowToId( row );
}
return ids;
}

void QgsAttributeTableFilterModel::setFilterMode( FilterMode filterMode )
{
if ( filterMode != mFilterMode )
@@ -77,13 +77,17 @@ class GUI_EXPORT QgsAttributeTableFilterModel: public QSortFilterProxyModel, pub
*/
virtual void setFilteredFeatures( QgsFeatureIds ids );

QgsFeatureIds filteredFeatures();

/**
* Set the filter mode the filter will use.
*
* @param filterMode Sets the current mode of the filter
*/
void setFilterMode( FilterMode filterMode );

FilterMode filterMode() { return mFilterMode; }

/**
* Returns the layer this filter acts on.
*
@@ -196,6 +196,7 @@ void QgsDualView::setView( QgsDualView::ViewMode view )
void QgsDualView::setFilterMode( QgsAttributeTableFilterModel::FilterMode filterMode )
{
mFilterModel->setFilterMode( filterMode );
emit filterChanged();
}

void QgsDualView::setSelectedOnTop( bool selectedOnTop )
@@ -95,6 +95,8 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
*/
void setFilterMode( QgsAttributeTableFilterModel::FilterMode filterMode );

QgsAttributeTableFilterModel::FilterMode filterMode() { return mFilterModel->filterMode(); }

/**
* Toggle the selectedOnTop flag. If enabled, selected features will be moved to top.
*
@@ -126,6 +128,8 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
*/
void setFilteredFeatures( QgsFeatureIds filteredFeatures );

QgsFeatureIds filteredFeatures() { return mFilterModel->filteredFeatures(); }

/**
* Returns the model which has the information about all features (not only filtered)
*
@@ -17,6 +17,9 @@
<property name="margin">
<number>0</number>
</property>
<property name="spacing">
<number>3</number>
</property>
<item row="3" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
@@ -422,14 +425,14 @@
</item>
</layout>
</item>
<item row="4" column="0">
<item row="7" column="0">
<widget class="QgsDualView" name="mMainView">
<property name="currentIndex">
<number>-1</number>
</property>
</widget>
</item>
<item row="6" column="0">
<item row="9" column="0">
<layout class="QHBoxLayout">
<property name="leftMargin">
<number>3</number>
@@ -510,7 +513,7 @@
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">mMainViewButtonGroup</string>
<string>mMainViewButtonGroup</string>
</attribute>
</widget>
</item>
@@ -530,14 +533,57 @@
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">mMainViewButtonGroup</string>
<string>mMainViewButtonGroup</string>
</attribute>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item row="6" column="0">
<widget class="QFrame" name="mUpdateExpressionBox">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>3</number>
</property>
<property name="topMargin">
<number>1</number>
</property>
<item>
<widget class="QComboBox" name="mFieldComboButton"/>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>=</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="mOpenExpressionWidget">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="mUpdateExpressionText">
<property name="placeholderText">
<string>update expression...</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="mRunFieldCalc">
<property name="text">
<string>Update All</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
<action name="mActionAdvancedFilter">
<property name="icon">

0 comments on commit ee9e17c

Please sign in to comment.