Skip to content

Commit acdbc56

Browse files
elpasonyalldawson
authored andcommittedFeb 1, 2022
Fix attr table sorting
Fix #34935
1 parent e947698 commit acdbc56

File tree

4 files changed

+84
-3
lines changed

4 files changed

+84
-3
lines changed
 

‎src/gui/attributetable/qgsattributetablemodel.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "qgsvectordataprovider.h"
3434
#include "qgssymbollayerutils.h"
3535
#include "qgsfieldformatterregistry.h"
36+
#include "qgsfallbackfieldformatter.h"
3637
#include "qgsgui.h"
3738
#include "qgsexpressionnodeimpl.h"
3839
#include "qgsvectorlayerjoininfo.h"
@@ -235,7 +236,13 @@ void QgsAttributeTableModel::featureAdded( QgsFeatureId fid )
235236
QgsFieldFormatter *fieldFormatter = mFieldFormatters.at( cache.sortFieldIndex );
236237
const QVariant &widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
237238
const QVariantMap &widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
238-
const QVariant sortValue = fieldFormatter->representValue( mLayer, cache.sortFieldIndex, widgetConfig, widgetCache, mFeat.attribute( cache.sortFieldIndex ) );
239+
// If using the default formatter use the raw value for sorting
240+
// (keep the correct QVariant type and do not convert to a possibly localized string)
241+
// See: https://github.com/qgis/QGIS/issues/34935
242+
const bool isFallbackFormatter { fieldFormatter->id().isEmpty() };
243+
const QVariant sortValue = isFallbackFormatter ?
244+
mFeat.attribute( cache.sortFieldIndex ) :
245+
fieldFormatter->representValue( mLayer, cache.sortFieldIndex, widgetConfig, widgetCache, mFeat.attribute( cache.sortFieldIndex ) );
239246
cache.sortCache.insert( mFeat.id(), sortValue );
240247
}
241248
else if ( cache.sortCacheExpression.isValid() )

‎src/gui/attributetable/qgsattributetableview.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ void QgsAttributeTableView::setAttributeTableConfig( const QgsAttributeTableConf
9494
{
9595
int i = 0;
9696
const auto constColumns = config.columns();
97+
QMap<QString, int> columns;
9798
for ( const QgsAttributeTableConfig::ColumnConfig &columnConfig : constColumns )
9899
{
99100
if ( columnConfig.hidden )
@@ -106,12 +107,29 @@ void QgsAttributeTableView::setAttributeTableConfig( const QgsAttributeTableConf
106107
else
107108
{
108109
setColumnWidth( i, horizontalHeader()->defaultSectionSize() );
110+
columns.insert( columnConfig.name, i );
109111
}
110112
i++;
111113
}
112114
mConfig = config;
113115
if ( config.sortExpression().isEmpty() )
116+
{
114117
horizontalHeader()->setSortIndicatorShown( false );
118+
}
119+
else
120+
{
121+
if ( mSortExpression != config.sortExpression() )
122+
{
123+
const QgsExpression sortExp { config.sortExpression() };
124+
if ( sortExp.isField() )
125+
{
126+
const QStringList refCols { sortExp.referencedColumns().values() };
127+
horizontalHeader()->setSortIndicatorShown( true );
128+
horizontalHeader()->setSortIndicator( columns.value( refCols.constFirst() ), config.sortOrder() );
129+
}
130+
}
131+
}
132+
mSortExpression = config.sortExpression();
115133
}
116134

117135
QList<QgsFeatureId> QgsAttributeTableView::selectedFeaturesIds() const

‎src/gui/attributetable/qgsattributetableview.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ class GUI_EXPORT QgsAttributeTableView : public QTableView
205205
QItemSelectionModel::SelectionFlag mCtrlDragSelectionFlag = QItemSelectionModel::Select;
206206
QMap< QModelIndex, QWidget * > mActionWidgets;
207207
QgsAttributeTableConfig mConfig;
208+
QString mSortExpression;
208209
};
209210

210211
#endif

‎tests/src/app/testqgsattributetable.cpp

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,11 @@ class TestQgsAttributeTable : public QObject
4242
TestQgsAttributeTable();
4343

4444
private slots:
45+
4546
void initTestCase();// will be called before the first testfunction is executed.
4647
void cleanupTestCase();// will be called after the last testfunction was executed.
47-
void init() {} // will be called before each testfunction is executed.
48+
void init();
49+
// will be called before each testfunction is executed.
4850
void cleanup() {} // will be called after every testfunction.
4951
void testRegression15974();
5052
void testFieldCalculation();
@@ -58,7 +60,7 @@ class TestQgsAttributeTable : public QObject
5860
void testFilteredFeatures();
5961
void testVisibleTemporal();
6062
void testCopySelectedRows();
61-
63+
void testSortNumbers();
6264

6365
private:
6466
QgisApp *mQgisApp = nullptr;
@@ -87,6 +89,11 @@ void TestQgsAttributeTable::cleanupTestCase()
8789
QgsApplication::exitQgis();
8890
}
8991

92+
void TestQgsAttributeTable::init()
93+
{
94+
QLocale::setDefault( QLocale::c() );
95+
}
96+
9097
void TestQgsAttributeTable::testFieldCalculation()
9198
{
9299
//test field calculation
@@ -411,6 +418,54 @@ void TestQgsAttributeTable::testSortByDisplayExpression()
411418
QCOMPARE( listModel->index( 2, 0 ).data( Qt::DisplayRole ), QVariant( 5.0 ) );
412419
}
413420

421+
void TestQgsAttributeTable::testSortNumbers()
422+
{
423+
424+
QLocale::setDefault( QLocale::Italian );
425+
426+
std::unique_ptr< QgsVectorLayer> tempLayer( new QgsVectorLayer( QStringLiteral( "LineString?crs=epsg:3111&field=pk:int&field=col1:double" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) ) );
427+
QVERIFY( tempLayer->isValid() );
428+
429+
QgsFeature f1( tempLayer->dataProvider()->fields(), 1 );
430+
f1.setAttribute( 0, 1 );
431+
f1.setAttribute( 1, 2.001 );
432+
QgsFeature f2( tempLayer->dataProvider()->fields(), 2 );
433+
f2.setAttribute( 0, 2 );
434+
f2.setAttribute( 1, 1001 );
435+
QgsFeature f3( tempLayer->dataProvider()->fields(), 3 );
436+
f3.setAttribute( 0, 3 );
437+
f3.setAttribute( 1, 10.0001 );
438+
QVERIFY( tempLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 << f2 << f3 ) );
439+
440+
std::unique_ptr< QgsAttributeTableDialog > dlg( new QgsAttributeTableDialog( tempLayer.get() ) );
441+
442+
QgsAttributeTableConfig cfg;
443+
cfg.setSortExpression( QStringLiteral( R"("col1")" ) );
444+
cfg.setSortOrder( Qt::SortOrder::DescendingOrder );
445+
QgsAttributeTableConfig::ColumnConfig cfg1;
446+
QgsAttributeTableConfig::ColumnConfig cfg2;
447+
cfg1.name = QStringLiteral( "pk" );
448+
cfg2.name = QStringLiteral( "col1" );
449+
cfg.setColumns( {{ cfg1, cfg2 }} );
450+
451+
dlg->mMainView->setAttributeTableConfig( cfg );
452+
453+
auto model { dlg->mMainView->mFilterModel };
454+
455+
QCOMPARE( model->data( model->index( 2, 1 ), Qt::ItemDataRole::DisplayRole ).toString(), QString( "2,00100" ) );
456+
QCOMPARE( model->data( model->index( 1, 1 ), Qt::ItemDataRole::DisplayRole ).toString(), QString( "10,00010" ) );
457+
QCOMPARE( model->data( model->index( 0, 1 ), Qt::ItemDataRole::DisplayRole ).toString(), QString( "1.001,00000" ) );
458+
459+
QCOMPARE( model->data( model->index( 2, 2 ), QgsAttributeTableModel::Role::SortRole ).toDouble(), 2.001 );
460+
QCOMPARE( model->data( model->index( 1, 2 ), QgsAttributeTableModel::Role::SortRole ).toDouble(), 10.0001 );
461+
QCOMPARE( model->data( model->index( 0, 2 ), QgsAttributeTableModel::Role::SortRole ).toDouble(), 1001.0 );
462+
463+
QCOMPARE( dlg->mMainView->mTableView->horizontalHeader()->sortIndicatorSection(), 1 );
464+
QCOMPARE( dlg->mMainView->mTableView->horizontalHeader()->sortIndicatorOrder(), Qt::SortOrder::DescendingOrder );
465+
QVERIFY( dlg->mMainView->mTableView->horizontalHeader()->isSortIndicatorShown() );
466+
467+
}
468+
414469
void TestQgsAttributeTable::testRegression15974()
415470
{
416471
// Test duplicated rows in attribute table + two crashes.

0 commit comments

Comments
 (0)
Please sign in to comment.