Skip to content

Commit 5e7f191

Browse files
author
morb_au
committed
Fix for bug in trac ticket #134 and #131.
* (#134) If a feature is edited using the Identify tool and QgsAttributeDialog, now only user-edited values are saved to the mChangedAttributes of the layer, previously it was all values. This now means that only those user-edited values are attempted to be committed when the user stops editing and saves (similar to if those values were edited using the Attribute Table). * (#131) Modify QgsVectorLayer::stopEditing so that if a commit fails, the editing state is left alone (previously editing was still turned off anyway). This should be a better fix than r5591. I triggered the bug in #131 while testing for #134, therefore getting a 2-for-1 fix. Some bonus features to assist people in the triggered situations described in #131 and #134: * The Postgres Provider now reports an error to the user if a commit of changed attributes fails. This brings it up to the same behaviour of if a commit of changed geometries fails. Previously there was no response given on an changed attribute error, leaving the user to think the commit was successful. * The Postgres Provider now reports the contents of the SQL used in a failed UPDATE statement. This may help the user to pinpoint which of his edits caused the error. git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@5694 c8812cc2-4d05-0410-92ff-de0c093fc19c
1 parent 2b686f4 commit 5e7f191

File tree

5 files changed

+177
-63
lines changed

5 files changed

+177
-63
lines changed

src/gui/qgsattributedialog.cpp

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,41 @@
2121
#include <QSettings>
2222

2323
QgsAttributeDialog::QgsAttributeDialog(const std::vector<QgsFeatureAttribute>* attributes)
24-
: QDialog(), _settingsPath("/Windows/AttributeDialog/")
24+
: QDialog(),
25+
_settingsPath("/Windows/AttributeDialog/"),
26+
mRowIsDirty(attributes->size(), FALSE)
2527
{
2628
restorePositionAndColumnWidth();
2729

2830
setupUi(this);
2931
mTable->setRowCount(attributes->size());
3032

3133
int index=0;
32-
for(std::vector<QgsFeatureAttribute>::const_iterator it=attributes->begin();it!=attributes->end();++it)
34+
for ( std::vector<QgsFeatureAttribute>::const_iterator
35+
it = attributes->begin();
36+
it != attributes->end();
37+
++it)
3338
{
39+
// set attribute name
40+
3441
QTableWidgetItem * myFieldItem = new QTableWidgetItem((*it).fieldName());
3542
myFieldItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
3643
mTable->setItem(index, 0, myFieldItem);
44+
45+
// set attribute value
46+
3747
QTableWidgetItem * myValueItem = new QTableWidgetItem((*it).fieldValue());
3848
mTable->setItem(index, 1, myValueItem);
49+
3950
++index;
4051
}
52+
53+
// setup the mechanism to track edited attribute values
54+
// if we do it this way, only edited attributes will
55+
// be attempted to be saved when the editing session stops.
56+
connect(mTable, SIGNAL(cellChanged(int, int)),
57+
this, SLOT (setAttributeValueChanged(int, int)));
58+
4159
mTable->resizeColumnsToContents();
4260
}
4361

@@ -48,26 +66,33 @@ QgsAttributeDialog::~QgsAttributeDialog()
4866

4967
QString QgsAttributeDialog::value(int row)
5068
{
51-
return mTable->item(row,1)->text();
69+
return mTable->item(row,1)->text();
70+
}
71+
72+
bool QgsAttributeDialog::isDirty(int row)
73+
{
74+
return mRowIsDirty.at(row);
5275
}
5376

5477
bool QgsAttributeDialog::queryAttributes(QgsFeature& f)
5578
{
5679
const std::vector<QgsFeatureAttribute> featureAttributes = f.attributeMap();
5780
QgsAttributeDialog attdialog(&featureAttributes);
58-
if(attdialog.exec()==QDialog::Accepted)
81+
82+
if (attdialog.exec() == QDialog::Accepted)
83+
{
84+
for (int i = 0; i < featureAttributes.size(); ++i)
5985
{
60-
for(int i=0;i<featureAttributes.size();++i)
61-
{
62-
f.changeAttributeValue(featureAttributes[i].fieldName(), attdialog.value(i));
63-
}
64-
return true;
86+
f.changeAttributeValue(featureAttributes[i].fieldName(), attdialog.value(i));
6587
}
88+
return true;
89+
}
6690
else
67-
{
68-
return false;
69-
}
91+
{
92+
return false;
93+
}
7094
}
95+
7196
void QgsAttributeDialog::savePositionAndColumnWidth()
7297
{
7398
QSettings settings;
@@ -91,3 +116,15 @@ void QgsAttributeDialog::restorePositionAndColumnWidth()
91116
resize(ww,wh);
92117
move(wx,wy);
93118
}
119+
120+
void QgsAttributeDialog::setAttributeValueChanged(int row, int column)
121+
{
122+
#ifdef QGISDEBUG
123+
std::cout << "QgsAttributeDialog::setAttributeValueChanged: Entered with "
124+
<< "row " << row
125+
<< "column " << column
126+
<< "." << std::endl;
127+
#endif
128+
129+
mRowIsDirty.at(row) = TRUE;
130+
}

src/gui/qgsattributedialog.h

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,38 @@ class QgsFeature;
2929
class QgsAttributeDialog: public QDialog, private Ui::QgsAttributeDialogBase
3030
{
3131
Q_OBJECT
32-
public:
32+
33+
public:
3334
QgsAttributeDialog(const std::vector<QgsFeatureAttribute>* attributes);
35+
3436
~QgsAttributeDialog();
35-
/**Returns the field value of a row*/
37+
38+
/** Returns the field value of a row */
3639
QString value(int row);
37-
/**Opens an attribute dialog and queries the attributes for a given feature. The
40+
41+
/** Returns if the field value of a row was edited since this dialog opened */
42+
bool isDirty(int row);
43+
44+
/** Opens an attribute dialog and queries the attributes for a given feature. The
3845
attribute values are set to the feature if the dialog is accepted.
39-
Returns true if accepted and false if canceled*/
46+
\retval true if accepted
47+
\retval false if canceled */
4048
static bool queryAttributes(QgsFeature& f);
4149

4250
// Saves and restores the size and position from the last time
4351
// this dialog box was used.
4452
void savePositionAndColumnWidth();
53+
4554
void restorePositionAndColumnWidth();
46-
private:
55+
56+
public slots:
57+
//! Slot to be called when an attribute value is edited in the table.
58+
void setAttributeValueChanged(int row, int column);
59+
60+
private:
4761
QString _settingsPath;
62+
63+
std::vector<bool> mRowIsDirty;
4864
};
4965

5066
#endif

src/gui/qgsmaptoolidentify.cpp

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ void QgsMapToolIdentify::identifyVectorLayer(QgsVectorLayer* layer, const QgsPoi
314314

315315
mResults->show();
316316
}
317-
else
317+
else // ( layer->isEditable() )
318318
{
319319
// Edit attributes
320320
// TODO: what to do if more features were selected? - nearest?
@@ -324,48 +324,70 @@ void QgsMapToolIdentify::identifyVectorLayer(QgsVectorLayer* layer, const QgsPoi
324324

325325
if ( (fet = dataProvider->getNextFeature(true)) )
326326
{
327-
// Was already changed?
328-
changed_attr_map::iterator it = changedAttributes.find(fet->featureId());
329-
327+
// these are the values to populate the dialog with
330328
std::vector < QgsFeatureAttribute > old;
329+
330+
// start off with list of committed attribute values
331+
old = fet->attributeMap();
332+
333+
// Test if this feature already changed since the last commit
334+
335+
changed_attr_map::iterator it = changedAttributes.find(fet->featureId());
331336
if ( it != changedAttributes.end() )
332337
{
338+
// Yes, this feature already has in-memory attribute changes
339+
340+
// go through and apply the modified-but-not-committed values
333341
std::map<QString,QString> oldattr = (*it).second;
334-
for( std::map<QString,QString>::iterator ait = oldattr.begin(); ait!=oldattr.end(); ++ait )
342+
int index=0;
343+
for ( std::vector<QgsFeatureAttribute>::const_iterator
344+
oldit = old.begin();
345+
oldit != old.end();
346+
++oldit)
335347
{
336-
old.push_back ( QgsFeatureAttribute ( (*ait).first, (*ait).second ) );
348+
std::map<QString,QString>::iterator ait =
349+
oldattr.find( (*oldit).fieldName() );
350+
if ( ait != oldattr.end() )
351+
{
352+
// replace the committed value with the
353+
// modified-but-not-committed value
354+
old[index] = QgsFeatureAttribute ( (*ait).first, (*ait).second );
355+
}
356+
357+
++index;
337358
}
338359
}
339-
else
340-
{
341-
old = fet->attributeMap();
342-
}
343-
360+
344361
QApplication::restoreOverrideCursor();
345-
362+
363+
// Show the attribute value editing dialog
346364
QgsAttributeDialog ad( &old );
347365

348-
if ( ad.exec()==QDialog::Accepted )
366+
if (ad.exec() == QDialog::Accepted)
349367
{
350-
std::map<QString,QString> attr;
351-
352-
// Do this only once rather than each pass through the loop
368+
353369
int oldSize = old.size();
354-
for(register int i= 0; i < oldSize; ++i)
370+
371+
372+
for (int i = 0; i < oldSize; ++i)
355373
{
356-
attr.insert ( std::make_pair( old[i].fieldName(), ad.value(i) ) );
357-
}
358-
359-
// Remove old if exists
360-
it = changedAttributes.find(fet->featureId());
374+
// only apply changed values if they were edited by the user
375+
if (ad.isDirty(i))
376+
{
377+
#ifdef QGISDEBUG
378+
std::cout << "QgsMapToolIdentify::identifyVectorLayer: found an changed attribute: "
379+
<< old[i].fieldName().toLocal8Bit().data()
380+
<< " = "
381+
<< ad.value(i).toLocal8Bit().data()
382+
<< "." << std::endl;
383+
#endif
384+
changedAttributes[ fet->featureId() ][ old[i].fieldName() ] = ad.value(i);
361385

362-
if ( it != changedAttributes.end() )
363-
{ // found
364-
changedAttributes.erase ( it );
386+
// propagate "dirtyness" to the layer
387+
layer->setModified();
388+
}
365389
}
366390

367-
changedAttributes.insert ( std::make_pair( fet->featureId(), attr ) );
368-
layer->setModified();
369391
}
370392
}
371393
else

src/gui/qgsvectorlayer.cpp

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1935,7 +1935,9 @@ void QgsVectorLayer::startEditing()
19351935

19361936
void QgsVectorLayer::stopEditing()
19371937
{
1938-
deleteCachedGeometries();
1938+
bool commitSuccessful = FALSE;
1939+
bool rollbackSuccessful = FALSE;
1940+
19391941
if(dataProvider)
19401942
{
19411943
if(mModified)
@@ -1945,9 +1947,15 @@ void QgsVectorLayer::stopEditing()
19451947

19461948
if(commit==0)
19471949
{
1948-
if(!commitChanges())
1950+
commitSuccessful = commitChanges();
1951+
1952+
if(!commitSuccessful)
19491953
{
19501954
QMessageBox::information(0,tr("Error"),tr("Could not commit changes"),QMessageBox::Ok);
1955+
1956+
// Leave the in-memory editing state alone,
1957+
// to give the user a chance to enter different values
1958+
// and try the commit again later
19511959
}
19521960
else
19531961
{
@@ -1976,22 +1984,36 @@ void QgsVectorLayer::stopEditing()
19761984
delete tabledisplay;
19771985
tabledisplay=0;
19781986
}
1987+
1988+
// Force this to TRUE otherwise the user will never be able
1989+
// to get out of an ediitng session
1990+
rollbackSuccessful = TRUE;
19791991
}
19801992
emit editingStopped(true);
19811993
}
1982-
else
1994+
else // if (!mModified)
19831995
{
19841996
emit editingStopped(false);
19851997
}
1986-
mEditable=false;
1987-
triggerRepaint();
1988-
mModified=false;
1989-
if(isValid())
1998+
1999+
if (
2000+
(commitSuccessful) ||
2001+
(rollbackSuccessful)
2002+
)
19902003
{
1991-
updateItemPixmap();
1992-
if(mToggleEditingAction)
2004+
// convert state to non-editing mode
2005+
deleteCachedGeometries();
2006+
2007+
mEditable=false;
2008+
triggerRepaint();
2009+
mModified=false;
2010+
if(isValid())
19932011
{
1994-
mToggleEditingAction->setChecked(false);
2012+
updateItemPixmap();
2013+
if(mToggleEditingAction)
2014+
{
2015+
mToggleEditingAction->setChecked(false);
2016+
}
19952017
}
19962018
}
19972019
}

src/providers/postgres/qgspostgresprovider.cpp

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2005,17 +2005,31 @@ bool QgsPostgresProvider::changeAttributeValues(std::map<int,std::map<QString,QS
20052005
qWarning(sql);
20062006
#endif
20072007

2008-
//send sql statement and do error handling
2008+
// s end sql statement and do error handling
2009+
// TODO: Make all error handling like this one
20092010
PGresult* result=PQexec(connection, (const char *)(sql.utf8()));
2010-
if(result==0)
2011+
if (result==0)
20112012
{
2012-
returnvalue=false;
2013-
ExecStatusType message=PQresultStatus(result);
2014-
if(message==PGRES_FATAL_ERROR)
2015-
{
2016-
QMessageBox::information(0,"UPDATE error",QString(PQresultErrorMessage(result)),QMessageBox::Ok);
2017-
}
2013+
QMessageBox::critical(0, tr("PostGIS error"),
2014+
tr("An error occured contacting the PostgreSQL databse"),
2015+
QMessageBox::Ok,
2016+
Qt::NoButton);
2017+
return false;
2018+
}
2019+
ExecStatusType message=PQresultStatus(result);
2020+
if(message==PGRES_FATAL_ERROR)
2021+
{
2022+
QMessageBox::information(0, tr("PostGIS error"),
2023+
tr("The PostgreSQL databse returned: ")
2024+
+ QString(PQresultErrorMessage(result))
2025+
+ "\n"
2026+
+ tr("When trying: ")
2027+
+ sql,
2028+
QMessageBox::Ok,
2029+
Qt::NoButton);
2030+
return false;
20182031
}
2032+
20192033
}
20202034
}
20212035
PQexec(connection,"COMMIT");
@@ -2153,7 +2167,10 @@ bool QgsPostgresProvider::changeGeometryValues(std::map<int, QgsGeometry> & geom
21532167
{
21542168
QMessageBox::information(0, tr("PostGIS error"),
21552169
tr("The PostgreSQL databse returned: ")
2156-
+ QString(PQresultErrorMessage(result)),
2170+
+ QString(PQresultErrorMessage(result))
2171+
+ "\n"
2172+
+ tr("When trying: ")
2173+
+ sql,
21572174
QMessageBox::Ok,
21582175
Qt::NoButton);
21592176
return false;

0 commit comments

Comments
 (0)