Skip to content

Commit

Permalink
Add allowEvalError flag to QgsExpression widgets
Browse files Browse the repository at this point in the history
Some expression functions insist on a complete context which we
sometimes are not able to provide. The user might still have enough
knowledge that the expression is valid and an error will still be shown.
  • Loading branch information
m-kuhn committed Dec 14, 2017
1 parent e9d4c06 commit 3abff0e
Show file tree
Hide file tree
Showing 10 changed files with 298 additions and 33 deletions.
27 changes: 27 additions & 0 deletions python/gui/qgsexpressionbuilderdialog.sip
Expand Up @@ -60,6 +60,33 @@ The builder widget that is used by the dialog
void setGeomCalculator( const QgsDistanceArea &da );
%Docstring
Sets geometry calculator used in distance/area calculations.
%End

bool allowEvalErrors() const;
%Docstring
Allow accepting invalid expressions. This can be useful when we are not able to
provide an expression context of which we are sure it's completely populated.

.. versionadded:: 3.0
:rtype: bool
%End

void setAllowEvalErrors( bool allowEvalErrors );
%Docstring
Allow accepting expressions with evaluation errors. This can be useful when we are not able to
provide an expression context of which we are sure it's completely populated.

.. versionadded:: 3.0
%End

signals:

void allowEvalErrorsChanged();
%Docstring
Allow accepting expressions with evaluation errors. This can be useful when we are not able to
provide an expression context of which we are sure it's completely populated.

.. versionadded:: 3.0
%End

protected:
Expand Down
34 changes: 34 additions & 0 deletions python/gui/qgsexpressionbuilderwidget.sip
Expand Up @@ -260,6 +260,24 @@ Sets the expression string for the widget
.. versionadded:: 3.0
%End

bool evalError() const;
%Docstring
Will be set to true if the current expression text reported an eval error
with the context.

.. versionadded:: 3.0
:rtype: bool
%End

bool parserError() const;
%Docstring
Will be set to true if the current expression text reports a parser error
with the context.

.. versionadded:: 3.0
:rtype: bool
%End

public slots:

void loadSampleValues();
Expand Down Expand Up @@ -294,6 +312,22 @@ Sets the expression string for the widget
\param isValid Is true if the expression the user has typed is valid.
%End

void evalErrorChanged();
%Docstring
Will be set to true if the current expression text reported an eval error
with the context.

.. versionadded:: 3.0
%End

void parserErrorChanged();
%Docstring
Will be set to true if the current expression text reported a parser error
with the context.

.. versionadded:: 3.0
%End

protected:
virtual void showEvent( QShowEvent *e );

Expand Down
25 changes: 25 additions & 0 deletions python/gui/qgsfieldexpressionwidget.sip
Expand Up @@ -123,6 +123,23 @@ set the geometry calculator used in the expression dialog
\param generator A QgsExpressionContextGenerator class that will be used to
create an expression context when required.
.. versionadded:: 3.0
%End

bool allowEvalErrors() const;
%Docstring
Allow accepting expressions with evaluation errors. This can be useful when we are not able to
provide an expression context of which we are sure it's completely populated.

.. versionadded:: 3.0
:rtype: bool
%End

void setAllowEvalErrors( bool allowEvalErrors );
%Docstring
Allow accepting expressions with evaluation errors. This can be useful when we are not able to
provide an expression context of which we are sure it's completely populated.

.. versionadded:: 3.0
%End

signals:
Expand All @@ -134,6 +151,14 @@ the signal is emitted when the currently selected field changes
void fieldChanged( const QString &fieldName, bool isValid );
%Docstring
fieldChanged signal with indication of the validity of the expression
%End

void allowEvalErrorsChanged();
%Docstring
Allow accepting expressions with evaluation errors. This can be useful when we are not able to
provide an expression context of which we are sure it's completely populated.

.. versionadded:: 3.0
%End

public slots:
Expand Down
41 changes: 19 additions & 22 deletions src/core/expression/qgsexpressionfunction.cpp
Expand Up @@ -3452,37 +3452,34 @@ static QVariant fcnRepresentValue( const QVariantList &values, const QgsExpressi
else if ( values.size() == 2 )
fieldName = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
}

QVariant value = values.at( 0 );

QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( "layer" ), parent );
const QgsFields fields = context->fields();
int fieldIndex = fields.lookupField( fieldName );

if ( layer )
if ( fieldIndex == -1 )
{
parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( QStringLiteral( "represent_value" ), fieldName ) );
}
else
{
const QgsFields fields = layer->fields();
int index = fields.lookupField( fieldName );
QgsVectorLayer *layer = QgsExpressionUtils::getVectorLayer( context->variable( "layer" ), parent );
const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
const QgsFieldFormatter *formatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );

const QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer ? layer->id() : QStringLiteral( "[None]" ), fieldName );

if ( index == -1 )
QVariant cache;
if ( !context->hasCachedValue( cacheKey ) )
{
parent->setEvalErrorString( QCoreApplication::translate( "expression", "%1: Field not found %2" ).arg( QStringLiteral( "represent_value" ), fieldName ) );
cache = formatter->createCache( layer, fieldIndex, setup.config() );
context->setCachedValue( cacheKey, cache );
}
else
{
QgsEditorWidgetSetup setup = layer->editorWidgetSetup( index );
QgsFieldFormatter *formatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
cache = context->cachedValue( cacheKey );

QString cacheKey = QStringLiteral( "repvalfcn:%1:%2" ).arg( layer->id(), fieldName );

QVariant cache;
if ( !context->hasCachedValue( cacheKey ) )
{
cache = formatter->createCache( layer, index, setup.config() );
context->setCachedValue( cacheKey, cache );
}
else
cache = context->cachedValue( cacheKey );

result = formatter->representValue( layer, index, setup.config(), cache, value );
}
result = formatter->representValue( layer, fieldIndex, setup.config(), cache, value );
}

return result;
Expand Down
32 changes: 29 additions & 3 deletions src/gui/qgsexpressionbuilderdialog.cpp
Expand Up @@ -25,16 +25,15 @@ QgsExpressionBuilderDialog::QgsExpressionBuilderDialog( QgsVectorLayer *layer, c
setupUi( this );
QgsGui::instance()->enableAutoGeometryRestore( this );

QPushButton *okButton = buttonBox->button( QDialogButtonBox::Ok );
connect( builder, &QgsExpressionBuilderWidget::expressionParsed, okButton, &QWidget::setEnabled );
connect( builder, &QgsExpressionBuilderWidget::parserErrorChanged, this, &QgsExpressionBuilderDialog::syncOkButtonEnabledState );
connect( builder, &QgsExpressionBuilderWidget::evalErrorChanged, this, &QgsExpressionBuilderDialog::syncOkButtonEnabledState );

builder->setExpressionContext( context );
builder->setLayer( layer );
builder->setExpressionText( startText );
builder->loadFieldNames();
builder->loadRecent( mRecentKey );


connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsExpressionBuilderDialog::showHelp );
}

Expand Down Expand Up @@ -80,7 +79,34 @@ void QgsExpressionBuilderDialog::setGeomCalculator( const QgsDistanceArea &da )
builder->setGeomCalculator( da );
}

bool QgsExpressionBuilderDialog::allowEvalErrors() const
{
return mAllowEvalErrors;
}

void QgsExpressionBuilderDialog::setAllowEvalErrors( bool allowEvalErrors )
{
if ( allowEvalErrors == mAllowEvalErrors )
return;

mAllowEvalErrors = allowEvalErrors;
syncOkButtonEnabledState();
emit allowEvalErrorsChanged();
}

void QgsExpressionBuilderDialog::showHelp()
{
QgsHelp::openHelp( QStringLiteral( "working_with_vector/expression.html" ) );
}

void QgsExpressionBuilderDialog::syncOkButtonEnabledState()
{
QPushButton *okButton = buttonBox->button( QDialogButtonBox::Ok );

if ( builder->parserError() )
okButton->setEnabled( false );
else if ( !builder->evalError() || mAllowEvalErrors )
okButton->setEnabled( true );
else
okButton->setEnabled( true );
}
30 changes: 30 additions & 0 deletions src/gui/qgsexpressionbuilderdialog.h
Expand Up @@ -31,6 +31,8 @@ class GUI_EXPORT QgsExpressionBuilderDialog : public QDialog, private Ui::QgsExp
{
Q_OBJECT

Q_PROPERTY( bool allowEvalErrors READ allowEvalErrors WRITE setAllowEvalErrors NOTIFY allowEvalErrorsChanged )

public:
QgsExpressionBuilderDialog( QgsVectorLayer *layer,
const QString &startText = QString(),
Expand Down Expand Up @@ -65,6 +67,32 @@ class GUI_EXPORT QgsExpressionBuilderDialog : public QDialog, private Ui::QgsExp
//! Sets geometry calculator used in distance/area calculations.
void setGeomCalculator( const QgsDistanceArea &da );

/**
* Allow accepting invalid expressions. This can be useful when we are not able to
* provide an expression context of which we are sure it's completely populated.
*
* \since QGIS 3.0
*/
bool allowEvalErrors() const;

/**
* Allow accepting expressions with evaluation errors. This can be useful when we are not able to
* provide an expression context of which we are sure it's completely populated.
*
* \since QGIS 3.0
*/
void setAllowEvalErrors( bool allowEvalErrors );

signals:

/**
* Allow accepting expressions with evaluation errors. This can be useful when we are not able to
* provide an expression context of which we are sure it's completely populated.
*
* \since QGIS 3.0
*/
void allowEvalErrorsChanged();

protected:

/**
Expand All @@ -79,9 +107,11 @@ class GUI_EXPORT QgsExpressionBuilderDialog : public QDialog, private Ui::QgsExp

private:
QString mRecentKey;
bool mAllowEvalErrors = false;

private slots:
void showHelp();
void syncOkButtonEnabledState();

};

Expand Down
40 changes: 37 additions & 3 deletions src/gui/qgsexpressionbuilderwidget.cpp
Expand Up @@ -578,6 +578,8 @@ void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
txtExpressionString->setToolTip( QLatin1String( "" ) );
lblPreview->setToolTip( QLatin1String( "" ) );
emit expressionParsed( false );
setParserError( true );
setEvalError( true );
return;
}

Expand Down Expand Up @@ -614,14 +616,18 @@ void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
txtExpressionString->setToolTip( tooltip );
lblPreview->setToolTip( tooltip );
emit expressionParsed( false );
setParserError( exp.hasParserError() );
setEvalError( exp.hasEvalError() );
return;
}
else
{
lblPreview->setStyleSheet( QLatin1String( "" ) );
txtExpressionString->setToolTip( QLatin1String( "" ) );
lblPreview->setToolTip( QLatin1String( "" ) );
lblPreview->setStyleSheet( QString() );
txtExpressionString->setToolTip( QString() );
lblPreview->setToolTip( QString() );
emit expressionParsed( true );
setParserError( false );
setEvalError( false );
}
}

Expand Down Expand Up @@ -672,6 +678,34 @@ QString QgsExpressionBuilderWidget::formatLayerHelp( const QgsMapLayer *layer )
return text;
}

bool QgsExpressionBuilderWidget::parserError() const
{
return mParserError;
}

void QgsExpressionBuilderWidget::setParserError( bool parserError )
{
if ( parserError == mParserError )
return;

mParserError = parserError;
emit parserErrorChanged();
}

bool QgsExpressionBuilderWidget::evalError() const
{
return mEvalError;
}

void QgsExpressionBuilderWidget::setEvalError( bool evalError )
{
if ( evalError == mEvalError )
return;

mEvalError = evalError;
emit evalErrorChanged();
}

QStandardItemModel *QgsExpressionBuilderWidget::model()
{
return mModel;
Expand Down

0 comments on commit 3abff0e

Please sign in to comment.