From 265740fd5ab04498f0b6910bc4bcb3bab8ff85be Mon Sep 17 00:00:00 2001 From: tomasMizera Date: Fri, 11 Sep 2020 11:51:16 +0200 Subject: [PATCH] get rid of model types and cache --- .../plugin/editor/qgsquickvaluerelation.qml | 15 +-- src/quickgui/qgsquickfeatureslistmodel.cpp | 122 +++++++++--------- src/quickgui/qgsquickfeatureslistmodel.h | 91 +++++-------- 3 files changed, 102 insertions(+), 126 deletions(-) diff --git a/src/quickgui/plugin/editor/qgsquickvaluerelation.qml b/src/quickgui/plugin/editor/qgsquickvaluerelation.qml index 0864fc44d1c..f5af93ef2d9 100644 --- a/src/quickgui/plugin/editor/qgsquickvaluerelation.qml +++ b/src/quickgui/plugin/editor/qgsquickvaluerelation.qml @@ -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: { @@ -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 ); } } } diff --git a/src/quickgui/qgsquickfeatureslistmodel.cpp b/src/quickgui/qgsquickfeatureslistmodel.cpp index ef0c9c66ca6..b56c59e3419 100644 --- a/src/quickgui/qgsquickfeatureslistmodel.cpp +++ b/src/quickgui/qgsquickfeatureslistmodel.cpp @@ -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 @@ -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 @@ -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( 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(); @@ -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 ); } @@ -201,8 +188,8 @@ void QgsQuickFeaturesListModel::emptyData() { mFeatures.clear(); mCurrentLayer = nullptr; - mCache.clear(); - mKeyFieldName.clear(); + mKeyField.clear(); + mFeatureTitleField.clear(); mFilterExpression.clear(); } @@ -214,7 +201,7 @@ QHash 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; } @@ -238,39 +225,54 @@ void QgsQuickFeaturesListModel::setFilterExpression( const QString &filterExpres loadFeaturesFromLayer(); } +void QgsQuickFeaturesListModel::setFeatureTitleField( const QString &attribute ) +{ + mFeatureTitleField = attribute; +} + +void QgsQuickFeaturesListModel::setKeyField( const QString &attribute ) +{ + mKeyField = attribute; +} + int QgsQuickFeaturesListModel::featuresLimit() const { return FEATURES_LIMIT; } -QgsQuickFeaturesListModel::modelTypes QgsQuickFeaturesListModel::modelType() const -{ - return mModelType; -} - -void QgsQuickFeaturesListModel::setModelType( modelTypes modelType ) -{ - mModelType = modelType; -} - -int QgsQuickFeaturesListModel::rowIndexFromKey( const QVariant &key ) const -{ - for ( int i = 0; i < mCache.count(); ++i ) - { - if ( mCache[i].key == key ) - return i; - } - QgsDebugMsg( "rowIndexFromKey: key not found: " + key.toString() ); - return -1; -} - -int QgsQuickFeaturesListModel::rowModelIndexFromKey( const QVariant &key ) const +int QgsQuickFeaturesListModel::rowFromAttribute( const int role, const QVariant &value ) const { for ( int i = 0; i < mFeatures.count(); ++i ) { - if ( mFeatures[i].feature().attribute( mKeyFieldName ) == key ) + QVariant d = data( index( i, 0 ), role ); + if ( d == value ) + { return i; + } } - QgsDebugMsg( "rowModelIndexFromKey: Could not find index in features model, index: " + key.toString() ); return -1; } + +int QgsQuickFeaturesListModel::keyFromAttribute( const int role, const QVariant &value ) const +{ + for ( int i = 0; i < mFeatures.count(); ++i ) + { + QVariant d = data( index( i, 0 ), role ); + if ( d == value ) + { + QVariant key = data( index( i, 0 ), KeyColumn ); + return key.toInt(); + } + } + return -1; +} + +QgsQuickFeatureLayerPair QgsQuickFeaturesListModel::featureLayerPair( const int &featureId ) +{ + for ( const QgsQuickFeatureLayerPair &i : mFeatures ) + { + if ( i.feature().id() == featureId ) + return i; + } + return QgsQuickFeatureLayerPair(); +} diff --git a/src/quickgui/qgsquickfeatureslistmodel.h b/src/quickgui/qgsquickfeatureslistmodel.h index 8572c13375d..cc64dbe2c53 100644 --- a/src/quickgui/qgsquickfeatureslistmodel.h +++ b/src/quickgui/qgsquickfeatureslistmodel.h @@ -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 @@ -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 ); @@ -102,10 +80,10 @@ class QUICK_EXPORT QgsQuickFeaturesListModel : public QAbstractListModel QHash 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 @@ -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; @@ -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: @@ -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 @@ -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 mFeatures; + QgsQuickFeatureLayerPairs mFeatures; //! Number of maximum features loaded from layer const int FEATURES_LIMIT = 10000; @@ -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