allow QgsFeatureListComboBox to handle multiple keys

This commit is contained in:
Denis Rouzaud 2019-08-22 16:21:46 +02:00
parent 8cdfc2554d
commit a5574eb96b
7 changed files with 376 additions and 78 deletions

View File

@ -26,6 +26,7 @@ Features are fetched asynchronously.
enum Role enum Role
{ {
IdentifierValueRole, IdentifierValueRole,
IdentifierValuesRole,
ValueRole ValueRole
}; };
@ -103,28 +104,69 @@ Can be used for spatial filtering etc.
Indicator if the model is currently performing any feature iteration in the background. Indicator if the model is currently performing any feature iteration in the background.
%End %End
QString identifierField() const; QString identifierField() const;
%Docstring %Docstring
The identifier field should be a unique field that can be used to identify individual features. The identifier field should be a unique field that can be used to identify individual features.
It is normally set to the primary key of the layer. It is normally set to the primary key of the layer.
If there are several identifier fields defined, the behavior is not guaranteed
.. deprecated:: since QGIS 3.10 use identifierFields instead
%End %End
void setIdentifierField( const QString &identifierField ); QStringList identifierFields() const;
%Docstring %Docstring
The identifier field should be a unique field that can be used to identify individual features. The identifier field should be a unique field that can be used to identify individual features.
It is normally set to the primary key of the layer. It is normally set to the primary key of the layer.
.. versionadded:: 3.10
%End %End
QVariant extraIdentifierValue() const; void setIdentifierField( const QString &identifierField );
%Docstring
The identifier field should be a unique field that can be used to identify individual features.
It is normally set to the primary key of the layer.
.. deprecated:: since QGIS 3.10
%End
void setIdentifierFields( const QStringList &identifierFields );
%Docstring
The identifier field should be a unique field that can be used to identify individual features.
It is normally set to the primary key of the layer.
.. versionadded:: 3.10
%End
QVariant extraIdentifierValue() const;
%Docstring %Docstring
Allows specifying one value that does not need to match the filter criteria but will Allows specifying one value that does not need to match the filter criteria but will
still be available in the model. still be available in the model.
.. deprecated:: since QGIS 3.10
%End %End
void setExtraIdentifierValue( const QVariant &extraIdentifierValue ); QVariantList extraIdentifierValues() const;
%Docstring %Docstring
Allows specifying one value that does not need to match the filter criteria but will Allows specifying one value that does not need to match the filter criteria but will
still be available in the model. still be available in the model.
.. versionadded:: 3.10
%End
void setExtraIdentifierValue( const QVariant &extraIdentifierValue );
%Docstring
Allows specifying one value that does not need to match the filter criteria but will
still be available in the model.
.. deprecated:: since QGIS 3.10
%End
void setExtraIdentifierValues( const QVariantList &extraIdentifierValues );
%Docstring
Allows specifying one value that does not need to match the filter criteria but will
still be available in the model.
.. versionadded:: 3.10
%End %End
int extraIdentifierValueIndex() const; int extraIdentifierValueIndex() const;

View File

@ -9,6 +9,7 @@
class QgsFeatureListComboBox : QComboBox class QgsFeatureListComboBox : QComboBox
{ {
%Docstring %Docstring
@ -38,6 +39,13 @@ The layer from which features should be listed.
void setSourceLayer( QgsVectorLayer *sourceLayer ); void setSourceLayer( QgsVectorLayer *sourceLayer );
%Docstring %Docstring
The layer from which features should be listed. The layer from which features should be listed.
%End
void setCurrentFeature( const QgsFeature &feature );
%Docstring
Sets the current index by using the given feature
.. versionadded:: 3.10
%End %End
QString displayExpression() const; QString displayExpression() const;
@ -74,16 +82,37 @@ This can be used to integrate additional spatial or other constraints.
TODO! TODO!
%End %End
QVariant identifierValue() const; QVariant identifierValue() const /Deprecated/;
%Docstring %Docstring
The identifier value of the currently selected feature. A value from the The identifier value of the currently selected feature. A value from the
identifierField. identifierField.
.. deprecated:: since QGIS 3.10
%End %End
void setIdentifierValue( const QVariant &identifierValue ); QVariantList identifierValues() const;
%Docstring
The identifier values of the currently selected feature. A value from the
identifierField.
.. versionadded:: 3.10
%End
void setIdentifierValue( const QVariant &identifierValue ) /Deprecated/;
%Docstring %Docstring
The identifier value of the currently selected feature. A value from the The identifier value of the currently selected feature. A value from the
identifierField. identifierField.
.. deprecated:: since QGIS 3.10 use setIdentifierValues
%End
void setIdentifierValues( const QVariantList &identifierValues );
%Docstring
The identifier values of the currently selected feature. A value from the
identifierFields.
.. versionadded:: 3.10
%End %End
QgsFeatureRequest currentFeatureRequest() const; QgsFeatureRequest currentFeatureRequest() const;
@ -102,16 +131,36 @@ Determines if a NULL value should be available in the list.
Determines if a NULL value should be available in the list. Determines if a NULL value should be available in the list.
%End %End
QString identifierField() const; QString identifierField() const /Deprecated/;
%Docstring %Docstring
Field name that will be used to uniquely identify the current feature. Field name that will be used to uniquely identify the current feature.
Normally the primary key of the layer. Normally the primary key of the layer.
.. deprecated:: since QGIS 3.10
%End %End
void setIdentifierField( const QString &identifierField ); QStringList identifierFields() const;
%Docstring %Docstring
Field name that will be used to uniquely identify the current feature. Field name that will be used to uniquely identify the current feature.
Normally the primary key of the layer. Normally the primary key of the layer.
.. versionadded:: 3.10
%End
void setIdentifierField( const QString &identifierField ) /Deprecated/;
%Docstring
Field name that will be used to uniquely identify the current feature.
Normally the primary key of the layer.
.. deprecated:: since QGIS 3.10
%End
void setIdentifierFields( const QStringList &identifierFields );
%Docstring
Field name that will be used to uniquely identify the current feature.
Normally the primary key of the layer.
.. versionadded:: 3.10
%End %End
QModelIndex currentModelIndex() const; QModelIndex currentModelIndex() const;
@ -170,6 +219,8 @@ Determines if a NULL value should be available in the list.
}; };
/************************************************************************ /************************************************************************
* This file has been generated automatically from * * This file has been generated automatically from *
* * * *

View File

@ -1,6 +1,5 @@
/*************************************************************************** /***************************************************************************
qgsfeaturefiltermodel.cpp - QgsFeatureFilterModel qgsfeaturefiltermodel.cpp - QgsFeatureFilterModel
--------------------- ---------------------
begin : 10.3.2017 begin : 10.3.2017
copyright : (C) 2017 by Matthias Kuhn copyright : (C) 2017 by Matthias Kuhn
@ -27,7 +26,7 @@ QgsFeatureFilterModel::QgsFeatureFilterModel( QObject *parent )
mReloadTimer.setInterval( 100 ); mReloadTimer.setInterval( 100 );
mReloadTimer.setSingleShot( true ); mReloadTimer.setSingleShot( true );
connect( &mReloadTimer, &QTimer::timeout, this, &QgsFeatureFilterModel::scheduledReload ); connect( &mReloadTimer, &QTimer::timeout, this, &QgsFeatureFilterModel::scheduledReload );
setExtraIdentifierValueUnguarded( QVariant() ); setExtraIdentifierValuesUnguarded( QVariantList() );
} }
QgsFeatureFilterModel::~QgsFeatureFilterModel() QgsFeatureFilterModel::~QgsFeatureFilterModel()
@ -102,6 +101,14 @@ bool QgsFeatureFilterModel::isLoading() const
return mGatherer; return mGatherer;
} }
QString QgsFeatureFilterModel::identifierField() const
{
if ( mIdentifierFields.isEmpty() )
return QString();
else
return mIdentifierFields.at( 0 );
}
QModelIndex QgsFeatureFilterModel::index( int row, int column, const QModelIndex &parent ) const QModelIndex QgsFeatureFilterModel::index( int row, int column, const QModelIndex &parent ) const
{ {
Q_UNUSED( parent ) Q_UNUSED( parent )
@ -140,30 +147,42 @@ QVariant QgsFeatureFilterModel::data( const QModelIndex &index, int role ) const
return mEntries.value( index.row() ).value; return mEntries.value( index.row() ).value;
case IdentifierValueRole: case IdentifierValueRole:
return mEntries.value( index.row() ).identifierValue; {
QVariantList values = mEntries.value( index.row() ).identifierValues;
return values.isEmpty() ? QVariant() : values.at( 0 );
}
case IdentifierValuesRole:
return mEntries.value( index.row() ).identifierValues;
case Qt::BackgroundColorRole: case Qt::BackgroundColorRole:
case Qt::TextColorRole: case Qt::TextColorRole:
case Qt::DecorationRole: case Qt::DecorationRole:
case Qt::FontRole: case Qt::FontRole:
{ {
if ( mEntries.value( index.row() ).identifierValue.isNull() ) bool isNull = true;
for ( const QVariant &value : mEntries.value( index.row() ).identifierValues )
{
if ( !value.isNull() )
{
isNull = false;
break;
}
}
if ( isNull )
{ {
// Representation for NULL value // Representation for NULL value
if ( role == Qt::TextColorRole ) if ( role == Qt::TextColorRole )
{ {
return QBrush( QColor( Qt::gray ) ); return QBrush( QColor( Qt::gray ) );
} }
else if ( role == Qt::FontRole ) if ( role == Qt::FontRole )
{ {
QFont font = QFont(); QFont font = QFont();
if ( index.row() == mExtraIdentifierValueIndex ) if ( index.row() == mExtraIdentifierValueIndex )
font.setBold( true ); font.setBold( true );
else
if ( mEntries.value( index.row() ).identifierValue.isNull() )
{
font.setItalic( true ); font.setItalic( true );
}
return font; return font;
} }
} }
@ -197,7 +216,7 @@ void QgsFeatureFilterModel::updateCompleter()
QVector<Entry> entries = mGatherer->entries(); QVector<Entry> entries = mGatherer->entries();
if ( mExtraIdentifierValueIndex == -1 ) if ( mExtraIdentifierValueIndex == -1 )
setExtraIdentifierValueUnguarded( QVariant() ); setExtraIdentifierValuesUnguarded( QVariantList() );
// Only reloading the current entry? // Only reloading the current entry?
if ( mGatherer->data().toBool() ) if ( mGatherer->data().toBool() )
@ -225,7 +244,9 @@ void QgsFeatureFilterModel::updateCompleter()
std::sort( entries.begin(), entries.end(), []( const Entry & a, const Entry & b ) { return a.value.localeAwareCompare( b.value ) < 0; } ); std::sort( entries.begin(), entries.end(), []( const Entry & a, const Entry & b ) { return a.value.localeAwareCompare( b.value ) < 0; } );
if ( mAllowNull ) if ( mAllowNull )
entries.prepend( Entry( QVariant( QVariant::Int ), QgsApplication::nullRepresentation(), QgsFeature() ) ); {
entries.prepend( nullEntry() );
}
const int newEntriesSize = entries.size(); const int newEntriesSize = entries.size();
@ -235,7 +256,7 @@ void QgsFeatureFilterModel::updateCompleter()
{ {
for ( int i = 0; i < newEntriesSize; ++i ) for ( int i = 0; i < newEntriesSize; ++i )
{ {
if ( entries.at( i ).identifierValue == mExtraIdentifierValue ) if ( entries.at( i ).identifierValues == mExtraIdentifierValues )
{ {
currentEntryInNewList = i; currentEntryInNewList = i;
mEntries.replace( mExtraIdentifierValueIndex, entries.at( i ) ); mEntries.replace( mExtraIdentifierValueIndex, entries.at( i ) );
@ -274,7 +295,7 @@ void QgsFeatureFilterModel::updateCompleter()
beginInsertRows( QModelIndex(), 1, entries.size() + 1 ); beginInsertRows( QModelIndex(), 1, entries.size() + 1 );
mEntries += entries; mEntries += entries;
endInsertRows(); endInsertRows();
setExtraIdentifierValueIndex( 0 ); setExtraIdentifierValuesIndex( 0 );
} }
else else
{ {
@ -294,7 +315,7 @@ void QgsFeatureFilterModel::updateCompleter()
beginInsertRows( QModelIndex(), currentEntryInNewList + 1, newEntriesSize - currentEntryInNewList - 1 ); beginInsertRows( QModelIndex(), currentEntryInNewList + 1, newEntriesSize - currentEntryInNewList - 1 );
mEntries += entries.mid( currentEntryInNewList + 1 ); mEntries += entries.mid( currentEntryInNewList + 1 );
endInsertRows(); endInsertRows();
setExtraIdentifierValueIndex( currentEntryInNewList ); setExtraIdentifierValuesIndex( currentEntryInNewList );
} }
emit filterJobCompleted(); emit filterJobCompleted();
@ -331,7 +352,19 @@ void QgsFeatureFilterModel::scheduledReload()
if ( mShouldReloadCurrentFeature ) if ( mShouldReloadCurrentFeature )
{ {
request.setFilterExpression( QStringLiteral( "%1 = %2" ).arg( QgsExpression::quotedColumnRef( mIdentifierField ), QgsExpression::quotedValue( mExtraIdentifierValue ) ) ); QStringList conditions;
for ( int i = 0; i < mIdentifierFields.count(); i++ )
{
if ( i >= mExtraIdentifierValues.count() )
{
conditions << QgsExpression::createFieldEqualityExpression( mIdentifierFields.at( i ), QVariant() );
}
else
{
conditions << QgsExpression::createFieldEqualityExpression( mIdentifierFields.at( i ), mExtraIdentifierValues.at( i ) );
}
}
request.setFilterExpression( conditions.join( QStringLiteral( " AND " ) ) );
} }
else else
{ {
@ -350,13 +383,14 @@ void QgsFeatureFilterModel::scheduledReload()
QSet<QString> attributes; QSet<QString> attributes;
if ( request.filterExpression() ) if ( request.filterExpression() )
attributes = request.filterExpression()->referencedColumns(); attributes = request.filterExpression()->referencedColumns();
attributes << mIdentifierField; for ( const QString &fieldName : qgis::as_const( mIdentifierFields ) )
attributes << fieldName;
request.setSubsetOfAttributes( attributes, mSourceLayer->fields() ); request.setSubsetOfAttributes( attributes, mSourceLayer->fields() );
request.setFlags( QgsFeatureRequest::NoGeometry ); request.setFlags( QgsFeatureRequest::NoGeometry );
request.setLimit( QgsSettings().value( QStringLiteral( "maxEntriesRelationWidget" ), 100, QgsSettings::Gui ).toInt() ); request.setLimit( QgsSettings().value( QStringLiteral( "maxEntriesRelationWidget" ), 100, QgsSettings::Gui ).toInt() );
mGatherer = new QgsFieldExpressionValuesGatherer( mSourceLayer, mDisplayExpression, mIdentifierField, request ); mGatherer = new QgsFieldExpressionValuesGatherer( mSourceLayer, mDisplayExpression, mIdentifierFields, request );
mGatherer->setData( mShouldReloadCurrentFeature ); mGatherer->setData( mShouldReloadCurrentFeature );
connect( mGatherer, &QgsFieldExpressionValuesGatherer::collectedValues, this, &QgsFeatureFilterModel::updateCompleter ); connect( mGatherer, &QgsFieldExpressionValuesGatherer::collectedValues, this, &QgsFeatureFilterModel::updateCompleter );
@ -393,7 +427,7 @@ QSet<QString> QgsFeatureFilterModel::requestedAttributes() const
return requestedAttrs; return requestedAttrs;
} }
void QgsFeatureFilterModel::setExtraIdentifierValueIndex( int index, bool force ) void QgsFeatureFilterModel::setExtraIdentifierValuesIndex( int index, bool force )
{ {
if ( mExtraIdentifierValueIndex == index && !force ) if ( mExtraIdentifierValueIndex == index && !force )
return; return;
@ -408,18 +442,16 @@ void QgsFeatureFilterModel::reloadCurrentFeature()
mReloadTimer.start(); mReloadTimer.start();
} }
void QgsFeatureFilterModel::setExtraIdentifierValueUnguarded( const QVariant &extraIdentifierValue ) void QgsFeatureFilterModel::setExtraIdentifierValuesUnguarded( const QVariantList &extraIdentifierValues )
{ {
const QVector<Entry> entries = mEntries; const QVector<Entry> entries = mEntries;
int index = 0; int index = 0;
for ( const Entry &entry : entries ) for ( const Entry &entry : entries )
{ {
if ( entry.identifierValue == extraIdentifierValue if ( entry.identifierValues == extraIdentifierValues )
&& entry.identifierValue.isNull() == extraIdentifierValue.isNull()
&& entry.identifierValue.isValid() == extraIdentifierValue.isValid() )
{ {
setExtraIdentifierValueIndex( index ); setExtraIdentifierValuesIndex( index );
break; break;
} }
@ -430,18 +462,37 @@ void QgsFeatureFilterModel::setExtraIdentifierValueUnguarded( const QVariant &ex
if ( mExtraIdentifierValueIndex != index ) if ( mExtraIdentifierValueIndex != index )
{ {
beginInsertRows( QModelIndex(), 0, 0 ); beginInsertRows( QModelIndex(), 0, 0 );
if ( extraIdentifierValue.isNull() ) if ( extraIdentifierValues.isEmpty() )
mEntries.prepend( Entry( QVariant( QVariant::Int ), QgsApplication::nullRepresentation( ), QgsFeature() ) ); {
mEntries.prepend( nullEntry() );
}
else else
mEntries.prepend( Entry( extraIdentifierValue, QStringLiteral( "(%1)" ).arg( extraIdentifierValue.toString() ), QgsFeature() ) ); {
QStringList values;
for ( const QVariant &v : qgis::as_const( extraIdentifierValues ) )
values << QStringLiteral( "(%1)" ).arg( v.toString() );
mEntries.prepend( Entry( extraIdentifierValues, values.join( QStringLiteral( " " ) ), QgsFeature() ) );
}
endInsertRows(); endInsertRows();
setExtraIdentifierValueIndex( 0, true ); setExtraIdentifierValuesIndex( 0, true );
reloadCurrentFeature(); reloadCurrentFeature();
} }
} }
QgsFeatureFilterModel::Entry QgsFeatureFilterModel::nullEntry()
{
QVariantList nullAttributes;
for ( const QString &fieldName : mIdentifierFields )
{
int idx = mSourceLayer->fields().indexOf( fieldName );
nullAttributes << QVariant( mSourceLayer->fields().at( idx ).type() );
}
return Entry( nullAttributes, QgsApplication::nullRepresentation(), QgsFeature() );
}
QgsConditionalStyle QgsFeatureFilterModel::featureStyle( const QgsFeature &feature ) const QgsConditionalStyle QgsFeatureFilterModel::featureStyle( const QgsFeature &feature ) const
{ {
if ( !mSourceLayer ) if ( !mSourceLayer )
@ -505,18 +556,24 @@ int QgsFeatureFilterModel::extraIdentifierValueIndex() const
return mExtraIdentifierValueIndex; return mExtraIdentifierValueIndex;
} }
QString QgsFeatureFilterModel::identifierField() const QStringList QgsFeatureFilterModel::identifierFields() const
{ {
return mIdentifierField; return mIdentifierFields;
} }
void QgsFeatureFilterModel::setIdentifierField( const QString &identifierField ) void QgsFeatureFilterModel::setIdentifierField( const QString &identifierField )
{ {
if ( mIdentifierField == identifierField ) setIdentifierFields( QStringList() << identifierField );
}
void QgsFeatureFilterModel::setIdentifierFields( const QStringList &identifierFields )
{
if ( mIdentifierFields == identifierFields )
return; return;
mIdentifierField = identifierField; mIdentifierFields = identifierFields;
emit identifierFieldChanged(); emit identifierFieldChanged();
setExtraIdentifierValues( QVariantList() );
} }
void QgsFeatureFilterModel::reload() void QgsFeatureFilterModel::reload()
@ -526,12 +583,25 @@ void QgsFeatureFilterModel::reload()
QVariant QgsFeatureFilterModel::extraIdentifierValue() const QVariant QgsFeatureFilterModel::extraIdentifierValue() const
{ {
return mExtraIdentifierValue; if ( mExtraIdentifierValues.isEmpty() )
return QVariant();
else
return mExtraIdentifierValues.at( 0 );
}
QVariantList QgsFeatureFilterModel::extraIdentifierValues() const
{
return mExtraIdentifierValues;
} }
void QgsFeatureFilterModel::setExtraIdentifierValue( const QVariant &extraIdentifierValue ) void QgsFeatureFilterModel::setExtraIdentifierValue( const QVariant &extraIdentifierValue )
{ {
if ( qgsVariantEqual( extraIdentifierValue, mExtraIdentifierValue ) && mExtraIdentifierValue.isValid() ) setExtraIdentifierValues( QVariantList() << extraIdentifierValue );
}
void QgsFeatureFilterModel::setExtraIdentifierValues( const QVariantList &extraIdentifierValues )
{
if ( extraIdentifierValues == mExtraIdentifierValues && !mExtraIdentifierValues.isEmpty() )
return; return;
if ( mIsSettingExtraIdentifierValue ) if ( mIsSettingExtraIdentifierValue )
@ -539,9 +609,9 @@ void QgsFeatureFilterModel::setExtraIdentifierValue( const QVariant &extraIdenti
mIsSettingExtraIdentifierValue = true; mIsSettingExtraIdentifierValue = true;
mExtraIdentifierValue = extraIdentifierValue; mExtraIdentifierValues = extraIdentifierValues;
setExtraIdentifierValueUnguarded( extraIdentifierValue ); setExtraIdentifierValuesUnguarded( extraIdentifierValues );
mIsSettingExtraIdentifierValue = false; mIsSettingExtraIdentifierValue = false;

View File

@ -1,6 +1,5 @@
/*************************************************************************** /***************************************************************************
qgsfeaturefiltermodel.h - QgsFeatureFilterModel qgsfeaturefiltermodel.h - QgsFeatureFilterModel
--------------------- ---------------------
begin : 10.3.2017 begin : 10.3.2017
copyright : (C) 2017 by Matthias Kuhn copyright : (C) 2017 by Matthias Kuhn
@ -61,7 +60,8 @@ class CORE_EXPORT QgsFeatureFilterModel : public QAbstractItemModel
*/ */
enum Role enum Role
{ {
IdentifierValueRole = Qt::UserRole, //!< Used to retrieve the identifierValue (primary key) of a feature. IdentifierValueRole = Qt::UserRole, //!< \deprecated Use IdentifierValuesRole instead
IdentifierValuesRole, //!< Used to retrieve the identifierValues (primary keys) of a feature.
ValueRole //!< Used to retrieve the displayExpression of a feature. ValueRole //!< Used to retrieve the displayExpression of a feature.
}; };
@ -137,26 +137,59 @@ class CORE_EXPORT QgsFeatureFilterModel : public QAbstractItemModel
/** /**
* The identifier field should be a unique field that can be used to identify individual features. * The identifier field should be a unique field that can be used to identify individual features.
* It is normally set to the primary key of the layer. * It is normally set to the primary key of the layer.
* If there are several identifier fields defined, the behavior is not guaranteed
* \deprecated since QGIS 3.10 use identifierFields instead
*/ */
QString identifierField() const; Q_DECL_DEPRECATED QString identifierField() const;
/** /**
* The identifier field should be a unique field that can be used to identify individual features. * The identifier field should be a unique field that can be used to identify individual features.
* It is normally set to the primary key of the layer. * It is normally set to the primary key of the layer.
* \since QGIS 3.10
*/ */
void setIdentifierField( const QString &identifierField ); QStringList identifierFields() const;
/**
* The identifier field should be a unique field that can be used to identify individual features.
* It is normally set to the primary key of the layer.
* \deprecated since QGIS 3.10
*/
Q_DECL_DEPRECATED void setIdentifierField( const QString &identifierField );
/**
* The identifier field should be a unique field that can be used to identify individual features.
* It is normally set to the primary key of the layer.
* \since QGIS 3.10
*/
void setIdentifierFields( const QStringList &identifierFields );
/** /**
* Allows specifying one value that does not need to match the filter criteria but will * Allows specifying one value that does not need to match the filter criteria but will
* still be available in the model. * still be available in the model.
* \deprecated since QGIS 3.10
*/ */
QVariant extraIdentifierValue() const; Q_DECL_DEPRECATED QVariant extraIdentifierValue() const;
/** /**
* Allows specifying one value that does not need to match the filter criteria but will * Allows specifying one value that does not need to match the filter criteria but will
* still be available in the model. * still be available in the model.
* \since QGIS 3.10
*/ */
void setExtraIdentifierValue( const QVariant &extraIdentifierValue ); QVariantList extraIdentifierValues() const;
/**
* Allows specifying one value that does not need to match the filter criteria but will
* still be available in the model.
* \deprecated since QGIS 3.10
*/
Q_DECL_DEPRECATED void setExtraIdentifierValue( const QVariant &extraIdentifierValue );
/**
* Allows specifying one value that does not need to match the filter criteria but will
* still be available in the model.
* \since QGIS 3.10
*/
void setExtraIdentifierValues( const QVariantList &extraIdentifierValues );
/** /**
* The index at which the extra identifier value is available within the model. * The index at which the extra identifier value is available within the model.
@ -260,27 +293,28 @@ class CORE_EXPORT QgsFeatureFilterModel : public QAbstractItemModel
private: private:
QSet<QString> requestedAttributes() const; QSet<QString> requestedAttributes() const;
void setExtraIdentifierValueIndex( int index, bool force = false ); void setExtraIdentifierValuesIndex( int index, bool force = false );
void setExtraValueDoesNotExist( bool extraValueDoesNotExist ); void setExtraValueDoesNotExist( bool extraValueDoesNotExist );
void reload(); void reload();
void reloadCurrentFeature(); void reloadCurrentFeature();
void setExtraIdentifierValueUnguarded( const QVariant &extraIdentifierValue ); void setExtraIdentifierValuesUnguarded( const QVariantList &extraIdentifierValues );
struct Entry struct Entry
{ {
Entry() = default; Entry() = default;
Entry( const QVariant &_identifierValue, const QString &_value, const QgsFeature &_feature ) Entry( const QVariantList &_identifierValues, const QString &_value, const QgsFeature &_feature )
: identifierValue( _identifierValue ) : identifierValues( _identifierValues )
, value( _value ) , value( _value )
, feature( _feature ) , feature( _feature )
{} {}
QVariant identifierValue; QVariantList identifierValues;
QString value; QString value;
QgsFeature feature; QgsFeature feature;
bool operator()( const Entry &lhs, const Entry &rhs ) const; bool operator()( const Entry &lhs, const Entry &rhs ) const;
}; };
Entry nullEntry();
QgsConditionalStyle featureStyle( const QgsFeature &feature ) const; QgsConditionalStyle featureStyle( const QgsFeature &feature ) const;
@ -299,9 +333,8 @@ class CORE_EXPORT QgsFeatureFilterModel : public QAbstractItemModel
bool mAllowNull = false; bool mAllowNull = false;
bool mIsSettingExtraIdentifierValue = false; bool mIsSettingExtraIdentifierValue = false;
QString mIdentifierField; QStringList mIdentifierFields;
QVariantList mExtraIdentifierValues;
QVariant mExtraIdentifierValue;
int mExtraIdentifierValueIndex = -1; int mExtraIdentifierValueIndex = -1;

View File

@ -1,6 +1,5 @@
/*************************************************************************** /***************************************************************************
qgsfeaturefiltermodel_p - QgsFieldExpressionValuesGatherer qgsfeaturefiltermodel_p - QgsFieldExpressionValuesGatherer
--------------------- ---------------------
begin : 10.3.2017 begin : 10.3.2017
copyright : (C) 2017 by Matthias Kuhn copyright : (C) 2017 by Matthias Kuhn
@ -37,11 +36,14 @@ class QgsFieldExpressionValuesGatherer: public QThread
Q_OBJECT Q_OBJECT
public: public:
QgsFieldExpressionValuesGatherer( QgsVectorLayer *layer, const QString &displayExpression, const QString &identifierField, const QgsFeatureRequest &request = QgsFeatureRequest() ) QgsFieldExpressionValuesGatherer( QgsVectorLayer *layer,
const QString &displayExpression,
const QStringList &identifierFields,
const QgsFeatureRequest &request = QgsFeatureRequest() )
: mSource( new QgsVectorLayerFeatureSource( layer ) ) : mSource( new QgsVectorLayerFeatureSource( layer ) )
, mDisplayExpression( displayExpression ) , mDisplayExpression( displayExpression )
, mRequest( request ) , mRequest( request )
, mIdentifierField( identifierField ) , mIdentifierFields( identifierFields )
{ {
} }
@ -54,12 +56,17 @@ class QgsFieldExpressionValuesGatherer: public QThread
mDisplayExpression.prepare( &mExpressionContext ); mDisplayExpression.prepare( &mExpressionContext );
QgsFeature feat; QgsFeature feat;
const int attribute = mSource->fields().indexOf( mIdentifierField ); QList<int> attributeIndexes;
for ( const QString &fieldName : qgis::as_const( mIdentifierFields ) )
attributeIndexes << mSource->fields().indexOf( fieldName );
while ( mIterator.nextFeature( feat ) ) while ( mIterator.nextFeature( feat ) )
{ {
mExpressionContext.setFeature( feat ); mExpressionContext.setFeature( feat );
mEntries.append( QgsFeatureFilterModel::Entry( feat.attribute( attribute ), mDisplayExpression.evaluate( &mExpressionContext ).toString(), feat ) ); QVariantList attributes;
for ( const int idx : attributeIndexes )
attributes << feat.attribute( idx );
mEntries.append( QgsFeatureFilterModel::Entry( attributes, mDisplayExpression.evaluate( &mExpressionContext ).toString(), feat ) );
if ( mWasCanceled ) if ( mWasCanceled )
return; return;
@ -120,7 +127,7 @@ class QgsFieldExpressionValuesGatherer: public QThread
QgsFeatureIterator mIterator; QgsFeatureIterator mIterator;
bool mWasCanceled = false; bool mWasCanceled = false;
QVector<QgsFeatureFilterModel::Entry> mEntries; QVector<QgsFeatureFilterModel::Entry> mEntries;
QString mIdentifierField; QStringList mIdentifierFields;
QVariant mData; QVariant mData;
}; };

View File

@ -1,6 +1,5 @@
/*************************************************************************** /***************************************************************************
qgsfieldlistcombobox.cpp - QgsFieldListComboBox qgsfeaturelistcombobox.cpp - QgsFeatureListComboBox
--------------------- ---------------------
begin : 10.3.2017 begin : 10.3.2017
copyright : (C) 2017 by Matthias Kuhn copyright : (C) 2017 by Matthias Kuhn
@ -75,6 +74,17 @@ void QgsFeatureListComboBox::setSourceLayer( QgsVectorLayer *sourceLayer )
mModel->setSourceLayer( sourceLayer ); mModel->setSourceLayer( sourceLayer );
} }
void QgsFeatureListComboBox::setCurrentFeature( const QgsFeature &feature )
{
QVariantList values;
const QStringList fields = mModel->identifierFields();
for ( const QString &field : fields )
{
values << feature.attribute( field );
}
setCurrentIndex( findData( values, QgsFeatureFilterModel::Role::IdentifierValueRole ) );
}
QString QgsFeatureListComboBox::displayExpression() const QString QgsFeatureListComboBox::displayExpression() const
{ {
return mModel->displayExpression(); return mModel->displayExpression();
@ -115,7 +125,7 @@ void QgsFeatureListComboBox::onCurrentIndexChanged( int i )
if ( !mHasStoredEditState ) if ( !mHasStoredEditState )
mIsCurrentlyEdited = false; mIsCurrentlyEdited = false;
QModelIndex modelIndex = mModel->index( i, 0, QModelIndex() ); QModelIndex modelIndex = mModel->index( i, 0, QModelIndex() );
mModel->setExtraIdentifierValue( mModel->data( modelIndex, QgsFeatureFilterModel::IdentifierValueRole ) ); mModel->setExtraIdentifierValues( mModel->data( modelIndex, QgsFeatureFilterModel::IdentifierValuesRole ).toList() );
mLineEdit->setText( mModel->data( modelIndex, QgsFeatureFilterModel::ValueRole ).toString() ); mLineEdit->setText( mModel->data( modelIndex, QgsFeatureFilterModel::ValueRole ).toString() );
mLineEdit->setFont( mModel->data( modelIndex, Qt::FontRole ).value<QFont>() ); mLineEdit->setFont( mModel->data( modelIndex, Qt::FontRole ).value<QFont>() );
QPalette palette = mLineEdit->palette(); QPalette palette = mLineEdit->palette();
@ -125,7 +135,7 @@ void QgsFeatureListComboBox::onCurrentIndexChanged( int i )
void QgsFeatureListComboBox::onActivated( QModelIndex modelIndex ) void QgsFeatureListComboBox::onActivated( QModelIndex modelIndex )
{ {
setIdentifierValue( mModel->data( modelIndex, QgsFeatureFilterModel::IdentifierValueRole ) ); setIdentifierValues( mModel->data( modelIndex, QgsFeatureFilterModel::IdentifierValuesRole ).toList() );
mLineEdit->setText( mModel->data( modelIndex, QgsFeatureFilterModel::ValueRole ).toString() ); mLineEdit->setText( mModel->data( modelIndex, QgsFeatureFilterModel::ValueRole ).toString() );
} }
@ -175,12 +185,26 @@ void QgsFeatureListComboBox::onDataChanged( const QModelIndex &topLeft, const QM
QString QgsFeatureListComboBox::identifierField() const QString QgsFeatureListComboBox::identifierField() const
{ {
return mModel->identifierField(); QStringList list = mModel->identifierFields();
if ( list.isEmpty() )
return QString();
else
return list.at( 0 );
}
QStringList QgsFeatureListComboBox::identifierFields() const
{
return mModel->identifierFields();
} }
void QgsFeatureListComboBox::setIdentifierField( const QString &identifierField ) void QgsFeatureListComboBox::setIdentifierField( const QString &identifierField )
{ {
mModel->setIdentifierField( identifierField ); mModel->setIdentifierFields( QStringList() << identifierField );
}
void QgsFeatureListComboBox::setIdentifierFields( const QStringList &identifierFields )
{
mModel->setIdentifierFields( identifierFields );
} }
QModelIndex QgsFeatureListComboBox::currentModelIndex() const QModelIndex QgsFeatureListComboBox::currentModelIndex() const
@ -219,17 +243,46 @@ QVariant QgsFeatureListComboBox::identifierValue() const
return mModel->extraIdentifierValue(); return mModel->extraIdentifierValue();
} }
QVariantList QgsFeatureListComboBox::identifierValues() const
{
return mModel->extraIdentifierValues();
}
void QgsFeatureListComboBox::setIdentifierValue( const QVariant &identifierValue ) void QgsFeatureListComboBox::setIdentifierValue( const QVariant &identifierValue )
{ {
mModel->setExtraIdentifierValue( identifierValue ); setIdentifierValues( QVariantList() << identifierValue );
}
void QgsFeatureListComboBox::setIdentifierValues( const QVariantList &identifierValues )
{
mModel->setExtraIdentifierValues( identifierValues );
} }
QgsFeatureRequest QgsFeatureListComboBox::currentFeatureRequest() const QgsFeatureRequest QgsFeatureListComboBox::currentFeatureRequest() const
{ {
if ( mModel->extraIdentifierValue().isNull() ) if ( mModel->extraIdentifierValues().isEmpty() )
{
return QgsFeatureRequest().setFilterFids( QgsFeatureIds() ); // NULL: Return a request that's guaranteed to not return anything return QgsFeatureRequest().setFilterFids( QgsFeatureIds() ); // NULL: Return a request that's guaranteed to not return anything
}
else else
return QgsFeatureRequest().setFilterExpression( QStringLiteral( "%1 = %2" ).arg( QgsExpression::quotedColumnRef( mModel->identifierField() ), QgsExpression::quotedValue( mModel->extraIdentifierValue() ) ) ); {
QStringList filtersAttrs;
const QStringList identifierFields = mModel->identifierFields();
const QVariantList values = mModel->extraIdentifierValues();
for ( int i = 0; i < identifierFields.count(); i++ )
{
if ( i >= values.count() )
{
filtersAttrs << QgsExpression::createFieldEqualityExpression( identifierFields.at( i ), QVariant() );
}
else
{
filtersAttrs << QgsExpression::createFieldEqualityExpression( identifierFields.at( i ), values.at( i ) );
}
}
const QString expression = filtersAttrs.join( QStringLiteral( " AND " ) );
return QgsFeatureRequest().setFilterExpression( expression );
}
} }
QString QgsFeatureListComboBox::filterExpression() const QString QgsFeatureListComboBox::filterExpression() const

View File

@ -1,6 +1,5 @@
/*************************************************************************** /***************************************************************************
qgsfieldlistcombobox.h - QgsFieldListComboBox qgsfeaturelistcombobox.h - QgsFeatureListComboBox
--------------------- ---------------------
begin : 10.3.2017 begin : 10.3.2017
copyright : (C) 2017 by Matthias Kuhn copyright : (C) 2017 by Matthias Kuhn
@ -27,6 +26,7 @@ class QgsFeatureFilterModel;
class QgsAnimatedIcon; class QgsAnimatedIcon;
class QgsFilterLineEdit; class QgsFilterLineEdit;
/** /**
* \ingroup gui * \ingroup gui
* This offers a combobox with autocompleter that allows selecting features from a layer. * This offers a combobox with autocompleter that allows selecting features from a layer.
@ -44,6 +44,7 @@ class GUI_EXPORT QgsFeatureListComboBox : public QComboBox
Q_PROPERTY( QString displayExpression READ displayExpression WRITE setDisplayExpression NOTIFY displayExpressionChanged ) Q_PROPERTY( QString displayExpression READ displayExpression WRITE setDisplayExpression NOTIFY displayExpressionChanged )
Q_PROPERTY( QString filterExpression READ filterExpression WRITE setFilterExpression NOTIFY filterExpressionChanged ) Q_PROPERTY( QString filterExpression READ filterExpression WRITE setFilterExpression NOTIFY filterExpressionChanged )
Q_PROPERTY( QVariant identifierValue READ identifierValue WRITE setIdentifierValue NOTIFY identifierValueChanged ) Q_PROPERTY( QVariant identifierValue READ identifierValue WRITE setIdentifierValue NOTIFY identifierValueChanged )
Q_PROPERTY( QVariantList identifierValues READ identifierValues WRITE setIdentifierValues NOTIFY identifierValueChanged )
Q_PROPERTY( QString identifierField READ identifierField WRITE setIdentifierField NOTIFY identifierFieldChanged ) Q_PROPERTY( QString identifierField READ identifierField WRITE setIdentifierField NOTIFY identifierFieldChanged )
Q_PROPERTY( bool allowNull READ allowNull WRITE setAllowNull NOTIFY allowNullChanged ) Q_PROPERTY( bool allowNull READ allowNull WRITE setAllowNull NOTIFY allowNullChanged )
@ -64,6 +65,12 @@ class GUI_EXPORT QgsFeatureListComboBox : public QComboBox
*/ */
void setSourceLayer( QgsVectorLayer *sourceLayer ); void setSourceLayer( QgsVectorLayer *sourceLayer );
/**
* Sets the current index by using the given feature
* \since QGIS 3.10
*/
void setCurrentFeature( const QgsFeature &feature );
/** /**
* The display expression will be used to display features as well as * The display expression will be used to display features as well as
* the value to match the typed text against. * the value to match the typed text against.
@ -101,14 +108,31 @@ class GUI_EXPORT QgsFeatureListComboBox : public QComboBox
/** /**
* The identifier value of the currently selected feature. A value from the * The identifier value of the currently selected feature. A value from the
* identifierField. * identifierField.
* \deprecated since QGIS 3.10
*/ */
QVariant identifierValue() const; Q_DECL_DEPRECATED QVariant identifierValue() const SIP_DEPRECATED;
/**
* The identifier values of the currently selected feature. A value from the
* identifierField.
* \since QGIS 3.10
*/
QVariantList identifierValues() const;
/** /**
* The identifier value of the currently selected feature. A value from the * The identifier value of the currently selected feature. A value from the
* identifierField. * identifierField.
* \deprecated since QGIS 3.10 use setIdentifierValues
*/ */
void setIdentifierValue( const QVariant &identifierValue ); Q_DECL_DEPRECATED void setIdentifierValue( const QVariant &identifierValue ) SIP_DEPRECATED;
/**
* The identifier values of the currently selected feature. A value from the
* identifierFields.
* \since QGIS 3.10
*/
void setIdentifierValues( const QVariantList &identifierValues );
/** /**
* Shorthand for getting a feature request to query the currently selected * Shorthand for getting a feature request to query the currently selected
@ -129,14 +153,30 @@ class GUI_EXPORT QgsFeatureListComboBox : public QComboBox
/** /**
* Field name that will be used to uniquely identify the current feature. * Field name that will be used to uniquely identify the current feature.
* Normally the primary key of the layer. * Normally the primary key of the layer.
* \deprecated since QGIS 3.10
*/ */
QString identifierField() const; Q_DECL_DEPRECATED QString identifierField() const SIP_DEPRECATED;
/** /**
* Field name that will be used to uniquely identify the current feature. * Field name that will be used to uniquely identify the current feature.
* Normally the primary key of the layer. * Normally the primary key of the layer.
* \since QGIS 3.10
*/ */
void setIdentifierField( const QString &identifierField ); QStringList identifierFields() const;
/**
* Field name that will be used to uniquely identify the current feature.
* Normally the primary key of the layer.
* \deprecated since QGIS 3.10
*/
Q_DECL_DEPRECATED void setIdentifierField( const QString &identifierField ) SIP_DEPRECATED;
/**
* Field name that will be used to uniquely identify the current feature.
* Normally the primary key of the layer.
* \since QGIS 3.10
*/
void setIdentifierFields( const QStringList &identifierFields );
/** /**
* The index of the currently selected item. * The index of the currently selected item.
@ -225,4 +265,6 @@ class GUI_EXPORT QgsFeatureListComboBox : public QComboBox
friend class TestQgsFeatureListComboBox; friend class TestQgsFeatureListComboBox;
}; };
#endif // QGSFIELDLISTCOMBOBOX_H #endif // QGSFIELDLISTCOMBOBOX_H