Skip to content

Commit

Permalink
get rid of model types and cache
Browse files Browse the repository at this point in the history
  • Loading branch information
tomasMizera authored and wonder-sk committed Sep 11, 2020
1 parent 7c005d7 commit 265740f
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 116 deletions.
15 changes: 7 additions & 8 deletions src/quickgui/plugin/editor/qgsquickvaluerelation.qml
Expand Up @@ -48,22 +48,21 @@ Item {
property var currentEditorValue: value

comboStyle: customStyle.fields
textRole: 'display'
textRole: 'FeatureTitle'
height: parent.height

model: QgsQuick.FeaturesListModel {
id: vrModel
modelType: QgsQuick.FeaturesListModel.ValueRelation

// recalculate index when model changes
onModelReset: {
combobox.currentIndex = vrModel.rowModelIndexFromKey( value )
combobox.currentIndex = vrModel.rowFromAttribute( QgsQuick.FeaturesListModel.KeyColumn, value )
}
}

Component.onCompleted: {
vrModel.populate(config)
currentIndex = vrModel.rowModelIndexFromKey( value )
vrModel.setupValueRelation( config )
currentIndex = vrModel.rowFromAttribute( QgsQuick.FeaturesListModel.KeyColumn, value )
}

onPressedChanged: {
Expand All @@ -76,13 +75,13 @@ Item {

// Called when user makes selection in the combo box
onItemClicked: {
currentIndex = vrModel.rowModelIndexFromKey( index )
valueChanged( index, false )
currentIndex = vrModel.rowFromAttribute( QgsQuick.FeaturesListModel.FeatureId, index )
valueChanged( vrModel.keyFromAttribute( QgsQuick.FeaturesListModel.FeatureId, index ), false )
}

// Called when the same form is used for a different feature
onCurrentEditorValueChanged: {
currentIndex = vrModel.rowModelIndexFromKey( value );
currentIndex = vrModel.rowFromAttribute( QgsQuick.FeaturesListModel.KeyColumn, value );
}
}
}
102 changes: 52 additions & 50 deletions src/quickgui/qgsquickfeatureslistmodel.cpp
Expand Up @@ -19,21 +19,10 @@

QgsQuickFeaturesListModel::QgsQuickFeaturesListModel( QObject *parent )
: QAbstractListModel( parent ),
mCurrentLayer( nullptr ),
mModelType( modelTypes::FeatureListing )
mCurrentLayer( nullptr )
{
}

QgsQuickFeatureLayerPair QgsQuickFeaturesListModel::featureLayerPair( const int &featureId )
{
for ( const QgsQuickFeatureLayerPair &i : mFeatures )
{
if ( i.feature().id() == featureId )
return i;
}
return QgsQuickFeatureLayerPair();
}

int QgsQuickFeaturesListModel::rowCount( const QModelIndex &parent ) const
{
// For list models only the root node (an invalid parent) should return the list's size. For all
Expand All @@ -46,15 +35,24 @@ int QgsQuickFeaturesListModel::rowCount( const QModelIndex &parent ) const

QVariant QgsQuickFeaturesListModel::featureTitle( const QgsQuickFeatureLayerPair &featurePair ) const
{
QString title;

if ( !mFeatureTitleField.isEmpty() )
{
title = featurePair.feature().attribute( mFeatureTitleField ).toString();
if ( !title.isEmpty() )
return title;
}

QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( featurePair.layer() ) );
context.setFeature( featurePair.feature() );
QgsExpression expr( featurePair.layer()->displayExpression() );
const QString title = expr.evaluate( &context ).toString();
title = expr.evaluate( &context ).toString();

if ( title.isEmpty() )
return QVariant( featurePair.feature().id() );
return featurePair.feature().id();

return QVariant( title );
return title;
}

QVariant QgsQuickFeaturesListModel::data( const QModelIndex &index, int role ) const
Expand All @@ -74,21 +72,9 @@ QVariant QgsQuickFeaturesListModel::data( const QModelIndex &index, int role ) c
case FeatureId: return QVariant( pair.feature().id() );
case Feature: return QVariant::fromValue<QgsFeature>( pair.feature() );
case Description: return QVariant( QString( "Feature ID %1" ).arg( pair.feature().id() ) );
case EmitableIndex:
{
if ( mModelType == modelTypes::ValueRelation )
return pair.feature().attribute( mKeyFieldName );
return pair.feature().id();
}
case KeyColumn: return mKeyField.isEmpty() ? QVariant() : pair.feature().attribute( mKeyField );
case FoundPair: return foundPair( pair );
case Qt::DisplayRole:
{
if ( row >= 0 && row < mCache.count() )
{
int r = rowIndexFromKey( pair.feature().attribute( mKeyFieldName ) );
return mCache[r].value;
}
}
case Qt::DisplayRole: return featureTitle( pair );
}

return QVariant();
Expand Down Expand Up @@ -163,19 +149,20 @@ void QgsQuickFeaturesListModel::loadFeaturesFromLayer( QgsVectorLayer *layer )
}
}

void QgsQuickFeaturesListModel::populate( const QVariantMap &config )
void QgsQuickFeaturesListModel::setupValueRelation( const QVariantMap &config )
{
beginResetModel();
emptyData();

mCache = QgsValueRelationFieldFormatter::createCache( config );
QgsVectorLayer *layer = QgsValueRelationFieldFormatter::resolveLayer( config, QgsProject::instance() );

if ( layer )
{
// save key field
// save key and value field
QgsFields fields = layer->fields();
mKeyFieldName = fields.field( config.value( QStringLiteral( "Key" ) ).toString() ).name();

setKeyField( fields.field( config.value( QStringLiteral( "Key" ) ).toString() ).name() );
setFeatureTitleField( fields.field( config.value( QStringLiteral( "Value" ) ).toString() ).name() );

loadFeaturesFromLayer( layer );
}
Expand All @@ -201,8 +188,8 @@ void QgsQuickFeaturesListModel::emptyData()
{
mFeatures.clear();
mCurrentLayer = nullptr;
mCache.clear();
mKeyFieldName.clear();
mKeyField.clear();
mFeatureTitleField.clear();
mFilterExpression.clear();
}

Expand All @@ -214,7 +201,7 @@ QHash<int, QByteArray> QgsQuickFeaturesListModel::roleNames() const
roleNames[Feature] = QStringLiteral( "Feature" ).toLatin1();
roleNames[Description] = QStringLiteral( "Description" ).toLatin1();
roleNames[FoundPair] = QStringLiteral( "FoundPair" ).toLatin1();
roleNames[EmitableIndex] = QStringLiteral( "EmitableIndex" ).toLatin1();
roleNames[KeyColumn] = QStringLiteral( "KeyColumn" ).toLatin1();
return roleNames;
}

Expand All @@ -238,39 +225,54 @@ void QgsQuickFeaturesListModel::setFilterExpression( const QString &filterExpres
loadFeaturesFromLayer();
}

int QgsQuickFeaturesListModel::featuresLimit() const
void QgsQuickFeaturesListModel::setFeatureTitleField( const QString &attribute )
{
return FEATURES_LIMIT;
mFeatureTitleField = attribute;
}

QgsQuickFeaturesListModel::modelTypes QgsQuickFeaturesListModel::modelType() const
void QgsQuickFeaturesListModel::setKeyField( const QString &attribute )
{
return mModelType;
mKeyField = attribute;
}

void QgsQuickFeaturesListModel::setModelType( modelTypes modelType )
int QgsQuickFeaturesListModel::featuresLimit() const
{
mModelType = modelType;
return FEATURES_LIMIT;
}

int QgsQuickFeaturesListModel::rowIndexFromKey( const QVariant &key ) const
int QgsQuickFeaturesListModel::rowFromAttribute( const int role, const QVariant &value ) const
{
for ( int i = 0; i < mCache.count(); ++i )
for ( int i = 0; i < mFeatures.count(); ++i )
{
if ( mCache[i].key == key )
QVariant d = data( index( i, 0 ), role );
if ( d == value )
{
return i;
}
}
QgsDebugMsg( "rowIndexFromKey: key not found: " + key.toString() );
return -1;
}

int QgsQuickFeaturesListModel::rowModelIndexFromKey( const QVariant &key ) const
int QgsQuickFeaturesListModel::keyFromAttribute( const int role, const QVariant &value ) const
{
for ( int i = 0; i < mFeatures.count(); ++i )
{
if ( mFeatures[i].feature().attribute( mKeyFieldName ) == key )
return i;
QVariant d = data( index( i, 0 ), role );
if ( d == value )
{
QVariant key = data( index( i, 0 ), KeyColumn );
return key.toInt();
}
}
QgsDebugMsg( "rowModelIndexFromKey: Could not find index in features model, index: " + key.toString() );
return -1;
}

QgsQuickFeatureLayerPair QgsQuickFeaturesListModel::featureLayerPair( const int &featureId )
{
for ( const QgsQuickFeatureLayerPair &i : mFeatures )
{
if ( i.feature().id() == featureId )
return i;
}
return QgsQuickFeatureLayerPair();
}
91 changes: 33 additions & 58 deletions src/quickgui/qgsquickfeatureslistmodel.h
Expand Up @@ -28,10 +28,6 @@
*
* Model allows searching by any string or number attribute.
*
* Note that the model can run in several modes:
* (1) as a features list - usable in listing features from specifig layer
* (2) as a value relation model - filling value-relation widget with data from config
*
* \note QML Type: FeaturesListModel
*
* \since QGIS 3.16
Expand All @@ -58,37 +54,19 @@ class QUICK_EXPORT QgsQuickFeaturesListModel : public QAbstractListModel
*/
Q_PROPERTY( int featuresLimit READ featuresLimit NOTIFY featuresLimitChanged )

/**
* Property determining type of Feature Model.
*
* \note ValueRelation type provides different attribute when opting for data with EmitableIndex role, it returns "key" attribute
*/
Q_PROPERTY( modelTypes modelType READ modelType WRITE setModelType )
public:

enum roleNames
//! Roles for FeaturesListModel
enum modelRoles
{
FeatureTitle = Qt::UserRole + 1,
FeatureId,
Feature,
Description, //! secondary text in list view
EmitableIndex, //! key in value relation
KeyColumn, //! key in value relation
FoundPair //! pair of attribute and its value by which the feature was found, empty if mFilterExpression is empty
};

public:

/**
* \brief The modelTypes enum
* ValueRelation type provides different attribute when opting for data with EmitableIndex role, it returns "key" attribute.
*
* Default type is FeatureListing
*/
enum modelTypes
{
FeatureListing,
ValueRelation
};
Q_ENUM( modelTypes );
Q_ENUM( modelRoles );

//! Create features list model
explicit QgsQuickFeaturesListModel( QObject *parent = nullptr );
Expand All @@ -102,10 +80,10 @@ class QUICK_EXPORT QgsQuickFeaturesListModel : public QAbstractListModel
QHash<int, QByteArray> roleNames() const override;

/**
* \brief populate populates model with value relation data from config
* \brief setupValueRelation populates model with value relation data from config
* \param config to be used
*/
Q_INVOKABLE void populate( const QVariantMap &config );
Q_INVOKABLE void setupValueRelation( const QVariantMap &config );

/**
* \brief populateFromLayer populates model with features from layer
Expand All @@ -119,25 +97,27 @@ class QUICK_EXPORT QgsQuickFeaturesListModel : public QAbstractListModel
Q_INVOKABLE void reloadFeatures();

/**
* \brief rowIndexFromKey translates value relation key into index from cache
* \param key value relation key
* \return index from cache (loaded from config)
* \brief rowFromAttribute finds feature with requested role and value, returns its row
* \param role to find from modelRoles
* \param value to find
* \return Row index for found feature, returns -1 if no feature is found. If more features
* match requested role and value, index of first is returned.
*/
Q_INVOKABLE int rowIndexFromKey( const QVariant &key ) const;
Q_INVOKABLE int rowFromAttribute( const int role, const QVariant &value ) const;

/**
* \brief rowModelIndexFromKey translates value relation key into model index
* \param key value relation key
* \return model index (row) for corresponding feature (from mFeatures)
* \brief keyFromAttribute finds feature with requested role and value, returns keycolumn
* \param role role to find from modelRoles
* \param value value to find
* \return KeyColumn role for found feature, returns -1 if no feature is found. If more features
* match requested role and value, KeyColumn for first is returned.
*/
Q_INVOKABLE int rowModelIndexFromKey( const QVariant &key ) const;
Q_INVOKABLE int keyFromAttribute( const int role, const QVariant &value ) const;

//! Returns maximum amount of features that can be queried from layer
int featuresLimit() const;
//! Returns number of features in layer, not number of loaded features
int featuresCount() const;
//! Returns type of this model
modelTypes modelType() const;
//! Returns filter expression, empty string represents no filter
QString filterExpression() const;

Expand All @@ -147,9 +127,14 @@ class QUICK_EXPORT QgsQuickFeaturesListModel : public QAbstractListModel
*/
void setFilterExpression( const QString &filterExpression );

public slots:
//! Sets corresponding type of model from modelTypes enum
void setModelType( modelTypes modelType );
/**
* \brief setFeatureTitleField Sets name of attribute that will be used for FeatureTitle and Qt::DisplayRole
* \param attribute Name of attribute to use. If empty, displayExpression will be used.
*/
void setFeatureTitleField( const QString &attribute );

//! Sets name of attribute used as "key" in value relation
void setKeyField( const QString &attribute );

signals:

Expand All @@ -167,10 +152,7 @@ class QUICK_EXPORT QgsQuickFeaturesListModel : public QAbstractListModel

private:

/**
* \brief loadFeaturesFromLayer
* \param layer
*/
//! Reloads features from layer, if layer is not provided, uses current layer, If layer is provided, saves it as current.
void loadFeaturesFromLayer( QgsVectorLayer *layer = nullptr );

//! Empty data when resetting model
Expand All @@ -190,7 +172,7 @@ class QUICK_EXPORT QgsQuickFeaturesListModel : public QAbstractListModel
* Hold maximum of FEATURES_LIMIT features
* \note mFeatures.size() is not always the same as mFeaturesCount
*/
QList<QgsQuickFeatureLayerPair> mFeatures;
QgsQuickFeatureLayerPairs mFeatures;

//! Number of maximum features loaded from layer
const int FEATURES_LIMIT = 10000;
Expand All @@ -201,18 +183,11 @@ class QUICK_EXPORT QgsQuickFeaturesListModel : public QAbstractListModel
//! Pointer to layer that is currently loaded
QgsVectorLayer *mCurrentLayer = nullptr;

/**
* Data from config for value relations
* mCache is not affected by reloading features when filter expression is changed and
* is kept until next call of emptyData.
*/
QgsValueRelationFieldFormatter::ValueRelationCache mCache;

//! Type of a model - Listing (browsing) features or use in value relation widget
modelTypes mModelType;

//! Field that is used as a "key" in value relation
QString mKeyFieldName;
QString mKeyField;

//! Field that represents field used as a feature title, if not set, display expression is used
QString mFeatureTitleField;
};

#endif // QGSQUICKFEATURESMODEL_H

0 comments on commit 265740f

Please sign in to comment.