Skip to content

Commit

Permalink
Sort attribute table by representation value where useful
Browse files Browse the repository at this point in the history
Fix #15096
And aligns some edge-cases of sort behavior
  • Loading branch information
m-kuhn committed Jul 7, 2016
1 parent 31fc2b0 commit 56a0af5
Show file tree
Hide file tree
Showing 12 changed files with 136 additions and 18 deletions.
15 changes: 15 additions & 0 deletions python/gui/editorwidgets/core/qgseditorwidgetfactory.sip
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,21 @@ class QgsEditorWidgetFactory
*/
virtual QString representValue( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, const QVariant& cache, const QVariant& value ) const;

/**
* If the default sort order should be overwritten for this widget, you can transform the value in here.
*
* @param vl The vector layer.
* @param fieldIdx The index of the field.
* @param config The editor widget config.
* @param cache The editor widget cache.
* @param value The value to represent.
*
* @return By default the value is returned unmodified.
*
* @note Added in 2.16
*/
virtual QVariant sortValue( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, const QVariant& cache, const QVariant& value ) const;

/**
* Return the alignment for a particular field. By default this will consider the field type but can be overwritten if mapped
* values are represented.
Expand Down
7 changes: 6 additions & 1 deletion python/gui/editorwidgets/qgsrelationreferencewidget.sip
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,24 @@ class QgsRelationReferenceWidget : QWidget

//! return the related feature (from the referenced layer)
//! if no feature is related, it returns an invalid feature
QgsFeature referencedFeature();
QgsFeature referencedFeature() const;

/** Sets the widget to display in an indeterminate "mixed value" state.
* @note added in QGIS 2.16
*/
void showIndeterminateState();

/**
* Determines if a button for adding new features should be shown.
*
* @note added in QGIS 2.16
*/
bool allowAddFeatures() const;

/**
* Determines if a button for adding new features should be shown.
*
* @note added in QGIS 2.16
*/
void setAllowAddFeatures( bool allowAddFeatures );

Expand Down
84 changes: 68 additions & 16 deletions src/gui/attributetable/qgsattributetablemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ QgsAttributeTableModel::QgsAttributeTableModel( QgsVectorLayerCache *layerCache,
, mLayerCache( layerCache )
, mFieldCount( 0 )
, mSortCacheExpression( "" )
, mSortFieldIndex( -1 )
, mExtraColumns( 0 )
{
mExpressionContext << QgsExpressionContextUtils::globalScope()
Expand Down Expand Up @@ -206,8 +207,19 @@ void QgsAttributeTableModel::featureAdded( QgsFeatureId fid )

if ( featOk && mFeatureRequest.acceptFeature( mFeat ) )
{
mExpressionContext.setFeature( mFeat );
mSortCache[fid] = mSortCacheExpression.evaluate();
if ( mSortFieldIndex == -1 )
{
mExpressionContext.setFeature( mFeat );
mSortCache[mFeat.id()] = mSortCacheExpression.evaluate( &mExpressionContext );
}
else
{
QgsEditorWidgetFactory* widgetFactory = mWidgetFactories.at( mSortFieldIndex );
const QVariant& widgetCache = mAttributeWidgetCaches.at( mSortFieldIndex );
const QgsEditorWidgetConfig& widgetConfig = mWidgetConfigs.at( mSortFieldIndex );
QVariant sortValue = widgetFactory->representValue( layer(), mSortFieldIndex, widgetConfig, widgetCache, mFeat.attribute( mSortFieldIndex ) );
mSortCache.insert( mFeat.id(), sortValue );
}

int n = mRowIdMap.size();
beginInsertRows( QModelIndex(), n, n );
Expand Down Expand Up @@ -257,10 +269,20 @@ void QgsAttributeTableModel::attributeValueChanged( QgsFeatureId fid, int idx, c

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;
if ( mSortFieldIndex == -1 )
{
loadFeatureAtId( fid );
mExpressionContext.setFeature( mFeat );
mSortCache[fid] = mSortCacheExpression.evaluate( &mExpressionContext );
}
else
{
QgsEditorWidgetFactory* widgetFactory = mWidgetFactories.at( mSortFieldIndex );
const QVariant& widgetCache = mAttributeWidgetCaches.at( mSortFieldIndex );
const QgsEditorWidgetConfig& widgetConfig = mWidgetConfigs.at( mSortFieldIndex );
QVariant sortValue = widgetFactory->representValue( layer(), mSortFieldIndex, widgetConfig, widgetCache, value );
mSortCache.insert( fid, sortValue );
}
}
// No filter request: skip all possibly heavy checks
if ( mFeatureRequest.filterType() == QgsFeatureRequest::FilterNone )
Expand Down Expand Up @@ -341,6 +363,9 @@ void QgsAttributeTableModel::loadAttributes()
mFieldCount = attributes.size();
mAttributes = attributes;

if ( mSortFieldIndex >= mAttributes.count() )
mSortFieldIndex = -1;

if ( ins )
{
endInsertColumns();
Expand Down Expand Up @@ -373,8 +398,7 @@ void QgsAttributeTableModel::loadLayer()
QTime t;
t.start();

QgsFeature feat;
while ( features.nextFeature( feat ) )
while ( features.nextFeature( mFeat ) )
{
++i;

Expand All @@ -387,8 +411,7 @@ void QgsAttributeTableModel::loadLayer()

t.restart();
}
mFeat = feat;
featureAdded( feat.id() );
featureAdded( mFeat.id() );
}

emit finished();
Expand Down Expand Up @@ -758,14 +781,35 @@ void QgsAttributeTableModel::prefetchSortData( const QString& expressionString )
{
mSortCache.clear();
mSortCacheAttributes.clear();

mSortFieldIndex = -1;
mSortCacheExpression = QgsExpression( expressionString );

mSortCacheExpression.prepare( &mExpressionContext );
QgsEditorWidgetFactory* widgetFactory = nullptr;
QVariant widgetCache;
QgsEditorWidgetConfig widgetConfig;

Q_FOREACH ( const QString& col, mSortCacheExpression.referencedColumns() )
if ( mSortCacheExpression.isField() )
{
mSortCacheAttributes.append( mLayerCache->layer()->fieldNameIndex( col ) );
QString fieldName = dynamic_cast<const QgsExpression::NodeColumnRef*>( mSortCacheExpression.rootNode() )->name();
mSortFieldIndex = mLayerCache->layer()->fieldNameIndex( fieldName );
}

if ( mSortFieldIndex == -1 )
{
mSortCacheExpression.prepare( &mExpressionContext );

Q_FOREACH ( const QString& col, mSortCacheExpression.referencedColumns() )
{
mSortCacheAttributes.append( mLayerCache->layer()->fieldNameIndex( col ) );
}
}
else
{
mSortCacheAttributes.append( mSortFieldIndex );

widgetFactory = mWidgetFactories.at( mSortFieldIndex );
widgetCache = mAttributeWidgetCaches.at( mSortFieldIndex );
widgetConfig = mWidgetConfigs.at( mSortFieldIndex );
}

QgsFeatureRequest request = QgsFeatureRequest( mFeatureRequest )
Expand All @@ -776,8 +820,16 @@ void QgsAttributeTableModel::prefetchSortData( const QString& expressionString )
QgsFeature f;
while ( it.nextFeature( f ) )
{
mExpressionContext.setFeature( f );
mSortCache.insert( f.id(), mSortCacheExpression.evaluate( &mExpressionContext ) );
if ( mSortFieldIndex == -1 )
{
mExpressionContext.setFeature( f );
mSortCache.insert( f.id(), mSortCacheExpression.evaluate( &mExpressionContext ) );
}
else
{
QVariant sortValue = widgetFactory->representValue( layer(), mSortFieldIndex, widgetConfig, widgetCache, f.attribute( mSortFieldIndex ) );
mSortCache.insert( f.id(), sortValue );
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/gui/attributetable/qgsattributetablemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
/**
* Get the the feature request
*/
// TODO QGIS 3: return copy instead of reference
const QgsFeatureRequest& request() const;

/**
Expand Down Expand Up @@ -350,6 +351,8 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
/** The currently cached column */
QgsExpression mSortCacheExpression;
QgsAttributeList mSortCacheAttributes;
/** If it is set, a simple field is used for sorting, if it's -1 it's the mSortCacheExpression*/
int mSortFieldIndex;
/** Allows caching of one value per column (used for sorting) */
QHash<QgsFeatureId, QVariant> mSortCache;

Expand Down
9 changes: 9 additions & 0 deletions src/gui/editorwidgets/core/qgseditorwidgetfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@ QString QgsEditorWidgetFactory::representValue( QgsVectorLayer* vl, int fieldIdx
return value == defVal ? defVal : vl->fields().at( fieldIdx ).displayString( value );
}

QVariant QgsEditorWidgetFactory::sortValue( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, const QVariant& cache, const QVariant& value ) const
{
Q_UNUSED( vl )
Q_UNUSED( fieldIdx )
Q_UNUSED( config )
Q_UNUSED( cache )
return value;
}

Qt::AlignmentFlag QgsEditorWidgetFactory::alignmentFlag( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config ) const
{
Q_UNUSED( config );
Expand Down
15 changes: 15 additions & 0 deletions src/gui/editorwidgets/core/qgseditorwidgetfactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,21 @@ class GUI_EXPORT QgsEditorWidgetFactory
*/
virtual QString representValue( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, const QVariant& cache, const QVariant& value ) const;

/**
* If the default sort order should be overwritten for this widget, you can transform the value in here.
*
* @param vl The vector layer.
* @param fieldIdx The index of the field.
* @param config The editor widget config.
* @param cache The editor widget cache.
* @param value The value to represent.
*
* @return By default the value is returned unmodified.
*
* @note Added in 2.16
*/
virtual QVariant sortValue( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, const QVariant& cache, const QVariant& value ) const;

/**
* Return the alignment for a particular field. By default this will consider the field type but can be overwritten if mapped
* values are represented.
Expand Down
5 changes: 5 additions & 0 deletions src/gui/editorwidgets/qgsrelationreferencefactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,8 @@ QString QgsRelationReferenceFactory::representValue( QgsVectorLayer* vl, int fie
}
return title;
}

QVariant QgsRelationReferenceFactory::sortValue( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, const QVariant& cache, const QVariant& value ) const
{
return representValue( vl, fieldIdx, config, cache, value );
}
4 changes: 3 additions & 1 deletion src/gui/editorwidgets/qgsrelationreferencefactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ class GUI_EXPORT QgsRelationReferenceFactory : public QgsEditorWidgetFactory
*/
virtual void writeConfig( const QgsEditorWidgetConfig& config, QDomElement& configElement, QDomDocument& doc, const QgsVectorLayer* layer, int fieldIdx ) override;

QString representValue( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, const QVariant& cache, const QVariant& value ) const override;
virtual QString representValue( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, const QVariant& cache, const QVariant& value ) const override;

virtual QVariant sortValue( QgsVectorLayer *vl, int fieldIdx, const QgsEditorWidgetConfig &config, const QVariant &cache, const QVariant &value ) const override;

virtual QMap<const char*, int> supportedWidgetTypes() override;

Expand Down
5 changes: 5 additions & 0 deletions src/gui/editorwidgets/qgsvaluemapwidgetfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ QString QgsValueMapWidgetFactory::representValue( QgsVectorLayer* vl, int fieldI
return config.key( value, QVariant( QString( "(%1)" ).arg( value.toString() ) ).toString() );
}

QVariant QgsValueMapWidgetFactory::sortValue( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, const QVariant& cache, const QVariant& value ) const
{
return representValue( vl, fieldIdx, config, cache, value );
}

Qt::AlignmentFlag QgsValueMapWidgetFactory::alignmentFlag( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config ) const
{
Q_UNUSED( vl );
Expand Down
1 change: 1 addition & 0 deletions src/gui/editorwidgets/qgsvaluemapwidgetfactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class GUI_EXPORT QgsValueMapWidgetFactory : public QgsEditorWidgetFactory
QgsEditorWidgetConfig readConfig( const QDomElement& configElement, QgsVectorLayer* layer, int fieldIdx ) override;
void writeConfig( const QgsEditorWidgetConfig& config, QDomElement& configElement, QDomDocument& doc, const QgsVectorLayer* layer, int fieldIdx ) override;
QString representValue( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, const QVariant& cache, const QVariant& value ) const override;
QVariant sortValue( QgsVectorLayer *vl, int fieldIdx, const QgsEditorWidgetConfig &config, const QVariant &cache, const QVariant &value ) const override;
Qt::AlignmentFlag alignmentFlag( QgsVectorLayer *vl, int fieldIdx, const QgsEditorWidgetConfig &config ) const override;
virtual QMap<const char*, int> supportedWidgetTypes() override;
};
Expand Down
5 changes: 5 additions & 0 deletions src/gui/editorwidgets/qgsvaluerelationwidgetfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ QString QgsValueRelationWidgetFactory::representValue( QgsVectorLayer* vl, int f
return QString( "(%1)" ).arg( value.toString() );
}

QVariant QgsValueRelationWidgetFactory::sortValue( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, const QVariant& cache, const QVariant& value ) const
{
return representValue( vl, fieldIdx, config, cache, value );
}

Qt::AlignmentFlag QgsValueRelationWidgetFactory::alignmentFlag( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config ) const
{
Q_UNUSED( vl );
Expand Down
1 change: 1 addition & 0 deletions src/gui/editorwidgets/qgsvaluerelationwidgetfactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class GUI_EXPORT QgsValueRelationWidgetFactory : public QgsEditorWidgetFactory
QgsEditorWidgetConfig readConfig( const QDomElement& configElement, QgsVectorLayer* layer, int fieldIdx ) override;
void writeConfig( const QgsEditorWidgetConfig& config, QDomElement& configElement, QDomDocument& doc, const QgsVectorLayer* layer, int fieldIdx ) override;
QString representValue( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config, const QVariant& cache, const QVariant& value ) const override;
QVariant sortValue( QgsVectorLayer *vl, int fieldIdx, const QgsEditorWidgetConfig &config, const QVariant &cache, const QVariant &value ) const override;
Qt::AlignmentFlag alignmentFlag( QgsVectorLayer *vl, int fieldIdx, const QgsEditorWidgetConfig &config ) const override;
QVariant createCache( QgsVectorLayer* vl, int fieldIdx, const QgsEditorWidgetConfig& config ) override;
};
Expand Down

0 comments on commit 56a0af5

Please sign in to comment.