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