Skip to content
Permalink
Browse files

Merge pull request #38659 from suricactus/relref_filter_expr

Add support for filter expressions in relation reference widget
  • Loading branch information
m-kuhn committed Sep 10, 2020
2 parents 571e70e + 5818359 commit a853f2643df7d35f0980e8f92acb688fe3f15344
@@ -136,6 +136,16 @@ Set if filters are chained.
Chained filters restrict the option of subsequent filters based on the selection of a previous filter.

:param chainFilters: If chaining should be enabled
%End

QString filterExpression() const;
%Docstring
Returns the currently set filter expression.
%End
void setFilterExpression( const QString &filterExpression );
%Docstring
If not empty, will be used as filter expression.
Only if this evaluates to ``True``, the value will be shown.
%End

QgsFeature referencedFeature() const;
@@ -21,6 +21,7 @@
#include "qgsrelationmanager.h"
#include "qgsvectorlayer.h"
#include "qgsexpressionbuilderdialog.h"
#include "qgsexpressioncontextutils.h"

QgsRelationReferenceConfigDlg::QgsRelationReferenceConfigDlg( QgsVectorLayer *vl, int fieldIdx, QWidget *parent )
: QgsEditorConfigWidget( vl, fieldIdx, parent )
@@ -59,6 +60,34 @@ QgsRelationReferenceConfigDlg::QgsRelationReferenceConfigDlg( QgsVectorLayer *vl
connect( mFilterFieldsList, &QListWidget::itemChanged, this, &QgsEditorConfigWidget::changed );
connect( mCbxChainFilters, &QAbstractButton::toggled, this, &QgsEditorConfigWidget::changed );
connect( mExpressionWidget, static_cast<void ( QgsFieldExpressionWidget::* )( const QString & )>( &QgsFieldExpressionWidget::fieldChanged ), this, &QgsEditorConfigWidget::changed );
connect( mEditExpression, &QAbstractButton::clicked, this, &QgsRelationReferenceConfigDlg::mEditExpression_clicked );
connect( mFilterExpression, &QTextEdit::textChanged, this, &QgsEditorConfigWidget::changed );
}

void QgsRelationReferenceConfigDlg::mEditExpression_clicked()
{
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer() );
if ( !vl )
return;

QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( vl ) );
context << QgsExpressionContextUtils::formScope( );
context << QgsExpressionContextUtils::parentFormScope( );

context.setHighlightedFunctions( QStringList() << QStringLiteral( "current_value" ) << QStringLiteral( "current_parent_value" ) );
context.setHighlightedVariables( QStringList() << QStringLiteral( "current_geometry" )
<< QStringLiteral( "current_feature" )
<< QStringLiteral( "form_mode" )
<< QStringLiteral( "current_parent_geometry" )
<< QStringLiteral( "current_parent_feature" ) );

QgsExpressionBuilderDialog dlg( vl, mFilterExpression->toPlainText(), this, QStringLiteral( "generic" ), context );
dlg.setWindowTitle( tr( "Edit Filter Expression" ) );

if ( dlg.exec() == QDialog::Accepted )
{
mFilterExpression->setPlainText( dlg.expressionBuilder()->expressionText() );
}
}

void QgsRelationReferenceConfigDlg::setConfig( const QVariantMap &config )
@@ -77,6 +106,7 @@ void QgsRelationReferenceConfigDlg::setConfig( const QVariantMap &config )
mCbxMapIdentification->setChecked( config.value( QStringLiteral( "MapIdentification" ), false ).toBool() );
mCbxAllowAddFeatures->setChecked( config.value( QStringLiteral( "AllowAddFeatures" ), false ).toBool() );
mCbxReadOnly->setChecked( config.value( QStringLiteral( "ReadOnly" ), false ).toBool() );
mFilterExpression->setPlainText( config.value( QStringLiteral( "FilterExpression" ) ).toString() );

if ( config.contains( QStringLiteral( "FilterFields" ) ) )
{
@@ -149,6 +179,7 @@ QVariantMap QgsRelationReferenceConfigDlg::config()
myConfig.insert( QStringLiteral( "FilterFields" ), filterFields );

myConfig.insert( QStringLiteral( "ChainFilters" ), mCbxChainFilters->isChecked() );
myConfig.insert( QStringLiteral( "FilterExpression" ), mFilterExpression->toPlainText() );
}

if ( mReferencedLayer )
@@ -51,6 +51,11 @@ class GUI_EXPORT QgsRelationReferenceConfigDlg : public QgsEditorConfigWidget, p
void relationChanged( int idx );
void mAddFilterButton_clicked();
void mRemoveFilterButton_clicked();

/**
* Opens an expression dialog and sets its value as filter expression for the relation reference.
*/
void mEditExpression_clicked();
};

#endif // QGSRELATIONREFERENCECONFIGDLGBASE_H
@@ -217,6 +217,7 @@ void QgsRelationReferenceSearchWidgetWrapper::initWidget( QWidget *editor )
{
mWidget->setFilterFields( config( QStringLiteral( "FilterFields" ) ).toStringList() );
mWidget->setChainFilters( config( QStringLiteral( "ChainFilters" ) ).toBool() );
mWidget->setFilterExpression( config( QStringLiteral( "FilterExpression" ) ).toString() );
}

QgsRelation relation = QgsProject::instance()->relationManager()->relation( config( QStringLiteral( "Relation" ) ).toString() );
@@ -189,6 +189,7 @@ void QgsRelationReferenceWidget::setRelation( const QgsRelation &relation, bool
mRelation = relation;
mReferencingLayer = relation.referencingLayer();
mReferencedLayer = relation.referencedLayer();

const QList<QgsRelation::FieldPair> fieldPairs = relation.fieldPairs();
for ( const QgsRelation::FieldPair &fieldPair : fieldPairs )
{
@@ -199,6 +200,7 @@ void QgsRelationReferenceWidget::setRelation( const QgsRelation &relation, bool
mComboBox->setAllowNull( mAllowNull );
mComboBox->setSourceLayer( mReferencedLayer );
mComboBox->setIdentifierFields( mReferencedFields );
mComboBox->setFilterExpression( mFilterExpression );
}
mAttributeEditorFrame->setObjectName( QStringLiteral( "referencing/" ) + relation.name() );

@@ -487,6 +489,11 @@ void QgsRelationReferenceWidget::setChainFilters( bool chainFilters )
mChainFilters = chainFilters;
}

void QgsRelationReferenceWidget::setFilterExpression( const QString &expression )
{
mFilterExpression = expression;
}

void QgsRelationReferenceWidget::showEvent( QShowEvent *e )
{
Q_UNUSED( e )
@@ -542,7 +549,9 @@ void QgsRelationReferenceWidget::init()
QVariant nullValue = QgsApplication::nullRepresentation();

QgsFeature ft;
QgsFeatureIterator fit = mReferencedLayer->getFeatures();
QgsFeatureIterator fit = mFilterExpression.isEmpty()
? mReferencedLayer->getFeatures()
: mReferencedLayer->getFeatures( mFilterExpression );
while ( fit.nextFeature( ft ) )
{
const int count = std::min( mFilterComboBoxes.count(), mFilterFields.count() );
@@ -574,6 +583,9 @@ void QgsRelationReferenceWidget::init()
mComboBox->setAllowNull( mAllowNull );
mComboBox->setIdentifierFields( mReferencedFields );

if ( ! mFilterExpression.isEmpty() )
mComboBox->setFilterExpression( mFilterExpression );

QVariant nullValue = QgsApplication::nullRepresentation();

if ( mChainFilters && mFeature.isValid() )
@@ -851,7 +863,7 @@ void QgsRelationReferenceWidget::filterChanged()

QgsFeature f;
QgsFeatureIds featureIds;
QString filterExpression;
QString filterExpression = mFilterExpression;

// comboboxes have to be disabled before building filters
if ( mChainFilters )
@@ -907,9 +919,16 @@ void QgsRelationReferenceWidget::filterChanged()
{
QMap<QString, QString> filtersAttrs = filters;
filtersAttrs[fieldName] = QgsExpression::createFieldEqualityExpression( fieldName, txt );
QString expression = filtersAttrs.values().join( QStringLiteral( " AND " ) );

QgsAttributeList subset = attrs;

QString expression = filterExpression;
if ( ! filterExpression.isEmpty() && ! filtersAttrs.values().isEmpty() )
expression += QStringLiteral( " AND " );

expression += filtersAttrs.isEmpty() ? QString() : QStringLiteral( " ( " );
expression += filtersAttrs.values().join( QStringLiteral( " AND " ) );
expression += filtersAttrs.isEmpty() ? QString() : QStringLiteral( " ) " );

subset << mReferencedLayer->fields().lookupField( fieldName );

QgsFeatureIterator it( mReferencedLayer->getFeatures( QgsFeatureRequest().setFilterExpression( expression ).setSubsetOfAttributes( subset ) ) );
@@ -938,7 +957,14 @@ void QgsRelationReferenceWidget::filterChanged()
}
}
}
filterExpression = filters.values().join( QStringLiteral( " AND " ) );

if ( ! filterExpression.isEmpty() && ! filters.values().isEmpty() )
filterExpression += QStringLiteral( " AND " );

filterExpression += filters.isEmpty() ? QString() : QStringLiteral( " ( " );
filterExpression += filters.values().join( QStringLiteral( " AND " ) );
filterExpression += filters.isEmpty() ? QString() : QStringLiteral( " ) " );

mComboBox->setFilterExpression( filterExpression );
}

@@ -157,6 +157,17 @@ class GUI_EXPORT QgsRelationReferenceWidget : public QWidget
*/
void setChainFilters( bool chainFilters );

/**
* Returns the currently set filter expression.
*/
QString filterExpression() const { return mFilterExpression; };

/**
* If not empty, will be used as filter expression.
* Only if this evaluates to TRUE, the value will be shown.
*/
void setFilterExpression( const QString &filterExpression );

/**
* Returns the related feature (from the referenced layer)
* if no feature is related, it returns an invalid feature
@@ -313,6 +324,7 @@ class GUI_EXPORT QgsRelationReferenceWidget : public QWidget
QgsVectorLayer *mReferencingLayer = nullptr;
QgsFeatureListComboBox *mComboBox = nullptr;
QList<QComboBox *> mFilterComboBoxes;
QString mFilterExpression;
QWidget *mWindowWidget = nullptr;
bool mShown = false;
QgsRelation mRelation;
@@ -18,6 +18,7 @@
#include "qgsproject.h"
#include "qgsrelationmanager.h"
#include "qgsrelationreferencewidget.h"
#include "qgsattributeform.h"

QgsRelationReferenceWidgetWrapper::QgsRelationReferenceWidgetWrapper( QgsVectorLayer *vl, int fieldIdx, QWidget *editor, QgsMapCanvas *canvas, QgsMessageBar *messageBar, QWidget *parent )
: QgsEditorWidgetWrapper( vl, fieldIdx, editor, parent )
@@ -62,6 +63,7 @@ void QgsRelationReferenceWidgetWrapper::initWidget( QWidget *editor )
{
mWidget->setFilterFields( config( QStringLiteral( "FilterFields" ) ).toStringList() );
mWidget->setChainFilters( config( QStringLiteral( "ChainFilters" ) ).toBool() );
mWidget->setFilterExpression( config( QStringLiteral( "FilterExpression" ) ).toString() );
}
mWidget->setAllowAddFeatures( config( QStringLiteral( "AllowAddFeatures" ), false ).toBool() );

@@ -73,6 +73,8 @@ class GUI_EXPORT QgsRelationReferenceWidgetWrapper : public QgsEditorWidgetWrapp
private:
void updateValues( const QVariant &val, const QVariantList &additionalValues = QVariantList() ) override;

QString mExpression;

QgsRelationReferenceWidget *mWidget = nullptr;
QgsMapCanvas *mCanvas = nullptr;
QgsMessageBar *mMessageBar = nullptr;
@@ -139,6 +139,7 @@ QWidget *QgsValueRelationWidgetWrapper::createWidget( QWidget *parent )
{
return new QgsFilterLineEdit( parent );
}
else
{
return new QComboBox( parent );
}
@@ -99,41 +99,46 @@
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="4" column="1">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="1">
<widget class="QToolButton" name="mAddFilterButton">
<property name="text">
<string>…</string>
</property>
<property name="icon">
<iconset>
<iconset resource="../../../images/images.qrc">
<normaloff>:/images/themes/default/symbologyAdd.svg</normaloff>:/images/themes/default/symbologyAdd.svg</iconset>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QToolButton" name="mRemoveFilterButton">
<property name="text">
<string>…</string>
<item row="1" column="0" rowspan="4">
<widget class="QListWidget" name="mAvailableFieldsList"/>
</item>
<item row="1" column="2" rowspan="4">
<widget class="QListWidget" name="mFilterFieldsList"/>
</item>
<item row="5" column="0" colspan="3">
<widget class="QCheckBox" name="mCbxChainFilters">
<property name="toolTip">
<string>When activated, the filters will restrict the choices of fields to options that are </string>
</property>
<property name="icon">
<iconset resource="../../../images/images.qrc">
<normaloff>:/images/themes/default/symbologyRemove.svg</normaloff>:/images/themes/default/symbologyRemove.svg</iconset>
<property name="text">
<string>Chain filters</string>
</property>
</widget>
</item>
<item row="4" column="1">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
@@ -147,22 +152,55 @@
</property>
</spacer>
</item>
<item row="1" column="2" rowspan="4">
<widget class="QListWidget" name="mFilterFieldsList"/>
</item>
<item row="1" column="0" rowspan="4">
<widget class="QListWidget" name="mAvailableFieldsList"/>
</item>
<item row="5" column="0" colspan="3">
<widget class="QCheckBox" name="mCbxChainFilters">
<property name="toolTip">
<string>When activated, the filters will restrict the choices of fields to options that are </string>
<item row="6" column="0" colspan="3">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="topMargin">
<number>10</number>
</property>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Filter expression</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="mEditExpression">
<property name="icon">
<iconset resource="../../../images/images.qrc">
<normaloff>:/images/themes/default/mIconExpression.svg</normaloff>:/images/themes/default/mIconExpression.svg</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="3" column="1">
<widget class="QToolButton" name="mRemoveFilterButton">
<property name="text">
<string>Chain filters</string>
<string>…</string>
</property>
<property name="icon">
<iconset resource="../../../images/images.qrc">
<normaloff>:/images/themes/default/symbologyRemove.svg</normaloff>:/images/themes/default/symbologyRemove.svg</iconset>
</property>
</widget>
</item>
<item row="7" column="0" colspan="3">
<widget class="QTextEdit" name="mFilterExpression"/>
</item>
</layout>
</widget>
</item>

0 comments on commit a853f26

Please sign in to comment.
You can’t perform that action at this time.