Skip to content

Commit f13d3f3

Browse files
author
mhugent
committed
[FEATURE]: Sorting for composer attribute table (several columns and ascending / descending)
git-svn-id: http://svn.osgeo.org/qgis/trunk@14367 c8812cc2-4d05-0410-92ff-de0c093fc19c
1 parent da38725 commit f13d3f3

6 files changed

+387
-42
lines changed

src/app/composer/qgsattributeselectiondialog.cpp

+84-35
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,20 @@
2626
#include <QScrollArea>
2727

2828
QgsAttributeSelectionDialog::QgsAttributeSelectionDialog( const QgsVectorLayer* vLayer, const QSet<int>& enabledAttributes, const QMap<int, QString>& aliasMap,
29-
QWidget * parent, Qt::WindowFlags f ): QDialog( parent, f ), mVectorLayer( vLayer )
29+
const QList< QPair<int, bool> >& sortColumns, QWidget* parent, Qt::WindowFlags f ): QDialog( parent, f ), mVectorLayer( vLayer )
3030
{
31+
setupUi( this );
3132
if ( vLayer )
3233
{
33-
QScrollArea* attributeScrollArea = new QScrollArea( this );
34-
QWidget* attributeWidget = new QWidget();
35-
36-
mAttributeGridLayout = new QGridLayout( attributeWidget );
37-
QLabel* attributeLabel = new QLabel( QString( "<b>" ) + tr( "Attribute" ) + QString( "</b>" ), this );
38-
attributeLabel->setTextFormat( Qt::RichText );
39-
mAttributeGridLayout->addWidget( attributeLabel, 0, 0 );
40-
QLabel* aliasLabel = new QLabel( QString( "<b>" ) + tr( "Alias" ) + QString( "</b>" ), this );
41-
aliasLabel->setTextFormat( Qt::RichText );
42-
mAttributeGridLayout->addWidget( aliasLabel, 0, 1 );
43-
4434
QgsFieldMap fieldMap = vLayer->pendingFields();
4535
QgsFieldMap::const_iterator fieldIt = fieldMap.constBegin();
4636
int layoutRowCounter = 1;
4737
for ( ; fieldIt != fieldMap.constEnd(); ++fieldIt )
4838
{
39+
//insert field into sorting combo first
40+
mSortColumnComboBox->addItem( fieldIt.value().name(), QVariant( fieldIt.key() ) );
41+
42+
//and into enabled / alias list
4943
QCheckBox* attributeCheckBox = new QCheckBox( fieldIt.value().name(), this );
5044
if ( enabledAttributes.size() < 1 || enabledAttributes.contains( fieldIt.key() ) )
5145
{
@@ -55,38 +49,32 @@ QgsAttributeSelectionDialog::QgsAttributeSelectionDialog( const QgsVectorLayer*
5549
{
5650
attributeCheckBox->setCheckState( Qt::Unchecked );
5751
}
58-
mAttributeGridLayout->addWidget( attributeCheckBox, layoutRowCounter, 0 );
52+
mAttributeGridLayout->addWidget(( QWidget* )attributeCheckBox, layoutRowCounter, 0, 1, 1 );
5953

6054
QLineEdit* attributeLineEdit = new QLineEdit( this );
6155
QMap<int, QString>::const_iterator aliasIt = aliasMap.find( fieldIt.key() );
6256
if ( aliasIt != aliasMap.constEnd() )
6357
{
6458
attributeLineEdit->setText( aliasIt.value() );
6559
}
66-
mAttributeGridLayout->addWidget( attributeLineEdit, layoutRowCounter, 1 );
60+
mAttributeGridLayout->addWidget(( QWidget* )attributeLineEdit, layoutRowCounter, 1, 1, 1 );
6761
++layoutRowCounter;
6862
}
6963

70-
attributeScrollArea->setWidget( attributeWidget );
71-
72-
QVBoxLayout* verticalLayout = new QVBoxLayout( this );
73-
verticalLayout->addWidget( attributeScrollArea );
74-
75-
QHBoxLayout* selectClearLayout = new QHBoxLayout( this );
76-
QPushButton* mSelectAllButton = new QPushButton( tr( "Select all" ), this );
77-
QObject::connect( mSelectAllButton, SIGNAL( clicked() ), this, SLOT( selectAllAttributes() ) );
78-
QPushButton* mClearButton = new QPushButton( tr( "Clear" ), this );
79-
QObject::connect( mClearButton, SIGNAL( clicked() ), this, SLOT( clearAttributes() ) );
80-
selectClearLayout->addWidget( mSelectAllButton );
81-
selectClearLayout->addWidget( mClearButton );
82-
verticalLayout->addLayout( selectClearLayout );
83-
84-
85-
QDialogButtonBox* buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this );
86-
QObject::connect( buttonBox, SIGNAL( accepted() ), this, SLOT( accept() ) );
87-
QObject::connect( buttonBox, SIGNAL( rejected() ), this, SLOT( reject() ) );
88-
verticalLayout->addWidget( buttonBox );
64+
//sort columns
65+
QList< QPair<int, bool> >::const_iterator sortIt = sortColumns.constBegin();
66+
for ( ; sortIt != sortColumns.constEnd(); ++sortIt )
67+
{
68+
QTreeWidgetItem* item = new QTreeWidgetItem();
69+
item->setText( 0, fieldMap[sortIt->first].name() );
70+
item->setData( 0, Qt::UserRole, sortIt->first );
71+
item->setText( 1, sortIt->second ? tr( "Ascending" ) : tr( "Descending" ) );
72+
mSortColumnTreeWidget->addTopLevelItem( item );
73+
}
8974
}
75+
76+
mOrderComboBox->insertItem( 0, tr( "Ascending" ) );
77+
mOrderComboBox->insertItem( 0, tr( "Descending" ) );
9078
}
9179

9280
QgsAttributeSelectionDialog::~QgsAttributeSelectionDialog()
@@ -149,12 +137,28 @@ QMap<int, QString> QgsAttributeSelectionDialog::aliasMap() const
149137
return result;
150138
}
151139

152-
void QgsAttributeSelectionDialog::selectAllAttributes()
140+
QList< QPair<int, bool> > QgsAttributeSelectionDialog::attributeSorting() const
141+
{
142+
QList< QPair<int, bool> > sortingList;
143+
144+
for ( int i = 0; i < mSortColumnTreeWidget->topLevelItemCount(); ++i )
145+
{
146+
QTreeWidgetItem* item = mSortColumnTreeWidget->topLevelItem( i );
147+
if ( item )
148+
{
149+
sortingList.push_back( qMakePair( item->data( 0, Qt::UserRole ).toInt(), item->text( 1 ) == tr( "Ascending" ) ) );
150+
}
151+
}
152+
153+
return sortingList;
154+
}
155+
156+
void QgsAttributeSelectionDialog::on_mSelectAllButton_clicked()
153157
{
154158
setAllEnabled( true );
155159
}
156160

157-
void QgsAttributeSelectionDialog::clearAttributes()
161+
void QgsAttributeSelectionDialog::on_mClearButton_clicked()
158162
{
159163
setAllEnabled( false );
160164
}
@@ -183,3 +187,48 @@ void QgsAttributeSelectionDialog::setAllEnabled( bool enabled )
183187
}
184188
}
185189

190+
void QgsAttributeSelectionDialog::on_mAddPushButton_clicked()
191+
{
192+
QTreeWidgetItem* item = new QTreeWidgetItem();
193+
item->setText( 0, mSortColumnComboBox->currentText() );
194+
item->setData( 0, Qt::UserRole, mSortColumnComboBox->itemData( mSortColumnComboBox->currentIndex() ) );
195+
item->setText( 1, mOrderComboBox->currentText() );
196+
mSortColumnTreeWidget->addTopLevelItem( item );
197+
}
198+
199+
void QgsAttributeSelectionDialog::on_mRemovePushButton_clicked()
200+
{
201+
int currentIndex = mSortColumnTreeWidget->indexOfTopLevelItem( mSortColumnTreeWidget->currentItem() );
202+
if ( currentIndex != -1 )
203+
{
204+
delete( mSortColumnTreeWidget->takeTopLevelItem( currentIndex ) );
205+
}
206+
}
207+
208+
void QgsAttributeSelectionDialog::on_mUpPushButton_clicked()
209+
{
210+
int currentIndex = mSortColumnTreeWidget->indexOfTopLevelItem( mSortColumnTreeWidget->currentItem() );
211+
if ( currentIndex != -1 )
212+
{
213+
if ( currentIndex > 0 )
214+
{
215+
QTreeWidgetItem* item = mSortColumnTreeWidget->takeTopLevelItem( currentIndex );
216+
mSortColumnTreeWidget->insertTopLevelItem( currentIndex - 1, item );
217+
mSortColumnTreeWidget->setCurrentItem( item );
218+
}
219+
}
220+
}
221+
222+
void QgsAttributeSelectionDialog::on_mDownPushButton_clicked()
223+
{
224+
int currentIndex = mSortColumnTreeWidget->indexOfTopLevelItem( mSortColumnTreeWidget->currentItem() );
225+
if ( currentIndex != -1 )
226+
{
227+
if ( currentIndex < ( mSortColumnTreeWidget->topLevelItemCount() - 1 ) )
228+
{
229+
QTreeWidgetItem* item = mSortColumnTreeWidget->takeTopLevelItem( currentIndex );
230+
mSortColumnTreeWidget->insertTopLevelItem( currentIndex + 1, item );
231+
mSortColumnTreeWidget->setCurrentItem( item );
232+
}
233+
}
234+
}

src/app/composer/qgsattributeselectiondialog.h

+11-5
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,37 @@
2121
#include <QDialog>
2222
#include <QMap>
2323
#include <QSet>
24+
#include "ui_qgsattributeselectiondialogbase.h"
2425

2526
class QGridLayout;
2627
class QgsVectorLayer;
2728
class QPushButton;
2829

2930
/**A dialog to select what attributes to display (in the table item) and with the possibility to set different aliases*/
30-
class QgsAttributeSelectionDialog: public QDialog
31+
class QgsAttributeSelectionDialog: public QDialog, private Ui::QgsAttributeSelectionDialogBase
3132
{
3233
Q_OBJECT
3334
public:
34-
QgsAttributeSelectionDialog( const QgsVectorLayer* vLayer, const QSet<int>& enabledAttributes, const QMap<int, QString>& aliasMap, QWidget * parent = 0, Qt::WindowFlags f = 0 );
35+
QgsAttributeSelectionDialog( const QgsVectorLayer* vLayer, const QSet<int>& enabledAttributes, const QMap<int, QString>& aliasMap, const QList< QPair<int, bool> >& sortColumns, QWidget * parent = 0, Qt::WindowFlags f = 0 );
3536
~QgsAttributeSelectionDialog();
3637

3738
/**Returns indices of selected attributes*/
3839
QSet<int> enabledAttributes() const;
3940
/**Returns alias map (alias might be different than for vector layer)*/
4041
QMap<int, QString> aliasMap() const;
42+
/**List of sorting attributes and ascending / descending (so sorting to multiple columns is possible)*/
43+
QList< QPair<int, bool> > attributeSorting() const;
4144

4245
private slots:
43-
void selectAllAttributes();
44-
void clearAttributes();
46+
void on_mSelectAllButton_clicked();
47+
void on_mClearButton_clicked();
48+
void on_mAddPushButton_clicked();
49+
void on_mRemovePushButton_clicked();
50+
void on_mUpPushButton_clicked();
51+
void on_mDownPushButton_clicked();
4552

4653
private:
4754
const QgsVectorLayer* mVectorLayer;
48-
QGridLayout* mAttributeGridLayout;
4955
QPushButton* mSelectAllButton;
5056
QPushButton* mClearButton;
5157

src/app/composer/qgscomposertablewidget.cpp

+7-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ QgsComposerTableWidget::QgsComposerTableWidget( QgsComposerAttributeTable* table
3232
QgsComposerItemWidget* itemPropertiesWidget = new QgsComposerItemWidget( this, mComposerTable );
3333
mToolBox->addItem( itemPropertiesWidget, tr( "General options" ) );
3434

35+
blockAllSignals( true );
36+
3537
//insert vector layers into combo
3638
QMap<QString, QgsMapLayer*> layerMap = QgsMapLayerRegistry::instance()->mapLayers();
3739
QMap<QString, QgsMapLayer*>::const_iterator mapIt = layerMap.constBegin();
@@ -46,6 +48,7 @@ QgsComposerTableWidget::QgsComposerTableWidget( QgsComposerAttributeTable* table
4648
}
4749

4850
//insert composer maps into combo
51+
mLayerComboBox->blockSignals( true );
4952
if ( mComposerTable )
5053
{
5154
const QgsComposition* tableComposition = mComposerTable->composition();
@@ -60,8 +63,10 @@ QgsComposerTableWidget::QgsComposerTableWidget( QgsComposerAttributeTable* table
6063
}
6164
}
6265
}
66+
mLayerComboBox->blockSignals( false );
6367

6468
updateGuiElements();
69+
blockAllSignals( false );
6570

6671
if ( mComposerTable )
6772
{
@@ -108,12 +113,13 @@ void QgsComposerTableWidget::on_mAttributesPushButton_clicked()
108113
return;
109114
}
110115

111-
QgsAttributeSelectionDialog d( mComposerTable->vectorLayer(), mComposerTable->displayAttributes(), mComposerTable->fieldAliasMap(), 0 );
116+
QgsAttributeSelectionDialog d( mComposerTable->vectorLayer(), mComposerTable->displayAttributes(), mComposerTable->fieldAliasMap(), mComposerTable->sortAttributes(), 0 );
112117
if ( d.exec() == QDialog::Accepted )
113118
{
114119
//change displayAttributes and aliases
115120
mComposerTable->setDisplayAttributes( d.enabledAttributes() );
116121
mComposerTable->setFieldAliasMap( d.aliasMap() );
122+
mComposerTable->setSortAttributes( d.attributeSorting() );
117123
mComposerTable->update();
118124
}
119125
}

src/core/composer/qgscomposerattributetable.cpp

+60-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,29 @@
2020
#include "qgsmaplayerregistry.h"
2121
#include "qgsvectorlayer.h"
2222

23+
QgsComposerAttributeTableCompare::QgsComposerAttributeTableCompare(): mCurrentSortColumn( 0 ), mAscending( true )
24+
{
25+
}
26+
27+
28+
bool QgsComposerAttributeTableCompare::operator()( const QgsAttributeMap& m1, const QgsAttributeMap& m2 )
29+
{
30+
QVariant v1 = m1[mCurrentSortColumn];
31+
QVariant v2 = m2[mCurrentSortColumn];
32+
33+
bool less = false;
34+
if ( v1.type() == QVariant::String && v2.type() == QVariant::String )
35+
{
36+
less = v1.toString() < v2.toString();
37+
}
38+
else
39+
{
40+
less = v1.toDouble() < v2.toDouble();
41+
}
42+
return ( mAscending ? less : !less );
43+
}
44+
45+
2346
QgsComposerAttributeTable::QgsComposerAttributeTable( QgsComposition* composition ): QgsComposerTable( composition ), mVectorLayer( 0 ), mComposerMap( 0 ), \
2447
mMaximumNumberOfFeatures( 5 ), mShowOnlyVisibleFeatures( true )
2548
{
@@ -110,6 +133,15 @@ bool QgsComposerAttributeTable::getFeatureAttributes( QList<QgsAttributeMap>& at
110133
attributes.push_back( f.attributeMap() );
111134
++counter;
112135
}
136+
137+
//sort the list, starting with the last attribute
138+
QgsComposerAttributeTableCompare c;
139+
for ( int i = mSortInformation.size() - 1; i >= 0; --i )
140+
{
141+
c.setSortColumn( mSortInformation.at( i ).first );
142+
c.setAscending( mSortInformation.at( i ).second );
143+
qStableSort( attributes.begin(), attributes.end(), c );
144+
}
113145
return true;
114146
}
115147

@@ -212,8 +244,20 @@ bool QgsComposerAttributeTable::writeXML( QDomElement& elem, QDomDocument & doc
212244
aliasMapElem.appendChild( mapEntryElem );
213245
}
214246
composerTableElem.appendChild( aliasMapElem );
215-
bool ok = tableWriteXML( composerTableElem, doc );
247+
248+
//sort info
249+
QDomElement sortColumnsElem = doc.createElement( "sortColumns" );
250+
QList< QPair<int, bool> >::const_iterator sortIt = mSortInformation.constBegin();
251+
for ( ; sortIt != mSortInformation.constEnd(); ++sortIt )
252+
{
253+
QDomElement columnElem = doc.createElement( "column" );
254+
columnElem.setAttribute( "index", QString::number( sortIt->first ) );
255+
columnElem.setAttribute( "ascending", sortIt->second == true ? "true" : "false" );
256+
sortColumnsElem.appendChild( columnElem );
257+
}
258+
composerTableElem.appendChild( sortColumnsElem );
216259
elem.appendChild( composerTableElem );
260+
bool ok = tableWriteXML( composerTableElem, doc );
217261
return ok;
218262
}
219263

@@ -291,5 +335,20 @@ bool QgsComposerAttributeTable::readXML( const QDomElement& itemElem, const QDom
291335
mFieldAliasMap.insert( key, value );
292336
}
293337
}
338+
339+
//restore sort columns
340+
mSortInformation.clear();
341+
QDomElement sortColumnsElem = itemElem.firstChildElement( "sortColumns" );
342+
if ( !sortColumnsElem.isNull() )
343+
{
344+
QDomNodeList columns = sortColumnsElem.elementsByTagName( "column" );
345+
for ( int i = 0; i < columns.size(); ++i )
346+
{
347+
QDomElement columnElem = columns.at( i ).toElement();
348+
int attribute = columnElem.attribute( "index" ).toInt();
349+
bool ascending = columnElem.attribute( "ascending" ) == "true" ? true : false;
350+
mSortInformation.push_back( qMakePair( attribute, ascending ) );
351+
}
352+
}
294353
return tableReadXML( itemElem, doc );
295354
}

src/core/composer/qgscomposerattributetable.h

+19
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,19 @@
2323
class QgsComposerMap;
2424
class QgsVectorLayer;
2525

26+
/**Helper class for sorting, takes into account sorting column and ascending / descending*/
27+
class QgsComposerAttributeTableCompare
28+
{
29+
public:
30+
QgsComposerAttributeTableCompare();
31+
bool operator()( const QgsAttributeMap& m1, const QgsAttributeMap& m2 );
32+
void setSortColumn( int col ) { mCurrentSortColumn = col; }
33+
void setAscending( bool asc ) { mAscending = asc; }
34+
private:
35+
int mCurrentSortColumn;
36+
bool mAscending;
37+
};
38+
2639
/**A table class that displays a vector attribute table*/
2740
class CORE_EXPORT QgsComposerAttributeTable: public QgsComposerTable
2841
{
@@ -58,6 +71,9 @@ class CORE_EXPORT QgsComposerAttributeTable: public QgsComposerTable
5871
/**Adapts mMaximumNumberOfFeatures depending on the rectangle height*/
5972
void setSceneRect( const QRectF& rectangle );
6073

74+
void setSortAttributes( const QList<QPair<int, bool> > att ) { mSortInformation = att; }
75+
QList<QPair<int, bool> > sortAttributes() const { return mSortInformation; }
76+
6177
protected:
6278
/**Retrieves feature attributes*/
6379
bool getFeatureAttributes( QList<QgsAttributeMap>& attributes );
@@ -79,6 +95,9 @@ class CORE_EXPORT QgsComposerAttributeTable: public QgsComposerTable
7995
/**Map of attribute name aliases. The aliases might be different to those of QgsVectorLayer (but those from the vector layer are the default)*/
8096
QMap<int, QString> mFieldAliasMap;
8197

98+
/**Contains information about sort attribute index / ascending (true/false). First entry has the highest priority*/
99+
QList< QPair<int, bool> > mSortInformation;
100+
82101
/**Inserts aliases from vector layer as starting configuration to the alias map*/
83102
void initializeAliasMap();
84103
/**Returns the attribute name to display in the item (attribute name or an alias if present)*/

0 commit comments

Comments
 (0)