QGIS API Documentation  3.24.2-Tisler (13c1a02865)
qgsexpressionpreviewwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsexpressionpreviewwidget.cpp
3  --------------------------------------
4  Date : march 2020 - quarantine day 12
5  Copyright : (C) 2020 by Denis Rouzaud
6  Email : [email protected]
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
17 #include "qgsmessageviewer.h"
18 #include "qgsvectorlayer.h"
19 #include "qgsfeaturepickerwidget.h"
20 
21 
22 
23 
24 
26  : QWidget( parent )
27 {
28  setupUi( this );
29  mPreviewLabel->clear();
30  mFeaturePickerWidget->setShowBrowserButtons( true );
31 
33  connect( mPreviewLabel, &QLabel::linkActivated, this, &QgsExpressionPreviewWidget::linkActivated );
34 }
35 
37 {
38  mLayer = layer;
39  mFeaturePickerWidget->setLayer( layer );
40 }
41 
42 void QgsExpressionPreviewWidget::setExpressionText( const QString &expression )
43 {
44  mExpressionText = expression;
45  refreshPreview();
46 }
47 
49 {
50  // todo: update the combo box if it has been set externaly?
51 
52  mExpressionContext.setFeature( feature );
53  refreshPreview();
54 }
55 
57 {
58  mDa = da;
59  mUseGeomCalculator = true;
60 }
61 
63 {
64  mExpressionContext = context;
65 }
66 
67 void QgsExpressionPreviewWidget::refreshPreview()
68 {
69  // If the string is empty the expression will still "fail" although
70  // we don't show the user an error as it will be confusing.
71  if ( mExpressionText.isEmpty() )
72  {
73  mPreviewLabel->clear();
74  mPreviewLabel->setStyleSheet( QString() );
75  setExpressionToolTip( QString() );
76  emit expressionParsed( false );
77  mExpression = QgsExpression();
78  }
79  else
80  {
81  mExpression = QgsExpression( mExpressionText );
82 
83  if ( mUseGeomCalculator )
84  {
85  // only set an explicit geometry calculator if a call to setGeomCalculator was made. If not,
86  // let the expression context handle this correctly
87  mExpression.setGeomCalculator( &mDa );
88  }
89 
90  const QVariant value = mExpression.evaluate( &mExpressionContext );
91  const QString preview = QgsExpression::formatPreviewString( value );
92  if ( !mExpression.hasEvalError() )
93  {
94  mPreviewLabel->setText( preview );
95  }
96 
97  if ( mExpression.hasParserError() || mExpression.hasEvalError() )
98  {
99  // if parser error was a result of missing feature, then skip the misleading parser error message
100  // and instead show a friendly message, and allow the user to accept the expression anyway
101  // refs https://github.com/qgis/QGIS/issues/42884
102  if ( !mExpressionContext.feature().isValid() )
103  {
104  if ( !mExpression.referencedColumns().isEmpty() || mExpression.needsGeometry() )
105  {
106  mPreviewLabel->setText( tr( "No feature was found on this layer to evaluate the expression." ) );
107  mPreviewLabel->setStyleSheet( QStringLiteral( "color: rgba(220, 125, 0, 255);" ) );
108  emit expressionParsed( true );
109  setParserError( false );
110  setEvalError( false );
111  return;
112  }
113  }
114 
115  const QString errorString = mExpression.parserErrorString().replace( QLatin1String( "\n" ), QLatin1String( "<br>" ) );
116  QString tooltip;
117  if ( mExpression.hasParserError() )
118  tooltip = QStringLiteral( "<b>%1:</b>"
119  "%2" ).arg( tr( "Parser Errors" ), errorString );
120  // Only show the eval error if there is no parser error.
121  if ( !mExpression.hasParserError() && mExpression.hasEvalError() )
122  tooltip += QStringLiteral( "<b>%1:</b> %2" ).arg( tr( "Eval Error" ), mExpression.evalErrorString() );
123 
124  mPreviewLabel->setText( tr( "Expression is invalid <a href=""more"">(more info)</a>" ) );
125  mPreviewLabel->setStyleSheet( QStringLiteral( "color: rgba(255, 6, 10, 255);" ) );
126  setExpressionToolTip( tooltip );
127  emit expressionParsed( false );
128  setParserError( mExpression.hasParserError() );
129  setEvalError( mExpression.hasEvalError() );
130  }
131  else
132  {
133  mPreviewLabel->setStyleSheet( QString() );
134  const QString longerPreview = QgsExpression::formatPreviewString( value, true, 255 );
135  if ( longerPreview != preview )
136  setExpressionToolTip( longerPreview );
137  else
138  setExpressionToolTip( QString() );
139  emit expressionParsed( true );
140  setParserError( false );
141  setEvalError( false );
142  }
143  }
144 }
145 
146 void QgsExpressionPreviewWidget::linkActivated( const QString & )
147 {
148  QgsMessageViewer mv( this, QgsGuiUtils::ModalDialogFlags, false );
149  mv.setWindowTitle( tr( "More Info on Expression Error" ) );
150  mv.setMessageAsHtml( mToolTip );
151  mv.exec();
152 }
153 
154 void QgsExpressionPreviewWidget::setExpressionToolTip( const QString &toolTip )
155 {
156  if ( toolTip == mToolTip )
157  return;
158 
159  mToolTip = toolTip;
160  mPreviewLabel->setToolTip( mToolTip );
161  emit toolTipChanged( mToolTip );
162 }
163 
164 void QgsExpressionPreviewWidget::setParserError( bool parserError )
165 {
166  if ( parserError != mParserError )
167  {
168  mParserError = parserError;
169  emit parserErrorChanged();
170  }
171 }
173 {
174  return mParserError;
175 }
176 
177 void QgsExpressionPreviewWidget::setEvalError( bool evalError )
178 {
179  if ( evalError == mEvalError )
180  return;
181 
182  mEvalError = evalError;
183  emit evalErrorChanged();
184 }
185 
187 {
188  return mEvalError;
189 }
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
QgsExpressionPreviewWidget(QWidget *parent=nullptr)
Constructor.
void parserErrorChanged()
Will be set to true if the current expression text reported a parser error with the context.
void evalErrorChanged()
Will be set to true if the current expression text reported an eval error with the context.
void toolTipChanged(const QString &toolTip)
Emitted whenever the tool tip changed.
bool parserError() const
Will be set to true if the current expression text reports a parser error with the context.
void setGeomCalculator(const QgsDistanceArea &da)
Sets geometry calculator used in distance/area calculations.
void expressionParsed(bool isValid)
Emitted when the user changes the expression in the widget.
bool evalError() const
Will be set to true if the current expression text reported an eval error with the context.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context for the widget.
void setLayer(QgsVectorLayer *layer)
Sets the layer used in the preview.
void setCurrentFeature(const QgsFeature &feature)
sets the current feature used
void setExpressionText(const QString &expression)
Sets the expression.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
QString evalErrorString() const
Returns evaluation error.
QString parserErrorString() const
Returns parser error.
static QString formatPreviewString(const QVariant &value, bool htmlOutput=true, int maximumPreviewLength=60)
Formats an expression result for friendly display to the user.
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
void setGeomCalculator(const QgsDistanceArea *calc)
Sets the geometry calculator used for distance and area calculations in expressions.
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QVariant evaluate()
Evaluate the feature and return the result.
void featureChanged(const QgsFeature &feature)
Sends the feature as soon as it is chosen.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:209
A generic message view for displaying QGIS messages.
Represents a vector layer which manages a vector based data sets.