QGIS API Documentation  3.13.0-Master (740be229cb)
qgsexpressionbuilderwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgisexpressionbuilderwidget.cpp - A generic expression string builder widget.
3  --------------------------------------
4  Date : 29-May-2011
5  Copyright : (C) 2011 by Nathan Woodrow
6  Email : woodrow.nathan at gmail dot com
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 <QFile>
18 #include <QTextStream>
19 #include <QDir>
20 #include <QInputDialog>
21 #include <QComboBox>
22 #include <QGraphicsOpacityEffect>
23 #include <QPropertyAnimation>
24 #include <QMessageBox>
25 #include <QVersionNumber>
26 #include <QDateTime>
27 #include <QJsonDocument>
28 #include <QJsonObject>
29 #include <QJsonArray>
30 #include <QFileDialog>
31 #include <QMenu>
32 
34 #include "qgslogger.h"
35 #include "qgsexpression.h"
36 #include "qgsexpressionfunction.h"
37 #include "qgsexpressionnodeimpl.h"
38 #include "qgsmessageviewer.h"
39 #include "qgsapplication.h"
40 #include "qgspythonrunner.h"
41 #include "qgsgeometry.h"
42 #include "qgsfeature.h"
43 #include "qgsfeatureiterator.h"
44 #include "qgsvectorlayer.h"
45 #include "qgssettings.h"
46 #include "qgsproject.h"
47 #include "qgsrelation.h"
50 #include "qgsfieldformatter.h"
52 #include "qgsexpressiontreeview.h"
53 
54 
55 
56 bool formatterCanProvideAvailableValues( QgsVectorLayer *layer, const QString &fieldName )
57 {
58  if ( layer )
59  {
60  const QgsFields fields = layer->fields();
61  int fieldIndex = fields.lookupField( fieldName );
62  if ( fieldIndex != -1 )
63  {
64  const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
66 
67  return ( formatter->flags() & QgsFieldFormatter::CanProvideAvailableValues );
68  }
69  }
70  return false;
71 }
72 
73 
75  : QWidget( parent )
76  , mProject( QgsProject::instance() )
77 {
78  setupUi( this );
79 
80  connect( btnRun, &QToolButton::pressed, this, &QgsExpressionBuilderWidget::btnRun_pressed );
81  connect( btnNewFile, &QToolButton::pressed, this, &QgsExpressionBuilderWidget::btnNewFile_pressed );
82  connect( cmbFileNames, &QListWidget::currentItemChanged, this, &QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged );
83  connect( txtExpressionString, &QgsCodeEditorExpression::textChanged, this, &QgsExpressionBuilderWidget::txtExpressionString_textChanged );
84  connect( txtPython, &QgsCodeEditorPython::textChanged, this, &QgsExpressionBuilderWidget::txtPython_textChanged );
85  connect( txtSearchEditValues, &QgsFilterLineEdit::textChanged, this, &QgsExpressionBuilderWidget::txtSearchEditValues_textChanged );
86  connect( lblPreview, &QLabel::linkActivated, this, &QgsExpressionBuilderWidget::lblPreview_linkActivated );
87  connect( mValuesListView, &QListView::doubleClicked, this, &QgsExpressionBuilderWidget::mValuesListView_doubleClicked );
88  connect( btnSaveExpression, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::storeCurrentUserExpression );
89  connect( btnEditExpression, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::editSelectedUserExpression );
90  connect( btnRemoveExpression, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::removeSelectedUserExpression );
91  connect( btnImportExpressions, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::importUserExpressions_pressed );
92  connect( btnExportExpressions, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::exportUserExpressions_pressed );
93  connect( btnClearEditor, &QPushButton::pressed, txtExpressionString, &QgsCodeEditorExpression::clear );
94 
95  connect( txtSearchEdit, &QgsFilterLineEdit::textChanged, mExpressionTreeView, &QgsExpressionTreeView::setSearchText );
96  connect( mExpressionTreeView, &QgsExpressionTreeView::expressionItemDoubleClicked, this, &QgsExpressionBuilderWidget::insertExpressionText );
97  connect( mExpressionTreeView, &QgsExpressionTreeView::currentExpressionItemChanged, this, &QgsExpressionBuilderWidget::expressionTreeItemChanged );
98 
99  mExpressionTreeMenuProvider = new ExpressionTreeMenuProvider( this );
100  mExpressionTreeView->setMenuProvider( mExpressionTreeMenuProvider );
101 
102  txtHelpText->setOpenExternalLinks( true );
103  mValueGroupBox->hide();
104 // highlighter = new QgsExpressionHighlighter( txtExpressionString->document() );
105 
106  // Note: must be in sync with the json help file for UserGroup
107  mUserExpressionsGroupName = QgsExpression::group( QStringLiteral( "UserGroup" ) );
108 
109  // Set icons for tool buttons
110  btnSaveExpression->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionFileSave.svg" ) ) );
111  btnEditExpression->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionToggleEditing.svg" ) ) );
112  btnRemoveExpression->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionDeleteSelected.svg" ) ) );
113  btnExportExpressions->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionSharingExport.svg" ) ) );
114  btnImportExpressions->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionSharingImport.svg" ) ) );
115  btnClearEditor->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionFileNew.svg" ) ) );
116 
117  connect( this, &QgsExpressionBuilderWidget::expressionParsed, this, &QgsExpressionBuilderWidget::setExpressionState );
118 
119  connect( btnLoadAll, &QAbstractButton::pressed, this, &QgsExpressionBuilderWidget::loadAllValues );
120  connect( btnLoadSample, &QAbstractButton::pressed, this, &QgsExpressionBuilderWidget::loadSampleValues );
121 
122  const auto pushButtons { mOperatorsGroupBox->findChildren<QPushButton *>() };
123  for ( QPushButton *button : pushButtons )
124  {
125  connect( button, &QAbstractButton::pressed, this, &QgsExpressionBuilderWidget::operatorButtonClicked );
126  }
127 
128  txtSearchEdit->setShowSearchIcon( true );
129  txtSearchEdit->setPlaceholderText( tr( "Search…" ) );
130 
131  mValuesModel = qgis::make_unique<QStandardItemModel>();
132  mProxyValues = qgis::make_unique<QSortFilterProxyModel>();
133  mProxyValues->setSourceModel( mValuesModel.get() );
134  mValuesListView->setModel( mProxyValues.get() );
135  txtSearchEditValues->setShowSearchIcon( true );
136  txtSearchEditValues->setPlaceholderText( tr( "Search…" ) );
137 
138  editorSplit->setSizes( QList<int>( {175, 300} ) );
139 
140  functionsplit->setCollapsible( 0, false );
141  connect( mShowHelpButton, &QPushButton::clicked, this, [ = ]()
142  {
143  functionsplit->setSizes( QList<int>( {mOperationListGroup->width() - mHelpAndValuesWidget->minimumWidth(),
144  mHelpAndValuesWidget->minimumWidth()
145  } ) );
146  mShowHelpButton->setEnabled( false );
147  } );
148  connect( functionsplit, &QSplitter::splitterMoved, this, [ = ]( int, int )
149  {
150  mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
151  } );
152 
153 
154  QgsSettings settings;
155  splitter->restoreState( settings.value( QStringLiteral( "Windows/QgsExpressionBuilderWidget/splitter" ) ).toByteArray() );
156  editorSplit->restoreState( settings.value( QStringLiteral( "Windows/QgsExpressionBuilderWidget/editorsplitter" ) ).toByteArray() );
157  functionsplit->restoreState( settings.value( QStringLiteral( "Windows/QgsExpressionBuilderWidget/functionsplitter" ) ).toByteArray() );
158  mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
159 
160  txtExpressionString->setFoldingVisible( false );
161 
162  if ( QgsPythonRunner::isValid() )
163  {
164  QgsPythonRunner::eval( QStringLiteral( "qgis.user.expressionspath" ), mFunctionsPath );
165  updateFunctionFileList( mFunctionsPath );
166  }
167  else
168  {
169  tab_2->hide();
170  }
171 
172  txtExpressionString->setWrapMode( QsciScintilla::WrapWord );
173  lblAutoSave->clear();
174 
175  // Note: If you add a indicator here you should add it to clearErrors method if you need to clear it on text parse.
176  txtExpressionString->indicatorDefine( QgsCodeEditor::SquiggleIndicator, QgsExpression::ParserError::FunctionUnknown );
177  txtExpressionString->indicatorDefine( QgsCodeEditor::SquiggleIndicator, QgsExpression::ParserError::FunctionWrongArgs );
178  txtExpressionString->indicatorDefine( QgsCodeEditor::SquiggleIndicator, QgsExpression::ParserError::FunctionInvalidParams );
179  txtExpressionString->indicatorDefine( QgsCodeEditor::SquiggleIndicator, QgsExpression::ParserError::FunctionNamedArgsError );
180 #if defined(QSCINTILLA_VERSION) && QSCINTILLA_VERSION >= 0x20a00
181  txtExpressionString->indicatorDefine( QgsCodeEditor::TriangleIndicator, QgsExpression::ParserError::Unknown );
182 #else
183  txtExpressionString->indicatorDefine( QgsCodeEditor::SquiggleIndicator, QgsExpression::ParserError::Unknown );
184 #endif
185 
186  // Set all the error markers as red. -1 is all.
187  txtExpressionString->setIndicatorForegroundColor( QColor( Qt::red ), -1 );
188  txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::red ), -1 );
189  txtExpressionString->setIndicatorOutlineColor( QColor( Qt::red ), -1 );
190 
191  // Hidden function markers.
192  txtExpressionString->indicatorDefine( QgsCodeEditor::HiddenIndicator, FUNCTION_MARKER_ID );
193  txtExpressionString->setIndicatorForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
194  txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
195  txtExpressionString->setIndicatorHoverStyle( QgsCodeEditor::DotsIndicator, FUNCTION_MARKER_ID );
196 
197  connect( txtExpressionString, &QgsCodeEditorExpression::indicatorClicked, this, &QgsExpressionBuilderWidget::indicatorClicked );
198  txtExpressionString->setAutoCompletionCaseSensitivity( true );
199  txtExpressionString->setAutoCompletionSource( QsciScintilla::AcsAPIs );
200  txtExpressionString->setCallTipsVisible( 0 );
201 
202  setExpectedOutputFormat( QString() );
203  mFunctionBuilderHelp->setMarginVisible( false );
204  mFunctionBuilderHelp->setEdgeMode( QsciScintilla::EdgeNone );
205  mFunctionBuilderHelp->setEdgeColumn( 0 );
206  mFunctionBuilderHelp->setReadOnly( true );
207  mFunctionBuilderHelp->setText( tr( "\"\"\"Define a new function using the @qgsfunction decorator.\n\
208 \n\
209  The function accepts the following parameters\n\
210 \n\
211  : param [any]: Define any parameters you want to pass to your function before\n\
212  the following arguments.\n\
213  : param feature: The current feature\n\
214  : param parent: The QgsExpression object\n\
215  : param context: If there is an argument called ``context`` found at the last\n\
216  position, this variable will contain a ``QgsExpressionContext``\n\
217  object, that gives access to various additional information like\n\
218  expression variables. E.g. ``context.variable( 'layer_id' )``\n\
219  : returns: The result of the expression.\n\
220 \n\
221 \n\
222 \n\
223  The @qgsfunction decorator accepts the following arguments:\n\
224 \n\
225 \n\
226  : param args: Defines the number of arguments. With ``args = 'auto'`` the number of\n\
227  arguments will automatically be extracted from the signature.\n\
228  With ``args = -1``, any number of arguments are accepted.\n\
229  : param group: The name of the group under which this expression function will\n\
230  be listed.\n\
231  : param handlesnull: Set this to True if your function has custom handling for NULL values.\n\
232  If False, the result will always be NULL as soon as any parameter is NULL.\n\
233  Defaults to False.\n\
234  : param usesgeometry : Set this to True if your function requires access to\n\
235  feature.geometry(). Defaults to False.\n\
236  : param referenced_columns: An array of attribute names that are required to run\n\
237  this function. Defaults to [QgsFeatureRequest.ALL_ATTRIBUTES].\n\
238  \"\"\"" ) );
239 }
240 
241 
243 {
244  QgsSettings settings;
245  settings.setValue( QStringLiteral( "Windows/QgsExpressionBuilderWidget/splitter" ), splitter->saveState() );
246  settings.setValue( QStringLiteral( "Windows/QgsExpressionBuilderWidget/editorsplitter" ), editorSplit->saveState() );
247  settings.setValue( QStringLiteral( "Windows/QgsExpressionBuilderWidget/functionsplitter" ), functionsplit->saveState() );
248  delete mExpressionTreeMenuProvider;
249 }
250 
251 void QgsExpressionBuilderWidget::init( const QgsExpressionContext &context, const QString &recentCollection, const Flags &flags )
252 {
253  setExpressionContext( context );
254 
255  if ( flags.testFlag( LoadRecent ) )
256  mExpressionTreeView->loadRecent( recentCollection );
257 
258  if ( flags.testFlag( LoadUserExpressions ) )
259  mExpressionTreeView->loadUserExpressions();
260 }
261 
262 void QgsExpressionBuilderWidget::initWithLayer( QgsVectorLayer *layer, const QgsExpressionContext &context, const QString &recentCollection, const Flags &flags )
263 {
264  init( context, recentCollection, flags );
265  setLayer( layer );
266 }
267 
268 void QgsExpressionBuilderWidget::initWithFields( const QgsFields &fields, const QgsExpressionContext &context, const QString &recentCollection, const Flags &flags )
269 {
270  init( context, recentCollection, flags );
271  mExpressionTreeView->loadFieldNames( fields );
272 }
273 
274 
276 {
277  mLayer = layer;
278  mExpressionTreeView->setLayer( mLayer );
279 
280  //TODO - remove existing layer scope from context
281 
282  if ( mLayer )
283  {
284  mExpressionContext << QgsExpressionContextUtils::layerScope( mLayer );
285 
286  txtExpressionString->setFields( mLayer->fields() );
287  }
288 }
289 
291 {
292  return mLayer;
293 }
294 
295 void QgsExpressionBuilderWidget::expressionTreeItemChanged( QgsExpressionItem *item )
296 {
297  txtSearchEditValues->clear();
298 
299  if ( !item )
300  return;
301 
302  bool isField = mLayer && item->getItemType() == QgsExpressionItem::Field;
303  if ( isField )
304  {
305  mValuesModel->clear();
306 
307  cbxValuesInUse->setVisible( formatterCanProvideAvailableValues( mLayer, item->text() ) );
308  cbxValuesInUse->setChecked( false );
309  }
310  mValueGroupBox->setVisible( isField );
311 
312  mShowHelpButton->setText( isField ? tr( "Show Values" ) : tr( "Show Help" ) );
313 
314  // Show the help for the current item.
315  QString help = loadFunctionHelp( item );
316  txtHelpText->setText( help );
317 
318  bool isUserExpression = item->parent() && item->parent()->text() == mUserExpressionsGroupName;
319 
320  btnRemoveExpression->setEnabled( isUserExpression );
321  btnEditExpression->setEnabled( isUserExpression );
322 }
323 
324 void QgsExpressionBuilderWidget::btnRun_pressed()
325 {
326  if ( !cmbFileNames->currentItem() )
327  return;
328 
329  QString file = cmbFileNames->currentItem()->text();
330  saveFunctionFile( file );
331  runPythonCode( txtPython->text() );
332 }
333 
334 void QgsExpressionBuilderWidget::runPythonCode( const QString &code )
335 {
336  if ( QgsPythonRunner::isValid() )
337  {
338  QString pythontext = code;
339  QgsPythonRunner::run( pythontext );
340  }
341  mExpressionTreeView->refresh();
342 }
343 
345 {
346  QDir myDir( mFunctionsPath );
347  if ( !myDir.exists() )
348  {
349  myDir.mkpath( mFunctionsPath );
350  }
351 
352  if ( !fileName.endsWith( QLatin1String( ".py" ) ) )
353  {
354  fileName.append( ".py" );
355  }
356 
357  fileName = mFunctionsPath + QDir::separator() + fileName;
358  QFile myFile( fileName );
359  if ( myFile.open( QIODevice::WriteOnly | QFile::Truncate ) )
360  {
361  QTextStream myFileStream( &myFile );
362  myFileStream << txtPython->text() << endl;
363  myFile.close();
364  }
365 }
366 
368 {
369  mFunctionsPath = path;
370  QDir dir( path );
371  dir.setNameFilters( QStringList() << QStringLiteral( "*.py" ) );
372  QStringList files = dir.entryList( QDir::Files );
373  cmbFileNames->clear();
374  const auto constFiles = files;
375  for ( const QString &name : constFiles )
376  {
377  QFileInfo info( mFunctionsPath + QDir::separator() + name );
378  if ( info.baseName() == QLatin1String( "__init__" ) ) continue;
379  QListWidgetItem *item = new QListWidgetItem( QgsApplication::getThemeIcon( QStringLiteral( "console/iconTabEditorConsole.svg" ) ), info.baseName() );
380  cmbFileNames->addItem( item );
381  }
382  if ( !cmbFileNames->currentItem() )
383  {
384  cmbFileNames->setCurrentRow( 0 );
385  }
386 
387  if ( cmbFileNames->count() == 0 )
388  {
389  // Create default sample entry.
390  newFunctionFile( "default" );
391  txtPython->setText( QStringLiteral( "'''\n#Sample custom function file\n "
392  "(uncomment to use and customize or Add button to create a new file) \n%1 \n '''" ).arg( txtPython->text() ) );
393  saveFunctionFile( "default" );
394  }
395 }
396 
397 void QgsExpressionBuilderWidget::newFunctionFile( const QString &fileName )
398 {
399  QList<QListWidgetItem *> items = cmbFileNames->findItems( fileName, Qt::MatchExactly );
400  if ( !items.isEmpty() )
401  return;
402 
403  QListWidgetItem *item = new QListWidgetItem( QgsApplication::getThemeIcon( QStringLiteral( "console/iconTabEditorConsole.svg" ) ), fileName );
404  cmbFileNames->insertItem( 0, item );
405  cmbFileNames->setCurrentRow( 0 );
406 
407  QString templatetxt;
408  QgsPythonRunner::eval( QStringLiteral( "qgis.user.default_expression_template" ), templatetxt );
409  txtPython->setText( templatetxt );
410  saveFunctionFile( fileName );
411 }
412 
413 void QgsExpressionBuilderWidget::btnNewFile_pressed()
414 {
415  bool ok;
416  QString text = QInputDialog::getText( this, tr( "New File" ),
417  tr( "New file name:" ), QLineEdit::Normal,
418  QString(), &ok );
419  if ( ok && !text.isEmpty() )
420  {
421  newFunctionFile( text );
422  }
423 }
424 
425 void QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem )
426 {
427  if ( lastitem )
428  {
429  QString filename = lastitem->text();
430  saveFunctionFile( filename );
431  }
432  QString path = mFunctionsPath + QDir::separator() + item->text();
433  loadCodeFromFile( path );
434 }
435 
437 {
438  if ( !path.endsWith( QLatin1String( ".py" ) ) )
439  path.append( ".py" );
440 
441  txtPython->loadScript( path );
442 }
443 
445 {
446  txtPython->setText( code );
447 }
448 
449 void QgsExpressionBuilderWidget::insertExpressionText( const QString &text )
450 {
451  // Insert the expression text or replace selected text
452  txtExpressionString->insertText( text );
453  txtExpressionString->setFocus();
454 }
455 
456 void QgsExpressionBuilderWidget::loadFieldsAndValues( const QMap<QString, QStringList> &fieldValues )
457 {
458  Q_UNUSED( fieldValues )
459  // This is not maintained and setLayer() should be used instead.
460 }
461 
462 void QgsExpressionBuilderWidget::fillFieldValues( const QString &fieldName, int countLimit, bool forceUsedValues )
463 {
464  // TODO We should really return a error the user of the widget that
465  // the there is no layer set.
466  if ( !mLayer )
467  return;
468 
469  // TODO We should thread this so that we don't hold the user up if the layer is massive.
470 
471  const QgsFields fields = mLayer->fields();
472  int fieldIndex = fields.lookupField( fieldName );
473 
474  if ( fieldIndex < 0 )
475  return;
476 
477  const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
479 
480  QVariantList values;
481  if ( cbxValuesInUse->isVisible() && !cbxValuesInUse->isChecked() && !forceUsedValues )
482  {
483  QgsFieldFormatterContext fieldFormatterContext;
484  fieldFormatterContext.setProject( mProject );
485  values = formatter->availableValues( setup.config(), countLimit, fieldFormatterContext );
486  }
487  else
488  {
489  values = mLayer->uniqueValues( fieldIndex, countLimit ).toList();
490  }
491  std::sort( values.begin(), values.end() );
492 
493  mValuesModel->clear();
494  for ( const QVariant &value : qgis::as_const( values ) )
495  {
496  QString strValue;
497  if ( value.isNull() )
498  strValue = QStringLiteral( "NULL" );
499  else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong )
500  strValue = value.toString();
501  else
502  strValue = '\'' + value.toString().replace( '\'', QLatin1String( "''" ) ) + '\'';
503 
504  QString representedValue = formatter->representValue( mLayer, fieldIndex, setup.config(), QVariant(), value );
505  if ( representedValue != value.toString() )
506  representedValue = representedValue + QStringLiteral( " [" ) + strValue + ']';
507 
508  QStandardItem *item = new QStandardItem( representedValue );
509  item->setData( strValue );
510  mValuesModel->appendRow( item );
511  }
512 }
513 
514 QString QgsExpressionBuilderWidget::getFunctionHelp( QgsExpressionFunction *function )
515 {
516  if ( !function )
517  return QString();
518 
519  QString helpContents = QgsExpression::helpText( function->name() );
520 
521  return QStringLiteral( "<head><style>" ) + helpStylesheet() + QStringLiteral( "</style></head><body>" ) + helpContents + QStringLiteral( "</body>" );
522 
523 }
524 
525 
526 
528 {
529  return mExpressionValid;
530 }
531 
532 void QgsExpressionBuilderWidget::saveToRecent( const QString &collection )
533 {
534  mExpressionTreeView->saveToRecent( expressionText(), collection );
535 }
536 
537 void QgsExpressionBuilderWidget::loadRecent( const QString &collection )
538 {
539  mExpressionTreeView->loadRecent( collection );
540 }
541 
543 {
544  return mExpressionTreeView;
545 }
546 
547 // this is potentially very slow if there are thousands of user expressions, every time entire cleanup and load
549 {
550  mExpressionTreeView->loadUserExpressions();
551 }
552 
553 void QgsExpressionBuilderWidget::saveToUserExpressions( const QString &label, const QString expression, const QString &helpText )
554 {
555  mExpressionTreeView->saveToUserExpressions( label, expression, helpText );
556 }
557 
559 {
560  mExpressionTreeView->removeFromUserExpressions( label );
561 }
562 
563 
565 {
566  mDa = da;
567 }
568 
570 {
571  return txtExpressionString->text();
572 }
573 
574 void QgsExpressionBuilderWidget::setExpressionText( const QString &expression )
575 {
576  txtExpressionString->setText( expression );
577 }
578 
580 {
581  return lblExpected->text();
582 }
583 
585 {
586  lblExpected->setText( expected );
587  mExpectedOutputFrame->setVisible( !expected.isNull() );
588 }
589 
591 {
592  mExpressionContext = context;
593  txtExpressionString->setExpressionContext( mExpressionContext );
594  mExpressionTreeView->setExpressionContext( context );
595 }
596 
597 void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
598 {
599  QString text = expressionText();
600  clearErrors();
601 
602  btnClearEditor->setEnabled( ! txtExpressionString->text().isEmpty() );
603  btnSaveExpression->setEnabled( false );
604 
605  // If the string is empty the expression will still "fail" although
606  // we don't show the user an error as it will be confusing.
607  if ( text.isEmpty() )
608  {
609  lblPreview->clear();
610  lblPreview->setStyleSheet( QString() );
611  txtExpressionString->setToolTip( QString() );
612  lblPreview->setToolTip( QString() );
613  emit expressionParsed( false );
614  setParserError( true );
615  setEvalError( true );
616  return;
617  }
618 
619 
620  QgsExpression exp( text );
621 
622  if ( mLayer )
623  {
624  // Only set calculator if we have layer, else use default.
625  exp.setGeomCalculator( &mDa );
626 
627  if ( !mExpressionContext.feature().isValid() )
628  {
629  // no feature passed yet, try to get from layer
630  QgsFeature f;
631  mLayer->getFeatures( QgsFeatureRequest().setLimit( 1 ) ).nextFeature( f );
632  mExpressionContext.setFeature( f );
633  }
634  }
635 
636  QVariant value = exp.evaluate( &mExpressionContext );
637  if ( !exp.hasEvalError() )
638  {
639  lblPreview->setText( QgsExpression::formatPreviewString( value ) );
640  }
641 
642  if ( exp.hasParserError() || exp.hasEvalError() )
643  {
644  QString errorString = exp.parserErrorString().replace( "\n", "<br>" );
645  QString tooltip;
646  if ( exp.hasParserError() )
647  tooltip = QStringLiteral( "<b>%1:</b>"
648  "%2" ).arg( tr( "Parser Errors" ), errorString );
649  // Only show the eval error if there is no parser error.
650  if ( !exp.hasParserError() && exp.hasEvalError() )
651  tooltip += QStringLiteral( "<b>%1:</b> %2" ).arg( tr( "Eval Error" ), exp.evalErrorString() );
652 
653  lblPreview->setText( tr( "Expression is invalid <a href=""more"">(more info)</a>" ) );
654  lblPreview->setStyleSheet( QStringLiteral( "color: rgba(255, 6, 10, 255);" ) );
655  txtExpressionString->setToolTip( tooltip );
656  lblPreview->setToolTip( tooltip );
657  emit expressionParsed( false );
658  setParserError( exp.hasParserError() );
659  setEvalError( exp.hasEvalError() );
660  createErrorMarkers( exp.parserErrors() );
661  return;
662  }
663  else
664  {
665  lblPreview->setStyleSheet( QString() );
666  txtExpressionString->setToolTip( QString() );
667  lblPreview->setToolTip( QString() );
668  emit expressionParsed( true );
669  setParserError( false );
670  setEvalError( false );
671  createMarkers( exp.rootNode() );
672  btnSaveExpression->setEnabled( true );
673  }
674 
675 }
676 
678 {
679  return mParserError;
680 }
681 
682 void QgsExpressionBuilderWidget::setParserError( bool parserError )
683 {
684  if ( parserError == mParserError )
685  return;
686 
687  mParserError = parserError;
688  emit parserErrorChanged();
689 }
690 
692 {
693  return mEvalError;
694 }
695 
696 void QgsExpressionBuilderWidget::setEvalError( bool evalError )
697 {
698  if ( evalError == mEvalError )
699  return;
700 
701  mEvalError = evalError;
702  emit evalErrorChanged();
703 }
704 
706 {
708  return mExpressionTreeView->model();
710 }
711 
713 {
714  return mProject;
715 }
716 
718 {
719  mProject = project;
720  mExpressionTreeView->setProject( project );
721 }
722 
724 {
725  QWidget::showEvent( e );
726  txtExpressionString->setFocus();
727 }
728 
729 void QgsExpressionBuilderWidget::createErrorMarkers( QList<QgsExpression::ParserError> errors )
730 {
731  clearErrors();
732  for ( const QgsExpression::ParserError &error : errors )
733  {
734  int errorFirstLine = error.firstLine - 1 ;
735  int errorFirstColumn = error.firstColumn - 1;
736  int errorLastColumn = error.lastColumn - 1;
737  int errorLastLine = error.lastLine - 1;
738 
739  // If we have a unknown error we just mark the point that hit the error for now
740  // until we can handle others more.
741  if ( error.errorType == QgsExpression::ParserError::Unknown )
742  {
743  errorFirstLine = errorLastLine;
744  errorFirstColumn = errorLastColumn - 1;
745  }
746  txtExpressionString->fillIndicatorRange( errorFirstLine,
747  errorFirstColumn,
748  errorLastLine,
749  errorLastColumn, error.errorType );
750  }
751 }
752 
753 void QgsExpressionBuilderWidget::createMarkers( const QgsExpressionNode *inNode )
754 {
755  switch ( inNode->nodeType() )
756  {
757  case QgsExpressionNode::NodeType::ntFunction:
758  {
759  const QgsExpressionNodeFunction *node = static_cast<const QgsExpressionNodeFunction *>( inNode );
760  txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
761  txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->fnIndex() );
762  int start = inNode->parserFirstColumn - 1;
763  int end = inNode->parserLastColumn - 1;
764  int start_pos = txtExpressionString->positionFromLineIndex( inNode->parserFirstLine - 1, start );
765  txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
766  if ( node->args() )
767  {
768  const QList< QgsExpressionNode * > nodeList = node->args()->list();
769  for ( QgsExpressionNode *n : nodeList )
770  {
771  createMarkers( n );
772  }
773  }
774  break;
775  }
776  case QgsExpressionNode::NodeType::ntLiteral:
777  {
778  break;
779  }
780  case QgsExpressionNode::NodeType::ntUnaryOperator:
781  {
782  const QgsExpressionNodeUnaryOperator *node = static_cast<const QgsExpressionNodeUnaryOperator *>( inNode );
783  createMarkers( node->operand() );
784  break;
785  }
786  case QgsExpressionNode::NodeType::ntBinaryOperator:
787  {
788  const QgsExpressionNodeBinaryOperator *node = static_cast<const QgsExpressionNodeBinaryOperator *>( inNode );
789  createMarkers( node->opLeft() );
790  createMarkers( node->opRight() );
791  break;
792  }
793  case QgsExpressionNode::NodeType::ntColumnRef:
794  {
795  break;
796  }
797  case QgsExpressionNode::NodeType::ntInOperator:
798  {
799  const QgsExpressionNodeInOperator *node = static_cast<const QgsExpressionNodeInOperator *>( inNode );
800  if ( node->list() )
801  {
802  const QList< QgsExpressionNode * > nodeList = node->list()->list();
803  for ( QgsExpressionNode *n : nodeList )
804  {
805  createMarkers( n );
806  }
807  }
808  break;
809  }
810  case QgsExpressionNode::NodeType::ntCondition:
811  {
812  const QgsExpressionNodeCondition *node = static_cast<const QgsExpressionNodeCondition *>( inNode );
813  for ( QgsExpressionNodeCondition::WhenThen *cond : node->conditions() )
814  {
815  createMarkers( cond->whenExp() );
816  createMarkers( cond->thenExp() );
817  }
818  if ( node->elseExp() )
819  {
820  createMarkers( node->elseExp() );
821  }
822  break;
823  }
824  case QgsExpressionNode::NodeType::ntIndexOperator:
825  {
826  break;
827  }
828  }
829 }
830 
831 void QgsExpressionBuilderWidget::clearFunctionMarkers()
832 {
833  int lastLine = txtExpressionString->lines() - 1;
834  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
835 }
836 
837 void QgsExpressionBuilderWidget::clearErrors()
838 {
839  int lastLine = txtExpressionString->lines() - 1;
840  // Note: -1 here doesn't seem to do the clear all like the other functions. Will need to make this a bit smarter.
841  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::Unknown );
842  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionInvalidParams );
843  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionUnknown );
844  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionWrongArgs );
845  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionNamedArgsError );
846 }
847 
848 void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
849 {
850  mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
851  mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
852 }
853 
854 void QgsExpressionBuilderWidget::lblPreview_linkActivated( const QString &link )
855 {
856  Q_UNUSED( link )
857  QgsMessageViewer *mv = new QgsMessageViewer( this );
858  mv->setWindowTitle( tr( "More Info on Expression Error" ) );
859  mv->setMessageAsHtml( txtExpressionString->toolTip() );
860  mv->exec();
861 }
862 
863 void QgsExpressionBuilderWidget::mValuesListView_doubleClicked( const QModelIndex &index )
864 {
865  // Insert the item text or replace selected text
866  txtExpressionString->insertText( ' ' + index.data( Qt::UserRole + 1 ).toString() + ' ' );
867  txtExpressionString->setFocus();
868 }
869 
870 void QgsExpressionBuilderWidget::operatorButtonClicked()
871 {
872  QPushButton *button = qobject_cast<QPushButton *>( sender() );
873 
874  // Insert the button text or replace selected text
875  txtExpressionString->insertText( ' ' + button->text() + ' ' );
876  txtExpressionString->setFocus();
877 }
878 
880 {
881  QgsExpressionItem *item = mExpressionTreeView->currentItem();
882  // TODO We should really return a error the user of the widget that
883  // the there is no layer set.
884  if ( !mLayer || !item )
885  return;
886 
887  mValueGroupBox->show();
888  fillFieldValues( item->text(), 10 );
889 }
890 
892 {
893  QgsExpressionItem *item = mExpressionTreeView->currentItem();
894  // TODO We should really return a error the user of the widget that
895  // the there is no layer set.
896  if ( !mLayer || !item )
897  return;
898 
899  mValueGroupBox->show();
900  fillFieldValues( item->text(), -1 );
901 }
902 
904 {
905  QgsExpressionItem *item = mExpressionTreeView->currentItem();
906  // TODO We should really return a error the user of the widget that
907  // the there is no layer set.
908  if ( !mLayer || !item )
909  return;
910 
911  mValueGroupBox->show();
912  fillFieldValues( item->text(), 10, true );
913 }
914 
916 {
917  QgsExpressionItem *item = mExpressionTreeView->currentItem();
918  // TODO We should really return a error the user of the widget that
919  // the there is no layer set.
920  if ( !mLayer || !item )
921  return;
922 
923  mValueGroupBox->show();
924  fillFieldValues( item->text(), -1, true );
925 }
926 
927 void QgsExpressionBuilderWidget::txtPython_textChanged()
928 {
929  lblAutoSave->setText( tr( "Saving…" ) );
930  if ( mAutoSave )
931  {
932  autosave();
933  }
934 }
935 
937 {
938  // Don't auto save if not on function editor that would be silly.
939  if ( tabWidget->currentIndex() != 1 )
940  return;
941 
942  QListWidgetItem *item = cmbFileNames->currentItem();
943  if ( !item )
944  return;
945 
946  QString file = item->text();
947  saveFunctionFile( file );
948  lblAutoSave->setText( QStringLiteral( "Saved" ) );
949  QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect();
950  lblAutoSave->setGraphicsEffect( effect );
951  QPropertyAnimation *anim = new QPropertyAnimation( effect, "opacity" );
952  anim->setDuration( 2000 );
953  anim->setStartValue( 1.0 );
954  anim->setEndValue( 0.0 );
955  anim->setEasingCurve( QEasingCurve::OutQuad );
956  anim->start( QAbstractAnimation::DeleteWhenStopped );
957 }
958 
960 {
961  const QString expression { this->expressionText() };
962  QgsExpressionStoreDialog dlg { expression, expression, QString( ), mExpressionTreeView->userExpressionLabels() };
963  if ( dlg.exec() == QDialog::DialogCode::Accepted )
964  {
965  mExpressionTreeView->saveToUserExpressions( dlg.label(), dlg.expression(), dlg.helpText() );
966  }
967 }
968 
970 {
971  // Get the item
972  QgsExpressionItem *item = mExpressionTreeView->currentItem();
973  if ( !item )
974  return;
975 
976  // Don't handle remove if we are on a header node or the parent
977  // is not the user group
978  if ( item->getItemType() == QgsExpressionItem::Header ||
979  ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) )
980  return;
981 
982  QgsSettings settings;
983  QString helpText = settings.value( QStringLiteral( "user/%1/helpText" ).arg( item->text() ), "", QgsSettings::Section::Expressions ).toString();
984  QgsExpressionStoreDialog dlg { item->text(), item->getExpressionText(), helpText };
985 
986  if ( dlg.exec() == QDialog::DialogCode::Accepted )
987  {
988  mExpressionTreeView->saveToUserExpressions( dlg.label(), dlg.expression(), dlg.helpText() );
989  }
990 }
991 
993 {
994  // Get the item
995  QgsExpressionItem *item = mExpressionTreeView->currentItem();
996 
997  if ( !item )
998  return;
999 
1000  // Don't handle remove if we are on a header node or the parent
1001  // is not the user group
1002  if ( item->getItemType() == QgsExpressionItem::Header ||
1003  ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) )
1004  return;
1005 
1006  if ( QMessageBox::Yes == QMessageBox::question( this, tr( "Remove Stored Expression" ),
1007  tr( "Do you really want to remove stored expressions '%1'?" ).arg( item->text() ),
1008  QMessageBox::Yes | QMessageBox::No ) )
1009  {
1010  mExpressionTreeView->removeFromUserExpressions( item->text() );
1011  }
1012 
1013 }
1014 
1015 void QgsExpressionBuilderWidget::exportUserExpressions_pressed()
1016 {
1017  QgsSettings settings;
1018  QString lastSaveDir = settings.value( QStringLiteral( "lastExportExpressionsDir" ), QDir::homePath(), QgsSettings::App ).toString();
1019  QString saveFileName = QFileDialog::getSaveFileName(
1020  this,
1021  tr( "Export User Expressions" ),
1022  lastSaveDir,
1023  tr( "User expressions" ) + " (*.json)" );
1024 
1025  if ( saveFileName.isEmpty() )
1026  return;
1027 
1028  QFileInfo saveFileInfo( saveFileName );
1029 
1030  if ( saveFileInfo.suffix().isEmpty() )
1031  {
1032  QString saveFileNameWithSuffix = saveFileName.append( ".json" );
1033  saveFileInfo = QFileInfo( saveFileNameWithSuffix );
1034  }
1035 
1036  settings.setValue( QStringLiteral( "lastExportExpressionsDir" ), saveFileInfo.absolutePath(), QgsSettings::App );
1037 
1038  QJsonDocument exportJson = mExpressionTreeView->exportUserExpressions();
1039  QFile jsonFile( saveFileName );
1040 
1041  if ( !jsonFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
1042  QMessageBox::warning( this, tr( "Export user expressions" ), tr( "Error while creating the expressions file." ) );
1043 
1044  if ( ! jsonFile.write( exportJson.toJson() ) )
1045  QMessageBox::warning( this, tr( "Export user expressions" ), tr( "Error while creating the expressions file." ) );
1046  else
1047  jsonFile.close();
1048 }
1049 
1050 void QgsExpressionBuilderWidget::importUserExpressions_pressed()
1051 {
1052  QgsSettings settings;
1053  QString lastImportDir = settings.value( QStringLiteral( "lastImportExpressionsDir" ), QDir::homePath(), QgsSettings::App ).toString();
1054  QString loadFileName = QFileDialog::getOpenFileName(
1055  this,
1056  tr( "Import User Expressions" ),
1057  lastImportDir,
1058  tr( "User expressions" ) + " (*.json)" );
1059 
1060  if ( loadFileName.isEmpty() )
1061  return;
1062 
1063  QFileInfo loadFileInfo( loadFileName );
1064 
1065  settings.setValue( QStringLiteral( "lastImportExpressionsDir" ), loadFileInfo.absolutePath(), QgsSettings::App );
1066 
1067  QFile jsonFile( loadFileName );
1068 
1069  if ( !jsonFile.open( QFile::ReadOnly ) )
1070  QMessageBox::warning( this, tr( "Import User Expressions" ), tr( "Error while reading the expressions file." ) );
1071 
1072  QTextStream jsonStream( &jsonFile );
1073  QString jsonString = jsonFile.readAll();
1074  jsonFile.close();
1075 
1076  QJsonDocument importJson = QJsonDocument::fromJson( jsonString.toUtf8() );
1077 
1078  if ( importJson.isNull() )
1079  {
1080  QMessageBox::warning( this, tr( "Import User Expressions" ), tr( "Error while reading the expressions file." ) );
1081  return;
1082  }
1083 
1084  mExpressionTreeView->loadExpressionsFromJson( importJson );
1085 }
1086 
1087 
1088 const QList<QgsExpressionItem *> QgsExpressionBuilderWidget::findExpressions( const QString &label )
1089 {
1090  return mExpressionTreeView->findExpressions( label );
1091 }
1092 
1093 void QgsExpressionBuilderWidget::indicatorClicked( int line, int index, Qt::KeyboardModifiers state )
1094 {
1095  if ( state & Qt::ControlModifier )
1096  {
1097  int position = txtExpressionString->positionFromLineIndex( line, index );
1098  long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID, static_cast<long int>( position ) );
1099  QgsExpressionFunction *func = QgsExpression::Functions()[fncIndex];
1100  QString help = getFunctionHelp( func );
1101  txtHelpText->setText( help );
1102  }
1103 }
1104 
1105 void QgsExpressionBuilderWidget::setExpressionState( bool state )
1106 {
1107  mExpressionValid = state;
1108 }
1109 
1110 QString QgsExpressionBuilderWidget::helpStylesheet() const
1111 {
1112  //start with default QGIS report style
1113  QString style = QgsApplication::reportStyleSheet();
1114 
1115  //add some tweaks
1116  style += " .functionname {color: #0a6099; font-weight: bold;} "
1117  " .argument {font-family: monospace; color: #bf0c0c; font-style: italic; } "
1118  " td.argument { padding-right: 10px; }";
1119 
1120  return style;
1121 }
1122 
1123 QString QgsExpressionBuilderWidget::loadFunctionHelp( QgsExpressionItem *expressionItem )
1124 {
1125  if ( !expressionItem )
1126  return QString();
1127 
1128  QString helpContents = expressionItem->getHelpText();
1129 
1130  // Return the function help that is set for the function if there is one.
1131  if ( helpContents.isEmpty() )
1132  {
1133  QString name = expressionItem->data( Qt::UserRole ).toString();
1134 
1135  if ( expressionItem->getItemType() == QgsExpressionItem::Field )
1136  helpContents = QgsExpression::helpText( QStringLiteral( "Field" ) );
1137  else
1138  helpContents = QgsExpression::helpText( name );
1139  }
1140 
1141  return "<head><style>" + helpStylesheet() + "</style></head><body>" + helpContents + "</body>";
1142 }
1143 
1144 
1145 // *************
1146 // Menu provider
1147 
1148 QMenu *QgsExpressionBuilderWidget::ExpressionTreeMenuProvider::createContextMenu( QgsExpressionItem *item )
1149 {
1150  QMenu *menu = nullptr;
1151  QgsVectorLayer *layer = mExpressionBuilderWidget->layer();
1152  if ( item->getItemType() == QgsExpressionItem::Field && layer )
1153  {
1154  menu = new QMenu( mExpressionBuilderWidget );
1155  menu->addAction( tr( "Load First 10 Unique Values" ), mExpressionBuilderWidget, &QgsExpressionBuilderWidget::loadSampleValues );
1156  menu->addAction( tr( "Load All Unique Values" ), mExpressionBuilderWidget, &QgsExpressionBuilderWidget::loadAllValues );
1157 
1158  if ( formatterCanProvideAvailableValues( layer, item->text() ) )
1159  {
1160  menu->addAction( tr( "Load First 10 Unique Used Values" ), mExpressionBuilderWidget, SLOT( loadSampleUsedValues() ) );
1161  menu->addAction( tr( "Load All Unique Used Values" ), mExpressionBuilderWidget, &QgsExpressionBuilderWidget::loadAllUsedValues );
1162  }
1163  }
1164  return menu;
1165 }
int lookupField(const QString &fieldName) const
Looks up field&#39;s index from the field name.
Definition: qgsfields.cpp:324
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:183
QgsProject * project()
Returns the project currently associated with the widget.
Class for parsing and evaluation of expressions (formerly called "search strings").
int parserFirstColumn
First column in the parser this node was found.
Q_DECL_DEPRECATED void saveToUserExpressions(const QString &label, const QString expression, const QString &helpText)
Stores the user expression with given label and helpText.
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
static QString formatPreviewString(const QVariant &value, bool htmlOutput=true)
Formats an expression result for friendly display to the user.
void setProject(QgsProject *project)
Sets the project currently associated with the widget.
void saveFunctionFile(QString fileName)
Saves the current function editor text to the given file.
void expressionItemDoubleClicked(const QString &text)
Emitted when a expression item is double clicked.
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
QVariantMap config() const
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
Q_DECL_DEPRECATED QStandardItemModel * model()
Returns a pointer to the dialog&#39;s function item model.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
const QList< QgsExpressionItem * > findExpressions(const QString &label)
Returns the list of expression items matching a label.
void init(const QgsExpressionContext &context=QgsExpressionContext(), const QString &recentCollection=QStringLiteral("generic"), const Flags &flags=LoadAll)
Initialize without any layer.
static QString group(const QString &group)
Returns the translated name for a function group.
void loadSampleUsedValues()
Load used sample values into the sample value area.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
QgsExpressionNode * opRight() const
Returns the node to the right of the operator.
WhenThenList conditions() const
The list of WHEN THEN expression parts of the expression.
void setGeomCalculator(const QgsDistanceArea &da)
Sets geometry calculator used in distance/area calculations.
void initWithFields(const QgsFields &fields, const QgsExpressionContext &context=QgsExpressionContext(), const QString &recentCollection=QStringLiteral("generic"), const Flags &flags=LoadAll)
Initialize with given fields without any layer.
Details about any parser errors that were found when parsing the expression.
void removeSelectedUserExpression()
Removes the selected expression from the stored user expressions, the selected expression must be a u...
A context for field formatter containing information like the project.
void evalErrorChanged()
Will be set to true if the current expression text reported an eval error with the context...
void loadSampleValues()
Load sample values into the sample value area.
void setLayer(QgsVectorLayer *layer)
Sets layer in order to get the fields and values.
QVariant evaluate()
Evaluate the feature and return the result.
An expression node for CASE WHEN clauses.
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:731
QString evalErrorString() const
Returns evaluation error.
static QString reportStyleSheet(QgsApplication::StyleSheetType styleSheetType=QgsApplication::StyleSheetType::Qt)
Returns a css style sheet for reports, the styleSheetType argument determines what type of stylesheet...
Container of fields for a vector layer.
Definition: qgsfields.h:42
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
QgsExpressionNode::NodeList * list() const
Returns the list of nodes to search for matching values within.
QList< QgsExpression::ParserError > parserErrors() const
Returns parser error details including location of error.
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:496
QgsExpressionNode * opLeft() const
Returns the node to the left of the operator.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
Q_DECL_DEPRECATED void loadRecent(const QString &collection=QStringLiteral("generic"))
Loads the recent expressions from the given collection.
QString parserErrorString() const
Returns parser error.
void loadFunctionCode(const QString &code)
Loads code into the function editor.
Function was called with invalid args.
void loadCodeFromFile(QString path)
Loads code from the given file into the function editor.
void newFunctionFile(const QString &fileName="scratch")
Creates a new file in the function editor.
Non named function arg used after named arg.
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
void setExpectedOutputFormat(const QString &expected)
The set expected format string.
void updateFunctionFileList(const QString &path)
Updates the list of function files found at the given path.
QgsExpressionTreeView is a tree view to list all expressions functions, variables and fields that can...
bool isExpressionValid()
Returns if the expression is valid.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
bool parserError() const
Will be set to true if the current expression text reports a parser error with the context...
QString expectedOutputFormat()
The set expected format string.
Function was called with the wrong number of args.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
bool formatterCanProvideAvailableValues(QgsVectorLayer *layer, const QString &fieldName)
An expression node for value IN or NOT IN clauses.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc.
Definition: qgsproject.h:92
Abstract base class for all nodes that can appear in an expression.
void showEvent(QShowEvent *e) override
Q_DECL_DEPRECATED void removeFromUserExpressions(const QString &label)
Removes the expression label from the user stored expressions.
Load recent expressions given the collection key.
An expression node for expression functions.
virtual QVariantList availableValues(const QVariantMap &config, int countLimit, const QgsFieldFormatterContext &context) const
Returns a list of the values that would be possible to select with this widget type On a RelationRefe...
void autosave()
Auto save the current Python function code.
QgsExpressionNode * elseExp() const
The ELSE expression used for the condition.
static const QList< QgsExpressionFunction * > & Functions()
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context for the widget.
static bool eval(const QString &command, QString &result)
Eval a Python statement.
bool evalError() const
Will be set to true if the current expression text reported an eval error with the context...
A field formatter helps to handle and display values for a field.
QgsExpressionItem::ItemType getItemType() const
Gets the type of expression item, e.g., header, field, ExpressionNode.
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:732
A general purpose distance and area calculator, capable of performing ellipsoid based calculations...
A abstract base class for defining QgsExpression functions.
An expression item that can be used in the QgsExpressionBuilderWidget tree.
void setGeomCalculator(const QgsDistanceArea *calc)
Sets the geometry calculator used for distance and area calculations in expressions.
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
int parserFirstLine
First line in the parser this node was found.
virtual QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
static bool run(const QString &command, const QString &messageOnError=QString())
Execute a Python statement.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
QString getExpressionText() const
const QgsExpressionNode * rootNode() const
Returns the root node of the expression.
Holder for the widget type and its configuration for a field.
A unary node is either negative as in boolean (not) or as in numbers (minus).
Q_DECL_DEPRECATED void loadFieldsAndValues(const QMap< QString, QStringList > &fieldValues)
Loads field names and values from the specified map.
void initWithLayer(QgsVectorLayer *layer, const QgsExpressionContext &context=QgsExpressionContext(), const QString &recentCollection=QStringLiteral("generic"), const Flags &flags=LoadAll)
Initialize with a layer.
A binary expression operator, which operates on two values.
QgsVectorLayer * layer() const
Returns the current layer or a nullptr.
QString expressionText()
Gets the expression string that has been set in the expression area.
QgsExpressionTreeView * expressionTree() const
Returns the expression tree.
static QString helpText(QString name)
Returns the help text for a specified function.
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
void setExpressionText(const QString &expression)
Sets the expression string for the widget.
void editSelectedUserExpression()
Edits the selected expression from the stored user expressions, the selected expression must be a use...
void parserErrorChanged()
Will be set to true if the current expression text reported a parser error with the context...
QString getHelpText() const
Gets the help text that is associated with this expression item.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsExpressionBuilderWidget(QWidget *parent=nullptr)
Create a new expression builder widget with an optional parent.
void currentExpressionItemChanged(QgsExpressionItem *item)
Emitter when the current expression item changed.
A generic dialog for editing expression text, label and help text.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool nextFeature(QgsFeature &f)
void setProject(QgsProject *project)
Sets the project used in field formatter.
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
Represents a vector layer which manages a vector based data sets.
void expressionParsed(bool isValid)
Emitted when the user changes the expression in the widget.
void loadAllUsedValues()
Load all unique values from the set layer into the sample area.
virtual QgsExpressionNode::NodeType nodeType() const =0
Gets the type of this node.
QgsExpressionNode * operand() const
Returns the node the operator will operate upon.
Q_DECL_DEPRECATED void saveToRecent(const QString &collection="generic")
Adds the current expression to the given collection.
static bool isValid()
Returns true if the runner has an instance (and thus is able to run commands)
void storeCurrentUserExpression()
Adds the current expressions to the stored user expressions.
Represents a "WHEN... THEN..." portation of a CASE WHEN clause in an expression.
Q_DECL_DEPRECATED void loadUserExpressions()
Loads the user expressions.
void loadAllValues()
Load all unique values from the set layer into the sample area.
int parserLastColumn
Last column in the parser this node was found.
Flags flags() const
Returns the flags.
int fnIndex() const
Returns the index of the node&#39;s function.
void setSearchText(const QString &text)
Sets the text to filter the expression tree.