Skip to content

Commit ee9e17c

Browse files
committed
[FEATURE] Quick field field calc bar (like Excel) in attribute table
1 parent 9873782 commit ee9e17c

7 files changed

+172
-4
lines changed

src/app/qgsattributetabledialog.cpp

+96
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "qgsmessagebar.h"
4242
#include "qgsexpressionselectiondialog.h"
4343
#include "qgsfeaturelistmodel.h"
44+
#include "qgsexpressionbuilderdialog.h"
4445

4546
class QgsAttributeTableDock : public QDockWidget
4647
{
@@ -199,6 +200,13 @@ QgsAttributeTableDialog::QgsAttributeTableDialog( QgsVectorLayer *theLayer, QWid
199200
filterShowAll();
200201
break;
201202
}
203+
204+
mFieldModel = new QgsFieldModel();
205+
mFieldModel->setLayer( mLayer );
206+
mFieldComboButton->setModel( mFieldModel );
207+
connect( mOpenExpressionWidget, SIGNAL( clicked() ), this, SLOT( openExpressionBuilder() ) );
208+
connect( mRunFieldCalc, SIGNAL( clicked() ), this, SLOT( updateFieldFromExpression() ) );
209+
editingToggled();
202210
}
203211

204212
QgsAttributeTableDialog::~QgsAttributeTableDialog()
@@ -214,6 +222,11 @@ void QgsAttributeTableDialog::updateTitle()
214222
.arg( mMainView->filteredFeatureCount() )
215223
.arg( mLayer->selectedFeatureCount() )
216224
);
225+
226+
if ( mMainView->filterMode() == QgsAttributeTableFilterModel::ShowAll )
227+
mRunFieldCalc->setText( tr( "Update All") );
228+
else
229+
mRunFieldCalc->setText( tr( "Update Filtered") );
217230
}
218231

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

291+
void QgsAttributeTableDialog::updateFieldFromExpression()
292+
{
293+
QApplication::setOverrideCursor( Qt::WaitCursor );
294+
295+
mLayer->beginEditCommand( "Field calculator" );
296+
297+
QModelIndex modelindex = mFieldModel->indexFromName( mFieldComboButton->currentText() );
298+
int fieldindex = modelindex.data( QgsFieldModel::FieldIndexRole ).toInt();
299+
300+
bool calculationSuccess = true;
301+
QString error;
302+
303+
QgsExpression exp( mUpdateExpressionText->text() );
304+
bool useGeometry = exp.needsGeometry();
305+
306+
QgsFeatureRequest request;
307+
request.setFlags( useGeometry ? QgsFeatureRequest::NoFlags : QgsFeatureRequest::NoGeometry );
308+
QgsFeatureIds filteredIds = mMainView->filteredFeatures();
309+
QgsDebugMsg( QString( filteredIds.size() ) );
310+
311+
// This would be nice but doesn't work on all providers
312+
// if ( mMainView->filterMode() != QgsAttributeTableFilterModel::ShowAll )
313+
// {
314+
// QgsDebugMsg( " Updating only selected features " );
315+
// request.setFilterFids( mMainView->filteredFeatures() );
316+
// }
317+
318+
bool filtered = mMainView->filterMode() != QgsAttributeTableFilterModel::ShowAll;
319+
int rownum = 1;
320+
321+
//go through all the features and change the new attributes
322+
QgsFeatureIterator fit = mLayer->getFeatures( request );
323+
QgsFeature feature;
324+
while ( fit.nextFeature( feature ) )
325+
{
326+
if ( filtered )
327+
{
328+
if ( !filteredIds.contains( feature.id() ) )
329+
{
330+
continue;
331+
}
332+
}
333+
334+
exp.setCurrentRowNumber( rownum );
335+
QVariant value = exp.evaluate( &feature );
336+
// Bail if we have a update error
337+
if ( exp.hasEvalError() )
338+
{
339+
calculationSuccess = false;
340+
error = exp.evalErrorString();
341+
break;
342+
}
343+
else
344+
{
345+
QVariant oldvalue = feature.attributes().value( fieldindex );
346+
mLayer->changeAttributeValue( feature.id(), fieldindex, value, oldvalue );
347+
}
348+
349+
rownum++;
350+
}
351+
352+
QApplication::restoreOverrideCursor();
353+
354+
if ( !calculationSuccess )
355+
{
356+
QMessageBox::critical( 0, tr( "Error" ), tr( "An error occured while evaluating the calculation string:\n%1" ).arg( error ) );
357+
mLayer->destroyEditCommand();
358+
return;
359+
}
360+
361+
mLayer->endEditCommand();
362+
}
363+
364+
void QgsAttributeTableDialog::openExpressionBuilder()
365+
{
366+
QgsExpressionBuilderDialog dlg( mLayer, mUpdateExpressionText->text(), this );
367+
if ( dlg.exec() )
368+
{
369+
mUpdateExpressionText->setText( dlg.expressionText() );
370+
}
371+
}
372+
278373
void QgsAttributeTableDialog::filterColumnChanged( QObject* filterAction )
279374
{
280375
mFilterButton->setDefaultAction( qobject_cast<QAction *>( filterAction ) );
@@ -475,6 +570,7 @@ void QgsAttributeTableDialog::editingToggled()
475570
mRemoveAttribute->setEnabled( canDeleteAttributes && mLayer->isEditable() );
476571
mAddFeature->setEnabled( canAddFeatures && mLayer->isEditable() && mLayer->geometryType() == QGis::NoGeometry );
477572

573+
mUpdateExpressionBox->setVisible( mLayer->isEditable() );
478574
// not necessary to set table read only if layer is not editable
479575
// because model always reflects actual state when returning item flags
480576
}

src/app/qgsattributetabledialog.h

+6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#include "qgsattributedialog.h"
3030
#include "qgsvectorlayer.h" //QgsFeatureIds
31+
#include "qgsfieldmodel.h"
3132

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

188+
void openExpressionBuilder();
189+
190+
void updateFieldFromExpression();
191+
187192
private:
188193
QMenu* mMenuActions;
189194
QAction* mActionToggleEditing;
@@ -194,6 +199,7 @@ class APP_EXPORT QgsAttributeTableDialog : public QDialog, private Ui::QgsAttrib
194199
QSignalMapper* mFilterActionMapper;
195200

196201
QgsVectorLayer* mLayer;
202+
QgsFieldModel* mFieldModel;
197203
};
198204

199205
#endif

src/gui/attributetable/qgsattributetablefiltermodel.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,17 @@ void QgsAttributeTableFilterModel::setFilteredFeatures( QgsFeatureIds ids )
130130
invalidateFilter();
131131
}
132132

133+
QgsFeatureIds QgsAttributeTableFilterModel::filteredFeatures()
134+
{
135+
QgsFeatureIds ids;
136+
for (int i = 0; i < rowCount(); ++i)
137+
{
138+
QModelIndex row = index( i, 0 );
139+
ids << rowToId( row );
140+
}
141+
return ids;
142+
}
143+
133144
void QgsAttributeTableFilterModel::setFilterMode( FilterMode filterMode )
134145
{
135146
if ( filterMode != mFilterMode )

src/gui/attributetable/qgsattributetablefiltermodel.h

+4
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,17 @@ class GUI_EXPORT QgsAttributeTableFilterModel: public QSortFilterProxyModel, pub
7777
*/
7878
virtual void setFilteredFeatures( QgsFeatureIds ids );
7979

80+
QgsFeatureIds filteredFeatures();
81+
8082
/**
8183
* Set the filter mode the filter will use.
8284
*
8385
* @param filterMode Sets the current mode of the filter
8486
*/
8587
void setFilterMode( FilterMode filterMode );
8688

89+
FilterMode filterMode() { return mFilterMode; }
90+
8791
/**
8892
* Returns the layer this filter acts on.
8993
*

src/gui/attributetable/qgsdualview.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ void QgsDualView::setView( QgsDualView::ViewMode view )
196196
void QgsDualView::setFilterMode( QgsAttributeTableFilterModel::FilterMode filterMode )
197197
{
198198
mFilterModel->setFilterMode( filterMode );
199+
emit filterChanged();
199200
}
200201

201202
void QgsDualView::setSelectedOnTop( bool selectedOnTop )

src/gui/attributetable/qgsdualview.h

+4
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
9595
*/
9696
void setFilterMode( QgsAttributeTableFilterModel::FilterMode filterMode );
9797

98+
QgsAttributeTableFilterModel::FilterMode filterMode() { return mFilterModel->filterMode(); }
99+
98100
/**
99101
* Toggle the selectedOnTop flag. If enabled, selected features will be moved to top.
100102
*
@@ -126,6 +128,8 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
126128
*/
127129
void setFilteredFeatures( QgsFeatureIds filteredFeatures );
128130

131+
QgsFeatureIds filteredFeatures() { return mFilterModel->filteredFeatures(); }
132+
129133
/**
130134
* Returns the model which has the information about all features (not only filtered)
131135
*

src/ui/qgsattributetabledialog.ui

+50-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
<property name="margin">
1818
<number>0</number>
1919
</property>
20+
<property name="spacing">
21+
<number>3</number>
22+
</property>
2023
<item row="3" column="0">
2124
<layout class="QHBoxLayout" name="horizontalLayout">
2225
<property name="leftMargin">
@@ -422,14 +425,14 @@
422425
</item>
423426
</layout>
424427
</item>
425-
<item row="4" column="0">
428+
<item row="7" column="0">
426429
<widget class="QgsDualView" name="mMainView">
427430
<property name="currentIndex">
428431
<number>-1</number>
429432
</property>
430433
</widget>
431434
</item>
432-
<item row="6" column="0">
435+
<item row="9" column="0">
433436
<layout class="QHBoxLayout">
434437
<property name="leftMargin">
435438
<number>3</number>
@@ -510,7 +513,7 @@
510513
<bool>true</bool>
511514
</property>
512515
<attribute name="buttonGroup">
513-
<string notr="true">mMainViewButtonGroup</string>
516+
<string>mMainViewButtonGroup</string>
514517
</attribute>
515518
</widget>
516519
</item>
@@ -530,14 +533,57 @@
530533
<bool>true</bool>
531534
</property>
532535
<attribute name="buttonGroup">
533-
<string notr="true">mMainViewButtonGroup</string>
536+
<string>mMainViewButtonGroup</string>
534537
</attribute>
535538
</widget>
536539
</item>
537540
</layout>
538541
</item>
539542
</layout>
540543
</item>
544+
<item row="6" column="0">
545+
<widget class="QFrame" name="mUpdateExpressionBox">
546+
<layout class="QHBoxLayout" name="horizontalLayout_3">
547+
<property name="spacing">
548+
<number>3</number>
549+
</property>
550+
<property name="topMargin">
551+
<number>1</number>
552+
</property>
553+
<item>
554+
<widget class="QComboBox" name="mFieldComboButton"/>
555+
</item>
556+
<item>
557+
<widget class="QLabel" name="label">
558+
<property name="text">
559+
<string>=</string>
560+
</property>
561+
</widget>
562+
</item>
563+
<item>
564+
<widget class="QToolButton" name="mOpenExpressionWidget">
565+
<property name="text">
566+
<string>...</string>
567+
</property>
568+
</widget>
569+
</item>
570+
<item>
571+
<widget class="QLineEdit" name="mUpdateExpressionText">
572+
<property name="placeholderText">
573+
<string>update expression...</string>
574+
</property>
575+
</widget>
576+
</item>
577+
<item>
578+
<widget class="QToolButton" name="mRunFieldCalc">
579+
<property name="text">
580+
<string>Update All</string>
581+
</property>
582+
</widget>
583+
</item>
584+
</layout>
585+
</widget>
586+
</item>
541587
</layout>
542588
<action name="mActionAdvancedFilter">
543589
<property name="icon">

0 commit comments

Comments
 (0)