Skip to content

Commit 50c3592

Browse files
committedAug 15, 2016
Make QgsVectorLayer uniqueValues/min/maxValue consider edits
Previously these methods would inconsistently handle the edit buffer, eg uniqueValues would consider changed attributes but not added features. Now uniqueValues, minimumValue and maximumValue all consider both added features and changed attribute values when performing their calculation. The most noticable effect of this fix is that the unique values widget now correctly shows values for features which have been added but not yet committed to the provider.
1 parent 251fffa commit 50c3592

File tree

5 files changed

+217
-11
lines changed

5 files changed

+217
-11
lines changed
 

‎python/core/qgsvectorlayer.sip

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1265,17 +1265,35 @@ class QgsVectorLayer : QgsMapLayer
12651265
// marked as const as these are just caches, and need to be created from const accessors
12661266
void createJoinCaches() const;
12671267

1268-
/** Returns unique values for column
1268+
/** Calculates a list of unique values contained within an attribute in the layer. Note that
1269+
* in some circumstances when unsaved changes are present for the layer then the returned list
1270+
* may contain outdated values (for instance when the attribute value in a saved feature has
1271+
* been changed inside the edit buffer then the previous saved value will be included in the
1272+
* returned list).
12691273
* @param index column index for attribute
12701274
* @param uniqueValues out: result list
1271-
* @param limit maximum number of values to return (-1 if unlimited)
1275+
* @param limit maximum number of values to return (or -1 if unlimited)
1276+
* @see minimumValue()
1277+
* @see maximumValue()
12721278
*/
12731279
void uniqueValues( int index, QList<QVariant> &uniqueValues /Out/, int limit = -1 ) const;
12741280

1275-
/** Returns minimum value for an attribute column or invalid variant in case of error */
1281+
/** Returns the minimum value for an attribute column or an invalid variant in case of error.
1282+
* Note that in some circumstances when unsaved changes are present for the layer then the
1283+
* returned value may be outdated (for instance when the attribute value in a saved feature has
1284+
* been changed inside the edit buffer then the previous saved value may be returned as the minimum).
1285+
* @see maximumValue()
1286+
* @see uniqueValues()
1287+
*/
12761288
QVariant minimumValue( int index ) const;
12771289

1278-
/** Returns maximum value for an attribute column or invalid variant in case of error */
1290+
/** Returns the maximum value for an attribute column or an invalid variant in case of error.
1291+
* Note that in some circumstances when unsaved changes are present for the layer then the
1292+
* returned value may be outdated (for instance when the attribute value in a saved feature has
1293+
* been changed inside the edit buffer then the previous saved value may be returned as the maximum).
1294+
* @see minimumValue()
1295+
* @see uniqueValues()
1296+
*/
12791297
QVariant maximumValue( int index ) const;
12801298

12811299
/** Calculates an aggregated value from the layer's features.

‎src/core/qgsvectorlayer.cpp

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2819,6 +2819,23 @@ void QgsVectorLayer::uniqueValues( int index, QList<QVariant> &uniqueValues, int
28192819
vals << v.toString();
28202820
}
28212821

2822+
QgsFeatureMap added = mEditBuffer->addedFeatures();
2823+
QMapIterator< QgsFeatureId, QgsFeature > addedIt( added );
2824+
while ( addedIt.hasNext() && ( limit < 0 || uniqueValues.count() < limit ) )
2825+
{
2826+
addedIt.next();
2827+
QVariant v = addedIt.value().attribute( index );
2828+
if ( v.isValid() )
2829+
{
2830+
QString vs = v.toString();
2831+
if ( !vals.contains( vs ) )
2832+
{
2833+
vals << vs;
2834+
uniqueValues << v;
2835+
}
2836+
}
2837+
}
2838+
28222839
QMapIterator< QgsFeatureId, QgsAttributeMap > it( mEditBuffer->changedAttributeValues() );
28232840
while ( it.hasNext() && ( limit < 0 || uniqueValues.count() < limit ) )
28242841
{
@@ -2897,7 +2914,35 @@ QVariant QgsVectorLayer::minimumValue( int index ) const
28972914
return QVariant();
28982915

28992916
case QgsFields::OriginProvider: //a provider field
2900-
return mDataProvider->minimumValue( index );
2917+
{
2918+
QVariant min = mDataProvider->minimumValue( index );
2919+
if ( mEditBuffer )
2920+
{
2921+
QgsFeatureMap added = mEditBuffer->addedFeatures();
2922+
QMapIterator< QgsFeatureId, QgsFeature > addedIt( added );
2923+
while ( addedIt.hasNext() )
2924+
{
2925+
addedIt.next();
2926+
QVariant v = addedIt.value().attribute( index );
2927+
if ( v.isValid() && qgsVariantLessThan( v, min ) )
2928+
{
2929+
min = v;
2930+
}
2931+
}
2932+
2933+
QMapIterator< QgsFeatureId, QgsAttributeMap > it( mEditBuffer->changedAttributeValues() );
2934+
while ( it.hasNext() )
2935+
{
2936+
it.next();
2937+
QVariant v = it.value().value( index );
2938+
if ( v.isValid() && qgsVariantLessThan( v, min ) )
2939+
{
2940+
min = v;
2941+
}
2942+
}
2943+
}
2944+
return min;
2945+
}
29012946

29022947
case QgsFields::OriginEdit:
29032948
{
@@ -2956,7 +3001,35 @@ QVariant QgsVectorLayer::maximumValue( int index ) const
29563001
return QVariant();
29573002

29583003
case QgsFields::OriginProvider: //a provider field
2959-
return mDataProvider->maximumValue( index );
3004+
{
3005+
QVariant min = mDataProvider->maximumValue( index );
3006+
if ( mEditBuffer )
3007+
{
3008+
QgsFeatureMap added = mEditBuffer->addedFeatures();
3009+
QMapIterator< QgsFeatureId, QgsFeature > addedIt( added );
3010+
while ( addedIt.hasNext() )
3011+
{
3012+
addedIt.next();
3013+
QVariant v = addedIt.value().attribute( index );
3014+
if ( v.isValid() && qgsVariantGreaterThan( v, min ) )
3015+
{
3016+
min = v;
3017+
}
3018+
}
3019+
3020+
QMapIterator< QgsFeatureId, QgsAttributeMap > it( mEditBuffer->changedAttributeValues() );
3021+
while ( it.hasNext() )
3022+
{
3023+
it.next();
3024+
QVariant v = it.value().value( index );
3025+
if ( v.isValid() && qgsVariantGreaterThan( v, min ) )
3026+
{
3027+
min = v;
3028+
}
3029+
}
3030+
}
3031+
return min;
3032+
}
29603033

29613034
case QgsFields::OriginEdit:
29623035
// the layer is editable, but in certain cases it can still be avoided going through all features

‎src/core/qgsvectorlayer.h

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1378,17 +1378,35 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
13781378
// marked as const as these are just caches, and need to be created from const accessors
13791379
void createJoinCaches() const;
13801380

1381-
/** Returns unique values for column
1381+
/** Calculates a list of unique values contained within an attribute in the layer. Note that
1382+
* in some circumstances when unsaved changes are present for the layer then the returned list
1383+
* may contain outdated values (for instance when the attribute value in a saved feature has
1384+
* been changed inside the edit buffer then the previous saved value will be included in the
1385+
* returned list).
13821386
* @param index column index for attribute
13831387
* @param uniqueValues out: result list
1384-
* @param limit maximum number of values to return (-1 if unlimited)
1388+
* @param limit maximum number of values to return (or -1 if unlimited)
1389+
* @see minimumValue()
1390+
* @see maximumValue()
13851391
*/
13861392
void uniqueValues( int index, QList<QVariant> &uniqueValues, int limit = -1 ) const;
13871393

1388-
/** Returns minimum value for an attribute column or invalid variant in case of error */
1394+
/** Returns the minimum value for an attribute column or an invalid variant in case of error.
1395+
* Note that in some circumstances when unsaved changes are present for the layer then the
1396+
* returned value may be outdated (for instance when the attribute value in a saved feature has
1397+
* been changed inside the edit buffer then the previous saved value may be returned as the minimum).
1398+
* @see maximumValue()
1399+
* @see uniqueValues()
1400+
*/
13891401
QVariant minimumValue( int index ) const;
13901402

1391-
/** Returns maximum value for an attribute column or invalid variant in case of error */
1403+
/** Returns the maximum value for an attribute column or an invalid variant in case of error.
1404+
* Note that in some circumstances when unsaved changes are present for the layer then the
1405+
* returned value may be outdated (for instance when the attribute value in a saved feature has
1406+
* been changed inside the edit buffer then the previous saved value may be returned as the maximum).
1407+
* @see minimumValue()
1408+
* @see uniqueValues()
1409+
*/
13921410
QVariant maximumValue( int index ) const;
13931411

13941412
/** Calculates an aggregated value from the layer's features.

‎src/gui/qgsrelationeditorwidget.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ void QgsRelationEditorWidget::unlinkFeature()
440440
while ( linkedIterator.nextFeature( f ) )
441441
{
442442
fids << f.id();
443-
QgsDebugMsg( f.id() );
443+
QgsDebugMsgLevel( FID_TO_STRING( f.id() ), 4 );
444444
}
445445

446446
mRelation.referencingLayer()->deleteFeatures( fids );

‎tests/src/python/test_qgsvectorlayer.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,6 +1172,103 @@ def test_JoinStats(self):
11721172
self.assertEqual(layer.maximumValue(3), 321)
11731173
self.assertEqual(set(layer.uniqueValues(3)), set([111, 321]))
11741174

1175+
def testUniqueValue(self):
1176+
""" test retrieving unique values """
1177+
layer = createLayerWithFivePoints()
1178+
1179+
# test layer with just provider features
1180+
self.assertEqual(set(layer.uniqueValues(1)), set([123, 457, 888, -1, 0]))
1181+
1182+
# add feature with new value
1183+
layer.startEditing()
1184+
f1 = QgsFeature()
1185+
f1.setAttributes(["test2", 999])
1186+
self.assertTrue(layer.addFeature(f1))
1187+
1188+
# should be included in unique values
1189+
self.assertEqual(set(layer.uniqueValues(1)), set([123, 457, 888, -1, 0, 999]))
1190+
# add it again, should be no change
1191+
f2 = QgsFeature()
1192+
f2.setAttributes(["test2", 999])
1193+
self.assertTrue(layer.addFeature(f1))
1194+
self.assertEqual(set(layer.uniqueValues(1)), set([123, 457, 888, -1, 0, 999]))
1195+
# add another feature
1196+
f3 = QgsFeature()
1197+
f3.setAttributes(["test2", 9999])
1198+
self.assertTrue(layer.addFeature(f3))
1199+
self.assertEqual(set(layer.uniqueValues(1)), set([123, 457, 888, -1, 0, 999, 9999]))
1200+
1201+
# change an attribute value to a new unique value
1202+
f = QgsFeature()
1203+
f1_id = layer.getFeatures().next().id()
1204+
self.assertTrue(layer.changeAttributeValue(f1_id, 1, 481523))
1205+
# note - this isn't 100% accurate, since 123 no longer exists - but it avoids looping through all features
1206+
self.assertEqual(set(layer.uniqueValues(1)), set([123, 457, 888, -1, 0, 999, 9999, 481523]))
1207+
1208+
def testMinValue(self):
1209+
""" test retrieving minimum values """
1210+
layer = createLayerWithFivePoints()
1211+
1212+
# test layer with just provider features
1213+
self.assertEqual(layer.minimumValue(1), -1)
1214+
1215+
# add feature with new value
1216+
layer.startEditing()
1217+
f1 = QgsFeature()
1218+
f1.setAttributes(["test2", -999])
1219+
self.assertTrue(layer.addFeature(f1))
1220+
1221+
# should be new minimum value
1222+
self.assertEqual(layer.minimumValue(1), -999)
1223+
# add it again, should be no change
1224+
f2 = QgsFeature()
1225+
f2.setAttributes(["test2", -999])
1226+
self.assertTrue(layer.addFeature(f1))
1227+
self.assertEqual(layer.minimumValue(1), -999)
1228+
# add another feature
1229+
f3 = QgsFeature()
1230+
f3.setAttributes(["test2", -1000])
1231+
self.assertTrue(layer.addFeature(f3))
1232+
self.assertEqual(layer.minimumValue(1), -1000)
1233+
1234+
# change an attribute value to a new minimum value
1235+
f = QgsFeature()
1236+
f1_id = layer.getFeatures().next().id()
1237+
self.assertTrue(layer.changeAttributeValue(f1_id, 1, -1001))
1238+
self.assertEqual(layer.minimumValue(1), -1001)
1239+
1240+
def testMaxValue(self):
1241+
""" test retrieving maximum values """
1242+
layer = createLayerWithFivePoints()
1243+
1244+
# test layer with just provider features
1245+
self.assertEqual(layer.maximumValue(1), 888)
1246+
1247+
# add feature with new value
1248+
layer.startEditing()
1249+
f1 = QgsFeature()
1250+
f1.setAttributes(["test2", 999])
1251+
self.assertTrue(layer.addFeature(f1))
1252+
1253+
# should be new maximum value
1254+
self.assertEqual(layer.maximumValue(1), 999)
1255+
# add it again, should be no change
1256+
f2 = QgsFeature()
1257+
f2.setAttributes(["test2", 999])
1258+
self.assertTrue(layer.addFeature(f1))
1259+
self.assertEqual(layer.maximumValue(1), 999)
1260+
# add another feature
1261+
f3 = QgsFeature()
1262+
f3.setAttributes(["test2", 1000])
1263+
self.assertTrue(layer.addFeature(f3))
1264+
self.assertEqual(layer.maximumValue(1), 1000)
1265+
1266+
# change an attribute value to a new maximum value
1267+
f = QgsFeature()
1268+
f1_id = layer.getFeatures().next().id()
1269+
self.assertTrue(layer.changeAttributeValue(f1_id, 1, 1001))
1270+
self.assertEqual(layer.maximumValue(1), 1001)
1271+
11751272
def test_InvalidOperations(self):
11761273
layer = createLayerWithOnePoint()
11771274

0 commit comments

Comments
 (0)
Please sign in to comment.