Skip to content

Commit 7811f38

Browse files
committedOct 26, 2017
[FEATURE] Add QgsFeatureListComboBox with live-filter-capabilities
1 parent d40447e commit 7811f38

12 files changed

+1281
-0
lines changed
 

‎python/core/core_auto.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@
311311
%Include qgsfieldmodel.sip
312312
%Include qgsfieldproxymodel.sip
313313
%Include qgsfiledownloader.sip
314+
%Include qgsfeaturefiltermodel.sip
314315
%Include qgsgeometryvalidator.sip
315316
%Include qgsgml.sip
316317
%Include qgsgmlschema.sip

‎python/core/qgsfeaturefiltermodel.sip

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/core/qgsfeaturefiltermodel.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
12+
class QgsFeatureFilterModel : QAbstractItemModel
13+
{
14+
%Docstring
15+
Provides a list of features based on filter conditions.
16+
Features are fetched asynchronously.
17+
%End
18+
19+
%TypeHeaderCode
20+
#include "qgsfeaturefiltermodel.h"
21+
%End
22+
public:
23+
enum Role
24+
{
25+
IdentifierValueRole,
26+
ValueRole
27+
};
28+
29+
QgsFeatureFilterModel( QObject *parent = 0 );
30+
~QgsFeatureFilterModel();
31+
32+
QgsVectorLayer *sourceLayer() const;
33+
%Docstring
34+
:rtype: QgsVectorLayer
35+
%End
36+
void setSourceLayer( QgsVectorLayer *sourceLayer );
37+
38+
QString displayExpression() const;
39+
%Docstring
40+
:rtype: str
41+
%End
42+
void setDisplayExpression( const QString &displayExpression );
43+
44+
QString filterValue() const;
45+
%Docstring
46+
:rtype: str
47+
%End
48+
void setFilterValue( const QString &filterValue );
49+
50+
virtual QModelIndex index( int row, int column, const QModelIndex &parent ) const;
51+
virtual QModelIndex parent( const QModelIndex &child ) const;
52+
virtual int rowCount( const QModelIndex &parent ) const;
53+
virtual int columnCount( const QModelIndex &parent ) const;
54+
virtual QVariant data( const QModelIndex &index, int role ) const;
55+
56+
QString filterExpression() const;
57+
%Docstring
58+
An additional filter expression to apply, next to the filterValue.
59+
Can be used for spatial filtering etc.
60+
:rtype: str
61+
%End
62+
63+
void setFilterExpression( const QString &filterExpression );
64+
%Docstring
65+
An additional filter expression to apply, next to the filterValue.
66+
Can be used for spatial filtering etc.
67+
%End
68+
69+
bool isLoading() const;
70+
%Docstring
71+
:rtype: bool
72+
%End
73+
74+
QString identifierField() const;
75+
%Docstring
76+
:rtype: str
77+
%End
78+
void setIdentifierField( const QString &identifierField );
79+
80+
QVariant extraIdentifierValue() const;
81+
%Docstring
82+
:rtype: QVariant
83+
%End
84+
void setExtraIdentifierValue( const QVariant &extraIdentifierValue );
85+
86+
int extraIdentifierValueIndex() const;
87+
%Docstring
88+
:rtype: int
89+
%End
90+
91+
bool extraValueDoesNotExist() const;
92+
%Docstring
93+
:rtype: bool
94+
%End
95+
96+
signals:
97+
void sourceLayerChanged();
98+
void displayExpressionChanged();
99+
void filterValueChanged();
100+
void filterExpressionChanged();
101+
void isLoadingChanged();
102+
void identifierFieldChanged();
103+
void filterJobCompleted();
104+
void extraIdentifierValueChanged();
105+
void extraIdentifierValueIndexChanged( int index );
106+
void extraValueDoesNotExistChanged();
107+
void beginUpdate();
108+
void endUpdate();
109+
110+
};
111+
112+
/************************************************************************
113+
* This file has been generated automatically from *
114+
* *
115+
* src/core/qgsfeaturefiltermodel.h *
116+
* *
117+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
118+
************************************************************************/

‎python/gui/gui_auto.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
%Include qgsfeatureselectiondlg.sip
109109
%Include qgsfieldcombobox.sip
110110
%Include qgsfieldexpressionwidget.sip
111+
%Include qgsfeaturelistcombobox.sip
111112
%Include qgsfieldvalidator.sip
112113
%Include qgsfieldvalueslineedit.sip
113114
%Include qgsfilewidget.sip

‎python/gui/qgsfeaturelistcombobox.sip

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/gui/qgsfeaturelistcombobox.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
12+
class QgsFeatureListComboBox : QComboBox
13+
{
14+
%Docstring
15+
*************************************************************************
16+
qgsfieldlistcombobox.h - QgsFieldListComboBox
17+
18+
---------------------
19+
begin : 10.3.2017
20+
copyright : (C) 2017 by Matthias Kuhn
21+
email : matthias@opengis.ch
22+
**************************************************************************
23+
*
24+
This program is free software; you can redistribute it and/or modify *
25+
it under the terms of the GNU General Public License as published by *
26+
the Free Software Foundation; either version 2 of the License, or *
27+
(at your option) any later version. *
28+
*
29+
**************************************************************************
30+
%End
31+
32+
%TypeHeaderCode
33+
#include "qgsfeaturelistcombobox.h"
34+
%End
35+
public:
36+
QgsFeatureListComboBox( QWidget *parent = 0 );
37+
38+
QgsVectorLayer *sourceLayer() const;
39+
%Docstring
40+
:rtype: QgsVectorLayer
41+
%End
42+
void setSourceLayer( QgsVectorLayer *sourceLayer );
43+
44+
QString displayExpression() const;
45+
%Docstring
46+
:rtype: str
47+
%End
48+
void setDisplayExpression( const QString &displayExpression );
49+
50+
QString filterExpression() const;
51+
%Docstring
52+
:rtype: str
53+
%End
54+
void setFilterExpression( const QString &filterExpression );
55+
56+
QVariant identifierValue() const;
57+
%Docstring
58+
:rtype: QVariant
59+
%End
60+
void setIdentifierValue( const QVariant &identifierValue );
61+
62+
QgsFeatureRequest currentFeatureRequest() const;
63+
%Docstring
64+
:rtype: QgsFeatureRequest
65+
%End
66+
67+
bool allowNull() const;
68+
%Docstring
69+
:rtype: bool
70+
%End
71+
void setAllowNull( bool allowNull );
72+
73+
QString identifierField() const;
74+
%Docstring
75+
:rtype: str
76+
%End
77+
void setIdentifierField( const QString &identifierField );
78+
79+
QModelIndex currentModelIndex() const;
80+
%Docstring
81+
:rtype: QModelIndex
82+
%End
83+
84+
virtual void focusOutEvent( QFocusEvent *event );
85+
86+
virtual void keyPressEvent( QKeyEvent *event );
87+
88+
signals:
89+
void sourceLayerChanged();
90+
void displayExpressionChanged();
91+
void filterExpressionChanged();
92+
void identifierValueChanged();
93+
void identifierFieldChanged();
94+
void allowNullChanged();
95+
96+
};
97+
98+
/************************************************************************
99+
* This file has been generated automatically from *
100+
* *
101+
* src/gui/qgsfeaturelistcombobox.h *
102+
* *
103+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
104+
************************************************************************/

‎src/core/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ SET(QGIS_CORE_SRCS
175175
qgsfeaturesink.cpp
176176
qgsfeaturesource.cpp
177177
qgsfeaturestore.cpp
178+
qgsfeaturefiltermodel.cpp
178179
qgsfield.cpp
179180
qgsfieldconstraints.cpp
180181
qgsfieldformatter.cpp
@@ -590,6 +591,8 @@ SET(QGIS_CORE_MOC_HDRS
590591
qgsfieldmodel.h
591592
qgsfieldproxymodel.h
592593
qgsfiledownloader.h
594+
qgsfeaturefiltermodel.h
595+
qgsfeaturefiltermodel_p.h
593596
qgsgeometryvalidator.h
594597
qgsgml.h
595598
qgsgmlschema.h

‎src/core/qgsfeaturefiltermodel.cpp

Lines changed: 412 additions & 0 deletions
Large diffs are not rendered by default.

‎src/core/qgsfeaturefiltermodel.h

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/***************************************************************************
2+
qgsfeaturefiltermodel.h - QgsFeatureFilterModel
3+
4+
---------------------
5+
begin : 10.3.2017
6+
copyright : (C) 2017 by Matthias Kuhn
7+
email : matthias@opengis.ch
8+
***************************************************************************
9+
* *
10+
* This program is free software; you can redistribute it and/or modify *
11+
* it under the terms of the GNU General Public License as published by *
12+
* the Free Software Foundation; either version 2 of the License, or *
13+
* (at your option) any later version. *
14+
* *
15+
***************************************************************************/
16+
#ifndef QGSFEATUREFILTERMODEL_H
17+
#define QGSFEATUREFILTERMODEL_H
18+
19+
#include <QAbstractItemModel>
20+
21+
#include "qgsvectorlayer.h"
22+
23+
class QgsFieldExpressionValuesGatherer;
24+
25+
/**
26+
* Provides a list of features based on filter conditions.
27+
* Features are fetched asynchronously.
28+
*/
29+
class CORE_EXPORT QgsFeatureFilterModel : public QAbstractItemModel
30+
{
31+
Q_OBJECT
32+
33+
Q_PROPERTY( QgsVectorLayer *sourceLayer READ sourceLayer WRITE setSourceLayer NOTIFY sourceLayerChanged )
34+
Q_PROPERTY( QString displayExpression READ displayExpression WRITE setDisplayExpression NOTIFY displayExpressionChanged )
35+
Q_PROPERTY( QString filterValue READ filterValue WRITE setFilterValue NOTIFY filterValueChanged )
36+
Q_PROPERTY( QString filterExpression READ filterExpression WRITE setFilterExpression NOTIFY filterExpressionChanged )
37+
Q_PROPERTY( bool isLoading READ isLoading NOTIFY isLoadingChanged )
38+
39+
/**
40+
* A field of sourceLayer that is unique and should be used to identify features.
41+
* Normally the primary key field.
42+
* Needs to match the identifierValue.
43+
*/
44+
Q_PROPERTY( QString identifierField READ identifierField WRITE setIdentifierField NOTIFY identifierFieldChanged )
45+
46+
/**
47+
* The value that identifies the current feature.
48+
*/
49+
Q_PROPERTY( QVariant extraIdentifierValue READ extraIdentifierValue WRITE setExtraIdentifierValue NOTIFY extraIdentifierValueChanged )
50+
51+
Q_PROPERTY( int extraIdentifierValueIndex READ extraIdentifierValueIndex NOTIFY extraIdentifierValueIndexChanged )
52+
53+
public:
54+
enum Role
55+
{
56+
IdentifierValueRole = Qt::UserRole,
57+
ValueRole
58+
};
59+
60+
QgsFeatureFilterModel( QObject *parent = nullptr );
61+
~QgsFeatureFilterModel();
62+
63+
QgsVectorLayer *sourceLayer() const;
64+
void setSourceLayer( QgsVectorLayer *sourceLayer );
65+
66+
QString displayExpression() const;
67+
void setDisplayExpression( const QString &displayExpression );
68+
69+
QString filterValue() const;
70+
void setFilterValue( const QString &filterValue );
71+
72+
virtual QModelIndex index( int row, int column, const QModelIndex &parent ) const override;
73+
virtual QModelIndex parent( const QModelIndex &child ) const override;
74+
virtual int rowCount( const QModelIndex &parent ) const override;
75+
virtual int columnCount( const QModelIndex &parent ) const override;
76+
virtual QVariant data( const QModelIndex &index, int role ) const override;
77+
78+
/**
79+
* An additional filter expression to apply, next to the filterValue.
80+
* Can be used for spatial filtering etc.
81+
*/
82+
QString filterExpression() const;
83+
84+
/**
85+
* An additional filter expression to apply, next to the filterValue.
86+
* Can be used for spatial filtering etc.
87+
*/
88+
void setFilterExpression( const QString &filterExpression );
89+
90+
bool isLoading() const;
91+
92+
QString identifierField() const;
93+
void setIdentifierField( const QString &identifierField );
94+
95+
QVariant extraIdentifierValue() const;
96+
void setExtraIdentifierValue( const QVariant &extraIdentifierValue );
97+
98+
int extraIdentifierValueIndex() const;
99+
100+
bool extraValueDoesNotExist() const;
101+
102+
signals:
103+
void sourceLayerChanged();
104+
void displayExpressionChanged();
105+
void filterValueChanged();
106+
void filterExpressionChanged();
107+
void isLoadingChanged();
108+
void identifierFieldChanged();
109+
void filterJobCompleted();
110+
void extraIdentifierValueChanged();
111+
void extraIdentifierValueIndexChanged( int index );
112+
void extraValueDoesNotExistChanged();
113+
void beginUpdate();
114+
void endUpdate();
115+
116+
private slots:
117+
void updateCompleter();
118+
void gathererThreadFinished();
119+
void scheduledReload();
120+
121+
private:
122+
void setExtraIdentifierValueIndex( int index );
123+
void setExtraValueDoesNotExist( bool extraValueDoesNotExist );
124+
void reload();
125+
void reloadCurrentFeature();
126+
void setExtraIdentifierValueUnguarded( const QVariant &extraIdentifierValue );
127+
struct Entry
128+
{
129+
Entry()
130+
{}
131+
132+
Entry( QVariant _identifierValue, const QString &_value )
133+
: identifierValue( _identifierValue )
134+
, value( _value )
135+
{}
136+
137+
QVariant identifierValue;
138+
QString value;
139+
140+
bool operator()( const Entry &lhs, const Entry &rhs ) const;
141+
};
142+
143+
QgsVectorLayer *mSourceLayer;
144+
QString mDisplayExpression;
145+
QString mFilterValue;
146+
QString mFilterExpression;
147+
148+
QVector<Entry> mEntries;
149+
QgsFieldExpressionValuesGatherer *mGatherer = nullptr;
150+
QTimer mReloadTimer;
151+
bool mShouldReloadCurrentFeature;
152+
bool mExtraValueDoesNotExist = false;
153+
154+
QString mIdentifierField;
155+
156+
QVariant mExtraIdentifierValue;
157+
158+
int mExtraIdentifierValueIndex = -1;
159+
160+
friend class QgsFieldExpressionValuesGatherer;
161+
};
162+
163+
#endif // QGSFEATUREFILTERMODEL_H

‎src/core/qgsfeaturefiltermodel_p.h

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/***************************************************************************
2+
qgsfeaturefiltermodel_p - QgsFieldExpressionValuesGatherer
3+
4+
---------------------
5+
begin : 10.3.2017
6+
copyright : (C) 2017 by Matthias Kuhn
7+
email : matthias@opengis.ch
8+
***************************************************************************
9+
* *
10+
* This program is free software; you can redistribute it and/or modify *
11+
* it under the terms of the GNU General Public License as published by *
12+
* the Free Software Foundation; either version 2 of the License, or *
13+
* (at your option) any later version. *
14+
* *
15+
***************************************************************************/
16+
#ifndef QGSFEATUREFILTERMODEL_P_H
17+
#define QGSFEATUREFILTERMODEL_P_H
18+
19+
#include <QThread>
20+
#include "qgsvectorlayer.h"
21+
#include "qgsfeaturefiltermodel.h"
22+
#include "qgslogger.h"
23+
#include "qgsvectorlayerfeatureiterator.h"
24+
25+
#define SIP_NO_FILE
26+
27+
// just internal guff - definitely not for exposing to public API!
28+
///@cond PRIVATE
29+
30+
/**
31+
* \class QgsFieldExpressionValuesGatherer
32+
* Gathers features with substring matching on an expression.
33+
*
34+
* \since QGIS 3.0
35+
*/
36+
class QgsFieldExpressionValuesGatherer: public QThread
37+
{
38+
Q_OBJECT
39+
40+
public:
41+
QgsFieldExpressionValuesGatherer( QgsVectorLayer *layer, const QString &displayExpression, const QString &identifierField, const QgsFeatureRequest &request = QgsFeatureRequest() )
42+
: mSource( new QgsVectorLayerFeatureSource( layer ) )
43+
, mDisplayExpression( displayExpression )
44+
, mRequest( request )
45+
, mWasCanceled( false )
46+
, mIdentifierField( identifierField )
47+
{
48+
}
49+
50+
~QgsFieldExpressionValuesGatherer()
51+
{
52+
}
53+
54+
virtual void run() override
55+
{
56+
mWasCanceled = false;
57+
58+
mIterator = mSource->getFeatures( mRequest );
59+
60+
QgsDebugMsg( QStringLiteral( "New gatherer: %1" ).arg( mRequest.filterExpression()->expression() ) );
61+
mDisplayExpression.prepare( &mExpressionContext );
62+
63+
QgsFeature feat;
64+
int attribute = mSource->fields().indexOf( mIdentifierField );
65+
66+
while ( mIterator.nextFeature( feat ) )
67+
{
68+
mExpressionContext.setFeature( feat );
69+
mEntries.append( QgsFeatureFilterModel::Entry( feat.attribute( attribute ), mDisplayExpression.evaluate( &mExpressionContext ).toString() ) );
70+
71+
if ( mWasCanceled )
72+
return;
73+
}
74+
75+
emit collectedValues();
76+
}
77+
78+
//! Informs the gatherer to immediately stop collecting values
79+
void stop()
80+
{
81+
mWasCanceled = true;
82+
}
83+
84+
//! Returns true if collection was canceled before completion
85+
bool wasCanceled() const { return mWasCanceled; }
86+
87+
QVector<QgsFeatureFilterModel::Entry> entries() const
88+
{
89+
return mEntries;
90+
}
91+
92+
QgsFeatureRequest request() const
93+
{
94+
return mRequest;
95+
}
96+
97+
/**
98+
* Internal data, use for whatever you want. Defaults to -1.
99+
*/
100+
QVariant data() const;
101+
102+
/**
103+
* Internal data, use for whatever you want. Defaults to -1.
104+
*/
105+
void setData( const QVariant &data ); // TODO: Do we still need this???
106+
107+
signals:
108+
109+
/**
110+
* Emitted when values have been collected
111+
* @param values list of unique matching string values
112+
*/
113+
void collectedValues();
114+
115+
private:
116+
117+
std::unique_ptr<QgsVectorLayerFeatureSource> mSource;
118+
QgsExpression mDisplayExpression;
119+
QgsExpressionContext mExpressionContext;
120+
QgsFeatureRequest mRequest;
121+
QgsFeatureIterator mIterator;
122+
bool mWasCanceled;
123+
QVector<QgsFeatureFilterModel::Entry> mEntries;
124+
QString mIdentifierField;
125+
QVariant mData;
126+
};
127+
128+
///@endcond
129+
130+
131+
#endif // QGSFEATUREFILTERMODEL_P_H

‎src/gui/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ SET(QGIS_GUI_SRCS
241241
qgsfeatureselectiondlg.cpp
242242
qgsfieldcombobox.cpp
243243
qgsfieldexpressionwidget.cpp
244+
qgsfeaturelistcombobox.cpp
244245
qgsfieldvalidator.cpp
245246
qgsfieldvalueslineedit.cpp
246247
qgsfilewidget.cpp
@@ -412,6 +413,7 @@ SET(QGIS_GUI_MOC_HDRS
412413
qgsfeatureselectiondlg.h
413414
qgsfieldcombobox.h
414415
qgsfieldexpressionwidget.h
416+
qgsfeaturelistcombobox.h
415417
qgsfieldvalidator.h
416418
qgsfieldvalueslineedit.h
417419
qgsfilewidget.h

‎src/gui/qgsfeaturelistcombobox.cpp

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
/***************************************************************************
2+
qgsfieldlistcombobox.cpp - QgsFieldListComboBox
3+
4+
---------------------
5+
begin : 10.3.2017
6+
copyright : (C) 2017 by Matthias Kuhn
7+
email : matthias@opengis.ch
8+
***************************************************************************
9+
* *
10+
* This program is free software; you can redistribute it and/or modify *
11+
* it under the terms of the GNU General Public License as published by *
12+
* the Free Software Foundation; either version 2 of the License, or *
13+
* (at your option) any later version. *
14+
* *
15+
***************************************************************************/
16+
#include "qgsfeaturelistcombobox.h"
17+
18+
#include "qgsfeaturefiltermodel.h"
19+
#include "qgsanimatedicon.h"
20+
#include "qgsfilterlineedit.h"
21+
#include "qgslogger.h"
22+
23+
#include <QCompleter>
24+
#include <QLineEdit>
25+
#include <QKeyEvent>
26+
27+
QgsFeatureListComboBox::QgsFeatureListComboBox( QWidget *parent )
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 );
36+
connect( mModel, &QgsFeatureFilterModel::sourceLayerChanged, this, &QgsFeatureListComboBox::sourceLayerChanged );
37+
connect( mModel, &QgsFeatureFilterModel::displayExpressionChanged, this, &QgsFeatureListComboBox::displayExpressionChanged );
38+
connect( mModel, &QgsFeatureFilterModel::filterExpressionChanged, this, &QgsFeatureListComboBox::filterExpressionChanged );
39+
connect( mModel, &QgsFeatureFilterModel::isLoadingChanged, this, &QgsFeatureListComboBox::onLoadingChanged );
40+
connect( mModel, &QgsFeatureFilterModel::filterJobCompleted, this, &QgsFeatureListComboBox::onFilterUpdateCompleted );
41+
connect( mModel, &QgsFeatureFilterModel::extraIdentifierValueChanged, this, &QgsFeatureListComboBox::identifierValueChanged );
42+
connect( mModel, &QgsFeatureFilterModel::extraIdentifierValueIndexChanged, this, &QgsFeatureListComboBox::setCurrentIndex );
43+
connect( mModel, &QgsFeatureFilterModel::identifierFieldChanged, this, &QgsFeatureListComboBox::identifierFieldChanged );
44+
connect( mCompleter, static_cast<void( QCompleter::* )( const QModelIndex & )>( &QCompleter::highlighted ), this, &QgsFeatureListComboBox::onItemSelected );
45+
connect( mCompleter, static_cast<void( QCompleter::* )( const QModelIndex & )>( &QCompleter::activated ), this, &QgsFeatureListComboBox::onActivated );
46+
connect( mModel, &QgsFeatureFilterModel::beginUpdate, this, &QgsFeatureListComboBox::storeLineEditState );
47+
connect( mModel, &QgsFeatureFilterModel::endUpdate, this, &QgsFeatureListComboBox::restoreLineEditState );
48+
49+
connect( this, static_cast<void( QgsFeatureListComboBox::* )( int )>( &QgsFeatureListComboBox::currentIndexChanged ), this, &QgsFeatureListComboBox::onCurrentIndexChanged );
50+
51+
mLineEdit = new QgsFilterLineEdit();
52+
setEditable( true );
53+
setLineEdit( mLineEdit );
54+
setModel( mModel );
55+
56+
connect( mLineEdit, &QgsFilterLineEdit::textEdited, this, &QgsFeatureListComboBox::onCurrentTextChanged );
57+
58+
connect( mLineEdit, &QgsFilterLineEdit::textChanged, this, []( const QString & text )
59+
{
60+
QgsDebugMsg( QStringLiteral( "Edit text changed to %1" ).arg( text ) );
61+
} );
62+
}
63+
64+
QgsVectorLayer *QgsFeatureListComboBox::sourceLayer() const
65+
{
66+
return mModel->sourceLayer();
67+
}
68+
69+
void QgsFeatureListComboBox::setSourceLayer( QgsVectorLayer *sourceLayer )
70+
{
71+
mModel->setSourceLayer( sourceLayer );
72+
}
73+
74+
QString QgsFeatureListComboBox::displayExpression() const
75+
{
76+
return mModel->displayExpression();
77+
}
78+
79+
void QgsFeatureListComboBox::setDisplayExpression( const QString &expression )
80+
{
81+
mModel->setDisplayExpression( expression );
82+
}
83+
84+
void QgsFeatureListComboBox::onCurrentTextChanged( const QString &text )
85+
{
86+
mPopupRequested = true;
87+
mModel->setFilterValue( text );
88+
}
89+
90+
void QgsFeatureListComboBox::onFilterUpdateCompleted()
91+
{
92+
if ( mPopupRequested )
93+
mCompleter->complete();
94+
95+
mPopupRequested = false;
96+
}
97+
98+
void QgsFeatureListComboBox::onLoadingChanged()
99+
{
100+
mLineEdit->setShowSpinner( mModel->isLoading() );
101+
}
102+
103+
void QgsFeatureListComboBox::onItemSelected( const QModelIndex &index )
104+
{
105+
setCurrentIndex( index.row() );
106+
}
107+
108+
void QgsFeatureListComboBox::onCurrentIndexChanged( int i )
109+
{
110+
QModelIndex modelIndex = mModel->index( i, 0, QModelIndex() );
111+
mModel->setExtraIdentifierValue( mModel->data( modelIndex, QgsFeatureFilterModel::IdentifierValueRole ) );
112+
mLineEdit->setText( mModel->data( modelIndex, QgsFeatureFilterModel::ValueRole ).toString() );
113+
}
114+
115+
void QgsFeatureListComboBox::onActivated( QModelIndex modelIndex )
116+
{
117+
setIdentifierValue( mModel->data( modelIndex, QgsFeatureFilterModel::IdentifierValueRole ) );
118+
QgsDebugMsg( QStringLiteral( "Activated index" ) );
119+
QgsDebugMsg( QStringLiteral( "%1 %2" ).arg( QString::number( modelIndex.row() ), mModel->data( modelIndex, QgsFeatureFilterModel::ValueRole ).toString() ) );
120+
mLineEdit->setText( mModel->data( modelIndex, QgsFeatureFilterModel::ValueRole ).toString() );
121+
}
122+
123+
void QgsFeatureListComboBox::storeLineEditState()
124+
{
125+
mLineEditState.store( mLineEdit );
126+
}
127+
128+
void QgsFeatureListComboBox::restoreLineEditState()
129+
{
130+
mLineEditState.restore( mLineEdit );
131+
}
132+
133+
QString QgsFeatureListComboBox::identifierField() const
134+
{
135+
return mModel->identifierField();
136+
}
137+
138+
void QgsFeatureListComboBox::setIdentifierField( const QString &identifierField )
139+
{
140+
mModel->setIdentifierField( identifierField );
141+
}
142+
143+
QModelIndex QgsFeatureListComboBox::currentModelIndex() const
144+
{
145+
return mModel->index( mModel->extraIdentifierValueIndex(), 0, QModelIndex() );
146+
}
147+
148+
void QgsFeatureListComboBox::focusOutEvent( QFocusEvent *event )
149+
{
150+
Q_UNUSED( event )
151+
QComboBox::focusOutEvent( event );
152+
mLineEdit->setText( mModel->data( currentModelIndex(), QgsFeatureFilterModel::ValueRole ).toString() );
153+
}
154+
155+
void QgsFeatureListComboBox::keyPressEvent( QKeyEvent *event )
156+
{
157+
if ( event->key() == Qt::Key_Escape )
158+
{
159+
mLineEdit->setText( mModel->data( currentModelIndex(), QgsFeatureFilterModel::ValueRole ).toString() );
160+
}
161+
QComboBox::keyReleaseEvent( event );
162+
}
163+
164+
bool QgsFeatureListComboBox::allowNull() const
165+
{
166+
return mAllowNull;
167+
}
168+
169+
void QgsFeatureListComboBox::setAllowNull( bool allowNull )
170+
{
171+
if ( mAllowNull == allowNull )
172+
return;
173+
174+
mAllowNull = allowNull;
175+
emit allowNullChanged();
176+
}
177+
178+
QVariant QgsFeatureListComboBox::identifierValue() const
179+
{
180+
return mModel->extraIdentifierValue();
181+
}
182+
183+
void QgsFeatureListComboBox::setIdentifierValue( const QVariant &identifierValue )
184+
{
185+
mModel->setExtraIdentifierValue( identifierValue );
186+
}
187+
188+
QgsFeatureRequest QgsFeatureListComboBox::currentFeatureRequest() const
189+
{
190+
return QgsFeatureRequest().setFilterExpression( QStringLiteral( "%1 = %2" ).arg( QgsExpression::quotedColumnRef( mModel->identifierField() ), QgsExpression::quotedValue( mModel->extraIdentifierValue() ) ) );
191+
}
192+
193+
QString QgsFeatureListComboBox::filterExpression() const
194+
{
195+
return mModel->filterExpression();
196+
}
197+
198+
void QgsFeatureListComboBox::setFilterExpression( const QString &filterExpression )
199+
{
200+
mModel->setFilterExpression( filterExpression );
201+
}
202+
203+
void QgsFeatureListComboBox::LineEditState::store( QLineEdit *lineEdit )
204+
{
205+
text = lineEdit->text();
206+
selectionStart = lineEdit->selectionStart();
207+
selectionLength = lineEdit->selectedText().length();
208+
cursorPosition = lineEdit->cursorPosition();
209+
210+
}
211+
212+
void QgsFeatureListComboBox::LineEditState::restore( QLineEdit *lineEdit ) const
213+
{
214+
lineEdit->setText( text );
215+
lineEdit->setCursorPosition( cursorPosition );
216+
lineEdit->setSelection( selectionStart, selectionLength );
217+
}

‎src/gui/qgsfeaturelistcombobox.h

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/***************************************************************************
2+
qgsfieldlistcombobox.h - QgsFieldListComboBox
3+
4+
---------------------
5+
begin : 10.3.2017
6+
copyright : (C) 2017 by Matthias Kuhn
7+
email : matthias@opengis.ch
8+
***************************************************************************
9+
* *
10+
* This program is free software; you can redistribute it and/or modify *
11+
* it under the terms of the GNU General Public License as published by *
12+
* the Free Software Foundation; either version 2 of the License, or *
13+
* (at your option) any later version. *
14+
* *
15+
***************************************************************************/
16+
#ifndef QGSFIELDLISTCOMBOBOX_H
17+
#define QGSFIELDLISTCOMBOBOX_H
18+
19+
#include <QComboBox>
20+
21+
#include "qgsfeature.h"
22+
#include "qgsfeaturerequest.h"
23+
#include "qgis_gui.h"
24+
25+
class QgsVectorLayer;
26+
class QgsFeatureFilterModel;
27+
class QgsAnimatedIcon;
28+
class QgsFilterLineEdit;
29+
30+
class GUI_EXPORT QgsFeatureListComboBox : public QComboBox
31+
{
32+
Q_OBJECT
33+
34+
Q_PROPERTY( QgsVectorLayer *sourceLayer READ sourceLayer WRITE setSourceLayer NOTIFY sourceLayerChanged )
35+
Q_PROPERTY( QString displayExpression READ displayExpression WRITE setDisplayExpression NOTIFY displayExpressionChanged )
36+
Q_PROPERTY( QString filterExpression READ filterExpression WRITE setFilterExpression NOTIFY filterExpressionChanged )
37+
Q_PROPERTY( QVariant identifierValue READ identifierValue WRITE setIdentifierValue NOTIFY identifierValueChanged )
38+
Q_PROPERTY( QString identifierField READ identifierField WRITE setIdentifierField NOTIFY identifierFieldChanged )
39+
Q_PROPERTY( bool allowNull READ allowNull WRITE setAllowNull NOTIFY allowNullChanged )
40+
41+
public:
42+
QgsFeatureListComboBox( QWidget *parent = nullptr );
43+
44+
QgsVectorLayer *sourceLayer() const;
45+
void setSourceLayer( QgsVectorLayer *sourceLayer );
46+
47+
QString displayExpression() const;
48+
void setDisplayExpression( const QString &displayExpression );
49+
50+
QString filterExpression() const;
51+
void setFilterExpression( const QString &filterExpression );
52+
53+
QVariant identifierValue() const;
54+
void setIdentifierValue( const QVariant &identifierValue );
55+
56+
QgsFeatureRequest currentFeatureRequest() const;
57+
58+
bool allowNull() const;
59+
void setAllowNull( bool allowNull );
60+
61+
QString identifierField() const;
62+
void setIdentifierField( const QString &identifierField );
63+
64+
QModelIndex currentModelIndex() const;
65+
66+
virtual void focusOutEvent( QFocusEvent *event ) override;
67+
68+
virtual void keyPressEvent( QKeyEvent *event ) override;
69+
70+
signals:
71+
void sourceLayerChanged();
72+
void displayExpressionChanged();
73+
void filterExpressionChanged();
74+
void identifierValueChanged();
75+
void identifierFieldChanged();
76+
void allowNullChanged();
77+
78+
private slots:
79+
void onCurrentTextChanged( const QString &text );
80+
void onFilterUpdateCompleted();
81+
void onLoadingChanged();
82+
void onItemSelected( const QModelIndex &index );
83+
void onCurrentIndexChanged( int i );
84+
void onActivated( QModelIndex index );
85+
void storeLineEditState();
86+
void restoreLineEditState();
87+
88+
private:
89+
struct LineEditState
90+
{
91+
void store( QLineEdit *lineEdit );
92+
void restore( QLineEdit *lineEdit ) const;
93+
94+
QString text;
95+
int selectionStart;
96+
int selectionLength;
97+
int cursorPosition;
98+
};
99+
100+
QgsFeatureFilterModel *mModel;
101+
QCompleter *mCompleter;
102+
QString mDisplayExpression;
103+
QgsFilterLineEdit *mLineEdit;
104+
bool mAllowNull = true;
105+
bool mPopupRequested = false;
106+
bool mIsCurrentlyEdited = false;
107+
LineEditState mLineEditState;
108+
};
109+
110+
#endif // QGSFIELDLISTCOMBOBOX_H

‎src/gui/qgsfeaturelistcombobox_p.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/***************************************************************************
2+
qgsfeaturelistcombobox_p.h
3+
4+
---------------------
5+
begin : 24.10.2017
6+
copyright : (C) 2017 by Matthias Kuhn
7+
email : matthias@opengis.ch
8+
***************************************************************************
9+
* *
10+
* This program is free software; you can redistribute it and/or modify *
11+
* it under the terms of the GNU General Public License as published by *
12+
* the Free Software Foundation; either version 2 of the License, or *
13+
* (at your option) any later version. *
14+
* *
15+
***************************************************************************/
16+
#ifndef QGSFEATURELISTCOMBOBOX_P_H
17+
#define QGSFEATURELISTCOMBOBOX_P_H
18+
19+
#endif // QGSFEATURELISTCOMBOBOX_P_H

0 commit comments

Comments
 (0)
Please sign in to comment.