mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-15 00:04:00 -04:00
[FEATURE] Add QgsFeatureListComboBox with live-filter-capabilities
This commit is contained in:
parent
d40447e7d0
commit
7811f38e48
@ -311,6 +311,7 @@
|
|||||||
%Include qgsfieldmodel.sip
|
%Include qgsfieldmodel.sip
|
||||||
%Include qgsfieldproxymodel.sip
|
%Include qgsfieldproxymodel.sip
|
||||||
%Include qgsfiledownloader.sip
|
%Include qgsfiledownloader.sip
|
||||||
|
%Include qgsfeaturefiltermodel.sip
|
||||||
%Include qgsgeometryvalidator.sip
|
%Include qgsgeometryvalidator.sip
|
||||||
%Include qgsgml.sip
|
%Include qgsgml.sip
|
||||||
%Include qgsgmlschema.sip
|
%Include qgsgmlschema.sip
|
||||||
|
118
python/core/qgsfeaturefiltermodel.sip
Normal file
118
python/core/qgsfeaturefiltermodel.sip
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/************************************************************************
|
||||||
|
* This file has been generated automatically from *
|
||||||
|
* *
|
||||||
|
* src/core/qgsfeaturefiltermodel.h *
|
||||||
|
* *
|
||||||
|
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||||
|
************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class QgsFeatureFilterModel : QAbstractItemModel
|
||||||
|
{
|
||||||
|
%Docstring
|
||||||
|
Provides a list of features based on filter conditions.
|
||||||
|
Features are fetched asynchronously.
|
||||||
|
%End
|
||||||
|
|
||||||
|
%TypeHeaderCode
|
||||||
|
#include "qgsfeaturefiltermodel.h"
|
||||||
|
%End
|
||||||
|
public:
|
||||||
|
enum Role
|
||||||
|
{
|
||||||
|
IdentifierValueRole,
|
||||||
|
ValueRole
|
||||||
|
};
|
||||||
|
|
||||||
|
QgsFeatureFilterModel( QObject *parent = 0 );
|
||||||
|
~QgsFeatureFilterModel();
|
||||||
|
|
||||||
|
QgsVectorLayer *sourceLayer() const;
|
||||||
|
%Docstring
|
||||||
|
:rtype: QgsVectorLayer
|
||||||
|
%End
|
||||||
|
void setSourceLayer( QgsVectorLayer *sourceLayer );
|
||||||
|
|
||||||
|
QString displayExpression() const;
|
||||||
|
%Docstring
|
||||||
|
:rtype: str
|
||||||
|
%End
|
||||||
|
void setDisplayExpression( const QString &displayExpression );
|
||||||
|
|
||||||
|
QString filterValue() const;
|
||||||
|
%Docstring
|
||||||
|
:rtype: str
|
||||||
|
%End
|
||||||
|
void setFilterValue( const QString &filterValue );
|
||||||
|
|
||||||
|
virtual QModelIndex index( int row, int column, const QModelIndex &parent ) const;
|
||||||
|
virtual QModelIndex parent( const QModelIndex &child ) const;
|
||||||
|
virtual int rowCount( const QModelIndex &parent ) const;
|
||||||
|
virtual int columnCount( const QModelIndex &parent ) const;
|
||||||
|
virtual QVariant data( const QModelIndex &index, int role ) const;
|
||||||
|
|
||||||
|
QString filterExpression() const;
|
||||||
|
%Docstring
|
||||||
|
An additional filter expression to apply, next to the filterValue.
|
||||||
|
Can be used for spatial filtering etc.
|
||||||
|
:rtype: str
|
||||||
|
%End
|
||||||
|
|
||||||
|
void setFilterExpression( const QString &filterExpression );
|
||||||
|
%Docstring
|
||||||
|
An additional filter expression to apply, next to the filterValue.
|
||||||
|
Can be used for spatial filtering etc.
|
||||||
|
%End
|
||||||
|
|
||||||
|
bool isLoading() const;
|
||||||
|
%Docstring
|
||||||
|
:rtype: bool
|
||||||
|
%End
|
||||||
|
|
||||||
|
QString identifierField() const;
|
||||||
|
%Docstring
|
||||||
|
:rtype: str
|
||||||
|
%End
|
||||||
|
void setIdentifierField( const QString &identifierField );
|
||||||
|
|
||||||
|
QVariant extraIdentifierValue() const;
|
||||||
|
%Docstring
|
||||||
|
:rtype: QVariant
|
||||||
|
%End
|
||||||
|
void setExtraIdentifierValue( const QVariant &extraIdentifierValue );
|
||||||
|
|
||||||
|
int extraIdentifierValueIndex() const;
|
||||||
|
%Docstring
|
||||||
|
:rtype: int
|
||||||
|
%End
|
||||||
|
|
||||||
|
bool extraValueDoesNotExist() const;
|
||||||
|
%Docstring
|
||||||
|
:rtype: bool
|
||||||
|
%End
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void sourceLayerChanged();
|
||||||
|
void displayExpressionChanged();
|
||||||
|
void filterValueChanged();
|
||||||
|
void filterExpressionChanged();
|
||||||
|
void isLoadingChanged();
|
||||||
|
void identifierFieldChanged();
|
||||||
|
void filterJobCompleted();
|
||||||
|
void extraIdentifierValueChanged();
|
||||||
|
void extraIdentifierValueIndexChanged( int index );
|
||||||
|
void extraValueDoesNotExistChanged();
|
||||||
|
void beginUpdate();
|
||||||
|
void endUpdate();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* This file has been generated automatically from *
|
||||||
|
* *
|
||||||
|
* src/core/qgsfeaturefiltermodel.h *
|
||||||
|
* *
|
||||||
|
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||||
|
************************************************************************/
|
@ -108,6 +108,7 @@
|
|||||||
%Include qgsfeatureselectiondlg.sip
|
%Include qgsfeatureselectiondlg.sip
|
||||||
%Include qgsfieldcombobox.sip
|
%Include qgsfieldcombobox.sip
|
||||||
%Include qgsfieldexpressionwidget.sip
|
%Include qgsfieldexpressionwidget.sip
|
||||||
|
%Include qgsfeaturelistcombobox.sip
|
||||||
%Include qgsfieldvalidator.sip
|
%Include qgsfieldvalidator.sip
|
||||||
%Include qgsfieldvalueslineedit.sip
|
%Include qgsfieldvalueslineedit.sip
|
||||||
%Include qgsfilewidget.sip
|
%Include qgsfilewidget.sip
|
||||||
|
104
python/gui/qgsfeaturelistcombobox.sip
Normal file
104
python/gui/qgsfeaturelistcombobox.sip
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/************************************************************************
|
||||||
|
* This file has been generated automatically from *
|
||||||
|
* *
|
||||||
|
* src/gui/qgsfeaturelistcombobox.h *
|
||||||
|
* *
|
||||||
|
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||||
|
************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class QgsFeatureListComboBox : QComboBox
|
||||||
|
{
|
||||||
|
%Docstring
|
||||||
|
*************************************************************************
|
||||||
|
qgsfieldlistcombobox.h - QgsFieldListComboBox
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
begin : 10.3.2017
|
||||||
|
copyright : (C) 2017 by Matthias Kuhn
|
||||||
|
email : matthias@opengis.ch
|
||||||
|
**************************************************************************
|
||||||
|
*
|
||||||
|
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. *
|
||||||
|
*
|
||||||
|
**************************************************************************
|
||||||
|
%End
|
||||||
|
|
||||||
|
%TypeHeaderCode
|
||||||
|
#include "qgsfeaturelistcombobox.h"
|
||||||
|
%End
|
||||||
|
public:
|
||||||
|
QgsFeatureListComboBox( QWidget *parent = 0 );
|
||||||
|
|
||||||
|
QgsVectorLayer *sourceLayer() const;
|
||||||
|
%Docstring
|
||||||
|
:rtype: QgsVectorLayer
|
||||||
|
%End
|
||||||
|
void setSourceLayer( QgsVectorLayer *sourceLayer );
|
||||||
|
|
||||||
|
QString displayExpression() const;
|
||||||
|
%Docstring
|
||||||
|
:rtype: str
|
||||||
|
%End
|
||||||
|
void setDisplayExpression( const QString &displayExpression );
|
||||||
|
|
||||||
|
QString filterExpression() const;
|
||||||
|
%Docstring
|
||||||
|
:rtype: str
|
||||||
|
%End
|
||||||
|
void setFilterExpression( const QString &filterExpression );
|
||||||
|
|
||||||
|
QVariant identifierValue() const;
|
||||||
|
%Docstring
|
||||||
|
:rtype: QVariant
|
||||||
|
%End
|
||||||
|
void setIdentifierValue( const QVariant &identifierValue );
|
||||||
|
|
||||||
|
QgsFeatureRequest currentFeatureRequest() const;
|
||||||
|
%Docstring
|
||||||
|
:rtype: QgsFeatureRequest
|
||||||
|
%End
|
||||||
|
|
||||||
|
bool allowNull() const;
|
||||||
|
%Docstring
|
||||||
|
:rtype: bool
|
||||||
|
%End
|
||||||
|
void setAllowNull( bool allowNull );
|
||||||
|
|
||||||
|
QString identifierField() const;
|
||||||
|
%Docstring
|
||||||
|
:rtype: str
|
||||||
|
%End
|
||||||
|
void setIdentifierField( const QString &identifierField );
|
||||||
|
|
||||||
|
QModelIndex currentModelIndex() const;
|
||||||
|
%Docstring
|
||||||
|
:rtype: QModelIndex
|
||||||
|
%End
|
||||||
|
|
||||||
|
virtual void focusOutEvent( QFocusEvent *event );
|
||||||
|
|
||||||
|
virtual void keyPressEvent( QKeyEvent *event );
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void sourceLayerChanged();
|
||||||
|
void displayExpressionChanged();
|
||||||
|
void filterExpressionChanged();
|
||||||
|
void identifierValueChanged();
|
||||||
|
void identifierFieldChanged();
|
||||||
|
void allowNullChanged();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/************************************************************************
|
||||||
|
* This file has been generated automatically from *
|
||||||
|
* *
|
||||||
|
* src/gui/qgsfeaturelistcombobox.h *
|
||||||
|
* *
|
||||||
|
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||||
|
************************************************************************/
|
@ -175,6 +175,7 @@ SET(QGIS_CORE_SRCS
|
|||||||
qgsfeaturesink.cpp
|
qgsfeaturesink.cpp
|
||||||
qgsfeaturesource.cpp
|
qgsfeaturesource.cpp
|
||||||
qgsfeaturestore.cpp
|
qgsfeaturestore.cpp
|
||||||
|
qgsfeaturefiltermodel.cpp
|
||||||
qgsfield.cpp
|
qgsfield.cpp
|
||||||
qgsfieldconstraints.cpp
|
qgsfieldconstraints.cpp
|
||||||
qgsfieldformatter.cpp
|
qgsfieldformatter.cpp
|
||||||
@ -590,6 +591,8 @@ SET(QGIS_CORE_MOC_HDRS
|
|||||||
qgsfieldmodel.h
|
qgsfieldmodel.h
|
||||||
qgsfieldproxymodel.h
|
qgsfieldproxymodel.h
|
||||||
qgsfiledownloader.h
|
qgsfiledownloader.h
|
||||||
|
qgsfeaturefiltermodel.h
|
||||||
|
qgsfeaturefiltermodel_p.h
|
||||||
qgsgeometryvalidator.h
|
qgsgeometryvalidator.h
|
||||||
qgsgml.h
|
qgsgml.h
|
||||||
qgsgmlschema.h
|
qgsgmlschema.h
|
||||||
|
412
src/core/qgsfeaturefiltermodel.cpp
Normal file
412
src/core/qgsfeaturefiltermodel.cpp
Normal file
@ -0,0 +1,412 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgsfeaturefiltermodel.cpp - QgsFeatureFilterModel
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
begin : 10.3.2017
|
||||||
|
copyright : (C) 2017 by Matthias Kuhn
|
||||||
|
email : matthias@opengis.ch
|
||||||
|
***************************************************************************
|
||||||
|
* *
|
||||||
|
* 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 "qgsfeaturefiltermodel.h"
|
||||||
|
#include "qgsfeaturefiltermodel_p.h"
|
||||||
|
|
||||||
|
QgsFeatureFilterModel::QgsFeatureFilterModel( QObject *parent )
|
||||||
|
: QAbstractItemModel( parent )
|
||||||
|
, mSourceLayer( nullptr )
|
||||||
|
{
|
||||||
|
mReloadTimer.setInterval( 100 );
|
||||||
|
mReloadTimer.setSingleShot( true );
|
||||||
|
connect( &mReloadTimer, &QTimer::timeout, this, &QgsFeatureFilterModel::scheduledReload );
|
||||||
|
setExtraIdentifierValueUnguarded( QVariant() );
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsFeatureFilterModel::~QgsFeatureFilterModel()
|
||||||
|
{
|
||||||
|
if ( mGatherer )
|
||||||
|
connect( mGatherer, &QgsFieldExpressionValuesGatherer::finished, mGatherer, &QgsFieldExpressionValuesGatherer::deleteLater );
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsVectorLayer *QgsFeatureFilterModel::sourceLayer() const
|
||||||
|
{
|
||||||
|
return mSourceLayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureFilterModel::setSourceLayer( QgsVectorLayer *sourceLayer )
|
||||||
|
{
|
||||||
|
if ( mSourceLayer == sourceLayer )
|
||||||
|
return;
|
||||||
|
|
||||||
|
mSourceLayer = sourceLayer;
|
||||||
|
reload();
|
||||||
|
emit sourceLayerChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QgsFeatureFilterModel::displayExpression() const
|
||||||
|
{
|
||||||
|
return mDisplayExpression;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureFilterModel::setDisplayExpression( const QString &displayExpression )
|
||||||
|
{
|
||||||
|
if ( mDisplayExpression == displayExpression )
|
||||||
|
return;
|
||||||
|
|
||||||
|
mDisplayExpression = displayExpression;
|
||||||
|
reload();
|
||||||
|
emit displayExpressionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QgsFeatureFilterModel::filterValue() const
|
||||||
|
{
|
||||||
|
return mFilterValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureFilterModel::setFilterValue( const QString &filterValue )
|
||||||
|
{
|
||||||
|
if ( mFilterValue == filterValue )
|
||||||
|
return;
|
||||||
|
|
||||||
|
mFilterValue = filterValue;
|
||||||
|
reload();
|
||||||
|
emit filterValueChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QgsFeatureFilterModel::filterExpression() const
|
||||||
|
{
|
||||||
|
return mFilterExpression;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureFilterModel::setFilterExpression( const QString &filterExpression )
|
||||||
|
{
|
||||||
|
if ( mFilterExpression == filterExpression )
|
||||||
|
return;
|
||||||
|
|
||||||
|
mFilterExpression = filterExpression;
|
||||||
|
reload();
|
||||||
|
emit filterExpressionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QgsFeatureFilterModel::isLoading() const
|
||||||
|
{
|
||||||
|
return mGatherer;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex QgsFeatureFilterModel::index( int row, int column, const QModelIndex &parent ) const
|
||||||
|
{
|
||||||
|
Q_UNUSED( parent )
|
||||||
|
return createIndex( row, column, nullptr );
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex QgsFeatureFilterModel::parent( const QModelIndex &child ) const
|
||||||
|
{
|
||||||
|
Q_UNUSED( child )
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
int QgsFeatureFilterModel::rowCount( const QModelIndex &parent ) const
|
||||||
|
{
|
||||||
|
Q_UNUSED( parent );
|
||||||
|
|
||||||
|
return mEntries.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int QgsFeatureFilterModel::columnCount( const QModelIndex &parent ) const
|
||||||
|
{
|
||||||
|
Q_UNUSED( parent )
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant QgsFeatureFilterModel::data( const QModelIndex &index, int role ) const
|
||||||
|
{
|
||||||
|
if ( !index.isValid() )
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
switch ( role )
|
||||||
|
{
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
case Qt::EditRole:
|
||||||
|
case ValueRole:
|
||||||
|
return mEntries.value( index.row() ).value;
|
||||||
|
|
||||||
|
case IdentifierValueRole:
|
||||||
|
return mEntries.value( index.row() ).identifierValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureFilterModel::updateCompleter()
|
||||||
|
{
|
||||||
|
emit beginUpdate();
|
||||||
|
QVector<Entry> entries = mGatherer->entries();
|
||||||
|
|
||||||
|
if ( mExtraIdentifierValueIndex == -1 )
|
||||||
|
setExtraIdentifierValueUnguarded( QVariant() );
|
||||||
|
|
||||||
|
// Only reloading the current entry?
|
||||||
|
if ( mGatherer->data().toBool() )
|
||||||
|
{
|
||||||
|
if ( !entries.isEmpty() )
|
||||||
|
{
|
||||||
|
mEntries.replace( mExtraIdentifierValueIndex, entries.at( 0 ) );
|
||||||
|
emit dataChanged( index( mExtraIdentifierValueIndex, 0, QModelIndex() ), index( mExtraIdentifierValueIndex, 0, QModelIndex() ) );
|
||||||
|
mShouldReloadCurrentFeature = false;
|
||||||
|
setExtraValueDoesNotExist( false );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setExtraValueDoesNotExist( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
mShouldReloadCurrentFeature = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We got strings for a filter selection
|
||||||
|
std::sort( entries.begin(), entries.end(), []( const Entry & a, const Entry & b ) { return a.value.localeAwareCompare( b.value ) < 0; } );
|
||||||
|
|
||||||
|
int newEntriesSize = entries.size();
|
||||||
|
|
||||||
|
// Find the index of the extra entry in the new list
|
||||||
|
int currentEntryInNewList = -1;
|
||||||
|
if ( mExtraIdentifierValueIndex != -1 )
|
||||||
|
{
|
||||||
|
for ( int i = 0; i < newEntriesSize; ++i )
|
||||||
|
{
|
||||||
|
if ( entries.at( i ).identifierValue == mExtraIdentifierValue )
|
||||||
|
{
|
||||||
|
currentEntryInNewList = i;
|
||||||
|
mEntries.replace( mExtraIdentifierValueIndex, entries.at( i ) );
|
||||||
|
emit dataChanged( index( mExtraIdentifierValueIndex, 0, QModelIndex() ), index( mExtraIdentifierValueIndex, 0, QModelIndex() ) );
|
||||||
|
setExtraValueDoesNotExist( false );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Q_ASSERT_X( false, "QgsFeatureFilterModel::updateCompleter", "No extra identifier value generated. Should not get here." );
|
||||||
|
}
|
||||||
|
|
||||||
|
int firstRow = 0;
|
||||||
|
|
||||||
|
// Move the extra entry to the first position
|
||||||
|
if ( mExtraIdentifierValueIndex != -1 )
|
||||||
|
{
|
||||||
|
if ( mExtraIdentifierValueIndex != 0 )
|
||||||
|
{
|
||||||
|
beginMoveRows( QModelIndex(), mExtraIdentifierValueIndex, mExtraIdentifierValueIndex, QModelIndex(), 0 );
|
||||||
|
mEntries.move( mExtraIdentifierValueIndex, 0 );
|
||||||
|
endMoveRows();
|
||||||
|
}
|
||||||
|
firstRow = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all entries (except for extra entry if existant)
|
||||||
|
beginRemoveRows( QModelIndex(), firstRow, mEntries.size() - firstRow );
|
||||||
|
mEntries.remove( firstRow, mEntries.size() - firstRow );
|
||||||
|
endRemoveRows();
|
||||||
|
|
||||||
|
if ( currentEntryInNewList == -1 )
|
||||||
|
{
|
||||||
|
beginInsertRows( QModelIndex(), 1, entries.size() + 1 );
|
||||||
|
mEntries += entries;
|
||||||
|
endInsertRows();
|
||||||
|
setExtraIdentifierValueIndex( 0 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( currentEntryInNewList != 0 )
|
||||||
|
{
|
||||||
|
beginInsertRows( QModelIndex(), 0, currentEntryInNewList - 1 );
|
||||||
|
mEntries = entries.mid( 0, currentEntryInNewList );
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mEntries.replace( 0, entries.at( 0 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
emit dataChanged( index( currentEntryInNewList, 0, QModelIndex() ), index( currentEntryInNewList, 0, QModelIndex() ) );
|
||||||
|
|
||||||
|
beginInsertRows( QModelIndex(), currentEntryInNewList + 1, newEntriesSize - currentEntryInNewList - 1 );
|
||||||
|
mEntries += entries.mid( currentEntryInNewList + 1 );
|
||||||
|
endInsertRows();
|
||||||
|
setExtraIdentifierValueIndex( currentEntryInNewList );
|
||||||
|
}
|
||||||
|
|
||||||
|
emit filterJobCompleted();
|
||||||
|
}
|
||||||
|
emit endUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureFilterModel::gathererThreadFinished()
|
||||||
|
{
|
||||||
|
delete mGatherer;
|
||||||
|
mGatherer = nullptr;
|
||||||
|
emit isLoadingChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureFilterModel::scheduledReload()
|
||||||
|
{
|
||||||
|
if ( !mSourceLayer )
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool wasLoading = false;
|
||||||
|
|
||||||
|
if ( mGatherer )
|
||||||
|
{
|
||||||
|
// Send the gatherer thread to the graveyard:
|
||||||
|
// forget about it, tell it to stop and delete when finished
|
||||||
|
disconnect( mGatherer, &QgsFieldExpressionValuesGatherer::collectedValues, this, &QgsFeatureFilterModel::updateCompleter );
|
||||||
|
disconnect( mGatherer, &QgsFieldExpressionValuesGatherer::finished, this, &QgsFeatureFilterModel::gathererThreadFinished );
|
||||||
|
connect( mGatherer, &QgsFieldExpressionValuesGatherer::finished, mGatherer, &QgsFieldExpressionValuesGatherer::deleteLater );
|
||||||
|
mGatherer->stop();
|
||||||
|
wasLoading = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsFeatureRequest request;
|
||||||
|
|
||||||
|
if ( mShouldReloadCurrentFeature )
|
||||||
|
{
|
||||||
|
request.setFilterExpression( QStringLiteral( "%1 = %2" ).arg( QgsExpression::quotedColumnRef( mIdentifierField ), QgsExpression::quotedValue( mExtraIdentifierValue ) ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QString filterClause;
|
||||||
|
|
||||||
|
if ( mFilterValue.isEmpty() && !mFilterExpression.isEmpty() )
|
||||||
|
filterClause = mFilterExpression;
|
||||||
|
else if ( mFilterExpression.isEmpty() && !mFilterValue.isEmpty() )
|
||||||
|
filterClause = QStringLiteral( "(%1) ILIKE '\%%2\%'" ).arg( mDisplayExpression, mFilterValue );
|
||||||
|
else if ( !mFilterExpression.isEmpty() && !mFilterValue.isEmpty() )
|
||||||
|
filterClause = QStringLiteral( "(%1) AND ((%2) ILIKE '\%%3\%')" ).arg( mFilterExpression, mDisplayExpression, mFilterValue );
|
||||||
|
|
||||||
|
request.setFilterExpression( filterClause );
|
||||||
|
}
|
||||||
|
|
||||||
|
request.setLimit( 100 );
|
||||||
|
|
||||||
|
mGatherer = new QgsFieldExpressionValuesGatherer( mSourceLayer, mDisplayExpression, mIdentifierField, request );
|
||||||
|
mGatherer->setData( mShouldReloadCurrentFeature );
|
||||||
|
|
||||||
|
connect( mGatherer, &QgsFieldExpressionValuesGatherer::collectedValues, this, &QgsFeatureFilterModel::updateCompleter );
|
||||||
|
connect( mGatherer, &QgsFieldExpressionValuesGatherer::finished, this, &QgsFeatureFilterModel::gathererThreadFinished );
|
||||||
|
|
||||||
|
mGatherer->start();
|
||||||
|
if ( !wasLoading )
|
||||||
|
emit isLoadingChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureFilterModel::setExtraIdentifierValueIndex( int index )
|
||||||
|
{
|
||||||
|
if ( mExtraIdentifierValueIndex == index )
|
||||||
|
return;
|
||||||
|
|
||||||
|
mExtraIdentifierValueIndex = index;
|
||||||
|
emit extraIdentifierValueIndexChanged( index );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureFilterModel::reloadCurrentFeature()
|
||||||
|
{
|
||||||
|
mShouldReloadCurrentFeature = true;
|
||||||
|
mReloadTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureFilterModel::setExtraIdentifierValueUnguarded( const QVariant &extraIdentifierValue )
|
||||||
|
{
|
||||||
|
const QVector<Entry> entries = mEntries;
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
for ( const Entry &entry : entries )
|
||||||
|
{
|
||||||
|
if ( entry.identifierValue == extraIdentifierValue )
|
||||||
|
{
|
||||||
|
setExtraIdentifierValueIndex( index );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value not found in current entries
|
||||||
|
if ( mExtraIdentifierValueIndex != index )
|
||||||
|
{
|
||||||
|
beginInsertRows( QModelIndex(), 0, 0 );
|
||||||
|
mEntries.prepend( Entry( extraIdentifierValue, QStringLiteral( "(%1)" ).arg( extraIdentifierValue.toString() ) ) );
|
||||||
|
endInsertRows();
|
||||||
|
setExtraIdentifierValueIndex( 0 );
|
||||||
|
|
||||||
|
reloadCurrentFeature();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QgsFeatureFilterModel::extraValueDoesNotExist() const
|
||||||
|
{
|
||||||
|
return mExtraValueDoesNotExist;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureFilterModel::setExtraValueDoesNotExist( bool extraValueDoesNotExist )
|
||||||
|
{
|
||||||
|
if ( mExtraValueDoesNotExist == extraValueDoesNotExist )
|
||||||
|
return;
|
||||||
|
|
||||||
|
mExtraValueDoesNotExist = extraValueDoesNotExist;
|
||||||
|
emit extraValueDoesNotExistChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
int QgsFeatureFilterModel::extraIdentifierValueIndex() const
|
||||||
|
{
|
||||||
|
return mExtraIdentifierValueIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QgsFeatureFilterModel::identifierField() const
|
||||||
|
{
|
||||||
|
return mIdentifierField;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureFilterModel::setIdentifierField( const QString &identifierField )
|
||||||
|
{
|
||||||
|
if ( mIdentifierField == identifierField )
|
||||||
|
return;
|
||||||
|
|
||||||
|
mIdentifierField = identifierField;
|
||||||
|
emit identifierFieldChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureFilterModel::reload()
|
||||||
|
{
|
||||||
|
mReloadTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant QgsFeatureFilterModel::extraIdentifierValue() const
|
||||||
|
{
|
||||||
|
return mExtraIdentifierValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureFilterModel::setExtraIdentifierValue( const QVariant &extraIdentifierValue )
|
||||||
|
{
|
||||||
|
if ( extraIdentifierValue == mExtraIdentifierValue && extraIdentifierValue.isNull() == mExtraIdentifierValue.isNull() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
setExtraIdentifierValueUnguarded( extraIdentifierValue );
|
||||||
|
|
||||||
|
mExtraIdentifierValue = extraIdentifierValue;
|
||||||
|
emit extraIdentifierValueChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant QgsFieldExpressionValuesGatherer::data() const
|
||||||
|
{
|
||||||
|
return mData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFieldExpressionValuesGatherer::setData( const QVariant &data )
|
||||||
|
{
|
||||||
|
mData = data;
|
||||||
|
}
|
163
src/core/qgsfeaturefiltermodel.h
Normal file
163
src/core/qgsfeaturefiltermodel.h
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgsfeaturefiltermodel.h - QgsFeatureFilterModel
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
begin : 10.3.2017
|
||||||
|
copyright : (C) 2017 by Matthias Kuhn
|
||||||
|
email : matthias@opengis.ch
|
||||||
|
***************************************************************************
|
||||||
|
* *
|
||||||
|
* 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. *
|
||||||
|
* *
|
||||||
|
***************************************************************************/
|
||||||
|
#ifndef QGSFEATUREFILTERMODEL_H
|
||||||
|
#define QGSFEATUREFILTERMODEL_H
|
||||||
|
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
|
||||||
|
#include "qgsvectorlayer.h"
|
||||||
|
|
||||||
|
class QgsFieldExpressionValuesGatherer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a list of features based on filter conditions.
|
||||||
|
* Features are fetched asynchronously.
|
||||||
|
*/
|
||||||
|
class CORE_EXPORT QgsFeatureFilterModel : public QAbstractItemModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY( QgsVectorLayer *sourceLayer READ sourceLayer WRITE setSourceLayer NOTIFY sourceLayerChanged )
|
||||||
|
Q_PROPERTY( QString displayExpression READ displayExpression WRITE setDisplayExpression NOTIFY displayExpressionChanged )
|
||||||
|
Q_PROPERTY( QString filterValue READ filterValue WRITE setFilterValue NOTIFY filterValueChanged )
|
||||||
|
Q_PROPERTY( QString filterExpression READ filterExpression WRITE setFilterExpression NOTIFY filterExpressionChanged )
|
||||||
|
Q_PROPERTY( bool isLoading READ isLoading NOTIFY isLoadingChanged )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A field of sourceLayer that is unique and should be used to identify features.
|
||||||
|
* Normally the primary key field.
|
||||||
|
* Needs to match the identifierValue.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY( QString identifierField READ identifierField WRITE setIdentifierField NOTIFY identifierFieldChanged )
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value that identifies the current feature.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY( QVariant extraIdentifierValue READ extraIdentifierValue WRITE setExtraIdentifierValue NOTIFY extraIdentifierValueChanged )
|
||||||
|
|
||||||
|
Q_PROPERTY( int extraIdentifierValueIndex READ extraIdentifierValueIndex NOTIFY extraIdentifierValueIndexChanged )
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Role
|
||||||
|
{
|
||||||
|
IdentifierValueRole = Qt::UserRole,
|
||||||
|
ValueRole
|
||||||
|
};
|
||||||
|
|
||||||
|
QgsFeatureFilterModel( QObject *parent = nullptr );
|
||||||
|
~QgsFeatureFilterModel();
|
||||||
|
|
||||||
|
QgsVectorLayer *sourceLayer() const;
|
||||||
|
void setSourceLayer( QgsVectorLayer *sourceLayer );
|
||||||
|
|
||||||
|
QString displayExpression() const;
|
||||||
|
void setDisplayExpression( const QString &displayExpression );
|
||||||
|
|
||||||
|
QString filterValue() const;
|
||||||
|
void setFilterValue( const QString &filterValue );
|
||||||
|
|
||||||
|
virtual QModelIndex index( int row, int column, const QModelIndex &parent ) const override;
|
||||||
|
virtual QModelIndex parent( const QModelIndex &child ) const override;
|
||||||
|
virtual int rowCount( const QModelIndex &parent ) const override;
|
||||||
|
virtual int columnCount( const QModelIndex &parent ) const override;
|
||||||
|
virtual QVariant data( const QModelIndex &index, int role ) const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An additional filter expression to apply, next to the filterValue.
|
||||||
|
* Can be used for spatial filtering etc.
|
||||||
|
*/
|
||||||
|
QString filterExpression() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An additional filter expression to apply, next to the filterValue.
|
||||||
|
* Can be used for spatial filtering etc.
|
||||||
|
*/
|
||||||
|
void setFilterExpression( const QString &filterExpression );
|
||||||
|
|
||||||
|
bool isLoading() const;
|
||||||
|
|
||||||
|
QString identifierField() const;
|
||||||
|
void setIdentifierField( const QString &identifierField );
|
||||||
|
|
||||||
|
QVariant extraIdentifierValue() const;
|
||||||
|
void setExtraIdentifierValue( const QVariant &extraIdentifierValue );
|
||||||
|
|
||||||
|
int extraIdentifierValueIndex() const;
|
||||||
|
|
||||||
|
bool extraValueDoesNotExist() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void sourceLayerChanged();
|
||||||
|
void displayExpressionChanged();
|
||||||
|
void filterValueChanged();
|
||||||
|
void filterExpressionChanged();
|
||||||
|
void isLoadingChanged();
|
||||||
|
void identifierFieldChanged();
|
||||||
|
void filterJobCompleted();
|
||||||
|
void extraIdentifierValueChanged();
|
||||||
|
void extraIdentifierValueIndexChanged( int index );
|
||||||
|
void extraValueDoesNotExistChanged();
|
||||||
|
void beginUpdate();
|
||||||
|
void endUpdate();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void updateCompleter();
|
||||||
|
void gathererThreadFinished();
|
||||||
|
void scheduledReload();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setExtraIdentifierValueIndex( int index );
|
||||||
|
void setExtraValueDoesNotExist( bool extraValueDoesNotExist );
|
||||||
|
void reload();
|
||||||
|
void reloadCurrentFeature();
|
||||||
|
void setExtraIdentifierValueUnguarded( const QVariant &extraIdentifierValue );
|
||||||
|
struct Entry
|
||||||
|
{
|
||||||
|
Entry()
|
||||||
|
{}
|
||||||
|
|
||||||
|
Entry( QVariant _identifierValue, const QString &_value )
|
||||||
|
: identifierValue( _identifierValue )
|
||||||
|
, value( _value )
|
||||||
|
{}
|
||||||
|
|
||||||
|
QVariant identifierValue;
|
||||||
|
QString value;
|
||||||
|
|
||||||
|
bool operator()( const Entry &lhs, const Entry &rhs ) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
QgsVectorLayer *mSourceLayer;
|
||||||
|
QString mDisplayExpression;
|
||||||
|
QString mFilterValue;
|
||||||
|
QString mFilterExpression;
|
||||||
|
|
||||||
|
QVector<Entry> mEntries;
|
||||||
|
QgsFieldExpressionValuesGatherer *mGatherer = nullptr;
|
||||||
|
QTimer mReloadTimer;
|
||||||
|
bool mShouldReloadCurrentFeature;
|
||||||
|
bool mExtraValueDoesNotExist = false;
|
||||||
|
|
||||||
|
QString mIdentifierField;
|
||||||
|
|
||||||
|
QVariant mExtraIdentifierValue;
|
||||||
|
|
||||||
|
int mExtraIdentifierValueIndex = -1;
|
||||||
|
|
||||||
|
friend class QgsFieldExpressionValuesGatherer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QGSFEATUREFILTERMODEL_H
|
131
src/core/qgsfeaturefiltermodel_p.h
Normal file
131
src/core/qgsfeaturefiltermodel_p.h
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgsfeaturefiltermodel_p - QgsFieldExpressionValuesGatherer
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
begin : 10.3.2017
|
||||||
|
copyright : (C) 2017 by Matthias Kuhn
|
||||||
|
email : matthias@opengis.ch
|
||||||
|
***************************************************************************
|
||||||
|
* *
|
||||||
|
* 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. *
|
||||||
|
* *
|
||||||
|
***************************************************************************/
|
||||||
|
#ifndef QGSFEATUREFILTERMODEL_P_H
|
||||||
|
#define QGSFEATUREFILTERMODEL_P_H
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
#include "qgsvectorlayer.h"
|
||||||
|
#include "qgsfeaturefiltermodel.h"
|
||||||
|
#include "qgslogger.h"
|
||||||
|
#include "qgsvectorlayerfeatureiterator.h"
|
||||||
|
|
||||||
|
#define SIP_NO_FILE
|
||||||
|
|
||||||
|
// just internal guff - definitely not for exposing to public API!
|
||||||
|
///@cond PRIVATE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class QgsFieldExpressionValuesGatherer
|
||||||
|
* Gathers features with substring matching on an expression.
|
||||||
|
*
|
||||||
|
* \since QGIS 3.0
|
||||||
|
*/
|
||||||
|
class QgsFieldExpressionValuesGatherer: public QThread
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
QgsFieldExpressionValuesGatherer( QgsVectorLayer *layer, const QString &displayExpression, const QString &identifierField, const QgsFeatureRequest &request = QgsFeatureRequest() )
|
||||||
|
: mSource( new QgsVectorLayerFeatureSource( layer ) )
|
||||||
|
, mDisplayExpression( displayExpression )
|
||||||
|
, mRequest( request )
|
||||||
|
, mWasCanceled( false )
|
||||||
|
, mIdentifierField( identifierField )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~QgsFieldExpressionValuesGatherer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void run() override
|
||||||
|
{
|
||||||
|
mWasCanceled = false;
|
||||||
|
|
||||||
|
mIterator = mSource->getFeatures( mRequest );
|
||||||
|
|
||||||
|
QgsDebugMsg( QStringLiteral( "New gatherer: %1" ).arg( mRequest.filterExpression()->expression() ) );
|
||||||
|
mDisplayExpression.prepare( &mExpressionContext );
|
||||||
|
|
||||||
|
QgsFeature feat;
|
||||||
|
int attribute = mSource->fields().indexOf( mIdentifierField );
|
||||||
|
|
||||||
|
while ( mIterator.nextFeature( feat ) )
|
||||||
|
{
|
||||||
|
mExpressionContext.setFeature( feat );
|
||||||
|
mEntries.append( QgsFeatureFilterModel::Entry( feat.attribute( attribute ), mDisplayExpression.evaluate( &mExpressionContext ).toString() ) );
|
||||||
|
|
||||||
|
if ( mWasCanceled )
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit collectedValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Informs the gatherer to immediately stop collecting values
|
||||||
|
void stop()
|
||||||
|
{
|
||||||
|
mWasCanceled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Returns true if collection was canceled before completion
|
||||||
|
bool wasCanceled() const { return mWasCanceled; }
|
||||||
|
|
||||||
|
QVector<QgsFeatureFilterModel::Entry> entries() const
|
||||||
|
{
|
||||||
|
return mEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsFeatureRequest request() const
|
||||||
|
{
|
||||||
|
return mRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal data, use for whatever you want. Defaults to -1.
|
||||||
|
*/
|
||||||
|
QVariant data() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal data, use for whatever you want. Defaults to -1.
|
||||||
|
*/
|
||||||
|
void setData( const QVariant &data ); // TODO: Do we still need this???
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emitted when values have been collected
|
||||||
|
* @param values list of unique matching string values
|
||||||
|
*/
|
||||||
|
void collectedValues();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::unique_ptr<QgsVectorLayerFeatureSource> mSource;
|
||||||
|
QgsExpression mDisplayExpression;
|
||||||
|
QgsExpressionContext mExpressionContext;
|
||||||
|
QgsFeatureRequest mRequest;
|
||||||
|
QgsFeatureIterator mIterator;
|
||||||
|
bool mWasCanceled;
|
||||||
|
QVector<QgsFeatureFilterModel::Entry> mEntries;
|
||||||
|
QString mIdentifierField;
|
||||||
|
QVariant mData;
|
||||||
|
};
|
||||||
|
|
||||||
|
///@endcond
|
||||||
|
|
||||||
|
|
||||||
|
#endif // QGSFEATUREFILTERMODEL_P_H
|
@ -241,6 +241,7 @@ SET(QGIS_GUI_SRCS
|
|||||||
qgsfeatureselectiondlg.cpp
|
qgsfeatureselectiondlg.cpp
|
||||||
qgsfieldcombobox.cpp
|
qgsfieldcombobox.cpp
|
||||||
qgsfieldexpressionwidget.cpp
|
qgsfieldexpressionwidget.cpp
|
||||||
|
qgsfeaturelistcombobox.cpp
|
||||||
qgsfieldvalidator.cpp
|
qgsfieldvalidator.cpp
|
||||||
qgsfieldvalueslineedit.cpp
|
qgsfieldvalueslineedit.cpp
|
||||||
qgsfilewidget.cpp
|
qgsfilewidget.cpp
|
||||||
@ -412,6 +413,7 @@ SET(QGIS_GUI_MOC_HDRS
|
|||||||
qgsfeatureselectiondlg.h
|
qgsfeatureselectiondlg.h
|
||||||
qgsfieldcombobox.h
|
qgsfieldcombobox.h
|
||||||
qgsfieldexpressionwidget.h
|
qgsfieldexpressionwidget.h
|
||||||
|
qgsfeaturelistcombobox.h
|
||||||
qgsfieldvalidator.h
|
qgsfieldvalidator.h
|
||||||
qgsfieldvalueslineedit.h
|
qgsfieldvalueslineedit.h
|
||||||
qgsfilewidget.h
|
qgsfilewidget.h
|
||||||
|
217
src/gui/qgsfeaturelistcombobox.cpp
Normal file
217
src/gui/qgsfeaturelistcombobox.cpp
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgsfieldlistcombobox.cpp - QgsFieldListComboBox
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
begin : 10.3.2017
|
||||||
|
copyright : (C) 2017 by Matthias Kuhn
|
||||||
|
email : matthias@opengis.ch
|
||||||
|
***************************************************************************
|
||||||
|
* *
|
||||||
|
* 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 "qgsfeaturelistcombobox.h"
|
||||||
|
|
||||||
|
#include "qgsfeaturefiltermodel.h"
|
||||||
|
#include "qgsanimatedicon.h"
|
||||||
|
#include "qgsfilterlineedit.h"
|
||||||
|
#include "qgslogger.h"
|
||||||
|
|
||||||
|
#include <QCompleter>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
|
||||||
|
QgsFeatureListComboBox::QgsFeatureListComboBox( QWidget *parent )
|
||||||
|
: QComboBox( parent )
|
||||||
|
, mModel( new QgsFeatureFilterModel( this ) )
|
||||||
|
, mCompleter( new QCompleter( mModel ) )
|
||||||
|
{
|
||||||
|
mCompleter->setCaseSensitivity( Qt::CaseInsensitive );
|
||||||
|
mCompleter->setFilterMode( Qt::MatchContains );
|
||||||
|
setCompleter( mCompleter );
|
||||||
|
mCompleter->setWidget( this );
|
||||||
|
connect( mModel, &QgsFeatureFilterModel::sourceLayerChanged, this, &QgsFeatureListComboBox::sourceLayerChanged );
|
||||||
|
connect( mModel, &QgsFeatureFilterModel::displayExpressionChanged, this, &QgsFeatureListComboBox::displayExpressionChanged );
|
||||||
|
connect( mModel, &QgsFeatureFilterModel::filterExpressionChanged, this, &QgsFeatureListComboBox::filterExpressionChanged );
|
||||||
|
connect( mModel, &QgsFeatureFilterModel::isLoadingChanged, this, &QgsFeatureListComboBox::onLoadingChanged );
|
||||||
|
connect( mModel, &QgsFeatureFilterModel::filterJobCompleted, this, &QgsFeatureListComboBox::onFilterUpdateCompleted );
|
||||||
|
connect( mModel, &QgsFeatureFilterModel::extraIdentifierValueChanged, this, &QgsFeatureListComboBox::identifierValueChanged );
|
||||||
|
connect( mModel, &QgsFeatureFilterModel::extraIdentifierValueIndexChanged, this, &QgsFeatureListComboBox::setCurrentIndex );
|
||||||
|
connect( mModel, &QgsFeatureFilterModel::identifierFieldChanged, this, &QgsFeatureListComboBox::identifierFieldChanged );
|
||||||
|
connect( mCompleter, static_cast<void( QCompleter::* )( const QModelIndex & )>( &QCompleter::highlighted ), this, &QgsFeatureListComboBox::onItemSelected );
|
||||||
|
connect( mCompleter, static_cast<void( QCompleter::* )( const QModelIndex & )>( &QCompleter::activated ), this, &QgsFeatureListComboBox::onActivated );
|
||||||
|
connect( mModel, &QgsFeatureFilterModel::beginUpdate, this, &QgsFeatureListComboBox::storeLineEditState );
|
||||||
|
connect( mModel, &QgsFeatureFilterModel::endUpdate, this, &QgsFeatureListComboBox::restoreLineEditState );
|
||||||
|
|
||||||
|
connect( this, static_cast<void( QgsFeatureListComboBox::* )( int )>( &QgsFeatureListComboBox::currentIndexChanged ), this, &QgsFeatureListComboBox::onCurrentIndexChanged );
|
||||||
|
|
||||||
|
mLineEdit = new QgsFilterLineEdit();
|
||||||
|
setEditable( true );
|
||||||
|
setLineEdit( mLineEdit );
|
||||||
|
setModel( mModel );
|
||||||
|
|
||||||
|
connect( mLineEdit, &QgsFilterLineEdit::textEdited, this, &QgsFeatureListComboBox::onCurrentTextChanged );
|
||||||
|
|
||||||
|
connect( mLineEdit, &QgsFilterLineEdit::textChanged, this, []( const QString & text )
|
||||||
|
{
|
||||||
|
QgsDebugMsg( QStringLiteral( "Edit text changed to %1" ).arg( text ) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsVectorLayer *QgsFeatureListComboBox::sourceLayer() const
|
||||||
|
{
|
||||||
|
return mModel->sourceLayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureListComboBox::setSourceLayer( QgsVectorLayer *sourceLayer )
|
||||||
|
{
|
||||||
|
mModel->setSourceLayer( sourceLayer );
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QgsFeatureListComboBox::displayExpression() const
|
||||||
|
{
|
||||||
|
return mModel->displayExpression();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureListComboBox::setDisplayExpression( const QString &expression )
|
||||||
|
{
|
||||||
|
mModel->setDisplayExpression( expression );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureListComboBox::onCurrentTextChanged( const QString &text )
|
||||||
|
{
|
||||||
|
mPopupRequested = true;
|
||||||
|
mModel->setFilterValue( text );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureListComboBox::onFilterUpdateCompleted()
|
||||||
|
{
|
||||||
|
if ( mPopupRequested )
|
||||||
|
mCompleter->complete();
|
||||||
|
|
||||||
|
mPopupRequested = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureListComboBox::onLoadingChanged()
|
||||||
|
{
|
||||||
|
mLineEdit->setShowSpinner( mModel->isLoading() );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureListComboBox::onItemSelected( const QModelIndex &index )
|
||||||
|
{
|
||||||
|
setCurrentIndex( index.row() );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureListComboBox::onCurrentIndexChanged( int i )
|
||||||
|
{
|
||||||
|
QModelIndex modelIndex = mModel->index( i, 0, QModelIndex() );
|
||||||
|
mModel->setExtraIdentifierValue( mModel->data( modelIndex, QgsFeatureFilterModel::IdentifierValueRole ) );
|
||||||
|
mLineEdit->setText( mModel->data( modelIndex, QgsFeatureFilterModel::ValueRole ).toString() );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureListComboBox::onActivated( QModelIndex modelIndex )
|
||||||
|
{
|
||||||
|
setIdentifierValue( mModel->data( modelIndex, QgsFeatureFilterModel::IdentifierValueRole ) );
|
||||||
|
QgsDebugMsg( QStringLiteral( "Activated index" ) );
|
||||||
|
QgsDebugMsg( QStringLiteral( "%1 %2" ).arg( QString::number( modelIndex.row() ), mModel->data( modelIndex, QgsFeatureFilterModel::ValueRole ).toString() ) );
|
||||||
|
mLineEdit->setText( mModel->data( modelIndex, QgsFeatureFilterModel::ValueRole ).toString() );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureListComboBox::storeLineEditState()
|
||||||
|
{
|
||||||
|
mLineEditState.store( mLineEdit );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureListComboBox::restoreLineEditState()
|
||||||
|
{
|
||||||
|
mLineEditState.restore( mLineEdit );
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QgsFeatureListComboBox::identifierField() const
|
||||||
|
{
|
||||||
|
return mModel->identifierField();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureListComboBox::setIdentifierField( const QString &identifierField )
|
||||||
|
{
|
||||||
|
mModel->setIdentifierField( identifierField );
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex QgsFeatureListComboBox::currentModelIndex() const
|
||||||
|
{
|
||||||
|
return mModel->index( mModel->extraIdentifierValueIndex(), 0, QModelIndex() );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureListComboBox::focusOutEvent( QFocusEvent *event )
|
||||||
|
{
|
||||||
|
Q_UNUSED( event )
|
||||||
|
QComboBox::focusOutEvent( event );
|
||||||
|
mLineEdit->setText( mModel->data( currentModelIndex(), QgsFeatureFilterModel::ValueRole ).toString() );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureListComboBox::keyPressEvent( QKeyEvent *event )
|
||||||
|
{
|
||||||
|
if ( event->key() == Qt::Key_Escape )
|
||||||
|
{
|
||||||
|
mLineEdit->setText( mModel->data( currentModelIndex(), QgsFeatureFilterModel::ValueRole ).toString() );
|
||||||
|
}
|
||||||
|
QComboBox::keyReleaseEvent( event );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QgsFeatureListComboBox::allowNull() const
|
||||||
|
{
|
||||||
|
return mAllowNull;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureListComboBox::setAllowNull( bool allowNull )
|
||||||
|
{
|
||||||
|
if ( mAllowNull == allowNull )
|
||||||
|
return;
|
||||||
|
|
||||||
|
mAllowNull = allowNull;
|
||||||
|
emit allowNullChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant QgsFeatureListComboBox::identifierValue() const
|
||||||
|
{
|
||||||
|
return mModel->extraIdentifierValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureListComboBox::setIdentifierValue( const QVariant &identifierValue )
|
||||||
|
{
|
||||||
|
mModel->setExtraIdentifierValue( identifierValue );
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsFeatureRequest QgsFeatureListComboBox::currentFeatureRequest() const
|
||||||
|
{
|
||||||
|
return QgsFeatureRequest().setFilterExpression( QStringLiteral( "%1 = %2" ).arg( QgsExpression::quotedColumnRef( mModel->identifierField() ), QgsExpression::quotedValue( mModel->extraIdentifierValue() ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QgsFeatureListComboBox::filterExpression() const
|
||||||
|
{
|
||||||
|
return mModel->filterExpression();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureListComboBox::setFilterExpression( const QString &filterExpression )
|
||||||
|
{
|
||||||
|
mModel->setFilterExpression( filterExpression );
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureListComboBox::LineEditState::store( QLineEdit *lineEdit )
|
||||||
|
{
|
||||||
|
text = lineEdit->text();
|
||||||
|
selectionStart = lineEdit->selectionStart();
|
||||||
|
selectionLength = lineEdit->selectedText().length();
|
||||||
|
cursorPosition = lineEdit->cursorPosition();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsFeatureListComboBox::LineEditState::restore( QLineEdit *lineEdit ) const
|
||||||
|
{
|
||||||
|
lineEdit->setText( text );
|
||||||
|
lineEdit->setCursorPosition( cursorPosition );
|
||||||
|
lineEdit->setSelection( selectionStart, selectionLength );
|
||||||
|
}
|
110
src/gui/qgsfeaturelistcombobox.h
Normal file
110
src/gui/qgsfeaturelistcombobox.h
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgsfieldlistcombobox.h - QgsFieldListComboBox
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
begin : 10.3.2017
|
||||||
|
copyright : (C) 2017 by Matthias Kuhn
|
||||||
|
email : matthias@opengis.ch
|
||||||
|
***************************************************************************
|
||||||
|
* *
|
||||||
|
* 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. *
|
||||||
|
* *
|
||||||
|
***************************************************************************/
|
||||||
|
#ifndef QGSFIELDLISTCOMBOBOX_H
|
||||||
|
#define QGSFIELDLISTCOMBOBOX_H
|
||||||
|
|
||||||
|
#include <QComboBox>
|
||||||
|
|
||||||
|
#include "qgsfeature.h"
|
||||||
|
#include "qgsfeaturerequest.h"
|
||||||
|
#include "qgis_gui.h"
|
||||||
|
|
||||||
|
class QgsVectorLayer;
|
||||||
|
class QgsFeatureFilterModel;
|
||||||
|
class QgsAnimatedIcon;
|
||||||
|
class QgsFilterLineEdit;
|
||||||
|
|
||||||
|
class GUI_EXPORT QgsFeatureListComboBox : public QComboBox
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY( QgsVectorLayer *sourceLayer READ sourceLayer WRITE setSourceLayer NOTIFY sourceLayerChanged )
|
||||||
|
Q_PROPERTY( QString displayExpression READ displayExpression WRITE setDisplayExpression NOTIFY displayExpressionChanged )
|
||||||
|
Q_PROPERTY( QString filterExpression READ filterExpression WRITE setFilterExpression NOTIFY filterExpressionChanged )
|
||||||
|
Q_PROPERTY( QVariant identifierValue READ identifierValue WRITE setIdentifierValue NOTIFY identifierValueChanged )
|
||||||
|
Q_PROPERTY( QString identifierField READ identifierField WRITE setIdentifierField NOTIFY identifierFieldChanged )
|
||||||
|
Q_PROPERTY( bool allowNull READ allowNull WRITE setAllowNull NOTIFY allowNullChanged )
|
||||||
|
|
||||||
|
public:
|
||||||
|
QgsFeatureListComboBox( QWidget *parent = nullptr );
|
||||||
|
|
||||||
|
QgsVectorLayer *sourceLayer() const;
|
||||||
|
void setSourceLayer( QgsVectorLayer *sourceLayer );
|
||||||
|
|
||||||
|
QString displayExpression() const;
|
||||||
|
void setDisplayExpression( const QString &displayExpression );
|
||||||
|
|
||||||
|
QString filterExpression() const;
|
||||||
|
void setFilterExpression( const QString &filterExpression );
|
||||||
|
|
||||||
|
QVariant identifierValue() const;
|
||||||
|
void setIdentifierValue( const QVariant &identifierValue );
|
||||||
|
|
||||||
|
QgsFeatureRequest currentFeatureRequest() const;
|
||||||
|
|
||||||
|
bool allowNull() const;
|
||||||
|
void setAllowNull( bool allowNull );
|
||||||
|
|
||||||
|
QString identifierField() const;
|
||||||
|
void setIdentifierField( const QString &identifierField );
|
||||||
|
|
||||||
|
QModelIndex currentModelIndex() const;
|
||||||
|
|
||||||
|
virtual void focusOutEvent( QFocusEvent *event ) override;
|
||||||
|
|
||||||
|
virtual void keyPressEvent( QKeyEvent *event ) override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void sourceLayerChanged();
|
||||||
|
void displayExpressionChanged();
|
||||||
|
void filterExpressionChanged();
|
||||||
|
void identifierValueChanged();
|
||||||
|
void identifierFieldChanged();
|
||||||
|
void allowNullChanged();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onCurrentTextChanged( const QString &text );
|
||||||
|
void onFilterUpdateCompleted();
|
||||||
|
void onLoadingChanged();
|
||||||
|
void onItemSelected( const QModelIndex &index );
|
||||||
|
void onCurrentIndexChanged( int i );
|
||||||
|
void onActivated( QModelIndex index );
|
||||||
|
void storeLineEditState();
|
||||||
|
void restoreLineEditState();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct LineEditState
|
||||||
|
{
|
||||||
|
void store( QLineEdit *lineEdit );
|
||||||
|
void restore( QLineEdit *lineEdit ) const;
|
||||||
|
|
||||||
|
QString text;
|
||||||
|
int selectionStart;
|
||||||
|
int selectionLength;
|
||||||
|
int cursorPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
QgsFeatureFilterModel *mModel;
|
||||||
|
QCompleter *mCompleter;
|
||||||
|
QString mDisplayExpression;
|
||||||
|
QgsFilterLineEdit *mLineEdit;
|
||||||
|
bool mAllowNull = true;
|
||||||
|
bool mPopupRequested = false;
|
||||||
|
bool mIsCurrentlyEdited = false;
|
||||||
|
LineEditState mLineEditState;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QGSFIELDLISTCOMBOBOX_H
|
19
src/gui/qgsfeaturelistcombobox_p.h
Normal file
19
src/gui/qgsfeaturelistcombobox_p.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
qgsfeaturelistcombobox_p.h
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
begin : 24.10.2017
|
||||||
|
copyright : (C) 2017 by Matthias Kuhn
|
||||||
|
email : matthias@opengis.ch
|
||||||
|
***************************************************************************
|
||||||
|
* *
|
||||||
|
* 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. *
|
||||||
|
* *
|
||||||
|
***************************************************************************/
|
||||||
|
#ifndef QGSFEATURELISTCOMBOBOX_P_H
|
||||||
|
#define QGSFEATURELISTCOMBOBOX_P_H
|
||||||
|
|
||||||
|
#endif // QGSFEATURELISTCOMBOBOX_P_H
|
Loading…
x
Reference in New Issue
Block a user