[FEATURE] allow filtering for field values in expression widget

This commit is contained in:
Salvatore Larosa 2015-09-24 23:53:43 +02:00
parent e655c2617a
commit d493a69069
4 changed files with 107 additions and 139 deletions

View File

@ -173,8 +173,9 @@ class QgsExpressionBuilderWidget : QWidget
void on_expressionTree_doubleClicked( const QModelIndex &index ); void on_expressionTree_doubleClicked( const QModelIndex &index );
void on_txtExpressionString_textChanged(); void on_txtExpressionString_textChanged();
void on_txtSearchEdit_textChanged(); void on_txtSearchEdit_textChanged();
void on_txtSearchEditValues_textChanged();
void on_lblPreview_linkActivated( QString link ); void on_lblPreview_linkActivated( QString link );
void on_mValueListWidget_itemDoubleClicked( QListWidgetItem* item ); void on_mValuesListView_doubleClicked( const QModelIndex &index );
void operatorButtonClicked(); void operatorButtonClicked();
void showContextMenu( const QPoint & ); void showContextMenu( const QPoint & );
void loadSampleValues(); void loadSampleValues();

View File

@ -58,13 +58,19 @@ QgsExpressionBuilderWidget::QgsExpressionBuilderWidget( QWidget *parent )
connect( btnLoadAll, SIGNAL( pressed() ), this, SLOT( loadAllValues() ) ); connect( btnLoadAll, SIGNAL( pressed() ), this, SLOT( loadAllValues() ) );
connect( btnLoadSample, SIGNAL( pressed() ), this, SLOT( loadSampleValues() ) ); connect( btnLoadSample, SIGNAL( pressed() ), this, SLOT( loadSampleValues() ) );
Q_FOREACH ( QPushButton* button, mOperatorsGroupBox->findChildren<QPushButton *>() ) Q_FOREACH( QPushButton* button, mOperatorsGroupBox->findChildren<QPushButton *>() )
{ {
connect( button, SIGNAL( pressed() ), this, SLOT( operatorButtonClicked() ) ); connect( button, SIGNAL( pressed() ), this, SLOT( operatorButtonClicked() ) );
} }
txtSearchEdit->setPlaceholderText( tr( "Search" ) ); txtSearchEdit->setPlaceholderText( tr( "Search" ) );
mValuesModel = new QStringListModel();
mProxyValues = new QSortFilterProxyModel();
mProxyValues->setSourceModel( mValuesModel );
mValuesListView->setModel( mProxyValues );
txtSearchEditValues->setPlaceholderText( tr( "Search" ) );
QSettings settings; QSettings settings;
splitter->restoreState( settings.value( "/windows/QgsExpressionBuilderWidget/splitter" ).toByteArray() ); splitter->restoreState( settings.value( "/windows/QgsExpressionBuilderWidget/splitter" ).toByteArray() );
functionsplit->restoreState( settings.value( "/windows/QgsExpressionBuilderWidget/functionsplitter" ).toByteArray() ); functionsplit->restoreState( settings.value( "/windows/QgsExpressionBuilderWidget/functionsplitter" ).toByteArray() );
@ -101,6 +107,8 @@ QgsExpressionBuilderWidget::~QgsExpressionBuilderWidget()
delete mModel; delete mModel;
delete mProxyModel; delete mProxyModel;
delete mValuesModel;
delete mProxyValues;
} }
void QgsExpressionBuilderWidget::setLayer( QgsVectorLayer *layer ) void QgsExpressionBuilderWidget::setLayer( QgsVectorLayer *layer )
@ -115,25 +123,22 @@ void QgsExpressionBuilderWidget::setLayer( QgsVectorLayer *layer )
void QgsExpressionBuilderWidget::currentChanged( const QModelIndex &index, const QModelIndex & ) void QgsExpressionBuilderWidget::currentChanged( const QModelIndex &index, const QModelIndex & )
{ {
txtSearchEditValues->setText( QString( "" ) );
// Get the item // Get the item
QModelIndex idx = mProxyModel->mapToSource( index ); QModelIndex idx = mProxyModel->mapToSource( index );
QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>( mModel->itemFromIndex( idx ) ); QgsExpressionItem* item = dynamic_cast<QgsExpressionItem*>( mModel->itemFromIndex( idx ) );
if ( !item ) if ( !item )
return; return;
mValueListWidget->clear();
if ( item->getItemType() == QgsExpressionItem::Field && mFieldValues.contains( item->text() ) ) if ( item->getItemType() == QgsExpressionItem::Field && mFieldValues.contains( item->text() ) )
{ {
const QStringList& values = mFieldValues[item->text()]; const QStringList& values = mFieldValues[item->text()];
mValueListWidget->setUpdatesEnabled( false ); mValuesModel->setStringList( values );
mValueListWidget->blockSignals( true );
mValueListWidget->addItems( values );
mValueListWidget->setUpdatesEnabled( true );
mValueListWidget->blockSignals( false );
} }
mLoadGroupBox->setVisible( item->getItemType() == QgsExpressionItem::Field && mLayer ); mLoadGroupBox->setVisible( item->getItemType() == QgsExpressionItem::Field && mLayer );
mValueGroupBox->setVisible(( item->getItemType() == QgsExpressionItem::Field && mLayer ) || mValueListWidget->count() > 0 ); mValueGroupBox->setVisible( ( item->getItemType() == QgsExpressionItem::Field && mLayer ) || mValuesListView->model()->rowCount() > 0 );
// Show the help for the current item. // Show the help for the current item.
QString help = loadFunctionHelp( item ); QString help = loadFunctionHelp( item );
@ -188,7 +193,7 @@ void QgsExpressionBuilderWidget::updateFunctionFileList( QString path )
dir.setNameFilters( QStringList() << "*.py" ); dir.setNameFilters( QStringList() << "*.py" );
QStringList files = dir.entryList( QDir::Files ); QStringList files = dir.entryList( QDir::Files );
cmbFileNames->clear(); cmbFileNames->clear();
Q_FOREACH ( const QString& name, files ) Q_FOREACH( const QString& name, files )
{ {
QFileInfo info( mFunctionsPath + QDir::separator() + name ); QFileInfo info( mFunctionsPath + QDir::separator() + name );
if ( info.baseName() == "__init__" ) continue; if ( info.baseName() == "__init__" ) continue;
@ -292,7 +297,7 @@ void QgsExpressionBuilderWidget::loadFieldNames( const QgsFields& fields )
void QgsExpressionBuilderWidget::loadFieldsAndValues( const QMap<QString, QStringList> &fieldValues ) void QgsExpressionBuilderWidget::loadFieldsAndValues( const QMap<QString, QStringList> &fieldValues )
{ {
QgsFields fields; QgsFields fields;
Q_FOREACH ( const QString& fieldName, fieldValues.keys() ) Q_FOREACH( const QString& fieldName, fieldValues.keys() )
{ {
fields.append( QgsField( fieldName ) ); fields.append( QgsField( fieldName ) );
} }
@ -308,20 +313,16 @@ void QgsExpressionBuilderWidget::fillFieldValues( const QString& fieldName, int
return; return;
// TODO We should thread this so that we don't hold the user up if the layer is massive. // TODO We should thread this so that we don't hold the user up if the layer is massive.
mValueListWidget->clear();
int fieldIndex = mLayer->fieldNameIndex( fieldName ); int fieldIndex = mLayer->fieldNameIndex( fieldName );
if ( fieldIndex < 0 ) if ( fieldIndex < 0 )
return; return;
mValueListWidget->setUpdatesEnabled( false );
mValueListWidget->blockSignals( true );
QList<QVariant> values; QList<QVariant> values;
QStringList strValues; QStringList strValues;
mLayer->uniqueValues( fieldIndex, values, countLimit ); mLayer->uniqueValues( fieldIndex, values, countLimit );
Q_FOREACH ( const QVariant& value, values ) Q_FOREACH( const QVariant& value, values )
{ {
QString strValue; QString strValue;
if ( value.isNull() ) if ( value.isNull() )
@ -330,13 +331,10 @@ void QgsExpressionBuilderWidget::fillFieldValues( const QString& fieldName, int
strValue = value.toString(); strValue = value.toString();
else else
strValue = "'" + value.toString().replace( "'", "''" ) + "'"; strValue = "'" + value.toString().replace( "'", "''" ) + "'";
mValueListWidget->addItem( strValue );
strValues.append( strValue ); strValues.append( strValue );
} }
mValuesModel->setStringList( strValues );
mFieldValues[fieldName] = strValues; mFieldValues[fieldName] = strValues;
mValueListWidget->setUpdatesEnabled( true );
mValueListWidget->blockSignals( false );
} }
void QgsExpressionBuilderWidget::registerItem( QString group, void QgsExpressionBuilderWidget::registerItem( QString group,
@ -417,7 +415,7 @@ void QgsExpressionBuilderWidget::loadRecent( QString key )
QSettings settings; QSettings settings;
QString location = QString( "/expressions/recent/%1" ).arg( key ); QString location = QString( "/expressions/recent/%1" ).arg( key );
QStringList expressions = settings.value( location ).toStringList(); QStringList expressions = settings.value( location ).toStringList();
Q_FOREACH ( const QString& expression, expressions ) Q_FOREACH( const QString& expression, expressions )
{ {
this->registerItem( name, expression, expression, expression ); this->registerItem( name, expression, expression, expression );
} }
@ -598,7 +596,7 @@ QString QgsExpressionBuilderWidget::formatPreviewString( const QString& previewS
void QgsExpressionBuilderWidget::loadExpressionContext() void QgsExpressionBuilderWidget::loadExpressionContext()
{ {
QStringList variableNames = mExpressionContext.filteredVariableNames(); QStringList variableNames = mExpressionContext.filteredVariableNames();
Q_FOREACH ( const QString& variable, variableNames ) Q_FOREACH( const QString& variable, variableNames )
{ {
registerItem( "Variables", variable, " @" + variable + " ", registerItem( "Variables", variable, " @" + variable + " ",
QgsExpression::variableHelpText( variable, true, mExpressionContext.variable( variable ) ), QgsExpression::variableHelpText( variable, true, mExpressionContext.variable( variable ) ),
@ -608,7 +606,7 @@ void QgsExpressionBuilderWidget::loadExpressionContext()
// Load the functions from the expression context // Load the functions from the expression context
QStringList contextFunctions = mExpressionContext.functionNames(); QStringList contextFunctions = mExpressionContext.functionNames();
Q_FOREACH ( const QString& functionName, contextFunctions ) Q_FOREACH( const QString& functionName, contextFunctions )
{ {
QgsExpression::Function* func = mExpressionContext.function( functionName ); QgsExpression::Function* func = mExpressionContext.function( functionName );
QString name = func->name(); QString name = func->name();
@ -629,6 +627,12 @@ void QgsExpressionBuilderWidget::on_txtSearchEdit_textChanged()
expressionTree->expandAll(); expressionTree->expandAll();
} }
void QgsExpressionBuilderWidget::on_txtSearchEditValues_textChanged()
{
mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
}
void QgsExpressionBuilderWidget::on_lblPreview_linkActivated( QString link ) void QgsExpressionBuilderWidget::on_lblPreview_linkActivated( QString link )
{ {
Q_UNUSED( link ); Q_UNUSED( link );
@ -638,10 +642,10 @@ void QgsExpressionBuilderWidget::on_lblPreview_linkActivated( QString link )
mv->exec(); mv->exec();
} }
void QgsExpressionBuilderWidget::on_mValueListWidget_itemDoubleClicked( QListWidgetItem *item ) void QgsExpressionBuilderWidget::on_mValuesListView_doubleClicked( const QModelIndex &index )
{ {
// Insert the item text or replace selected text // Insert the item text or replace selected text
txtExpressionString->insertText( " " + item->text() + " " ); txtExpressionString->insertText( " " + index.data( Qt::DisplayRole ).toString() + " " );
txtExpressionString->setFocus(); txtExpressionString->setFocus();
} }

View File

@ -25,6 +25,7 @@
#include "QStandardItemModel" #include "QStandardItemModel"
#include "QStandardItem" #include "QStandardItem"
#include "QSortFilterProxyModel" #include "QSortFilterProxyModel"
#include "QStringListModel"
/** An expression item that can be used in the QgsExpressionBuilderWidget tree. /** An expression item that can be used in the QgsExpressionBuilderWidget tree.
*/ */
@ -136,6 +137,7 @@ class QgsExpressionItemSearchProxy : public QSortFilterProxyModel
} }
}; };
/** A reusable widget that can be used to build a expression string. /** A reusable widget that can be used to build a expression string.
* See QgsExpressionBuilderDialog for exmaple of usage. * See QgsExpressionBuilderDialog for exmaple of usage.
*/ */
@ -238,8 +240,9 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp
void on_expressionTree_doubleClicked( const QModelIndex &index ); void on_expressionTree_doubleClicked( const QModelIndex &index );
void on_txtExpressionString_textChanged(); void on_txtExpressionString_textChanged();
void on_txtSearchEdit_textChanged(); void on_txtSearchEdit_textChanged();
void on_txtSearchEditValues_textChanged();
void on_lblPreview_linkActivated( QString link ); void on_lblPreview_linkActivated( QString link );
void on_mValueListWidget_itemDoubleClicked( QListWidgetItem* item ); void on_mValuesListView_doubleClicked( const QModelIndex &index );
void operatorButtonClicked(); void operatorButtonClicked();
void showContextMenu( const QPoint & ); void showContextMenu( const QPoint & );
void loadSampleValues(); void loadSampleValues();
@ -274,6 +277,8 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp
QString mFunctionsPath; QString mFunctionsPath;
QgsVectorLayer *mLayer; QgsVectorLayer *mLayer;
QStandardItemModel *mModel; QStandardItemModel *mModel;
QStringListModel *mValuesModel;
QSortFilterProxyModel *mProxyValues;
QgsExpressionItemSearchProxy *mProxyModel; QgsExpressionItemSearchProxy *mProxyModel;
QMap<QString, QgsExpressionItem*> mExpressionGroups; QMap<QString, QgsExpressionItem*> mExpressionGroups;
QgsFeature mFeature; QgsFeature mFeature;

View File

@ -321,15 +321,12 @@
</layout> </layout>
</widget> </widget>
<widget class="QFrame" name="moperationListGroup"> <widget class="QFrame" name="moperationListGroup">
<layout class="QGridLayout" name="gridLayout_5"> <layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QSplitter" name="functionsplit"> <widget class="QSplitter" name="functionsplit">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="opaqueResize">
<bool>false</bool>
</property>
<widget class="QWidget" name="layoutWidget"> <widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout_4"> <layout class="QVBoxLayout" name="verticalLayout_4">
<item> <item>
@ -366,8 +363,8 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="layoutWidget2"> <widget class="QWidget" name="">
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<widget class="QTextEdit" name="txtHelpText"> <widget class="QTextEdit" name="txtHelpText">
<property name="readOnly"> <property name="readOnly">
@ -376,116 +373,77 @@
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout_2"> <widget class="QFrame" name="mValueGroupBox">
<property name="spacing"> <layout class="QGridLayout" name="gridLayout_5">
<number>0</number> <property name="margin">
</property> <number>0</number>
<property name="sizeConstraint"> </property>
<enum>QLayout::SetDefaultConstraint</enum> <item row="0" column="0">
</property> <widget class="QLabel" name="label_4">
<property name="topMargin"> <property name="text">
<number>0</number> <string>Values</string>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="mValueGroupBox">
<layout class="QGridLayout" name="gridLayout_7">
<property name="margin">
<number>0</number>
</property> </property>
<property name="spacing"> </widget>
<number>0</number> </item>
<item row="0" column="1">
<widget class="QgsFilterLineEdit" name="txtSearchEditValues">
<property name="enabled">
<bool>true</bool>
</property> </property>
<item row="1" column="0" colspan="2"> </widget>
<widget class="QListWidget" name="mValueListWidget"> </item>
<property name="sizePolicy"> <item row="1" column="0" colspan="2">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <widget class="QListView" name="mValuesListView">
<horstretch>0</horstretch> <property name="editTriggers">
<verstretch>0</verstretch> <set>QAbstractItemView::NoEditTriggers</set>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="viewMode">
<enum>QListView::ListMode</enum>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Values</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="mLoadGroupBox" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="margin">
<number>0</number>
</property> </property>
<item> </widget>
<spacer name="horizontalSpacer"> </item>
<property name="orientation"> </layout>
<enum>Qt::Horizontal</enum> </widget>
</property> </item>
<property name="sizeHint" stdset="0"> <item>
<size> <widget class="QWidget" name="mLoadGroupBox" native="true">
<width>5</width> <layout class="QHBoxLayout" name="horizontalLayout">
<height>20</height> <property name="margin">
</size> <number>0</number>
</property> </property>
</spacer> <item>
</item> <spacer name="horizontalSpacer">
<item> <property name="orientation">
<widget class="QLabel" name="lblLoad"> <enum>Qt::Horizontal</enum>
<property name="text"> </property>
<string>Load values</string> <property name="sizeHint" stdset="0">
</property> <size>
</widget> <width>5</width>
</item> <height>20</height>
<item> </size>
<widget class="QPushButton" name="btnLoadAll"> </property>
<property name="text"> </spacer>
<string>all unique</string> </item>
</property> <item>
</widget> <widget class="QLabel" name="lblLoad">
</item> <property name="text">
<item> <string>Load values</string>
<widget class="QPushButton" name="btnLoadSample"> </property>
<property name="text"> </widget>
<string>10 samples</string> </item>
</property> <item>
</widget> <widget class="QPushButton" name="btnLoadAll">
</item> <property name="text">
</layout> <string>all unique</string>
</widget> </property>
</item> </widget>
</layout> </item>
<item>
<widget class="QPushButton" name="btnLoadSample">
<property name="text">
<string>10 samples</string>
</property>
</widget>
</item>
</layout>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>