Skip to content

Commit

Permalink
Introduce a FeaturesProxyModel to conditionally sort features list an…
Browse files Browse the repository at this point in the history
…d value relations

MMFeaturesListPage is sorted according to the layer's QgsAttributeTableConfig
MMFormValueRelationEditor is sorted by value when the widget has the OrderByValue config set
  • Loading branch information
uclaros committed Jul 22, 2024
1 parent 9f3ac1b commit 4302331
Show file tree
Hide file tree
Showing 14 changed files with 175 additions and 7 deletions.
2 changes: 2 additions & 0 deletions app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ set(MM_SRCS
compass.cpp
featurelayerpair.cpp
featuresmodel.cpp
featuresproxymodel.cpp
fieldsmodel.cpp
guidelinecontroller.cpp
identifykit.cpp
Expand Down Expand Up @@ -135,6 +136,7 @@ set(MM_HDRS
enumhelper.h
featurelayerpair.h
featuresmodel.h
featuresproxymodel.h
fieldsmodel.h
guidelinecontroller.h
identifykit.h
Expand Down
28 changes: 28 additions & 0 deletions app/featuresmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ QVariant FeaturesModel::data( const QModelIndex &index, int role ) const
case LayerName: return pair.layer() ? pair.layer()->name() : QString();
case LayerIcon: return pair.layer() ? InputUtils::loadIconFromLayer( pair.layer() ) : QString();
case Qt::DisplayRole: return featureTitle( pair );
case SortValue: return sortValue( pair );
}

return QVariant();
Expand Down Expand Up @@ -171,6 +172,15 @@ QVariant FeaturesModel::featureTitle( const FeatureLayerPair &featurePair ) cons
return title;
}

QVariant FeaturesModel::sortValue( const FeatureLayerPair &featurePair ) const
{
QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( featurePair.layer() ) );
context.setFeature( featurePair.feature() );
QgsExpression expr( mSortExpression );
QVariant result = expr.evaluate( &context );
return result;
}

QString FeaturesModel::searchResultPair( const FeatureLayerPair &pair ) const
{
if ( mSearchExpression.isEmpty() )
Expand Down Expand Up @@ -274,6 +284,7 @@ QHash<int, QByteArray> FeaturesModel::roleNames() const
roleNames[SearchResult] = QStringLiteral( "SearchResult" ).toLatin1();
roleNames[LayerName] = QStringLiteral( "LayerName" ).toLatin1();
roleNames[LayerIcon] = QStringLiteral( "LayerIcon" ).toLatin1();
roleNames[SortValue] = QStringLiteral( "SortValue" ).toLatin1();
return roleNames;
}

Expand Down Expand Up @@ -359,10 +370,27 @@ void FeaturesModel::setLayer( QgsVectorLayer *newLayer )
}

emit layerFeaturesCountChanged( layerFeaturesCount() );
setupSorting();
}
}

QgsVectorLayer *FeaturesModel::layer() const
{
return mLayer;
}

void FeaturesModel::setupSorting()
{
mSortExpression = mLayer ? mLayer->attributeTableConfig().sortExpression() : QString();
mSortOrder = mLayer ? mLayer->attributeTableConfig().sortOrder() : Qt::AscendingOrder;
}

bool FeaturesModel::sortingEnabled() const
{
return !mSortExpression.isEmpty();
}

Qt::SortOrder FeaturesModel::sortOrder() const
{
return mSortOrder;
}
16 changes: 16 additions & 0 deletions app/featuresmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class FeaturesModel : public QAbstractListModel
SearchResult, // pair of attribute and its value by which the feature was found, empty if search expression is empty
LayerName,
LayerIcon,
SortValue,
};
Q_ENUM( ModelRoles );

Expand Down Expand Up @@ -117,6 +118,12 @@ class FeaturesModel : public QAbstractListModel

int layerFeaturesCount() const;

//! Returns true if there is a sort expression set for the model
bool sortingEnabled() const;

//! Returns the order in witch the model should be sorted
Qt::SortOrder sortOrder() const;

signals:

void featuresLimitChanged( int featuresLimit );
Expand All @@ -139,6 +146,12 @@ class FeaturesModel : public QAbstractListModel

virtual QVariant featureTitle( const FeatureLayerPair &featurePair ) const;

//! Populates the sort expression and sort order for the model
virtual void setupSorting();

QString mSortExpression;
Qt::SortOrder mSortOrder = Qt::AscendingOrder;

private slots:
void onFutureFinished();

Expand All @@ -151,6 +164,9 @@ class FeaturesModel : public QAbstractListModel
//! Returns found attribute and its value from search expression for feature
QString searchResultPair( const FeatureLayerPair &feat ) const;

//! Evaluates the sort expression and returns the value used for this feature when sorting the model
QVariant sortValue( const FeatureLayerPair &featurePair ) const;

const int FEATURES_LIMIT = 10000; //!< Number of maximum features loaded from layer

FeatureLayerPairs mFeatures;
Expand Down
41 changes: 41 additions & 0 deletions app/featuresproxymodel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "featuresproxymodel.h"

FeaturesProxyModel::FeaturesProxyModel( QObject *parent ) : QSortFilterProxyModel( parent )
{
}

void FeaturesProxyModel::initialize()
{
setSourceModel( mModel );

// don't sort if there is no sort expression for the layer
if ( mModel->sortingEnabled() )
{
setSortRole( FeaturesModel::SortValue );
setSortCaseSensitivity( Qt::CaseInsensitive );
sort( 0, mModel->sortOrder() );
}
}

FeaturesModel *FeaturesProxyModel::featuresSourceModel() const
{
return mModel;
}

void FeaturesProxyModel::setFeaturesSourceModel( FeaturesModel *sourceModel )
{
if ( mModel == sourceModel )
return;

mModel = sourceModel;
QObject::connect( mModel, &FeaturesModel::fetchingResultsChanged, this, [ = ]( bool pending ) { if ( !pending ) initialize(); } );
}
51 changes: 51 additions & 0 deletions app/featuresproxymodel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef FEATURESPROXYMODEL_H
#define FEATURESPROXYMODEL_H

#include <QObject>
#include <QSortFilterProxyModel>

#include "inputconfig.h"
#include "featuresmodel.h"

/**
* \brief The FeaturesProxyModel class used as a proxy sort model for the \see FeaturesModel class.
*
* FeaturesProxyModel is a QML type with required property of FeatureSourceModel. Without source model, this model does nothing (is not initialized).
* After setting source model, this model starts sorting and allows filtering (search) from view.
*/
class FeaturesProxyModel : public QSortFilterProxyModel
{
Q_OBJECT

Q_PROPERTY( FeaturesModel *featuresSourceModel READ featuresSourceModel WRITE setFeaturesSourceModel )

public:
explicit FeaturesProxyModel( QObject *parent = nullptr );
~FeaturesProxyModel() override {};

FeaturesModel *featuresSourceModel() const;

public slots:
void setFeaturesSourceModel( FeaturesModel *sourceModel );

private:
void initialize();

FeaturesModel *mModel = nullptr; // not owned by this, needs to be set in order to proxy model to work

Qt::SortOrder mSortOrder = Qt::SortOrder::AscendingOrder;
QString mSortExpression;

friend class TestModels;
};

#endif // FEATURESPROXYMODEL_H
2 changes: 2 additions & 0 deletions app/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
#include "position/positionkit.h"
#include "scalebarkit.h"
#include "featuresmodel.h"
#include "featuresproxymodel.h"
#include "relationfeaturesmodel.h"
#include "relationreferencefeaturesmodel.h"
#include "fieldvalidator.h"
Expand Down Expand Up @@ -326,6 +327,7 @@ void initDeclarative()
qmlRegisterType< MapThemesModel >( "mm", 1, 0, "MapThemesModel" );
qmlRegisterType< GuidelineController >( "mm", 1, 0, "GuidelineController" );
qmlRegisterType< FeaturesModel >( "mm", 1, 0, "FeaturesModel" );
qmlRegisterType< FeaturesProxyModel >( "mm", 1, 0, "FeaturesProxyModel" );
qmlRegisterType< RelationFeaturesModel >( "mm", 1, 0, "RelationFeaturesModel" );
qmlRegisterType< ValueRelationFeaturesModel >( "mm", 1, 0, "ValueRelationFeaturesModel" );
qmlRegisterType< RelationReferenceFeaturesModel >( "mm", 1, 0, "RelationReferenceFeaturesModel" );
Expand Down
12 changes: 8 additions & 4 deletions app/qml/form/editors/MMFormValueRelationEditor.qml
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,15 @@ MMFormComboboxBaseEditor {
valueRole: "FeatureId"
textRole: "FeatureTitle"

list.model: MM.ValueRelationFeaturesModel {
id: vrDropdownModel
list.model: MM.FeaturesProxyModel {
id: vrDropdownProxyModel

config: root._fieldConfig
pair: root._fieldFeatureLayerPair
featuresSourceModel: MM.ValueRelationFeaturesModel {
id: vrDropdownModel

config: root._fieldConfig
pair: root._fieldFeatureLayerPair
}
}

onSearchTextChanged: ( searchText ) => vrDropdownModel.searchExpression = searchText
Expand Down
10 changes: 7 additions & 3 deletions app/qml/layers/MMFeaturesListPage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,14 @@ MMComponents.MMPage {
topMargin: __style.spacing20
}

model: MM.FeaturesModel {
id: featuresModel
model: MM.FeaturesProxyModel {
id: featuresProxyModel

layer: root.selectedLayer
featuresSourceModel: MM.FeaturesModel {
id: featuresModel

layer: root.selectedLayer
}
}

clip: true
Expand Down
5 changes: 5 additions & 0 deletions app/relationfeaturesmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,8 @@ void RelationFeaturesModel::setHomePath( const QString &homePath )
emit homePathChanged();
}
}

void RelationFeaturesModel::setupSorting()
{
mSortExpression.clear();
}
1 change: 1 addition & 0 deletions app/relationfeaturesmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class RelationFeaturesModel : public FeaturesModel

void setup() override;
void setupFeatureRequest( QgsFeatureRequest &request ) override;
void setupSorting() override;

void setParentFeatureLayerPair( FeatureLayerPair pair );
void setRelation( QgsRelation relation );
Expand Down
5 changes: 5 additions & 0 deletions app/relationreferencefeaturesmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,8 @@ void RelationReferenceFeaturesModel::setProject( QgsProject *project )
setup();
}
}

void RelationReferenceFeaturesModel::setupSorting()
{
mSortExpression.clear();
}
2 changes: 2 additions & 0 deletions app/relationreferencefeaturesmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class RelationReferenceFeaturesModel : public FeaturesModel
//! Reads config and with project instance queries all features from parent layer. Emits populated signal after loading features.
void setup() override;

void setupSorting() override;

signals:
void configChanged( QVariantMap config );
void projectChanged( QgsProject *project );
Expand Down
6 changes: 6 additions & 0 deletions app/valuerelationfeaturesmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,9 @@ void ValueRelationFeaturesModel::setConfig( const QVariantMap &newConfig )

setup();
}

void ValueRelationFeaturesModel::setupSorting()
{
const bool orderByValue = mConfig.value( QStringLiteral( "OrderByValue" ) ).toBool();
mSortExpression = orderByValue ? mTitleField : QString();
}
1 change: 1 addition & 0 deletions app/valuerelationfeaturesmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class ValueRelationFeaturesModel : public FeaturesModel
void reset() override;
void setupFeatureRequest( QgsFeatureRequest &request ) override;
QVariant featureTitle( const FeatureLayerPair &pair ) const override;
void setupSorting() override;

Q_INVOKABLE QVariant convertToKey( const QVariant &id );
Q_INVOKABLE QVariant convertToQgisType( const QVariantList &featureIds ); // feature id -> key
Expand Down

1 comment on commit 4302331

@inputapp-bot
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iOS - version 24.7.643011 just submitted!

Please sign in to comment.