QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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 : [email protected]
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  setCompleter( mCompleter );
35  mCompleter->setWidget( this );
39  connect( mModel, &QgsFeatureFilterModel::isLoadingChanged, this, &QgsFeatureListComboBox::onLoadingChanged );
40  connect( mModel, &QgsFeatureFilterModel::filterJobCompleted, this, &QgsFeatureListComboBox::onFilterUpdateCompleted );
43  connect( mModel, &QgsFeatureFilterModel::extraIdentifierValueIndexChanged, this, &QgsFeatureListComboBox::setCurrentIndex );
45  connect( mCompleter, static_cast<void( QCompleter::* )( const QModelIndex & )>( &QCompleter::highlighted ), this, &QgsFeatureListComboBox::onItemSelected );
46  connect( mCompleter, static_cast<void( QCompleter::* )( const QModelIndex & )>( &QCompleter::activated ), this, &QgsFeatureListComboBox::onActivated );
47  connect( mModel, &QgsFeatureFilterModel::beginUpdate, this, &QgsFeatureListComboBox::storeLineEditState );
48  connect( mModel, &QgsFeatureFilterModel::endUpdate, this, &QgsFeatureListComboBox::restoreLineEditState );
50  connect( mModel, &QgsFeatureFilterModel::dataChanged, this, &QgsFeatureListComboBox::onDataChanged );
51 
52  connect( this, static_cast<void( QgsFeatureListComboBox::* )( int )>( &QgsFeatureListComboBox::currentIndexChanged ), this, &QgsFeatureListComboBox::onCurrentIndexChanged );
53 
54  mLineEdit = new QgsFilterLineEdit( nullptr, QgsApplication::nullRepresentation() );
55  mLineEdit->setSelectOnFocus( true );
56  mLineEdit->setShowClearButton( true );
57 
58  setEditable( true );
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 ( !mHasStoredEditState )
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  mHasStoredEditState = true;
147  mLineEditState.store( mLineEdit );
148  }
149 }
150 
151 void QgsFeatureListComboBox::restoreLineEditState()
152 {
153  if ( mIsCurrentlyEdited )
154  {
155  mHasStoredEditState = false;
156  mLineEditState.restore( mLineEdit );
157  }
158 }
159 
161 {
162  int index = -1;
163 
164  if ( allowNull() )
165  {
166  index = findText( QgsApplication::nullRepresentation( ) );
167  }
168 
169  return index;
170 }
171 
172 void QgsFeatureListComboBox::onDataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles )
173 {
174  Q_UNUSED( roles )
175  if ( !mIsCurrentlyEdited )
176  {
177  const int currentIndex = mModel->extraIdentifierValueIndex();
178  if ( currentIndex >= topLeft.row() && currentIndex <= bottomRight.row() )
179  {
180  QModelIndex modelIndex = mModel->index( currentIndex, 0, QModelIndex() );
181  mLineEdit->setText( mModel->data( modelIndex, QgsFeatureFilterModel::ValueRole ).toString() );
182  }
183  }
184 }
185 
187 {
188  QStringList list = mModel->identifierFields();
189  if ( list.isEmpty() )
190  return QString();
191  else
192  return list.at( 0 );
193 }
194 
196 {
197  return mModel->identifierFields();
198 }
199 
201 {
202  mModel->setIdentifierFields( QStringList() << identifierField );
203 }
204 
206 {
207  mModel->setIdentifierFields( identifierFields );
208 }
209 
211 {
212  return mModel->index( mModel->extraIdentifierValueIndex(), 0, QModelIndex() );
213 }
214 
215 void QgsFeatureListComboBox::focusOutEvent( QFocusEvent *event )
216 {
217  Q_UNUSED( event )
218  QComboBox::focusOutEvent( event );
219  mLineEdit->setText( mModel->data( currentModelIndex(), QgsFeatureFilterModel::ValueRole ).toString() );
220 }
221 
223 {
224  if ( event->key() == Qt::Key_Escape )
225  {
226  mLineEdit->setText( mModel->data( currentModelIndex(), QgsFeatureFilterModel::ValueRole ).toString() );
227  }
228  QComboBox::keyReleaseEvent( event );
229 }
230 
232 {
233  return mModel->allowNull();
234 }
235 
237 {
238  mModel->setAllowNull( allowNull );
239 }
240 
242 {
244  return mModel->extraIdentifierValue();
246 }
247 
248 QVariantList QgsFeatureListComboBox::identifierValues() const
249 {
250  return mModel->extraIdentifierValues();
251 }
252 
254 {
255  setIdentifierValues( QVariantList() << identifierValue );
256 }
257 
259 {
260  mModel->setExtraIdentifierValues( identifierValues );
261 }
262 
264 {
266 }
267 
269 {
270  if ( mModel->extraIdentifierValues().isEmpty() )
271  {
272  return QgsFeatureRequest().setFilterFids( QgsFeatureIds() ); // NULL: Return a request that's guaranteed to not return anything
273  }
274  else
275  {
276  QStringList filtersAttrs;
277  const QStringList identifierFields = mModel->identifierFields();
278  const QVariantList values = mModel->extraIdentifierValues();
279  for ( int i = 0; i < identifierFields.count(); i++ )
280  {
281  if ( i >= values.count() )
282  {
283  filtersAttrs << QgsExpression::createFieldEqualityExpression( identifierFields.at( i ), QVariant() );
284  }
285  else
286  {
287  filtersAttrs << QgsExpression::createFieldEqualityExpression( identifierFields.at( i ), values.at( i ) );
288  }
289  }
290  const QString expression = filtersAttrs.join( QStringLiteral( " AND " ) );
291  return QgsFeatureRequest().setFilterExpression( expression );
292  }
293 }
294 
296 {
297  return mModel->filterExpression();
298 }
299 
301 {
302  mModel->setFilterExpression( filterExpression );
303 }
304 
305 void QgsFeatureListComboBox::LineEditState::store( QLineEdit *lineEdit )
306 {
307  text = lineEdit->text();
308  selectionStart = lineEdit->selectionStart();
309  selectionLength = lineEdit->selectedText().length();
310  cursorPosition = lineEdit->cursorPosition();
311 
312 }
313 
314 void QgsFeatureListComboBox::LineEditState::restore( QLineEdit *lineEdit ) const
315 {
316  lineEdit->setText( text );
317  lineEdit->setCursorPosition( cursorPosition );
318  if ( selectionStart > -1 )
319  lineEdit->setSelection( selectionStart, selectionLength );
320 }
Q_DECL_DEPRECATED void setIdentifierValue(const QVariant &identifierValue)
The identifier value of the currently selected feature.
Provides a list of features based on filter conditions.
This offers a combobox with autocompleter that allows selecting features from a layer.
QgsFeatureRequest currentFeatureRequest() const
Shorthand for getting a feature request to query the currently selected feature.
QModelIndex currentModelIndex() const
The index of the currently selected item.
void setExtraIdentifierValues(const QVariantList &extraIdentifierValues)
Allows specifying one value that does not need to match the filter criteria but will still be availab...
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
int nullIndex() const
Returns the current index of the NULL value, or -1 if NULL values are not allowed.
void setSelectOnFocus(bool selectOnFocus)
Will select all text when this widget receives the focus.
void setIdentifierValues(const QVariantList &identifierValues)
The identifier values of the currently selected feature.
void beginUpdate()
Notification that the model is about to be changed because a job was completed.
QStringList identifierFields() const
The identifier field should be a unique field that can be used to identify individual features...
Used to retrieve the displayExpression of a feature.
void identifierFieldChanged()
Field name that will be used to uniquely identify the current feature.
void setFilterExpression(const QString &filterExpression)
An additional filter expression to apply, next to the filterValue.
QString filterExpression() const
An additional expression to further restrict the available features.
void setShowSpinner(bool showSpinner)
Show a spinner icon.
void setIdentifierFields(const QStringList &identifierFields)
Field name that will be used to uniquely identify the current feature.
QVariantList extraIdentifierValues() const
Allows specifying one value that does not need to match the filter criteria but will still be availab...
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:649
QVariantList identifierValues() const
The identifier values of the currently selected feature.
Q_DECL_DEPRECATED QVariant identifierValue() const
The identifier value of the currently selected feature.
void displayExpressionChanged()
The display expression will be used to display features as well as the the value to match the typed t...
void filterJobCompleted()
Indicates that a filter job has been completed and new data may be available.
void setExtraIdentifierValuesToNull()
Allows specifying one value that does not need to match the filter criteria but will still be availab...
void extraIdentifierValueIndexChanged(int index)
The index at which the extra identifier value is available within the model.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
void setSourceLayer(QgsVectorLayer *sourceLayer)
The layer from which features should be listed.
void filterExpressionChanged()
An additional expression to further restrict the available features.
QgsVectorLayer * sourceLayer() const
The layer from which features should be listed.
Q_DECL_DEPRECATED QString identifierField() const
Field name that will be used to uniquely identify the current feature.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
void identifierFieldChanged()
The identifier field should be a unique field that can be used to identify individual features...
QStringList identifierFields() const
Field name that will be used to uniquely identify the current feature.
void setIdentifierValuesToNull()
Sets the identifier values of the currently selected feature to NULL value(s).
void setAllowNull(bool allowNull)
Determines if a NULL value should be available in the list.
QString displayExpression() const
The display expression will be used to display features as well as the value to match the typed text ...
QgsFeatureListComboBox(QWidget *parent=nullptr)
Create a new QgsFeatureListComboBox, optionally specifying a parent.
void setFilterValue(const QString &filterValue)
This value will be used to filter the features available from this model.
QModelIndex index(int row, int column, const QModelIndex &parent) const override
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QLineEdit subclass with built in support for clearing the widget&#39;s value and handling custom null val...
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
QVariant extraIdentifierValue
The value that identifies the current feature.
void identifierValueChanged()
The identifier value of the currently selected feature.
void isLoadingChanged()
Indicator if the model is currently performing any feature iteration in the background.
void allowNullChanged()
Add a NULL entry to the list.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
Create an expression allowing to evaluate if a field is equal to a value.
void filterExpressionChanged()
An additional filter expression to apply, next to the filterValue.
void endUpdate()
Notification that the model change is finished.
void setCurrentFeature(const QgsFeature &feature)
Sets the current index by using the given feature.
void extraIdentifierValueChanged()
Allows specifying one value that does not need to match the filter criteria but will still be availab...
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:650
void setSourceLayer(QgsVectorLayer *sourceLayer)
The source layer from which features will be fetched.
void setFilterExpression(const QString &filterExpression)
An additional expression to further restrict the available features.
void sourceLayerChanged()
The source layer from which features will be fetched.
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets feature IDs that should be fetched.
void allowNullChanged()
Determines if a NULL value should be available in the list.
void keyPressEvent(QKeyEvent *event) override
void sourceLayerChanged()
The layer from which features should be listed.
void setIdentifierFields(const QStringList &identifierFields)
The identifier field should be a unique field that can be used to identify individual features...
void focusOutEvent(QFocusEvent *event) override
QVariant data(const QModelIndex &index, int role) const override
void setAllowNull(bool allowNull)
Add a NULL entry to the list.
void setDisplayExpression(const QString &displayExpression)
The display expression will be used for.
void setShowClearButton(bool visible)
Sets whether the widget&#39;s clear button is visible.
Represents a vector layer which manages a vector based data sets.
void setDisplayExpression(const QString &displayExpression)
The display expression will be used to display features as well as the value to match the typed text ...
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:262
Used to retrieve the identifierValues (primary keys) of a feature.
void modelUpdated()
The underlying model has been updated.
Q_DECL_DEPRECATED void setIdentifierField(const QString &identifierField)
Field name that will be used to uniquely identify the current feature.
bool allowNull() const
Determines if a NULL value should be available in the list.
void displayExpressionChanged()
The display expression will be used for.