QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 
16 
17 #include "qclipboard.h"
18 #include "qaction.h"
19 #include "qgsapplication.h"
21 #include "qgsmessageviewer.h"
22 #include "qgsvectorlayer.h"
23 #include "qgsfeaturepickerwidget.h"
24 
25 
26 
28  : QWidget( parent )
29 {
30  setupUi( this );
31  mPreviewLabel->clear();
32  mPreviewLabel->setContextMenuPolicy( Qt::ActionsContextMenu );
33  mCopyPreviewAction = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditCopy.svg" ) ), tr( "Copy Expression Value" ), this );
34  mPreviewLabel->addAction( mCopyPreviewAction );
35  mFeaturePickerWidget->setShowBrowserButtons( true );
36 
38  connect( mPreviewLabel, &QLabel::linkActivated, this, &QgsExpressionPreviewWidget::linkActivated );
39  connect( mCopyPreviewAction, &QAction::triggered, this, &QgsExpressionPreviewWidget::copyFullExpressionValue );
40 }
41 
43 {
44  mLayer = layer;
45  mFeaturePickerWidget->setLayer( layer );
46 }
47 
48 void QgsExpressionPreviewWidget::setExpressionText( const QString &expression )
49 {
50  mExpressionText = expression;
51  refreshPreview();
52 }
53 
55 {
56  // todo: update the combo box if it has been set externaly?
57 
58  mExpressionContext.setFeature( feature );
59  refreshPreview();
60 }
61 
63 {
64  mDa = da;
65  mUseGeomCalculator = true;
66 }
67 
69 {
70  mExpressionContext = context;
71 }
72 
73 void QgsExpressionPreviewWidget::refreshPreview()
74 {
75  // If the string is empty the expression will still "fail" although
76  // we don't show the user an error as it will be confusing.
77  if ( mExpressionText.isEmpty() )
78  {
79  mPreviewLabel->clear();
80  mPreviewLabel->setStyleSheet( QString() );
81  mCopyPreviewAction->setEnabled( false );
82  setExpressionToolTip( QString() );
83  emit expressionParsed( false );
84  mExpression = QgsExpression();
85  }
86  else
87  {
88  mExpression = QgsExpression( mExpressionText );
89 
90  if ( mUseGeomCalculator )
91  {
92  // only set an explicit geometry calculator if a call to setGeomCalculator was made. If not,
93  // let the expression context handle this correctly
94  mExpression.setGeomCalculator( &mDa );
95  }
96  const QVariant value = mExpression.evaluate( &mExpressionContext );
97  const QString preview = QgsExpression::formatPreviewString( value );
98  if ( !mExpression.hasEvalError() )
99  {
100  mPreviewLabel->setText( preview );
101  mCopyPreviewAction->setEnabled( true );
102  }
103 
104  if ( mExpression.hasParserError() || mExpression.hasEvalError() )
105  {
106  // if parser error was a result of missing feature, then skip the misleading parser error message
107  // and instead show a friendly message, and allow the user to accept the expression anyway
108  // refs https://github.com/qgis/QGIS/issues/42884
109  if ( !mExpressionContext.feature().isValid() )
110  {
111  if ( !mExpression.referencedColumns().isEmpty() || mExpression.needsGeometry() )
112  {
113  mPreviewLabel->setText( tr( "No feature was found on this layer to evaluate the expression." ) );
114  mPreviewLabel->setStyleSheet( QStringLiteral( "color: rgba(220, 125, 0, 255);" ) );
115  emit expressionParsed( true );
116  setParserError( false );
117  setEvalError( false );
118  return;
119  }
120  }
121 
122  const QString errorString = mExpression.parserErrorString().replace( QLatin1String( "\n" ), QLatin1String( "<br>" ) );
123  QString tooltip;
124  if ( mExpression.hasParserError() )
125  tooltip = QStringLiteral( "<b>%1:</b>"
126  "%2" ).arg( tr( "Parser Errors" ), errorString );
127  // Only show the eval error if there is no parser error.
128  if ( !mExpression.hasParserError() && mExpression.hasEvalError() )
129  tooltip += QStringLiteral( "<b>%1:</b> %2" ).arg( tr( "Eval Error" ), mExpression.evalErrorString() );
130 
131  mPreviewLabel->setText( tr( "Expression is invalid <a href=""more"">(more info)</a>" ) );
132  mPreviewLabel->setStyleSheet( QStringLiteral( "color: rgba(255, 6, 10, 255);" ) );
133  setExpressionToolTip( tooltip );
134  emit expressionParsed( false );
135  setParserError( mExpression.hasParserError() );
136  setEvalError( mExpression.hasEvalError() );
137  mCopyPreviewAction->setEnabled( false );
138  }
139  else
140  {
141  mPreviewLabel->setStyleSheet( QString() );
142  const QString longerPreview = QgsExpression::formatPreviewString( value, true, 255 );
143  if ( longerPreview != preview )
144  setExpressionToolTip( longerPreview );
145  else
146  setExpressionToolTip( QString() );
147  emit expressionParsed( true );
148  setParserError( false );
149  setEvalError( false );
150  mCopyPreviewAction->setEnabled( true );
151  }
152  }
153 }
154 
155 void QgsExpressionPreviewWidget::linkActivated( const QString & )
156 {
157  QgsMessageViewer mv( this, QgsGuiUtils::ModalDialogFlags, false );
158  mv.setWindowTitle( tr( "More Info on Expression Error" ) );
159  mv.setMessageAsHtml( mToolTip );
160  mv.exec();
161 }
162 
163 void QgsExpressionPreviewWidget::setExpressionToolTip( const QString &toolTip )
164 {
165  if ( toolTip == mToolTip )
166  return;
167 
168  mToolTip = toolTip;
169  if ( toolTip.isEmpty() )
170  {
171  mPreviewLabel->setToolTip( tr( "Right-click to copy" ) );
172  }
173  else
174  {
175  mPreviewLabel->setToolTip( tr( "%1 (right-click to copy)" ).arg( mToolTip ) );
176  }
177  emit toolTipChanged( mToolTip );
178 }
179 
180 void QgsExpressionPreviewWidget::setParserError( bool parserError )
181 {
182  if ( parserError != mParserError )
183  {
184  mParserError = parserError;
185  emit parserErrorChanged();
186  }
187 }
189 {
190  return mParserError;
191 }
192 
193 void QgsExpressionPreviewWidget::setEvalError( bool evalError )
194 {
195  if ( evalError == mEvalError )
196  return;
197 
198  mEvalError = evalError;
199  emit evalErrorChanged();
200 }
201 
203 {
204  return mEvalError;
205 }
206 
207 void QgsExpressionPreviewWidget::copyFullExpressionValue()
208 {
209  QClipboard *clipboard = QApplication::clipboard();
210  const QVariant value = mExpression.evaluate( &mExpressionContext );
211  const QString copiedValue = QgsExpression::formatPreviewString( value, false, 100000 );
212  QgsDebugMsgLevel( QStringLiteral( "set clipboard: %1" ).arg( copiedValue ), 4 );
213  clipboard->setText( copiedValue );
214 }
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:406
QgsExpression::evalErrorString
QString evalErrorString() const
Returns evaluation error.
Definition: qgsexpression.cpp:383
QgsExpressionPreviewWidget::parserError
bool parserError() const
Will be set to true if the current expression text reports a parser error with the context.
Definition: qgsexpressionpreviewwidget.cpp:188
QgsExpression::referencedColumns
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
Definition: qgsexpression.cpp:221
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsExpressionPreviewWidget::setGeomCalculator
void setGeomCalculator(const QgsDistanceArea &da)
Sets geometry calculator used in distance/area calculations.
Definition: qgsexpressionpreviewwidget.cpp:62
QgsExpressionPreviewWidget::QgsExpressionPreviewWidget
QgsExpressionPreviewWidget(QWidget *parent=nullptr)
Constructor.
Definition: qgsexpressionpreviewwidget.cpp:27
QgsFeaturePickerWidget::featureChanged
void featureChanged(const QgsFeature &feature)
Sends the feature as soon as it is chosen.
QgsExpressionPreviewWidget::setCurrentFeature
void setCurrentFeature(const QgsFeature &feature)
sets the current feature used
Definition: qgsexpressionpreviewwidget.cpp:54
qgsfeaturepickerwidget.h
qgsapplication.h
QgsExpression::setGeomCalculator
void setGeomCalculator(const QgsDistanceArea *calc)
Sets the geometry calculator used for distance and area calculations in expressions.
Definition: qgsexpression.cpp:318
QgsExpressionPreviewWidget::setExpressionContext
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context for the widget.
Definition: qgsexpressionpreviewwidget.cpp:68
QgsExpression::hasEvalError
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
Definition: qgsexpression.cpp:378
QgsExpression::parserErrorString
QString parserErrorString() const
Returns parser error.
Definition: qgsexpression.cpp:211
QgsFeature::isValid
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:216
QgsExpressionPreviewWidget::expressionParsed
void expressionParsed(bool isValid)
Emitted when the user changes the expression in the widget.
QgsExpressionPreviewWidget::evalErrorChanged
void evalErrorChanged()
Will be set to true if the current expression text reported an eval error with the context.
qgsexpressionpreviewwidget.h
qgsmessageviewer.h
QgsExpressionPreviewWidget::toolTipChanged
void toolTipChanged(const QString &toolTip)
Emitted whenever the tool tip changed.
QgsExpressionPreviewWidget::evalError
bool evalError() const
Will be set to true if the current expression text reported an eval error with the context.
Definition: qgsexpressionpreviewwidget.cpp:202
QgsExpression::evaluate
QVariant evaluate()
Evaluate the feature and return the result.
Definition: qgsexpression.cpp:350
qgsvectorlayer.h
QgsExpression::formatPreviewString
static QString formatPreviewString(const QVariant &value, bool htmlOutput=true, int maximumPreviewLength=60)
Formats an expression result for friendly display to the user.
Definition: qgsexpression.cpp:978
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
QgsExpressionContext::feature
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
Definition: qgsexpressioncontext.cpp:543
QgsExpressionPreviewWidget::setLayer
void setLayer(QgsVectorLayer *layer)
Sets the layer used in the preview.
Definition: qgsexpressionpreviewwidget.cpp:42
QgsExpression::needsGeometry
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
Definition: qgsexpression.cpp:270
QgsDistanceArea
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
Definition: qgsdistancearea.h:52
QgsFeature
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:55
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Definition: qgsapplication.cpp:693
QgsExpressionPreviewWidget::setExpressionText
void setExpressionText(const QString &expression)
Sets the expression.
Definition: qgsexpressionpreviewwidget.cpp:48
QgsExpression::hasParserError
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
Definition: qgsexpression.cpp:206
QgsExpression
Class for parsing and evaluation of expressions (formerly called "search strings")....
Definition: qgsexpression.h:102
QgsExpressionPreviewWidget::parserErrorChanged
void parserErrorChanged()
Will be set to true if the current expression text reported a parser error with the context.
QgsExpressionContext::setFeature
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Definition: qgsexpressioncontext.cpp:525