QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
qgsfeaturelistcombobox.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsfeaturelistcombobox.cpp - QgsFeatureListComboBox
3  ---------------------
4  begin : 10.3.2017
5  copyright : (C) 2017 by Matthias Kuhn
6  email : matthias@opengis.ch
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 #include "qgsfeaturelistcombobox.h"
16 
17 #include "qgsfeaturefiltermodel.h"
18 #include "qgsanimatedicon.h"
19 #include "qgsfilterlineedit.h"
20 #include "qgslogger.h"
21 #include "qgsapplication.h"
22 
23 #include <QCompleter>
24 #include <QLineEdit>
25 #include <QKeyEvent>
26 
28  : QComboBox( parent )
29  , mModel( new QgsFeatureFilterModel( this ) )
30  , mCompleter( new QCompleter( mModel ) )
31 {
32  mCompleter->setCaseSensitivity( Qt::CaseInsensitive );
33  mCompleter->setFilterMode( Qt::MatchContains );
34  setEditable( true );
35  setCompleter( mCompleter );
36  mCompleter->setWidget( this );
40  connect( mModel, &QgsFeatureFilterModel::isLoadingChanged, this, &QgsFeatureListComboBox::onLoadingChanged );
41  connect( mModel, &QgsFeatureFilterModel::filterJobCompleted, this, &QgsFeatureListComboBox::onFilterUpdateCompleted );
44  connect( mModel, &QgsFeatureFilterModel::extraIdentifierValueIndexChanged, this, &QgsFeatureListComboBox::setCurrentIndex );
46  connect( mCompleter, static_cast<void( QCompleter::* )( const QModelIndex & )>( &QCompleter::highlighted ), this, &QgsFeatureListComboBox::onItemSelected );
47  connect( mCompleter, static_cast<void( QCompleter::* )( const QModelIndex & )>( &QCompleter::activated ), this, &QgsFeatureListComboBox::onActivated );
48  connect( mModel, &QgsFeatureFilterModel::beginUpdate, this, &QgsFeatureListComboBox::storeLineEditState );
49  connect( mModel, &QgsFeatureFilterModel::endUpdate, this, &QgsFeatureListComboBox::restoreLineEditState );
51  connect( mModel, &QgsFeatureFilterModel::dataChanged, this, &QgsFeatureListComboBox::onDataChanged );
52 
53  connect( this, static_cast<void( QgsFeatureListComboBox::* )( int )>( &QgsFeatureListComboBox::currentIndexChanged ), this, &QgsFeatureListComboBox::onCurrentIndexChanged );
54 
55  mLineEdit = new QgsFilterLineEdit( nullptr, QgsApplication::nullRepresentation() );
56  mLineEdit->setSelectOnFocus( true );
57  mLineEdit->setShowClearButton( true );
58 
59  setLineEdit( mLineEdit );
60  setModel( mModel );
61 
62  connect( mLineEdit, &QgsFilterLineEdit::textEdited, this, &QgsFeatureListComboBox::onCurrentTextChanged );
63 
64  setToolTip( tr( "Just start typing what you are looking for." ) );
65 }
66 
68 {
69  return mModel->sourceLayer();
70 }
71 
73 {
74  mModel->setSourceLayer( sourceLayer );
75 }
76 
78 {
79  QVariantList values;
80  const QStringList fields = mModel->identifierFields();
81  for ( const QString &field : fields )
82  {
83  values << feature.attribute( field );
84  }
85  setIdentifierValues( values );
86 }
87 
89 {
90  return mModel->displayExpression();
91 }
92 
93 void QgsFeatureListComboBox::setDisplayExpression( const QString &expression )
94 {
95  mModel->setDisplayExpression( expression );
96 }
97 
98 void QgsFeatureListComboBox::onCurrentTextChanged( const QString &text )
99 {
100  mIsCurrentlyEdited = true;
101  mPopupRequested = true;
102  mModel->setFilterValue( text );
103 }
104 
105 void QgsFeatureListComboBox::onFilterUpdateCompleted()
106 {
107  if ( mPopupRequested )
108  mCompleter->complete();
109 
110  mPopupRequested = false;
111 }
112 
113 void QgsFeatureListComboBox::onLoadingChanged()
114 {
115  mLineEdit->setShowSpinner( mModel->isLoading() );
116 }
117 
118 void QgsFeatureListComboBox::onItemSelected( const QModelIndex &index )
119 {
120  setCurrentIndex( index.row() );
121 }
122 
123 void QgsFeatureListComboBox::onCurrentIndexChanged( int i )
124 {
125  if ( !mLineEdit->hasStateStored() )
126  mIsCurrentlyEdited = false;
127  QModelIndex modelIndex = mModel->index( i, 0, QModelIndex() );
128  mModel->setExtraIdentifierValues( mModel->data( modelIndex, QgsFeatureFilterModel::IdentifierValuesRole ).toList() );
129  mLineEdit->setText( mModel->data( modelIndex, QgsFeatureFilterModel::ValueRole ).toString() );
130  mLineEdit->setFont( mModel->data( modelIndex, Qt::FontRole ).value<QFont>() );
131  QPalette palette = mLineEdit->palette();
132  palette.setBrush( mLineEdit->foregroundRole(), mModel->data( modelIndex, Qt::ForegroundRole ).value<QBrush>() );
133  mLineEdit->setPalette( palette );
134 }
135 
136 void QgsFeatureListComboBox::onActivated( QModelIndex modelIndex )
137 {
138  setIdentifierValues( mModel->data( modelIndex, QgsFeatureFilterModel::IdentifierValuesRole ).toList() );
139  mLineEdit->setText( mModel->data( modelIndex, QgsFeatureFilterModel::ValueRole ).toString() );
140 }
141 
142 void QgsFeatureListComboBox::storeLineEditState()
143 {
144  if ( mIsCurrentlyEdited )
145  {
146  mLineEdit->storeState( );
147  }
148 }
149 
150 void QgsFeatureListComboBox::restoreLineEditState()
151 {
152  if ( mIsCurrentlyEdited )
153  {
154  mLineEdit->restoreState( );
155  }
156 }
157 
159 {
160  int index = -1;
161 
162  if ( allowNull() )
163  {
164  index = findText( QgsApplication::nullRepresentation( ) );
165  }
166 
167  return index;
168 }
169 
170 void QgsFeatureListComboBox::onDataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles )
171 {
172  Q_UNUSED( roles )
173  if ( !mIsCurrentlyEdited )
174  {
175  const int currentIndex = mModel->extraIdentifierValueIndex();
176  if ( currentIndex >= topLeft.row() && currentIndex <= bottomRight.row() )
177  {
178  QModelIndex modelIndex = mModel->index( currentIndex, 0, QModelIndex() );
179  mLineEdit->setText( mModel->data( modelIndex, QgsFeatureFilterModel::ValueRole ).toString() );
180  }
181  }
182 }
183 
185 {
186  QStringList list = mModel->identifierFields();
187  if ( list.isEmpty() )
188  return QString();
189  else
190  return list.at( 0 );
191 }
192 
194 {
195  return mModel->identifierFields();
196 }
197 
198 void QgsFeatureListComboBox::setIdentifierField( const QString &identifierField )
199 {
200  mModel->setIdentifierFields( QStringList() << identifierField );
201 }
202 
203 void QgsFeatureListComboBox::setIdentifierFields( const QStringList &identifierFields )
204 {
206 }
207 
209 {
210  return mModel->index( mModel->extraIdentifierValueIndex(), 0, QModelIndex() );
211 }
212 
213 void QgsFeatureListComboBox::focusOutEvent( QFocusEvent *event )
214 {
215  Q_UNUSED( event )
216  QComboBox::focusOutEvent( event );
217  mLineEdit->setText( mModel->data( currentModelIndex(), QgsFeatureFilterModel::ValueRole ).toString() );
218 }
219 
221 {
222  if ( event->key() == Qt::Key_Escape )
223  {
224  mLineEdit->setText( mModel->data( currentModelIndex(), QgsFeatureFilterModel::ValueRole ).toString() );
225  }
226  QComboBox::keyReleaseEvent( event );
227 }
228 
230 {
231  return mModel->allowNull();
232 }
233 
235 {
236  mModel->setAllowNull( allowNull );
238 }
239 
241 {
243  return mModel->extraIdentifierValues().value( 0 );
245 }
246 
248 {
249  return mModel->extraIdentifierValues();
250 }
251 
252 void QgsFeatureListComboBox::setIdentifierValue( const QVariant &identifierValue )
253 {
254  setIdentifierValues( QVariantList() << identifierValue );
255 }
256 
257 void QgsFeatureListComboBox::setIdentifierValues( const QVariantList &identifierValues )
258 {
260 }
261 
263 {
265 }
266 
268 {
269  if ( mModel->extraIdentifierValues().isEmpty() )
270  {
271  return QgsFeatureRequest().setFilterFids( QgsFeatureIds() ); // NULL: Return a request that's guaranteed to not return anything
272  }
273  else
274  {
275  QStringList filtersAttrs;
276  const QStringList identifierFields = mModel->identifierFields();
277  const QVariantList values = mModel->extraIdentifierValues();
278  for ( int i = 0; i < identifierFields.count(); i++ )
279  {
280  if ( i >= values.count() )
281  {
282  filtersAttrs << QgsExpression::createFieldEqualityExpression( identifierFields.at( i ), QVariant() );
283  }
284  else
285  {
286  filtersAttrs << QgsExpression::createFieldEqualityExpression( identifierFields.at( i ), values.at( i ) );
287  }
288  }
289  const QString expression = filtersAttrs.join( QLatin1String( " AND " ) );
290  return QgsFeatureRequest().setFilterExpression( expression );
291  }
292 }
293 
295 {
296  return mModel->filterExpression();
297 }
298 
299 void QgsFeatureListComboBox::setFilterExpression( const QString &filterExpression )
300 {
302 }
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
Create an expression allowing to evaluate if a field is equal to a value.
Provides a list of features based on filter conditions.
void setExtraIdentifierValues(const QVariantList &extraIdentifierValues)
Allows specifying one value that does not need to match the filter criteria but will still be availab...
void setExtraIdentifierValueToNull() override
Allows specifying one value that does not need to match the filter criteria but will still be availab...
QStringList identifierFields
A set of fields of sourceLayer that is unique and should be used to identify features.
QVariantList extraIdentifierValues
The values that identifies the current feature.
void identifierFieldsChanged()
The identifier field should be a unique field that can be used to identify individual features.
void setIdentifierFields(const QStringList &identifierFields)
The identifier field should be a unique field that can be used to identify individual features.
This offers a combobox with autocompleter that allows selecting features from a layer.
void setIdentifierValues(const QVariantList &identifierValues)
The identifier values of the currently selected feature.
void keyPressEvent(QKeyEvent *event) override
Q_DECL_DEPRECATED void setIdentifierField(const QString &identifierField)
Field name that will be used to uniquely identify the current feature.
void setDisplayExpression(const QString &displayExpression)
The display expression will be used to display features as well as the value to match the typed text ...
QModelIndex currentModelIndex() const
The index of the currently selected item.
void setFilterExpression(const QString &filterExpression)
An additional expression to further restrict the available features.
void setIdentifierFields(const QStringList &identifierFields)
Field name that will be used to uniquely identify the current feature.
void allowNullChanged()
Determines if a NULL value should be available in the list.
QgsFeatureListComboBox(QWidget *parent=nullptr)
Create a new QgsFeatureListComboBox, optionally specifying a parent.
void focusOutEvent(QFocusEvent *event) override
void setSourceLayer(QgsVectorLayer *sourceLayer)
The layer from which features should be listed.
void modelUpdated()
The underlying model has been updated.
QgsFeatureRequest currentFeatureRequest() const
Shorthand for getting a feature request to query the currently selected feature.
void setIdentifierValuesToNull()
Sets the identifier values of the currently selected feature to NULL value(s).
void identifierValueChanged()
The identifier value of the currently selected feature.
void sourceLayerChanged()
The layer from which features should be listed.
void setCurrentFeature(const QgsFeature &feature)
Sets the current index by using the given feature.
int nullIndex() const
Returns the current index of the NULL value, or -1 if NULL values are not allowed.
Q_DECL_DEPRECATED void setIdentifierValue(const QVariant &identifierValue)
The identifier value of the currently selected feature.
QStringList identifierFields() const
Field name that will be used to uniquely identify the current feature.
void setAllowNull(bool allowNull)
Determines if a NULL value should be available in the list.
void displayExpressionChanged()
The display expression will be used to display features as well as the the value to match the typed t...
void identifierFieldChanged()
Field name that will be used to uniquely identify the current feature.
void filterExpressionChanged()
An additional expression to further restrict the available features.
void extraIdentifierValueIndexChanged(int index)
The index at which the extra identifier value is available within the model.
void beginUpdate()
Notification that the model is about to be changed because a job was completed.
void setFilterValue(const QString &filterValue)
This value will be used to filter the features available from this model.
void filterExpressionChanged()
An additional filter expression to apply, next to the filterValue.
void extraIdentifierValueChanged()
Allows specifying one value that does not need to match the filter criteria but will still be availab...
void filterJobCompleted()
Indicates that a filter job has been completed and new data may be available.
void setAllowNull(bool allowNull)
Add a NULL entry to the list.
void setDisplayExpression(const QString &displayExpression)
The display expression will be used for.
bool isLoading() const
Indicator if the model is currently performing any feature iteration in the background.
QVariant data(const QModelIndex &index, int role) const override
QModelIndex index(int row, int column, const QModelIndex &parent) const override
void sourceLayerChanged()
The source layer from which features will be fetched.
@ IdentifierValuesRole
Used to retrieve the identifierValues (primary keys) of a feature.
@ ValueRole
Used to retrieve the displayExpression of a feature.
void setFilterExpression(const QString &filterExpression)
An additional filter expression to apply, next to the filterValue.
void allowNullChanged()
Add a NULL entry to the list.
void isLoadingChanged()
Indicator if the model is currently performing any feature iteration in the background.
void endUpdate()
Notification that the model change is finished.
void displayExpressionChanged()
The display expression will be used for.
void setSourceLayer(QgsVectorLayer *sourceLayer)
The source layer from which features will be fetched.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets feature IDs that should be fetched.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:287
QLineEdit subclass with built in support for clearing the widget's value and handling custom null val...
void restoreState()
Restores the current state of the line edit (selection and cursor position)
void storeState()
Stores the current state of the line edit (selection and cursor position)
@ ClearToNull
Reset value to null.
@ ClearToDefault
Reset value to default value (see defaultValue() )
void setShowClearButton(bool visible)
Sets whether the widget's clear button is visible.
bool hasStateStored() const
Returns if a state is already saved.
void setSelectOnFocus(bool selectOnFocus)
Will select all text when this widget receives the focus.
void setShowSpinner(bool showSpinner)
Show a spinner icon.
void setClearMode(ClearMode mode)
Sets the clear mode for the widget.
Represents a vector layer which manages a vector based data sets.
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:798
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:797
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
const QgsField & field
Definition: qgsfield.h:472