Skip to content

Commit

Permalink
[FEATURE]: Sorting for composer attribute table (several columns and …
Browse files Browse the repository at this point in the history
…ascending / descending)

git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@14367 c8812cc2-4d05-0410-92ff-de0c093fc19c
  • Loading branch information
mhugent committed Oct 11, 2010
1 parent 88af0a1 commit a643c6b
Show file tree
Hide file tree
Showing 6 changed files with 387 additions and 42 deletions.
119 changes: 84 additions & 35 deletions src/app/composer/qgsattributeselectiondialog.cpp
Expand Up @@ -26,26 +26,20 @@
#include <QScrollArea>

QgsAttributeSelectionDialog::QgsAttributeSelectionDialog( const QgsVectorLayer* vLayer, const QSet<int>& enabledAttributes, const QMap<int, QString>& aliasMap,
QWidget * parent, Qt::WindowFlags f ): QDialog( parent, f ), mVectorLayer( vLayer )
const QList< QPair<int, bool> >& sortColumns, QWidget* parent, Qt::WindowFlags f ): QDialog( parent, f ), mVectorLayer( vLayer )
{
setupUi( this );
if ( vLayer )
{
QScrollArea* attributeScrollArea = new QScrollArea( this );
QWidget* attributeWidget = new QWidget();

mAttributeGridLayout = new QGridLayout( attributeWidget );
QLabel* attributeLabel = new QLabel( QString( "<b>" ) + tr( "Attribute" ) + QString( "</b>" ), this );
attributeLabel->setTextFormat( Qt::RichText );
mAttributeGridLayout->addWidget( attributeLabel, 0, 0 );
QLabel* aliasLabel = new QLabel( QString( "<b>" ) + tr( "Alias" ) + QString( "</b>" ), this );
aliasLabel->setTextFormat( Qt::RichText );
mAttributeGridLayout->addWidget( aliasLabel, 0, 1 );

QgsFieldMap fieldMap = vLayer->pendingFields();
QgsFieldMap::const_iterator fieldIt = fieldMap.constBegin();
int layoutRowCounter = 1;
for ( ; fieldIt != fieldMap.constEnd(); ++fieldIt )
{
//insert field into sorting combo first
mSortColumnComboBox->addItem( fieldIt.value().name(), QVariant( fieldIt.key() ) );

//and into enabled / alias list
QCheckBox* attributeCheckBox = new QCheckBox( fieldIt.value().name(), this );
if ( enabledAttributes.size() < 1 || enabledAttributes.contains( fieldIt.key() ) )
{
Expand All @@ -55,38 +49,32 @@ QgsAttributeSelectionDialog::QgsAttributeSelectionDialog( const QgsVectorLayer*
{
attributeCheckBox->setCheckState( Qt::Unchecked );
}
mAttributeGridLayout->addWidget( attributeCheckBox, layoutRowCounter, 0 );
mAttributeGridLayout->addWidget(( QWidget* )attributeCheckBox, layoutRowCounter, 0, 1, 1 );

QLineEdit* attributeLineEdit = new QLineEdit( this );
QMap<int, QString>::const_iterator aliasIt = aliasMap.find( fieldIt.key() );
if ( aliasIt != aliasMap.constEnd() )
{
attributeLineEdit->setText( aliasIt.value() );
}
mAttributeGridLayout->addWidget( attributeLineEdit, layoutRowCounter, 1 );
mAttributeGridLayout->addWidget(( QWidget* )attributeLineEdit, layoutRowCounter, 1, 1, 1 );
++layoutRowCounter;
}

attributeScrollArea->setWidget( attributeWidget );

QVBoxLayout* verticalLayout = new QVBoxLayout( this );
verticalLayout->addWidget( attributeScrollArea );

QHBoxLayout* selectClearLayout = new QHBoxLayout( this );
QPushButton* mSelectAllButton = new QPushButton( tr( "Select all" ), this );
QObject::connect( mSelectAllButton, SIGNAL( clicked() ), this, SLOT( selectAllAttributes() ) );
QPushButton* mClearButton = new QPushButton( tr( "Clear" ), this );
QObject::connect( mClearButton, SIGNAL( clicked() ), this, SLOT( clearAttributes() ) );
selectClearLayout->addWidget( mSelectAllButton );
selectClearLayout->addWidget( mClearButton );
verticalLayout->addLayout( selectClearLayout );


QDialogButtonBox* buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this );
QObject::connect( buttonBox, SIGNAL( accepted() ), this, SLOT( accept() ) );
QObject::connect( buttonBox, SIGNAL( rejected() ), this, SLOT( reject() ) );
verticalLayout->addWidget( buttonBox );
//sort columns
QList< QPair<int, bool> >::const_iterator sortIt = sortColumns.constBegin();
for ( ; sortIt != sortColumns.constEnd(); ++sortIt )
{
QTreeWidgetItem* item = new QTreeWidgetItem();
item->setText( 0, fieldMap[sortIt->first].name() );
item->setData( 0, Qt::UserRole, sortIt->first );
item->setText( 1, sortIt->second ? tr( "Ascending" ) : tr( "Descending" ) );
mSortColumnTreeWidget->addTopLevelItem( item );
}
}

mOrderComboBox->insertItem( 0, tr( "Ascending" ) );
mOrderComboBox->insertItem( 0, tr( "Descending" ) );
}

QgsAttributeSelectionDialog::~QgsAttributeSelectionDialog()
Expand Down Expand Up @@ -149,12 +137,28 @@ QMap<int, QString> QgsAttributeSelectionDialog::aliasMap() const
return result;
}

void QgsAttributeSelectionDialog::selectAllAttributes()
QList< QPair<int, bool> > QgsAttributeSelectionDialog::attributeSorting() const
{
QList< QPair<int, bool> > sortingList;

for ( int i = 0; i < mSortColumnTreeWidget->topLevelItemCount(); ++i )
{
QTreeWidgetItem* item = mSortColumnTreeWidget->topLevelItem( i );
if ( item )
{
sortingList.push_back( qMakePair( item->data( 0, Qt::UserRole ).toInt(), item->text( 1 ) == tr( "Ascending" ) ) );
}
}

return sortingList;
}

void QgsAttributeSelectionDialog::on_mSelectAllButton_clicked()
{
setAllEnabled( true );
}

void QgsAttributeSelectionDialog::clearAttributes()
void QgsAttributeSelectionDialog::on_mClearButton_clicked()
{
setAllEnabled( false );
}
Expand Down Expand Up @@ -183,3 +187,48 @@ void QgsAttributeSelectionDialog::setAllEnabled( bool enabled )
}
}

void QgsAttributeSelectionDialog::on_mAddPushButton_clicked()
{
QTreeWidgetItem* item = new QTreeWidgetItem();
item->setText( 0, mSortColumnComboBox->currentText() );
item->setData( 0, Qt::UserRole, mSortColumnComboBox->itemData( mSortColumnComboBox->currentIndex() ) );
item->setText( 1, mOrderComboBox->currentText() );
mSortColumnTreeWidget->addTopLevelItem( item );
}

void QgsAttributeSelectionDialog::on_mRemovePushButton_clicked()
{
int currentIndex = mSortColumnTreeWidget->indexOfTopLevelItem( mSortColumnTreeWidget->currentItem() );
if ( currentIndex != -1 )
{
delete( mSortColumnTreeWidget->takeTopLevelItem( currentIndex ) );
}
}

void QgsAttributeSelectionDialog::on_mUpPushButton_clicked()
{
int currentIndex = mSortColumnTreeWidget->indexOfTopLevelItem( mSortColumnTreeWidget->currentItem() );
if ( currentIndex != -1 )
{
if ( currentIndex > 0 )
{
QTreeWidgetItem* item = mSortColumnTreeWidget->takeTopLevelItem( currentIndex );
mSortColumnTreeWidget->insertTopLevelItem( currentIndex - 1, item );
mSortColumnTreeWidget->setCurrentItem( item );
}
}
}

void QgsAttributeSelectionDialog::on_mDownPushButton_clicked()
{
int currentIndex = mSortColumnTreeWidget->indexOfTopLevelItem( mSortColumnTreeWidget->currentItem() );
if ( currentIndex != -1 )
{
if ( currentIndex < ( mSortColumnTreeWidget->topLevelItemCount() - 1 ) )
{
QTreeWidgetItem* item = mSortColumnTreeWidget->takeTopLevelItem( currentIndex );
mSortColumnTreeWidget->insertTopLevelItem( currentIndex + 1, item );
mSortColumnTreeWidget->setCurrentItem( item );
}
}
}
16 changes: 11 additions & 5 deletions src/app/composer/qgsattributeselectiondialog.h
Expand Up @@ -21,31 +21,37 @@
#include <QDialog>
#include <QMap>
#include <QSet>
#include "ui_qgsattributeselectiondialogbase.h"

class QGridLayout;
class QgsVectorLayer;
class QPushButton;

/**A dialog to select what attributes to display (in the table item) and with the possibility to set different aliases*/
class QgsAttributeSelectionDialog: public QDialog
class QgsAttributeSelectionDialog: public QDialog, private Ui::QgsAttributeSelectionDialogBase
{
Q_OBJECT
public:
QgsAttributeSelectionDialog( const QgsVectorLayer* vLayer, const QSet<int>& enabledAttributes, const QMap<int, QString>& aliasMap, QWidget * parent = 0, Qt::WindowFlags f = 0 );
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 );
~QgsAttributeSelectionDialog();

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

private slots:
void selectAllAttributes();
void clearAttributes();
void on_mSelectAllButton_clicked();
void on_mClearButton_clicked();
void on_mAddPushButton_clicked();
void on_mRemovePushButton_clicked();
void on_mUpPushButton_clicked();
void on_mDownPushButton_clicked();

private:
const QgsVectorLayer* mVectorLayer;
QGridLayout* mAttributeGridLayout;
QPushButton* mSelectAllButton;
QPushButton* mClearButton;

Expand Down
8 changes: 7 additions & 1 deletion src/app/composer/qgscomposertablewidget.cpp
Expand Up @@ -32,6 +32,8 @@ QgsComposerTableWidget::QgsComposerTableWidget( QgsComposerAttributeTable* table
QgsComposerItemWidget* itemPropertiesWidget = new QgsComposerItemWidget( this, mComposerTable );
mToolBox->addItem( itemPropertiesWidget, tr( "General options" ) );

blockAllSignals( true );

//insert vector layers into combo
QMap<QString, QgsMapLayer*> layerMap = QgsMapLayerRegistry::instance()->mapLayers();
QMap<QString, QgsMapLayer*>::const_iterator mapIt = layerMap.constBegin();
Expand All @@ -46,6 +48,7 @@ QgsComposerTableWidget::QgsComposerTableWidget( QgsComposerAttributeTable* table
}

//insert composer maps into combo
mLayerComboBox->blockSignals( true );
if ( mComposerTable )
{
const QgsComposition* tableComposition = mComposerTable->composition();
Expand All @@ -60,8 +63,10 @@ QgsComposerTableWidget::QgsComposerTableWidget( QgsComposerAttributeTable* table
}
}
}
mLayerComboBox->blockSignals( false );

updateGuiElements();
blockAllSignals( false );

if ( mComposerTable )
{
Expand Down Expand Up @@ -108,12 +113,13 @@ void QgsComposerTableWidget::on_mAttributesPushButton_clicked()
return;
}

QgsAttributeSelectionDialog d( mComposerTable->vectorLayer(), mComposerTable->displayAttributes(), mComposerTable->fieldAliasMap(), 0 );
QgsAttributeSelectionDialog d( mComposerTable->vectorLayer(), mComposerTable->displayAttributes(), mComposerTable->fieldAliasMap(), mComposerTable->sortAttributes(), 0 );
if ( d.exec() == QDialog::Accepted )
{
//change displayAttributes and aliases
mComposerTable->setDisplayAttributes( d.enabledAttributes() );
mComposerTable->setFieldAliasMap( d.aliasMap() );
mComposerTable->setSortAttributes( d.attributeSorting() );
mComposerTable->update();
}
}
Expand Down
61 changes: 60 additions & 1 deletion src/core/composer/qgscomposerattributetable.cpp
Expand Up @@ -20,6 +20,29 @@
#include "qgsmaplayerregistry.h"
#include "qgsvectorlayer.h"

QgsComposerAttributeTableCompare::QgsComposerAttributeTableCompare(): mCurrentSortColumn( 0 ), mAscending( true )
{
}


bool QgsComposerAttributeTableCompare::operator()( const QgsAttributeMap& m1, const QgsAttributeMap& m2 )
{
QVariant v1 = m1[mCurrentSortColumn];
QVariant v2 = m2[mCurrentSortColumn];

bool less = false;
if ( v1.type() == QVariant::String && v2.type() == QVariant::String )
{
less = v1.toString() < v2.toString();
}
else
{
less = v1.toDouble() < v2.toDouble();
}
return ( mAscending ? less : !less );
}


QgsComposerAttributeTable::QgsComposerAttributeTable( QgsComposition* composition ): QgsComposerTable( composition ), mVectorLayer( 0 ), mComposerMap( 0 ), \
mMaximumNumberOfFeatures( 5 ), mShowOnlyVisibleFeatures( true )
{
Expand Down Expand Up @@ -110,6 +133,15 @@ bool QgsComposerAttributeTable::getFeatureAttributes( QList<QgsAttributeMap>& at
attributes.push_back( f.attributeMap() );
++counter;
}

//sort the list, starting with the last attribute
QgsComposerAttributeTableCompare c;
for ( int i = mSortInformation.size() - 1; i >= 0; --i )
{
c.setSortColumn( mSortInformation.at( i ).first );
c.setAscending( mSortInformation.at( i ).second );
qStableSort( attributes.begin(), attributes.end(), c );
}
return true;
}

Expand Down Expand Up @@ -212,8 +244,20 @@ bool QgsComposerAttributeTable::writeXML( QDomElement& elem, QDomDocument & doc
aliasMapElem.appendChild( mapEntryElem );
}
composerTableElem.appendChild( aliasMapElem );
bool ok = tableWriteXML( composerTableElem, doc );

//sort info
QDomElement sortColumnsElem = doc.createElement( "sortColumns" );
QList< QPair<int, bool> >::const_iterator sortIt = mSortInformation.constBegin();
for ( ; sortIt != mSortInformation.constEnd(); ++sortIt )
{
QDomElement columnElem = doc.createElement( "column" );
columnElem.setAttribute( "index", QString::number( sortIt->first ) );
columnElem.setAttribute( "ascending", sortIt->second == true ? "true" : "false" );
sortColumnsElem.appendChild( columnElem );
}
composerTableElem.appendChild( sortColumnsElem );
elem.appendChild( composerTableElem );
bool ok = tableWriteXML( composerTableElem, doc );
return ok;
}

Expand Down Expand Up @@ -291,5 +335,20 @@ bool QgsComposerAttributeTable::readXML( const QDomElement& itemElem, const QDom
mFieldAliasMap.insert( key, value );
}
}

//restore sort columns
mSortInformation.clear();
QDomElement sortColumnsElem = itemElem.firstChildElement( "sortColumns" );
if ( !sortColumnsElem.isNull() )
{
QDomNodeList columns = sortColumnsElem.elementsByTagName( "column" );
for ( int i = 0; i < columns.size(); ++i )
{
QDomElement columnElem = columns.at( i ).toElement();
int attribute = columnElem.attribute( "index" ).toInt();
bool ascending = columnElem.attribute( "ascending" ) == "true" ? true : false;
mSortInformation.push_back( qMakePair( attribute, ascending ) );
}
}
return tableReadXML( itemElem, doc );
}
19 changes: 19 additions & 0 deletions src/core/composer/qgscomposerattributetable.h
Expand Up @@ -23,6 +23,19 @@
class QgsComposerMap;
class QgsVectorLayer;

/**Helper class for sorting, takes into account sorting column and ascending / descending*/
class QgsComposerAttributeTableCompare
{
public:
QgsComposerAttributeTableCompare();
bool operator()( const QgsAttributeMap& m1, const QgsAttributeMap& m2 );
void setSortColumn( int col ) { mCurrentSortColumn = col; }
void setAscending( bool asc ) { mAscending = asc; }
private:
int mCurrentSortColumn;
bool mAscending;
};

/**A table class that displays a vector attribute table*/
class CORE_EXPORT QgsComposerAttributeTable: public QgsComposerTable
{
Expand Down Expand Up @@ -58,6 +71,9 @@ class CORE_EXPORT QgsComposerAttributeTable: public QgsComposerTable
/**Adapts mMaximumNumberOfFeatures depending on the rectangle height*/
void setSceneRect( const QRectF& rectangle );

void setSortAttributes( const QList<QPair<int, bool> > att ) { mSortInformation = att; }
QList<QPair<int, bool> > sortAttributes() const { return mSortInformation; }

protected:
/**Retrieves feature attributes*/
bool getFeatureAttributes( QList<QgsAttributeMap>& attributes );
Expand All @@ -79,6 +95,9 @@ class CORE_EXPORT QgsComposerAttributeTable: public QgsComposerTable
/**Map of attribute name aliases. The aliases might be different to those of QgsVectorLayer (but those from the vector layer are the default)*/
QMap<int, QString> mFieldAliasMap;

/**Contains information about sort attribute index / ascending (true/false). First entry has the highest priority*/
QList< QPair<int, bool> > mSortInformation;

/**Inserts aliases from vector layer as starting configuration to the alias map*/
void initializeAliasMap();
/**Returns the attribute name to display in the item (attribute name or an alias if present)*/
Expand Down

0 comments on commit a643c6b

Please sign in to comment.