Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[FEATURE] Show field values in autocompleter in form filter mode
This adds a new gui widget QgsFieldValuesLineEdit which includes an autocompleter populated with current field values. The autocompleter is nicely updated in the background so that the gui remains nice and responsive, even if there's millions of records in the associated table. It's now used as a search widget for text fields, so can be seen in the browser window if you set the filter to a text field, or if you launch the form based select/filter by selecting a layer and pressing F3.
- Loading branch information
1 parent
e27822b
commit 0bfc9bb
Showing
7 changed files
with
430 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/** \class QgsFieldValuesLineEdit | ||
* \ingroup gui | ||
* A line edit with an autocompleter which takes unique values from a vector layer's fields. | ||
* The autocompleter is populated from the vector layer in the background to ensure responsive | ||
* interaction with the widget. | ||
* \note added in QGIS 3.0 | ||
*/ | ||
class QgsFieldValuesLineEdit: QgsFilterLineEdit | ||
{ | ||
%TypeHeaderCode | ||
#include <qgsfieldvalueslineedit.h> | ||
%End | ||
public: | ||
|
||
/** Constructor for QgsFieldValuesLineEdit | ||
* @param parent parent widget | ||
*/ | ||
QgsFieldValuesLineEdit( QWidget *parent /TransferThis/ = nullptr ); | ||
|
||
virtual ~QgsFieldValuesLineEdit(); | ||
|
||
/** Sets the layer containing the field that values will be shown from. | ||
* @param layer vector layer | ||
* @see layer() | ||
* @see setAttributeIndex() | ||
*/ | ||
void setLayer( QgsVectorLayer* layer ); | ||
|
||
/** Returns the layer containing the field that values will be shown from. | ||
* @see setLayer() | ||
* @see attributeIndex() | ||
*/ | ||
QgsVectorLayer* layer() const; | ||
|
||
/** Sets the attribute index for the field containing values to show in the widget. | ||
* @param index index of attribute | ||
* @see attributeIndex() | ||
* @see setLayer() | ||
*/ | ||
void setAttributeIndex( int index ); | ||
|
||
/** Returns the attribute index for the field containing values shown in the widget. | ||
* @see setAttributeIndex() | ||
* @see layer() | ||
*/ | ||
int attributeIndex() const; | ||
|
||
signals: | ||
|
||
/** Emitted when the layer associated with the widget changes. | ||
* @param layer vector layer | ||
*/ | ||
void layerChanged( QgsVectorLayer* layer ); | ||
|
||
/** Emitted when the field associated with the widget changes. | ||
* @param index new attribute index for field | ||
*/ | ||
void attributeIndexChanged( int index ); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
/*************************************************************************** | ||
qgsfieldvalueslineedit.cpp | ||
------------------------- | ||
Date : 20-08-2016 | ||
Copyright : (C) 2016 by Nyall Dawson | ||
Email : nyall dot dawson at gmail dot com | ||
*************************************************************************** | ||
* * | ||
* This program is free software; you can redistribute it and/or modify * | ||
* it under the terms of the GNU General Public License as published by * | ||
* the Free Software Foundation; either version 2 of the License, or * | ||
* (at your option) any later version. * | ||
* * | ||
***************************************************************************/ | ||
|
||
#include "qgsfieldvalueslineedit.h" | ||
#include "qgsvectorlayer.h" | ||
#include "qgsfloatingwidget.h" | ||
#include <QCompleter> | ||
#include <QStringListModel> | ||
#include <QTimer> | ||
#include <QHBoxLayout> | ||
|
||
QgsFieldValuesLineEdit::QgsFieldValuesLineEdit( QWidget *parent ) | ||
: QgsFilterLineEdit( parent ) | ||
, mLayer( nullptr ) | ||
, mAttributeIndex( -1 ) | ||
, mUpdateRequested( false ) | ||
, mGatherer( nullptr ) | ||
{ | ||
QCompleter* c = new QCompleter( this ); | ||
c->setCaseSensitivity( Qt::CaseInsensitive ); | ||
c->setFilterMode( Qt::MatchContains ); | ||
setCompleter( c ); | ||
connect( this, &QgsFieldValuesLineEdit::textEdited, this, &QgsFieldValuesLineEdit::requestCompleterUpdate ); | ||
mShowPopupTimer.setSingleShot( true ); | ||
mShowPopupTimer.setInterval( 100 ); | ||
connect( &mShowPopupTimer, &QTimer::timeout, this, &QgsFieldValuesLineEdit::triggerCompleterUpdate ); | ||
} | ||
|
||
QgsFieldValuesLineEdit::~QgsFieldValuesLineEdit() | ||
{ | ||
if ( mGatherer ) | ||
{ | ||
mGatherer->stop(); | ||
mGatherer->wait(); // mGatherer is deleted when wait completes | ||
} | ||
} | ||
|
||
void QgsFieldValuesLineEdit::setLayer( QgsVectorLayer* layer ) | ||
{ | ||
if ( mLayer == layer ) | ||
return; | ||
|
||
mLayer = layer; | ||
emit layerChanged( layer ); | ||
} | ||
|
||
void QgsFieldValuesLineEdit::setAttributeIndex( int index ) | ||
{ | ||
if ( mAttributeIndex == index ) | ||
return; | ||
|
||
mAttributeIndex = index; | ||
emit attributeIndexChanged( index ); | ||
} | ||
|
||
void QgsFieldValuesLineEdit::requestCompleterUpdate() | ||
{ | ||
mUpdateRequested = true; | ||
mShowPopupTimer.start(); | ||
} | ||
|
||
void QgsFieldValuesLineEdit::triggerCompleterUpdate() | ||
{ | ||
mShowPopupTimer.stop(); | ||
QString currentText = text(); | ||
|
||
if ( currentText.isEmpty() ) | ||
{ | ||
if ( mGatherer ) | ||
mGatherer->stop(); | ||
return; | ||
} | ||
|
||
updateCompletionList( currentText ); | ||
} | ||
|
||
void QgsFieldValuesLineEdit::updateCompletionList( const QString &text ) | ||
{ | ||
if ( text.isEmpty() ) | ||
{ | ||
if ( mGatherer ) | ||
mGatherer->stop(); | ||
return; | ||
} | ||
|
||
mUpdateRequested = true; | ||
if ( mGatherer ) | ||
{ | ||
mRequestedCompletionText = text; | ||
mGatherer->stop(); | ||
return; | ||
} | ||
|
||
mGatherer = new QgsFieldValuesLineEditValuesGatherer( mLayer, mAttributeIndex ); | ||
mGatherer->setSubstring( text ); | ||
|
||
connect( mGatherer, &QgsFieldValuesLineEditValuesGatherer::collectedValues, this, &QgsFieldValuesLineEdit::updateCompleter ); | ||
connect( mGatherer, &QgsFieldValuesLineEditValuesGatherer::finished, this, &QgsFieldValuesLineEdit::gathererThreadFinished ); | ||
|
||
mGatherer->start(); | ||
} | ||
|
||
void QgsFieldValuesLineEdit::gathererThreadFinished() | ||
{ | ||
bool wasCancelled = mGatherer->wasCancelled(); | ||
|
||
delete mGatherer; | ||
mGatherer = nullptr; | ||
|
||
if ( wasCancelled ) | ||
{ | ||
QString text = mRequestedCompletionText; | ||
mRequestedCompletionText.clear(); | ||
updateCompletionList( text ); | ||
return; | ||
} | ||
} | ||
|
||
void QgsFieldValuesLineEdit::updateCompleter( const QStringList& values ) | ||
{ | ||
mUpdateRequested = false; | ||
completer()->setModel( new QStringListModel( values ) ); | ||
completer()->complete(); | ||
} | ||
|
Oops, something went wrong.
0bfc9bb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!!
I wonder if it could be made to support also expressions. For the RelationReference widget, this would be very handy to look for a feature preview text (expression) that contains some string.
Let's say you reference a person and show
CONCAT( "name", ' ,', "street", ' ,', "city" )
and it would just match on any part of the expression (possibly compiled and serverside). I guess that would need to be implemented as another Gatherer?0bfc9bb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nyalldawson fantastic job.
What about making the search by form filter action the one shown by default in the layer toolbar? Right now, it's the search by expression, which isn't as user friendly as the form filter.
0bfc9bb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nirvn sounds sensible - I'll +1 a PR if you open it. I'm on email only at the moment so can't make one myself.
0bfc9bb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@m-kuhn yes a new gatherer would be the correct approach. I'm wondering what the use case is here though? Are you just wanting to take advantage of the background loading for a combo box?
0bfc9bb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.