mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-04 00:06:46 -05:00
Allow sorting attribute table by expression
This commit is contained in:
parent
11ee2fc809
commit
d07d9edda6
@ -583,6 +583,7 @@
|
||||
<file>themes/default/multieditMixedValues.svg</file>
|
||||
<file>themes/default/multieditSameValues.svg</file>
|
||||
<file>themes/default/locked_repeating.svg</file>
|
||||
<file>themes/default/sort.svg</file>
|
||||
</qresource>
|
||||
<qresource prefix="/images/tips">
|
||||
<file alias="symbol_levels.png">qgis_tips/symbol_levels.png</file>
|
||||
|
||||
73
images/themes/default/sort.svg
Normal file
73
images/themes/default/sort.svg
Normal file
@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="svg5477"
|
||||
viewBox="0 0 4.2333333 4.2333335"
|
||||
height="16"
|
||||
width="16"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="sort.svg">
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1016"
|
||||
id="namedview13"
|
||||
showgrid="true"
|
||||
inkscape:zoom="39.333334"
|
||||
inkscape:cx="1.7617969"
|
||||
inkscape:cy="7.4154532"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg5477">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3341" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs5479" />
|
||||
<metadata
|
||||
id="metadata5482">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="display:inline;fill:#6d97c4;fill-opacity:1;fill-rule:evenodd;stroke:#415a75;stroke-width:0.19843751;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;enable-background:new"
|
||||
d="m 2.9088665,3.7628126 -1.0794756,-1.2402344 0.7733851,0 0.2887393,-2.12183447 0.3139285,2.12183447 0.7828981,0 z"
|
||||
id="path2848-7-3-9-4"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path31772"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:tb-rl;text-anchor:start;fill:#f79191;fill-opacity:1;stroke:#a40000;stroke-width:0.19843751;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 1.2400171,1.4925339 -0.54129826,0 -0.0854208,0.2668105 -0.3479775,0 0.49723917,-1.46451483 0.41271749,0 0.4972392,1.46451483 -0.3479775,0 -0.084522,-0.2668105 m -0.45497826,-0.271715 0.36775916,0 L 0.969368,0.63815188 0.78503884,1.2208189"
|
||||
sodipodi:nodetypes="ccccccccccccc" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path31774"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:tb-rl;text-anchor:start;fill:#91bbf7;fill-opacity:1;stroke:#0044a4;stroke-width:0.19843751;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 0.40469133,2.2147734 1.12845407,0 0,0.2285545 -0.72023241,0.9505123 0.74091331,0 0,0.285448 -1.16981581,0 0,-0.2285546 0.72023251,-0.9505123 -0.69955167,0 0,-0.2854479"
|
||||
sodipodi:nodetypes="ccccccccccc" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
@ -104,4 +104,15 @@ class QgsAttributeTableConfig
|
||||
* Deserialize to XML on layer load
|
||||
*/
|
||||
void readXml( const QDomNode& node );
|
||||
|
||||
/**
|
||||
* Get the expression used for sorting.
|
||||
*/
|
||||
QString sortExpression() const;
|
||||
|
||||
/**
|
||||
* Set the sort expression used for sorting.
|
||||
*/
|
||||
void setSortExpression( const QString& sortExpression );
|
||||
|
||||
};
|
||||
|
||||
@ -142,6 +142,20 @@ class QgsAttributeTableFilterModel: QSortFilterProxyModel, QgsFeatureModel
|
||||
*/
|
||||
virtual void sort( int column, Qt::SortOrder order = Qt::AscendingOrder );
|
||||
|
||||
/**
|
||||
* Sort by the given expression using the given order.
|
||||
* Prefetches all the data from the layer to speed up sorting.
|
||||
*
|
||||
* @param column The expression which should be used for sorting
|
||||
* @param order The order ( Qt::AscendingOrder or Qt::DescendingOrder )
|
||||
*/
|
||||
void sort( QString expression, Qt::SortOrder order = Qt::AscendingOrder );
|
||||
|
||||
/**
|
||||
* The expression which is used to sort the attribute table.
|
||||
*/
|
||||
QString sortExpression() const;
|
||||
|
||||
/** Returns the map canvas*/
|
||||
QgsMapCanvas* mapCanvas() const;
|
||||
|
||||
|
||||
@ -148,6 +148,19 @@ class QgsAttributeTableModel : QAbstractTableModel
|
||||
*/
|
||||
void prefetchColumnData( int column );
|
||||
|
||||
/**
|
||||
* Prefetches the entire data for one expression. Based on this cached information
|
||||
* the sorting can later be done in a performant way.
|
||||
*
|
||||
* @param expression The expression to cache
|
||||
*/
|
||||
void prefetchSortData( const QString& expression );
|
||||
|
||||
/**
|
||||
* The expression which was used to fill the sorting cache
|
||||
*/
|
||||
QString sortCacheExpression() const;
|
||||
|
||||
/**
|
||||
* Set a request that will be used to fill this attribute table model.
|
||||
* In contrast to a filter, the request will constrain the data shown without the possibility
|
||||
@ -160,7 +173,7 @@ class QgsAttributeTableModel : QAbstractTableModel
|
||||
/**
|
||||
* Get the the feature request
|
||||
*/
|
||||
const QgsFeatureRequest &request() const;
|
||||
const QgsFeatureRequest& request() const;
|
||||
|
||||
/**
|
||||
* Sets the context in which this table is shown.
|
||||
@ -188,7 +201,7 @@ class QgsAttributeTableModel : QAbstractTableModel
|
||||
* Empty extra columns to announce from this model.
|
||||
* Any extra columns need to be implemented by proxy models in front of this model.
|
||||
*/
|
||||
void setExtraColumns(int extraColumns);
|
||||
void setExtraColumns( int extraColumns );
|
||||
|
||||
public slots:
|
||||
|
||||
@ -226,7 +239,7 @@ class QgsAttributeTableModel : QAbstractTableModel
|
||||
* Launched when eatures have been deleted
|
||||
* @param fids feature ids
|
||||
*/
|
||||
virtual void featuresDeleted( const QgsFeatureIds& fid );
|
||||
virtual void featuresDeleted( const QgsFeatureIds& fids );
|
||||
/**
|
||||
* Launched when a feature has been added
|
||||
* @param fid feature id
|
||||
|
||||
@ -115,10 +115,25 @@ class QgsDualView : QStackedWidget
|
||||
*/
|
||||
QgsAttributeTableModel* masterModel() const;
|
||||
|
||||
/**
|
||||
* Set the request
|
||||
*
|
||||
* @param request The request
|
||||
*/
|
||||
void setRequest( const QgsFeatureRequest& request );
|
||||
|
||||
/**
|
||||
* Set the feature selection model
|
||||
*
|
||||
* @param featureSelectionManager the feature selection model
|
||||
*/
|
||||
void setFeatureSelectionManager( QgsIFeatureSelectionManager* featureSelectionManager );
|
||||
|
||||
/**
|
||||
* Returns the table view
|
||||
*
|
||||
* @return The table view
|
||||
*/
|
||||
QgsAttributeTableView* tableView();
|
||||
/**
|
||||
* Set the attribute table config which should be used to control
|
||||
@ -126,6 +141,16 @@ class QgsDualView : QStackedWidget
|
||||
*/
|
||||
void setAttributeTableConfig( const QgsAttributeTableConfig& config );
|
||||
|
||||
/**
|
||||
* Set the expression used for sorting the table and feature list.
|
||||
*/
|
||||
void setSortExpression( const QString& sortExpression );
|
||||
|
||||
/**
|
||||
* Get the expression used for sorting the table and feature list.
|
||||
*/
|
||||
QString sortExpression() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Initializes widgets which depend on the attributes of this layer
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QMessageBox>
|
||||
#include <QGridLayout>
|
||||
#include <QDialogButtonBox>
|
||||
|
||||
#include "qgsattributetabledialog.h"
|
||||
#include "qgsattributetablemodel.h"
|
||||
@ -23,11 +25,11 @@
|
||||
#include "qgsattributetableview.h"
|
||||
#include "qgsorganizetablecolumnsdialog.h"
|
||||
|
||||
#include <qgsapplication.h>
|
||||
#include <qgsvectordataprovider.h>
|
||||
#include <qgsvectorlayer.h>
|
||||
#include <qgsexpression.h>
|
||||
|
||||
#include "qgsapplication.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsvectordataprovider.h"
|
||||
#include "qgsexpression.h"
|
||||
#include "qgsexpressionbuilderwidget.h"
|
||||
#include "qgisapp.h"
|
||||
#include "qgsaddattrdialog.h"
|
||||
#include "qgsdelattrdialog.h"
|
||||
@ -616,6 +618,52 @@ void QgsAttributeTableDialog::on_mAddFeature_clicked()
|
||||
}
|
||||
}
|
||||
|
||||
void QgsAttributeTableDialog::on_mSortButton_clicked()
|
||||
{
|
||||
QgsAttributeTableConfig config = mLayer->attributeTableConfig();
|
||||
|
||||
QDialog orderByDlg;
|
||||
orderByDlg.setWindowTitle( tr( "Configure attribute table sort order" ) );
|
||||
QDialogButtonBox* dialogButtonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
|
||||
QGridLayout* layout = new QGridLayout();
|
||||
connect( dialogButtonBox, SIGNAL( accepted() ), &orderByDlg, SLOT( accept() ) );
|
||||
connect( dialogButtonBox, SIGNAL( rejected() ), &orderByDlg, SLOT( reject() ) );
|
||||
orderByDlg.setLayout( layout );
|
||||
|
||||
QGroupBox* sortingGroupBox = new QGroupBox();
|
||||
sortingGroupBox->setTitle( tr( "Enable sorting order in attribute table" ) );
|
||||
sortingGroupBox->setCheckable( true );
|
||||
sortingGroupBox->setChecked( !mMainView->sortExpression().isEmpty() );
|
||||
layout->addWidget( sortingGroupBox );
|
||||
sortingGroupBox->setLayout( new QGridLayout() );
|
||||
|
||||
QgsExpressionBuilderWidget* expressionBuilder = new QgsExpressionBuilderWidget();
|
||||
expressionBuilder->setExpressionText( mMainView->sortExpression().isEmpty() ? mLayer->displayExpression() : mMainView->sortExpression() );
|
||||
QgsExpressionContext context;
|
||||
context << QgsExpressionContextUtils::globalScope()
|
||||
<< QgsExpressionContextUtils::projectScope();
|
||||
expressionBuilder->setExpressionContext( context );
|
||||
expressionBuilder->setLayer( mLayer );
|
||||
sortingGroupBox->layout()->addWidget( expressionBuilder );
|
||||
|
||||
layout->addWidget( dialogButtonBox );
|
||||
if ( orderByDlg.exec() )
|
||||
{
|
||||
if ( sortingGroupBox->isChecked() )
|
||||
{
|
||||
mMainView->setSortExpression( expressionBuilder->expressionText() );
|
||||
config.setSortExpression( expressionBuilder->expressionText() );
|
||||
}
|
||||
else
|
||||
{
|
||||
mMainView->setSortExpression( QString() );
|
||||
config.setSortExpression( QString() );
|
||||
}
|
||||
|
||||
mLayer->setAttributeTableConfig( config );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsAttributeTableDialog::on_mExpressionSelectButton_clicked()
|
||||
{
|
||||
QgsExpressionSelectionDialog* dlg = new QgsExpressionSelectionDialog( mLayer );
|
||||
|
||||
@ -154,6 +154,8 @@ class APP_EXPORT QgsAttributeTableDialog : public QDialog, private Ui::QgsAttrib
|
||||
|
||||
void on_mHelpButton_clicked() { QgsContextHelp::run( metaObject()->className() ); }
|
||||
|
||||
void on_mSortButton_clicked();
|
||||
|
||||
void on_mExpressionSelectButton_clicked();
|
||||
void filterColumnChanged( QObject* filterAction );
|
||||
void filterExpressionBuilder();
|
||||
|
||||
@ -173,6 +173,18 @@ void QgsAttributeTableConfig::readXml( const QDomNode& node )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mSortExpression = configNode.toElement().attribute( "sortExpression" );
|
||||
}
|
||||
|
||||
QString QgsAttributeTableConfig::sortExpression() const
|
||||
{
|
||||
return mSortExpression;
|
||||
}
|
||||
|
||||
void QgsAttributeTableConfig::setSortExpression( const QString& sortExpression )
|
||||
{
|
||||
mSortExpression = sortExpression;
|
||||
}
|
||||
|
||||
void QgsAttributeTableConfig::writeXml( QDomNode& node ) const
|
||||
@ -182,6 +194,8 @@ void QgsAttributeTableConfig::writeXml( QDomNode& node ) const
|
||||
QDomElement configElement = doc.createElement( "attributetableconfig" );
|
||||
configElement.setAttribute( "actionWidgetStyle", mActionWidgetStyle == ButtonList ? "buttonList" : "dropDown" );
|
||||
|
||||
configElement.setAttribute( "sortExpression", mSortExpression );
|
||||
|
||||
QDomElement columnsElement = doc.createElement( "columns" );
|
||||
|
||||
Q_FOREACH ( const ColumnConfig& column, mColumns )
|
||||
|
||||
@ -110,10 +110,20 @@ class CORE_EXPORT QgsAttributeTableConfig
|
||||
*/
|
||||
void readXml( const QDomNode& node );
|
||||
|
||||
/**
|
||||
* Get the expression used for sorting.
|
||||
*/
|
||||
QString sortExpression() const;
|
||||
|
||||
/**
|
||||
* Set the sort expression used for sorting.
|
||||
*/
|
||||
void setSortExpression( const QString& sortExpression );
|
||||
|
||||
private:
|
||||
QVector<ColumnConfig> mColumns;
|
||||
ActionWidgetStyle mActionWidgetStyle;
|
||||
QString mSortExpression;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE( QgsAttributeTableConfig::ColumnConfig )
|
||||
|
||||
@ -194,6 +194,20 @@ void QgsAttributeTableFilterModel::setAttributeTableConfig( const QgsAttributeTa
|
||||
endResetModel();
|
||||
}
|
||||
}
|
||||
|
||||
sort( config.sortExpression() );
|
||||
}
|
||||
|
||||
void QgsAttributeTableFilterModel::sort( QString expression, Qt::SortOrder order )
|
||||
{
|
||||
QSortFilterProxyModel::sort( -1 );
|
||||
masterModel()->prefetchSortData( expression );
|
||||
QSortFilterProxyModel::sort( 0, order ) ;
|
||||
}
|
||||
|
||||
QString QgsAttributeTableFilterModel::sortExpression() const
|
||||
{
|
||||
return masterModel()->sortCacheExpression();
|
||||
}
|
||||
|
||||
void QgsAttributeTableFilterModel::setSelectedOnTop( bool selectedOnTop )
|
||||
|
||||
@ -178,6 +178,20 @@ class GUI_EXPORT QgsAttributeTableFilterModel: public QSortFilterProxyModel, pub
|
||||
*/
|
||||
virtual void sort( int column, Qt::SortOrder order = Qt::AscendingOrder ) override;
|
||||
|
||||
/**
|
||||
* Sort by the given expression using the given order.
|
||||
* Prefetches all the data from the layer to speed up sorting.
|
||||
*
|
||||
* @param expression The expression which should be used for sorting
|
||||
* @param order The order ( Qt::AscendingOrder or Qt::DescendingOrder )
|
||||
*/
|
||||
void sort( QString expression, Qt::SortOrder order = Qt::AscendingOrder );
|
||||
|
||||
/**
|
||||
* The expression which is used to sort the attribute table.
|
||||
*/
|
||||
QString sortExpression() const;
|
||||
|
||||
/** Returns the map canvas*/
|
||||
QgsMapCanvas* mapCanvas() const { return mCanvas; }
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@ QgsAttributeTableModel::QgsAttributeTableModel( QgsVectorLayerCache *layerCache,
|
||||
: QAbstractTableModel( parent )
|
||||
, mLayerCache( layerCache )
|
||||
, mFieldCount( 0 )
|
||||
, mCachedField( -1 )
|
||||
, mSortCacheExpression( "" )
|
||||
, mExtraColumns( 0 )
|
||||
{
|
||||
mExpressionContext << QgsExpressionContextUtils::globalScope()
|
||||
@ -160,7 +160,7 @@ bool QgsAttributeTableModel::removeRows( int row, int count, const QModelIndex &
|
||||
// clean old references
|
||||
for ( int i = row; i < row + count; i++ )
|
||||
{
|
||||
mFieldCache.remove( mRowIdMap[i] );
|
||||
mSortCache.remove( mRowIdMap[i] );
|
||||
mIdRowMap.remove( mRowIdMap[i] );
|
||||
mRowIdMap.remove( i );
|
||||
}
|
||||
@ -206,7 +206,8 @@ void QgsAttributeTableModel::featureAdded( QgsFeatureId fid )
|
||||
|
||||
if ( featOk && mFeatureRequest.acceptFeature( mFeat ) )
|
||||
{
|
||||
mFieldCache[fid] = mFeat.attribute( mCachedField );
|
||||
mExpressionContext.setFeature( mFeat );
|
||||
mSortCache[fid] = mSortCacheExpression.evaluate();
|
||||
|
||||
int n = mRowIdMap.size();
|
||||
beginInsertRows( QModelIndex(), n, n );
|
||||
@ -236,10 +237,8 @@ void QgsAttributeTableModel::editCommandEnded()
|
||||
|
||||
void QgsAttributeTableModel::attributeDeleted( int idx )
|
||||
{
|
||||
if ( idx == mCachedField )
|
||||
{
|
||||
prefetchColumnData( -1 );
|
||||
}
|
||||
if ( mSortCacheAttributes.contains( idx ) )
|
||||
prefetchSortData( "" );
|
||||
}
|
||||
|
||||
void QgsAttributeTableModel::layerDeleted()
|
||||
@ -256,9 +255,13 @@ void QgsAttributeTableModel::attributeValueChanged( QgsFeatureId fid, int idx, c
|
||||
{
|
||||
QgsDebugMsgLevel( QString( "(%4) fid: %1, idx: %2, value: %3" ).arg( fid ).arg( idx ).arg( value.toString() ).arg( mFeatureRequest.filterType() ), 3 );
|
||||
|
||||
if ( idx == mCachedField )
|
||||
mFieldCache[fid] = value;
|
||||
|
||||
if ( mSortCacheAttributes.contains( idx ) )
|
||||
{
|
||||
// if the expression used multiple fields, this will not work but this way we don't have
|
||||
// to run the expensive query in the 80% cases where we just have a simple column for sorting
|
||||
// or it's the first used column, this works just fine
|
||||
mSortCache[fid] = value;
|
||||
}
|
||||
// No filter request: skip all possibly heavy checks
|
||||
if ( mFeatureRequest.filterType() == QgsFeatureRequest::FilterNone )
|
||||
{
|
||||
@ -577,27 +580,22 @@ QVariant QgsAttributeTableModel::data( const QModelIndex &index, int role ) cons
|
||||
return QVariant( Qt::AlignLeft );
|
||||
}
|
||||
|
||||
QVariant val;
|
||||
|
||||
// if we don't have the row in current cache, load it from layer first
|
||||
if ( mCachedField == fieldId )
|
||||
if ( role == SortRole )
|
||||
{
|
||||
val = mFieldCache[rowId];
|
||||
return mSortCache[rowId];
|
||||
}
|
||||
else
|
||||
|
||||
if ( mFeat.id() != rowId || !mFeat.isValid() )
|
||||
{
|
||||
if ( mFeat.id() != rowId || !mFeat.isValid() )
|
||||
{
|
||||
if ( !loadFeatureAtId( rowId ) )
|
||||
return QVariant( "ERROR" );
|
||||
if ( !loadFeatureAtId( rowId ) )
|
||||
return QVariant( "ERROR" );
|
||||
|
||||
if ( mFeat.id() != rowId )
|
||||
return QVariant( "ERROR" );
|
||||
}
|
||||
|
||||
val = mFeat.attribute( fieldId );
|
||||
if ( mFeat.id() != rowId )
|
||||
return QVariant( "ERROR" );
|
||||
}
|
||||
|
||||
QVariant val = mFeat.attribute( fieldId );
|
||||
|
||||
if ( role == Qt::DisplayRole )
|
||||
{
|
||||
return mWidgetFactories[index.column()]->representValue( layer(), fieldId, mWidgetConfigs[index.column()], mAttributeWidgetCaches[index.column()], val );
|
||||
@ -730,34 +728,51 @@ QgsFeature QgsAttributeTableModel::feature( const QModelIndex &idx ) const
|
||||
|
||||
void QgsAttributeTableModel::prefetchColumnData( int column )
|
||||
{
|
||||
mFieldCache.clear();
|
||||
|
||||
if ( column == -1 )
|
||||
if ( column == -1 || column >= mAttributes.count() )
|
||||
{
|
||||
mCachedField = -1;
|
||||
prefetchSortData( "" );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( column >= mAttributes.count() )
|
||||
return;
|
||||
int fieldId = mAttributes.at( column );
|
||||
const QgsFields& fields = layer()->fields();
|
||||
QStringList fldNames;
|
||||
fldNames << fields[fieldId].name();
|
||||
|
||||
QgsFeatureRequest r( mFeatureRequest );
|
||||
QgsFeatureIterator it = mLayerCache->getFeatures( r.setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( fldNames, fields ) );
|
||||
|
||||
QgsFeature f;
|
||||
while ( it.nextFeature( f ) )
|
||||
{
|
||||
mFieldCache.insert( f.id(), f.attribute( fieldId ) );
|
||||
}
|
||||
|
||||
mCachedField = fieldId;
|
||||
prefetchSortData( mLayerCache->layer()->fields().at( mAttributes.at( column ) ).name() );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsAttributeTableModel::prefetchSortData( const QString& expressionString )
|
||||
{
|
||||
mSortCache.clear();
|
||||
mSortCacheAttributes.clear();
|
||||
|
||||
mSortCacheExpression = QgsExpression( expressionString );
|
||||
|
||||
mSortCacheExpression.prepare( &mExpressionContext );
|
||||
|
||||
Q_FOREACH ( const QString& col, mSortCacheExpression.referencedColumns() )
|
||||
{
|
||||
mSortCacheAttributes.append( mLayerCache->layer()->fieldNameIndex( col ) );
|
||||
}
|
||||
|
||||
QgsFeatureRequest request = QgsFeatureRequest( mFeatureRequest )
|
||||
.setFlags( QgsFeatureRequest::NoGeometry )
|
||||
.setSubsetOfAttributes( mSortCacheAttributes );
|
||||
QgsFeatureIterator it = mLayerCache->getFeatures( request );
|
||||
|
||||
QgsFeature f;
|
||||
while ( it.nextFeature( f ) )
|
||||
{
|
||||
mExpressionContext.setFeature( f );
|
||||
mSortCache.insert( f.id(), mSortCacheExpression.evaluate( &mExpressionContext ) );
|
||||
}
|
||||
}
|
||||
|
||||
QString QgsAttributeTableModel::sortCacheExpression() const
|
||||
{
|
||||
if ( mSortCacheExpression.rootNode() )
|
||||
return mSortCacheExpression.expression();
|
||||
else
|
||||
return QString();
|
||||
}
|
||||
|
||||
void QgsAttributeTableModel::setRequest( const QgsFeatureRequest& request )
|
||||
{
|
||||
mFeatureRequest = request;
|
||||
|
||||
@ -193,6 +193,19 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
|
||||
*/
|
||||
void prefetchColumnData( int column );
|
||||
|
||||
/**
|
||||
* Prefetches the entire data for one expression. Based on this cached information
|
||||
* the sorting can later be done in a performant way.
|
||||
*
|
||||
* @param expression The expression to cache
|
||||
*/
|
||||
void prefetchSortData( const QString& expression );
|
||||
|
||||
/**
|
||||
* The expression which was used to fill the sorting cache
|
||||
*/
|
||||
QString sortCacheExpression() const;
|
||||
|
||||
/**
|
||||
* Set a request that will be used to fill this attribute table model.
|
||||
* In contrast to a filter, the request will constrain the data shown without the possibility
|
||||
@ -335,9 +348,10 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
|
||||
QgsFeatureRequest mFeatureRequest;
|
||||
|
||||
/** The currently cached column */
|
||||
int mCachedField;
|
||||
/** Allows caching of one specific column (used for sorting) */
|
||||
QHash<QgsFeatureId, QVariant> mFieldCache;
|
||||
QgsExpression mSortCacheExpression;
|
||||
QgsAttributeList mSortCacheAttributes;
|
||||
/** Allows caching of one value per column (used for sorting) */
|
||||
QHash<QgsFeatureId, QVariant> mSortCache;
|
||||
|
||||
/**
|
||||
* Holds the bounds of changed cells while an update operation is running
|
||||
|
||||
@ -196,6 +196,10 @@ void QgsDualView::columnBoxInit()
|
||||
{
|
||||
mFeatureListPreviewButton->defaultAction()->trigger();
|
||||
}
|
||||
|
||||
QAction* sortByPreviewExpression = new QAction( QgsApplication::getThemeIcon( "sort.svg" ), tr( "Sort by preview expression" ), this );
|
||||
connect( sortByPreviewExpression, SIGNAL( triggered( bool ) ), this, SLOT( sortByPreviewExpression() ) );
|
||||
mFeatureListPreviewButton->addAction( sortByPreviewExpression );
|
||||
}
|
||||
|
||||
void QgsDualView::setView( QgsDualView::ViewMode view )
|
||||
@ -439,6 +443,11 @@ void QgsDualView::previewExpressionChanged( const QString& expression )
|
||||
mLayerCache->layer()->setDisplayExpression( expression );
|
||||
}
|
||||
|
||||
void QgsDualView::sortByPreviewExpression()
|
||||
{
|
||||
setSortExpression( mFeatureList->displayExpression() );
|
||||
}
|
||||
|
||||
void QgsDualView::featureFormAttributeChanged()
|
||||
{
|
||||
mFeatureList->setCurrentFeatureEdited( true );
|
||||
@ -470,6 +479,19 @@ void QgsDualView::setAttributeTableConfig( const QgsAttributeTableConfig& config
|
||||
mFilterModel->setAttributeTableConfig( config );
|
||||
}
|
||||
|
||||
void QgsDualView::setSortExpression( const QString& sortExpression )
|
||||
{
|
||||
if ( sortExpression.isNull() )
|
||||
mFilterModel->sort( -1 );
|
||||
else
|
||||
mFilterModel->sort( sortExpression );
|
||||
}
|
||||
|
||||
QString QgsDualView::sortExpression() const
|
||||
{
|
||||
return mFilterModel->sortExpression();
|
||||
}
|
||||
|
||||
void QgsDualView::progress( int i, bool& cancel )
|
||||
{
|
||||
if ( !mProgressDlg )
|
||||
|
||||
@ -180,6 +180,16 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
|
||||
*/
|
||||
void setAttributeTableConfig( const QgsAttributeTableConfig& config );
|
||||
|
||||
/**
|
||||
* Set the expression used for sorting the table and feature list.
|
||||
*/
|
||||
void setSortExpression( const QString& sortExpression );
|
||||
|
||||
/**
|
||||
* Get the expression used for sorting the table and feature list.
|
||||
*/
|
||||
QString sortExpression() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Initializes widgets which depend on the attributes of this layer
|
||||
@ -240,6 +250,8 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
|
||||
|
||||
void previewExpressionChanged( const QString& expression );
|
||||
|
||||
void sortByPreviewExpression();
|
||||
|
||||
/**
|
||||
* Will be called whenever the currently shown feature form changes.
|
||||
* Will forward this signal to the feature list to visually represent
|
||||
|
||||
@ -14,7 +14,16 @@
|
||||
<string>Attribute Table</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
@ -566,6 +575,29 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="mSortButton">
|
||||
<property name="toolTip">
|
||||
<string>Control the sort order</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../images/images.qrc">
|
||||
<normaloff>:/images/themes/default/sort.svg</normaloff>:/images/themes/default/sort.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>18</width>
|
||||
<height>18</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
@ -670,7 +702,16 @@
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user