QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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::pressed, this, &QgsExpressionBuilderWidget::btnNewFile_pressed );
81  connect( btnRemoveFile, &QPushButton::pressed, 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, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::storeCurrentUserExpression );
88  connect( btnEditExpression, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::editSelectedUserExpression );
89  connect( btnRemoveExpression, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::removeSelectedUserExpression );
90  connect( btnImportExpressions, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::importUserExpressions_pressed );
91  connect( btnExportExpressions, &QPushButton::pressed, this, &QgsExpressionBuilderWidget::exportUserExpressions_pressed );
92  connect( btnClearEditor, &QPushButton::pressed, 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::pressed, this, &QgsExpressionBuilderWidget::loadAllValues );
124  connect( btnLoadSample, &QAbstractButton::pressed, this, &QgsExpressionBuilderWidget::loadSampleValues );
125 
126  const auto pushButtons { mOperatorsGroupBox->findChildren<QPushButton *>() };
127  for ( QPushButton *button : pushButtons )
128  {
129  connect( button, &QAbstractButton::pressed, this, &QgsExpressionBuilderWidget::operatorButtonClicked );
130  }
131 
132  txtSearchEdit->setShowSearchIcon( true );
133  txtSearchEdit->setPlaceholderText( tr( "Search…" ) );
134 
135  mValuesModel = qgis::make_unique<QStandardItemModel>();
136  mProxyValues = qgis::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, const 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, const 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, const 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 
290  txtExpressionString->setFields( mLayer->fields() );
291  }
292 }
293 
295 {
296  return mLayer;
297 }
298 
299 void QgsExpressionBuilderWidget::expressionTreeItemChanged( QgsExpressionItem *item )
300 {
301  txtSearchEditValues->clear();
302 
303  if ( !item )
304  return;
305 
306  bool isField = mLayer && item->getItemType() == QgsExpressionItem::Field;
307  if ( isField )
308  {
309  mValuesModel->clear();
310 
311  cbxValuesInUse->setVisible( formatterCanProvideAvailableValues( mLayer, item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString() ) );
312  cbxValuesInUse->setChecked( false );
313  }
314  mValueGroupBox->setVisible( isField );
315 
316  mShowHelpButton->setText( isField ? tr( "Show Values" ) : tr( "Show Help" ) );
317 
318  // Show the help for the current item.
319  QString help = loadFunctionHelp( item );
320  txtHelpText->setText( help );
321 
322  bool isUserExpression = item->parent() && item->parent()->text() == mUserExpressionsGroupName;
323 
324  btnRemoveExpression->setEnabled( isUserExpression );
325  btnEditExpression->setEnabled( isUserExpression );
326 }
327 
328 void QgsExpressionBuilderWidget::btnRun_pressed()
329 {
330  if ( !cmbFileNames->currentItem() )
331  return;
332 
333  QString file = cmbFileNames->currentItem()->text();
334  saveFunctionFile( file );
335  runPythonCode( txtPython->text() );
336 }
337 
338 void QgsExpressionBuilderWidget::runPythonCode( const QString &code )
339 {
340  if ( QgsPythonRunner::isValid() )
341  {
342  QString pythontext = code;
343  QgsPythonRunner::run( pythontext );
344  }
345  mExpressionTreeView->refresh();
346 }
347 
349 {
350  QDir myDir( mFunctionsPath );
351  if ( !myDir.exists() )
352  {
353  myDir.mkpath( mFunctionsPath );
354  }
355 
356  if ( !fileName.endsWith( QLatin1String( ".py" ) ) )
357  {
358  fileName.append( ".py" );
359  }
360 
361  fileName = mFunctionsPath + QDir::separator() + fileName;
362  QFile myFile( fileName );
363  if ( myFile.open( QIODevice::WriteOnly | QFile::Truncate ) )
364  {
365  QTextStream myFileStream( &myFile );
366  myFileStream << txtPython->text() << endl;
367  myFile.close();
368  }
369 }
370 
372 {
373  mFunctionsPath = path;
374  QDir dir( path );
375  dir.setNameFilters( QStringList() << QStringLiteral( "*.py" ) );
376  QStringList files = dir.entryList( QDir::Files );
377  cmbFileNames->clear();
378  const auto constFiles = files;
379  for ( const QString &name : constFiles )
380  {
381  QFileInfo info( mFunctionsPath + QDir::separator() + name );
382  if ( info.baseName() == QLatin1String( "__init__" ) ) continue;
383  QListWidgetItem *item = new QListWidgetItem( QgsApplication::getThemeIcon( QStringLiteral( "console/iconTabEditorConsole.svg" ) ), info.baseName() );
384  cmbFileNames->addItem( item );
385  }
386  if ( !cmbFileNames->currentItem() )
387  {
388  cmbFileNames->setCurrentRow( 0 );
389  }
390 
391  if ( cmbFileNames->count() == 0 )
392  {
393  // Create default sample entry.
394  newFunctionFile( "default" );
395  txtPython->setText( QStringLiteral( "'''\n#Sample custom function file\n "
396  "(uncomment to use and customize or Add button to create a new file) \n%1 \n '''" ).arg( txtPython->text() ) );
397  saveFunctionFile( "default" );
398  }
399 }
400 
401 void QgsExpressionBuilderWidget::newFunctionFile( const QString &fileName )
402 {
403  QList<QListWidgetItem *> items = cmbFileNames->findItems( fileName, Qt::MatchExactly );
404  if ( !items.isEmpty() )
405  return;
406 
407  QListWidgetItem *item = new QListWidgetItem( QgsApplication::getThemeIcon( QStringLiteral( "console/iconTabEditorConsole.svg" ) ), fileName );
408  cmbFileNames->insertItem( 0, item );
409  cmbFileNames->setCurrentRow( 0 );
410 
411  QString templatetxt;
412  QgsPythonRunner::eval( QStringLiteral( "qgis.user.default_expression_template" ), templatetxt );
413  txtPython->setText( templatetxt );
414  saveFunctionFile( fileName );
415 }
416 
417 void QgsExpressionBuilderWidget::btnNewFile_pressed()
418 {
419  bool ok;
420  QString text = QInputDialog::getText( this, tr( "New File" ),
421  tr( "New file name:" ), QLineEdit::Normal,
422  QString(), &ok );
423  if ( ok && !text.isEmpty() )
424  {
425  newFunctionFile( text );
426  }
427 }
428 
429 void QgsExpressionBuilderWidget::btnRemoveFile_pressed()
430 {
431  if ( QMessageBox::question( this, tr( "Remove File" ),
432  tr( "Are you sure you want to remove current functions file?" ),
433  QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No )
434  return;
435 
436  int currentRow = cmbFileNames->currentRow();
437  QString fileName = cmbFileNames->currentItem()->text();
438  if ( QFile::remove( mFunctionsPath + QDir::separator() + fileName.append( ".py" ) ) )
439  {
440  {
441  QListWidgetItem *itemToRemove = whileBlocking( cmbFileNames )->takeItem( currentRow );
442  delete itemToRemove;
443  }
444 
445  if ( cmbFileNames->count() > 0 )
446  {
447  cmbFileNames->setCurrentRow( currentRow > 0 ? currentRow - 1 : 0 );
448  loadCodeFromFile( mFunctionsPath + QDir::separator() + cmbFileNames->currentItem()->text() );
449  }
450  else
451  {
452  btnRemoveFile->setEnabled( false );
453  txtPython->clear();
454  }
455  }
456  else
457  {
458  QMessageBox::warning( this, tr( "Remove file" ), tr( "Failed to remove function file '%1'." ).arg( fileName ) );
459  }
460 }
461 
462 void QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem )
463 {
464  if ( lastitem )
465  {
466  QString filename = lastitem->text();
467  saveFunctionFile( filename );
468  }
469  QString path = mFunctionsPath + QDir::separator() + item->text();
470  loadCodeFromFile( path );
471 }
472 
474 {
475  if ( !path.endsWith( QLatin1String( ".py" ) ) )
476  path.append( ".py" );
477 
478  txtPython->loadScript( path );
479 }
480 
482 {
483  txtPython->setText( code );
484 }
485 
486 void QgsExpressionBuilderWidget::insertExpressionText( const QString &text )
487 {
488  // Insert the expression text or replace selected text
489  txtExpressionString->insertText( text );
490  txtExpressionString->setFocus();
491 }
492 
493 void QgsExpressionBuilderWidget::loadFieldsAndValues( const QMap<QString, QStringList> &fieldValues )
494 {
495  Q_UNUSED( fieldValues )
496  // This is not maintained and setLayer() should be used instead.
497 }
498 
499 void QgsExpressionBuilderWidget::fillFieldValues( const QString &fieldName, int countLimit, bool forceUsedValues )
500 {
501  // TODO We should really return a error the user of the widget that
502  // the there is no layer set.
503  if ( !mLayer )
504  return;
505 
506  // TODO We should thread this so that we don't hold the user up if the layer is massive.
507 
508  const QgsFields fields = mLayer->fields();
509  int fieldIndex = fields.lookupField( fieldName );
510 
511  if ( fieldIndex < 0 )
512  return;
513 
514  const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
516 
517  QVariantList values;
518  if ( cbxValuesInUse->isVisible() && !cbxValuesInUse->isChecked() && !forceUsedValues )
519  {
520  QgsFieldFormatterContext fieldFormatterContext;
521  fieldFormatterContext.setProject( mProject );
522  values = formatter->availableValues( setup.config(), countLimit, fieldFormatterContext );
523  }
524  else
525  {
526  values = qgis::setToList( mLayer->uniqueValues( fieldIndex, countLimit ) );
527  }
528  std::sort( values.begin(), values.end() );
529 
530  mValuesModel->clear();
531  for ( const QVariant &value : qgis::as_const( values ) )
532  {
533  QString strValue;
534  if ( value.isNull() )
535  strValue = QStringLiteral( "NULL" );
536  else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong )
537  strValue = value.toString();
538  else
539  strValue = '\'' + value.toString().replace( '\'', QLatin1String( "''" ) ) + '\'';
540 
541  QString representedValue = formatter->representValue( mLayer, fieldIndex, setup.config(), QVariant(), value );
542  if ( representedValue != value.toString() )
543  representedValue = representedValue + QStringLiteral( " [" ) + strValue + ']';
544 
545  QStandardItem *item = new QStandardItem( representedValue );
546  item->setData( strValue );
547  mValuesModel->appendRow( item );
548  }
549 }
550 
551 QString QgsExpressionBuilderWidget::getFunctionHelp( QgsExpressionFunction *function )
552 {
553  if ( !function )
554  return QString();
555 
556  QString helpContents = QgsExpression::helpText( function->name() );
557 
558  return QStringLiteral( "<head><style>" ) + helpStylesheet() + QStringLiteral( "</style></head><body>" ) + helpContents + QStringLiteral( "</body>" );
559 
560 }
561 
562 
563 
565 {
566  return mExpressionValid;
567 }
568 
569 void QgsExpressionBuilderWidget::saveToRecent( const QString &collection )
570 {
571  mExpressionTreeView->saveToRecent( expressionText(), collection );
572 }
573 
574 void QgsExpressionBuilderWidget::loadRecent( const QString &collection )
575 {
576  mExpressionTreeView->loadRecent( collection );
577 }
578 
580 {
581  return mExpressionTreeView;
582 }
583 
584 // this is potentially very slow if there are thousands of user expressions, every time entire cleanup and load
586 {
587  mExpressionTreeView->loadUserExpressions();
588 }
589 
590 void QgsExpressionBuilderWidget::saveToUserExpressions( const QString &label, const QString expression, const QString &helpText )
591 {
592  mExpressionTreeView->saveToUserExpressions( label, expression, helpText );
593 }
594 
596 {
597  mExpressionTreeView->removeFromUserExpressions( label );
598 }
599 
600 
602 {
603  mExpressionPreviewWidget->setGeomCalculator( da );
604 }
605 
607 {
608  return txtExpressionString->text();
609 }
610 
611 void QgsExpressionBuilderWidget::setExpressionText( const QString &expression )
612 {
613  txtExpressionString->setText( expression );
614 }
615 
617 {
618  return lblExpected->text();
619 }
620 
622 {
623  lblExpected->setText( expected );
624  mExpectedOutputFrame->setVisible( !expected.isNull() );
625 }
626 
628 {
629  mExpressionContext = context;
630  txtExpressionString->setExpressionContext( mExpressionContext );
631  mExpressionTreeView->setExpressionContext( context );
632  mExpressionPreviewWidget->setExpressionContext( context );
633 }
634 
635 void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
636 {
637  QString text = expressionText();
638 
639  btnClearEditor->setEnabled( ! txtExpressionString->text().isEmpty() );
640  btnSaveExpression->setEnabled( false );
641 
642  mExpressionPreviewWidget->setExpressionText( text );
643 }
644 
646 {
647  return mExpressionPreviewWidget->parserError();
648 }
649 
651 {
652  return mExpressionPreviewWidget->evalError();
653 }
654 
656 {
658  return mExpressionTreeView->model();
660 }
661 
663 {
664  return mProject;
665 }
666 
668 {
669  mProject = project;
670  mExpressionTreeView->setProject( project );
671 }
672 
674 {
675  QWidget::showEvent( e );
676  txtExpressionString->setFocus();
677 }
678 
679 void QgsExpressionBuilderWidget::createErrorMarkers( QList<QgsExpression::ParserError> errors )
680 {
681  clearErrors();
682  for ( const QgsExpression::ParserError &error : errors )
683  {
684  int errorFirstLine = error.firstLine - 1 ;
685  int errorFirstColumn = error.firstColumn - 1;
686  int errorLastColumn = error.lastColumn - 1;
687  int errorLastLine = error.lastLine - 1;
688 
689  // If we have a unknown error we just mark the point that hit the error for now
690  // until we can handle others more.
691  if ( error.errorType == QgsExpression::ParserError::Unknown )
692  {
693  errorFirstLine = errorLastLine;
694  errorFirstColumn = errorLastColumn - 1;
695  }
696  txtExpressionString->fillIndicatorRange( errorFirstLine,
697  errorFirstColumn,
698  errorLastLine,
699  errorLastColumn, error.errorType );
700  }
701 }
702 
703 void QgsExpressionBuilderWidget::createMarkers( const QgsExpressionNode *inNode )
704 {
705  switch ( inNode->nodeType() )
706  {
707  case QgsExpressionNode::NodeType::ntFunction:
708  {
709  const QgsExpressionNodeFunction *node = static_cast<const QgsExpressionNodeFunction *>( inNode );
710  txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
711  txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->fnIndex() );
712  int start = inNode->parserFirstColumn - 1;
713  int end = inNode->parserLastColumn - 1;
714  int start_pos = txtExpressionString->positionFromLineIndex( inNode->parserFirstLine - 1, start );
715  txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
716  if ( node->args() )
717  {
718  const QList< QgsExpressionNode * > nodeList = node->args()->list();
719  for ( QgsExpressionNode *n : nodeList )
720  {
721  createMarkers( n );
722  }
723  }
724  break;
725  }
726  case QgsExpressionNode::NodeType::ntLiteral:
727  {
728  break;
729  }
730  case QgsExpressionNode::NodeType::ntUnaryOperator:
731  {
732  const QgsExpressionNodeUnaryOperator *node = static_cast<const QgsExpressionNodeUnaryOperator *>( inNode );
733  createMarkers( node->operand() );
734  break;
735  }
736  case QgsExpressionNode::NodeType::ntBinaryOperator:
737  {
738  const QgsExpressionNodeBinaryOperator *node = static_cast<const QgsExpressionNodeBinaryOperator *>( inNode );
739  createMarkers( node->opLeft() );
740  createMarkers( node->opRight() );
741  break;
742  }
743  case QgsExpressionNode::NodeType::ntColumnRef:
744  {
745  break;
746  }
747  case QgsExpressionNode::NodeType::ntInOperator:
748  {
749  const QgsExpressionNodeInOperator *node = static_cast<const QgsExpressionNodeInOperator *>( inNode );
750  if ( node->list() )
751  {
752  const QList< QgsExpressionNode * > nodeList = node->list()->list();
753  for ( QgsExpressionNode *n : nodeList )
754  {
755  createMarkers( n );
756  }
757  }
758  break;
759  }
760  case QgsExpressionNode::NodeType::ntCondition:
761  {
762  const QgsExpressionNodeCondition *node = static_cast<const QgsExpressionNodeCondition *>( inNode );
763  for ( QgsExpressionNodeCondition::WhenThen *cond : node->conditions() )
764  {
765  createMarkers( cond->whenExp() );
766  createMarkers( cond->thenExp() );
767  }
768  if ( node->elseExp() )
769  {
770  createMarkers( node->elseExp() );
771  }
772  break;
773  }
774  case QgsExpressionNode::NodeType::ntIndexOperator:
775  {
776  break;
777  }
778  }
779 }
780 
781 void QgsExpressionBuilderWidget::clearFunctionMarkers()
782 {
783  int lastLine = txtExpressionString->lines() - 1;
784  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
785 }
786 
787 void QgsExpressionBuilderWidget::clearErrors()
788 {
789  int lastLine = txtExpressionString->lines() - 1;
790  // Note: -1 here doesn't seem to do the clear all like the other functions. Will need to make this a bit smarter.
791  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::Unknown );
792  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionInvalidParams );
793  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionUnknown );
794  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionWrongArgs );
795  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionNamedArgsError );
796 }
797 
798 void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
799 {
800  mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
801  mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
802 }
803 
804 void QgsExpressionBuilderWidget::mValuesListView_doubleClicked( const QModelIndex &index )
805 {
806  // Insert the item text or replace selected text
807  txtExpressionString->insertText( ' ' + index.data( Qt::UserRole + 1 ).toString() + ' ' );
808  txtExpressionString->setFocus();
809 }
810 
811 void QgsExpressionBuilderWidget::operatorButtonClicked()
812 {
813  QPushButton *button = qobject_cast<QPushButton *>( sender() );
814 
815  // Insert the button text or replace selected text
816  txtExpressionString->insertText( ' ' + button->text() + ' ' );
817  txtExpressionString->setFocus();
818 }
819 
821 {
822  QgsExpressionItem *item = mExpressionTreeView->currentItem();
823  // TODO We should really return a error the user of the widget that
824  // the there is no layer set.
825  if ( !mLayer || !item )
826  return;
827 
828  mValueGroupBox->show();
829  fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), 10 );
830 }
831 
833 {
834  QgsExpressionItem *item = mExpressionTreeView->currentItem();
835  // TODO We should really return a error the user of the widget that
836  // the there is no layer set.
837  if ( !mLayer || !item )
838  return;
839 
840  mValueGroupBox->show();
841  fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), -1 );
842 }
843 
845 {
846  QgsExpressionItem *item = mExpressionTreeView->currentItem();
847  // TODO We should really return a error the user of the widget that
848  // the there is no layer set.
849  if ( !mLayer || !item )
850  return;
851 
852  mValueGroupBox->show();
853  fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), 10, true );
854 }
855 
857 {
858  QgsExpressionItem *item = mExpressionTreeView->currentItem();
859  // TODO We should really return a error the user of the widget that
860  // the there is no layer set.
861  if ( !mLayer || !item )
862  return;
863 
864  mValueGroupBox->show();
865  fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), -1, true );
866 }
867 
868 void QgsExpressionBuilderWidget::txtPython_textChanged()
869 {
870  lblAutoSave->setText( tr( "Saving…" ) );
871  if ( mAutoSave )
872  {
873  autosave();
874  }
875 }
876 
878 {
879  // Don't auto save if not on function editor that would be silly.
880  if ( tabWidget->currentIndex() != 1 )
881  return;
882 
883  QListWidgetItem *item = cmbFileNames->currentItem();
884  if ( !item )
885  return;
886 
887  QString file = item->text();
888  saveFunctionFile( file );
889  lblAutoSave->setText( QStringLiteral( "Saved" ) );
890  QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect();
891  lblAutoSave->setGraphicsEffect( effect );
892  QPropertyAnimation *anim = new QPropertyAnimation( effect, "opacity" );
893  anim->setDuration( 2000 );
894  anim->setStartValue( 1.0 );
895  anim->setEndValue( 0.0 );
896  anim->setEasingCurve( QEasingCurve::OutQuad );
897  anim->start( QAbstractAnimation::DeleteWhenStopped );
898 }
899 
901 {
902  const QString expression { this->expressionText() };
903  QgsExpressionStoreDialog dlg { expression, expression, QString( ), mExpressionTreeView->userExpressionLabels() };
904  if ( dlg.exec() == QDialog::DialogCode::Accepted )
905  {
906  mExpressionTreeView->saveToUserExpressions( dlg.label(), dlg.expression(), dlg.helpText() );
907  }
908 }
909 
911 {
912  // Get the item
913  QgsExpressionItem *item = mExpressionTreeView->currentItem();
914  if ( !item )
915  return;
916 
917  // Don't handle remove if we are on a header node or the parent
918  // is not the user group
919  if ( item->getItemType() == QgsExpressionItem::Header ||
920  ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) )
921  return;
922 
923  QgsSettings settings;
924  QString helpText = settings.value( QStringLiteral( "user/%1/helpText" ).arg( item->text() ), "", QgsSettings::Section::Expressions ).toString();
925  QgsExpressionStoreDialog dlg { item->text(), item->getExpressionText(), helpText };
926 
927  if ( dlg.exec() == QDialog::DialogCode::Accepted )
928  {
929  // label has changed removed the old one before adding the new one
930  if ( dlg.label() != item->text() )
931  {
932  mExpressionTreeView->removeFromUserExpressions( item->text() );
933  }
934 
935  mExpressionTreeView->saveToUserExpressions( dlg.label(), dlg.expression(), dlg.helpText() );
936  }
937 }
938 
940 {
941  // Get the item
942  QgsExpressionItem *item = mExpressionTreeView->currentItem();
943 
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  if ( QMessageBox::Yes == QMessageBox::question( this, tr( "Remove Stored Expression" ),
954  tr( "Do you really want to remove stored expressions '%1'?" ).arg( item->text() ),
955  QMessageBox::Yes | QMessageBox::No ) )
956  {
957  mExpressionTreeView->removeFromUserExpressions( item->text() );
958  }
959 
960 }
961 
962 void QgsExpressionBuilderWidget::exportUserExpressions_pressed()
963 {
964  QgsSettings settings;
965  QString lastSaveDir = settings.value( QStringLiteral( "lastExportExpressionsDir" ), QDir::homePath(), QgsSettings::App ).toString();
966  QString saveFileName = QFileDialog::getSaveFileName(
967  this,
968  tr( "Export User Expressions" ),
969  lastSaveDir,
970  tr( "User expressions" ) + " (*.json)" );
971 
972  if ( saveFileName.isEmpty() )
973  return;
974 
975  QFileInfo saveFileInfo( saveFileName );
976 
977  if ( saveFileInfo.suffix().isEmpty() )
978  {
979  QString saveFileNameWithSuffix = saveFileName.append( ".json" );
980  saveFileInfo = QFileInfo( saveFileNameWithSuffix );
981  }
982 
983  settings.setValue( QStringLiteral( "lastExportExpressionsDir" ), saveFileInfo.absolutePath(), QgsSettings::App );
984 
985  QJsonDocument exportJson = mExpressionTreeView->exportUserExpressions();
986  QFile jsonFile( saveFileName );
987 
988  if ( !jsonFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
989  QMessageBox::warning( this, tr( "Export user expressions" ), tr( "Error while creating the expressions file." ) );
990 
991  if ( ! jsonFile.write( exportJson.toJson() ) )
992  QMessageBox::warning( this, tr( "Export user expressions" ), tr( "Error while creating the expressions file." ) );
993  else
994  jsonFile.close();
995 }
996 
997 void QgsExpressionBuilderWidget::importUserExpressions_pressed()
998 {
999  QgsSettings settings;
1000  QString lastImportDir = settings.value( QStringLiteral( "lastImportExpressionsDir" ), QDir::homePath(), QgsSettings::App ).toString();
1001  QString loadFileName = QFileDialog::getOpenFileName(
1002  this,
1003  tr( "Import User Expressions" ),
1004  lastImportDir,
1005  tr( "User expressions" ) + " (*.json)" );
1006 
1007  if ( loadFileName.isEmpty() )
1008  return;
1009 
1010  QFileInfo loadFileInfo( loadFileName );
1011 
1012  settings.setValue( QStringLiteral( "lastImportExpressionsDir" ), loadFileInfo.absolutePath(), QgsSettings::App );
1013 
1014  QFile jsonFile( loadFileName );
1015 
1016  if ( !jsonFile.open( QFile::ReadOnly ) )
1017  QMessageBox::warning( this, tr( "Import User Expressions" ), tr( "Error while reading the expressions file." ) );
1018 
1019  QTextStream jsonStream( &jsonFile );
1020  QString jsonString = jsonFile.readAll();
1021  jsonFile.close();
1022 
1023  QJsonDocument importJson = QJsonDocument::fromJson( jsonString.toUtf8() );
1024 
1025  if ( importJson.isNull() )
1026  {
1027  QMessageBox::warning( this, tr( "Import User Expressions" ), tr( "Error while reading the expressions file." ) );
1028  return;
1029  }
1030 
1031  mExpressionTreeView->loadExpressionsFromJson( importJson );
1032 }
1033 
1034 
1035 const QList<QgsExpressionItem *> QgsExpressionBuilderWidget::findExpressions( const QString &label )
1036 {
1037  return mExpressionTreeView->findExpressions( label );
1038 }
1039 
1040 void QgsExpressionBuilderWidget::indicatorClicked( int line, int index, Qt::KeyboardModifiers state )
1041 {
1042  if ( state & Qt::ControlModifier )
1043  {
1044  int position = txtExpressionString->positionFromLineIndex( line, index );
1045  long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID, static_cast<long int>( position ) );
1046  QgsExpressionFunction *func = QgsExpression::Functions()[fncIndex];
1047  QString help = getFunctionHelp( func );
1048  txtHelpText->setText( help );
1049  }
1050 }
1051 
1052 void QgsExpressionBuilderWidget::onExpressionParsed( bool state )
1053 {
1054  clearErrors();
1055 
1056  mExpressionValid = state;
1057  if ( state )
1058  {
1059  createMarkers( mExpressionPreviewWidget->rootNode() );
1060  }
1061  else
1062  {
1063  createErrorMarkers( mExpressionPreviewWidget->parserErrors() );
1064  }
1065 }
1066 
1067 QString QgsExpressionBuilderWidget::helpStylesheet() const
1068 {
1069  //start with default QGIS report style
1070  QString style = QgsApplication::reportStyleSheet();
1071 
1072  //add some tweaks
1073  style += " .functionname {color: #0a6099; font-weight: bold;} "
1074  " .argument {font-family: monospace; color: #bf0c0c; font-style: italic; } "
1075  " td.argument { padding-right: 10px; }";
1076 
1077  return style;
1078 }
1079 
1080 QString QgsExpressionBuilderWidget::loadFunctionHelp( QgsExpressionItem *expressionItem )
1081 {
1082  if ( !expressionItem )
1083  return QString();
1084 
1085  QString helpContents = expressionItem->getHelpText();
1086 
1087  // Return the function help that is set for the function if there is one.
1088  if ( helpContents.isEmpty() )
1089  {
1090  QString name = expressionItem->data( Qt::UserRole ).toString();
1091 
1092  if ( expressionItem->getItemType() == QgsExpressionItem::Field )
1093  helpContents = QgsExpression::helpText( QStringLiteral( "Field" ) );
1094  else
1095  helpContents = QgsExpression::helpText( name );
1096  }
1097 
1098  return "<head><style>" + helpStylesheet() + "</style></head><body>" + helpContents + "</body>";
1099 }
1100 
1101 
1102 // *************
1103 // Menu provider
1104 
1105 QMenu *QgsExpressionBuilderWidget::ExpressionTreeMenuProvider::createContextMenu( QgsExpressionItem *item )
1106 {
1107  QMenu *menu = nullptr;
1108  QgsVectorLayer *layer = mExpressionBuilderWidget->layer();
1109  if ( item->getItemType() == QgsExpressionItem::Field && layer )
1110  {
1111  menu = new QMenu( mExpressionBuilderWidget );
1112  menu->addAction( tr( "Load First 10 Unique Values" ), mExpressionBuilderWidget, &QgsExpressionBuilderWidget::loadSampleValues );
1113  menu->addAction( tr( "Load All Unique Values" ), mExpressionBuilderWidget, &QgsExpressionBuilderWidget::loadAllValues );
1114 
1115  if ( formatterCanProvideAvailableValues( layer, item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString() ) )
1116  {
1117  menu->addAction( tr( "Load First 10 Unique Used Values" ), mExpressionBuilderWidget, SLOT( loadSampleUsedValues() ) );
1118  menu->addAction( tr( "Load All Unique Used Values" ), mExpressionBuilderWidget, &QgsExpressionBuilderWidget::loadAllUsedValues );
1119  }
1120  }
1121  return menu;
1122 }
QgsExpressionBuilderWidget::project
QgsProject * project()
Returns the project currently associated with the widget.
Definition: qgsexpressionbuilderwidget.cpp:662
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:370
formatter
Definition: qgsbasicnumericformat.cpp:25
qgsexpressioncontextutils.h
QgsExpression::ParserError::FunctionInvalidParams
@ FunctionInvalidParams
Function was called with invalid args.
Definition: qgsexpression.h:120
QgsExpressionItem::ITEM_NAME_ROLE
static const int ITEM_NAME_ROLE
Item name role.
Definition: qgsexpressiontreeview.h:99
QgsExpressionNodeCondition::elseExp
QgsExpressionNode * elseExp() const
The ELSE expression used for the condition.
Definition: qgsexpressionnodeimpl.h:522
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Definition: qgsapplication.cpp:626
QgsExpressionNodeBinaryOperator
A binary expression operator, which operates on two values.
Definition: qgsexpressionnodeimpl.h:92
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:174
QgsEditorWidgetSetup
Holder for the widget type and its configuration for a field.
Definition: qgseditorwidgetsetup.h:29
QgsSettings::App
@ App
Definition: qgssettings.h:75
QgsExpressionBuilderWidget::findExpressions
const QList< QgsExpressionItem * > findExpressions(const QString &label)
Returns the list of expression items matching a label.
Definition: qgsexpressionbuilderwidget.cpp:1035
QgsExpressionBuilderWidget::init
void init(const QgsExpressionContext &context=QgsExpressionContext(), const QString &recentCollection=QStringLiteral("generic"), const Flags &flags=LoadAll)
Initialize without any layer.
Definition: qgsexpressionbuilderwidget.cpp:254
QgsExpressionBuilderWidget::LoadUserExpressions
@ LoadUserExpressions
Load user expressions.
Definition: qgsexpressionbuilderwidget.h:55
qgsexpression.h
qgsexpressionstoredialog.h
QgsExpressionBuilderWidget::loadSampleValues
void loadSampleValues()
Load sample values into the sample value area.
Definition: qgsexpressionbuilderwidget.cpp:820
QgsExpressionNodeFunction::fnIndex
int fnIndex() const
Returns the index of the node's function.
Definition: qgsexpressionnodeimpl.h:331
qgsfeatureiterator.h
QgsExpressionBuilderWidget::initWithLayer
void initWithLayer(QgsVectorLayer *layer, const QgsExpressionContext &context=QgsExpressionContext(), const QString &recentCollection=QStringLiteral("generic"), const Flags &flags=LoadAll)
Initialize with a layer.
Definition: qgsexpressionbuilderwidget.cpp:265
QgsFields
Container of fields for a vector layer.
Definition: qgsfields.h:45
QgsEditorWidgetSetup::config
QVariantMap config() const
Definition: qgseditorwidgetsetup.h:51
QgsExpressionContextUtils::layerScope
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
Definition: qgsexpressioncontextutils.cpp:265
qgsfeature.h
qgsfieldformatterregistry.h
QgsExpressionNodeUnaryOperator
A unary node is either negative as in boolean (not) or as in numbers (minus).
Definition: qgsexpressionnodeimpl.h:28
QgsFieldFormatterContext
A context for field formatter containing information like the project.
Definition: qgsfieldformatter.h:34
QgsExpressionNodeCondition
An expression node for CASE WHEN clauses.
Definition: qgsexpressionnodeimpl.h:441
QgsExpressionBuilderWidget::loadCodeFromFile
void loadCodeFromFile(QString path)
Loads code from the given file into the function editor.
Definition: qgsexpressionbuilderwidget.cpp:473
QgsExpressionItem::Field
@ Field
Definition: qgsexpressiontreeview.h:43
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QgsExpressionBuilderWidget::parserError
bool parserError() const
Will be set to true if the current expression text reports a parser error with the context.
Definition: qgsexpressionbuilderwidget.cpp:645
QgsExpressionTreeView::setSearchText
void setSearchText(const QString &text)
Sets the text to filter the expression tree.
Definition: qgsexpressiontreeview.cpp:194
QgsExpressionBuilderWidget::newFunctionFile
void newFunctionFile(const QString &fileName="scratch")
Creates a new file in the function editor.
Definition: qgsexpressionbuilderwidget.cpp:401
QgsExpressionBuilderWidget::layer
QgsVectorLayer * layer() const
Returns the current layer or a nullptr.
Definition: qgsexpressionbuilderwidget.cpp:294
QgsExpressionBuilderWidget::expressionTree
QgsExpressionTreeView * expressionTree() const
Returns the expression tree.
Definition: qgsexpressionbuilderwidget.cpp:579
QgsProject
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:95
QgsExpressionNodeInOperator
An expression node for value IN or NOT IN clauses.
Definition: qgsexpressionnodeimpl.h:265
QgsExpressionTreeView
QgsExpressionTreeView is a tree view to list all expressions functions, variables and fields that can...
Definition: qgsexpressiontreeview.h:137
qgsapplication.h
QgsVectorLayer::fields
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Definition: qgsvectorlayer.cpp:3283
Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:797
QgsEditorWidgetSetup::type
QString type() const
Definition: qgseditorwidgetsetup.h:46
QgsExpressionBuilderWidget::setExpressionContext
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context for the widget.
Definition: qgsexpressionbuilderwidget.cpp:627
whileBlocking
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:262
QgsExpressionNodeFunction::args
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
Definition: qgsexpressionnodeimpl.h:336
QgsExpressionItem::getItemType
QgsExpressionItem::ItemType getItemType() const
Gets the type of expression item, e.g., header, field, ExpressionNode.
Definition: qgsexpressiontreeview.h:90
QgsExpressionPreviewWidget::expressionParsed
void expressionParsed(bool isValid)
Emitted when the user changes the expression in the widget.
QgsExpressionPreviewWidget::evalErrorChanged
void evalErrorChanged()
Will be set to true if the current expression text reported an eval error with the context.
QgsExpressionBuilderWidget::~QgsExpressionBuilderWidget
~QgsExpressionBuilderWidget() override
Definition: qgsexpressionbuilderwidget.cpp:245
QgsExpressionNodeCondition::conditions
WhenThenList conditions() const
The list of WHEN THEN expression parts of the expression.
Definition: qgsexpressionnodeimpl.h:516
QgsVectorLayer::uniqueValues
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
Definition: qgsvectorlayer.cpp:3968
QgsExpressionBuilderWidget::loadAllUsedValues
void loadAllUsedValues()
Load all unique values from the set layer into the sample area.
Definition: qgsexpressionbuilderwidget.cpp:856
QgsExpression::ParserError::Unknown
@ Unknown
Unknown error type.
Definition: qgsexpression.h:117
qgsexpressionfunction.h
QgsExpressionBuilderWidget::loadSampleUsedValues
void loadSampleUsedValues()
Load used sample values into the sample value area.
Definition: qgsexpressionbuilderwidget.cpp:844
QgsExpressionNodeUnaryOperator::operand
QgsExpressionNode * operand() const
Returns the node the operator will operate upon.
Definition: qgsexpressionnodeimpl.h:58
formatterCanProvideAvailableValues
bool formatterCanProvideAvailableValues(QgsVectorLayer *layer, const QString &fieldName)
Definition: qgsexpressionbuilderwidget.cpp:55
QgsExpression::group
static QString group(const QString &group)
Returns the translated name for a function group.
Definition: qgsexpression.cpp:911
qgsexpressiontreeview.h
QgsExpressionPreviewWidget::toolTipChanged
void toolTipChanged(const QString &toolTip)
Emitted whenever the tool tip changed.
QgsExpressionBuilderWidget::evalError
bool evalError() const
Will be set to true if the current expression text reported an eval error with the context.
Definition: qgsexpressionbuilderwidget.cpp:650
QgsSettings::setValue
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Definition: qgssettings.cpp:289
qgsrelation.h
QgsExpression::ParserError::FunctionUnknown
@ FunctionUnknown
Function was unknown.
Definition: qgsexpression.h:118
QgsExpressionNodeFunction
An expression node for expression functions.
Definition: qgsexpressionnodeimpl.h:317
qgsexpressionbuilderwidget.h
QgsExpressionStoreDialog
A generic dialog for editing expression text, label and help text.
Definition: qgsexpressionstoredialog.h:31
QgsExpression::Functions
static const QList< QgsExpressionFunction * > & Functions()
Definition: qgsexpressionfunction.cpp:6008
QgsExpressionBuilderWidget::removeFromUserExpressions
Q_DECL_DEPRECATED void removeFromUserExpressions(const QString &label)
Removes the expression label from the user stored expressions.
Definition: qgsexpressionbuilderwidget.cpp:595
QgsApplication::fieldFormatterRegistry
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
Definition: qgsapplication.cpp:2278
QgsExpressionNode::NodeList::list
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
Definition: qgsexpressionnode.h:144
qgsvectorlayer.h
QgsExpressionNode
Abstract base class for all nodes that can appear in an expression.
Definition: qgsexpressionnode.h:35
QgsPythonRunner::eval
static bool eval(const QString &command, QString &result)
Eval a Python statement.
Definition: qgspythonrunner.cpp:42
QgsExpressionBuilderWidget::initWithFields
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.
Definition: qgsexpressionbuilderwidget.cpp:271
QgsExpressionBuilderWidget::saveToRecent
Q_DECL_DEPRECATED void saveToRecent(const QString &collection="generic")
Adds the current expression to the given collection.
Definition: qgsexpressionbuilderwidget.cpp:569
QgsExpressionBuilderWidget::evalErrorChanged
void evalErrorChanged()
Will be set to true if the current expression text reported an eval error with the context.
QgsExpressionBuilderWidget::storeCurrentUserExpression
void storeCurrentUserExpression()
Adds the current expressions to the stored user expressions.
Definition: qgsexpressionbuilderwidget.cpp:900
QgsFieldFormatter::CanProvideAvailableValues
@ CanProvideAvailableValues
Can provide possible values.
Definition: qgsfieldformatter.h:90
QgsExpressionBuilderWidget::updateFunctionFileList
void updateFunctionFileList(const QString &path)
Updates the list of function files found at the given path.
Definition: qgsexpressionbuilderwidget.cpp:371
QgsExpressionBuilderWidget::LoadRecent
@ LoadRecent
Load recent expressions given the collection key.
Definition: qgsexpressionbuilderwidget.h:54
qgsgeometry.h
qgsexpressionnodeimpl.h
QgsExpressionBuilderWidget::editSelectedUserExpression
void editSelectedUserExpression()
Edits the selected expression from the stored user expressions, the selected expression must be a use...
Definition: qgsexpressionbuilderwidget.cpp:910
QgsExpressionFunction
A abstract base class for defining QgsExpression functions.
Definition: qgsexpressionfunction.h:41
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:387
QgsExpressionBuilderWidget::setExpectedOutputFormat
void setExpectedOutputFormat(const QString &expected)
The set expected format string.
Definition: qgsexpressionbuilderwidget.cpp:621
QgsExpression::ParserError::FunctionNamedArgsError
@ FunctionNamedArgsError
Non named function arg used after named arg.
Definition: qgsexpression.h:121
QgsExpressionNodeBinaryOperator::opLeft
QgsExpressionNode * opLeft() const
Returns the node to the left of the operator.
Definition: qgsexpressionnodeimpl.h:152
QgsExpressionNode::parserFirstLine
int parserFirstLine
First line in the parser this node was found.
Definition: qgsexpressionnode.h:279
QgsExpressionTreeView::currentExpressionItemChanged
void currentExpressionItemChanged(QgsExpressionItem *item)
Emitter when the current expression item changed.
QgsExpressionNodeCondition::WhenThen
Represents a "WHEN... THEN..." portation of a CASE WHEN clause in an expression.
Definition: qgsexpressionnodeimpl.h:449
QgsFieldFormatterRegistry::fieldFormatter
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
Definition: qgsfieldformatterregistry.cpp:72
qgssettings.h
QgsExpression::helpText
static QString helpText(QString name)
Returns the help text for a specified function.
Definition: qgsexpression.cpp:531
QgsPythonRunner::run
static bool run(const QString &command, const QString &messageOnError=QString())
Execute a Python statement.
Definition: qgspythonrunner.cpp:28
QgsExpressionBuilderWidget::loadRecent
Q_DECL_DEPRECATED void loadRecent(const QString &collection=QStringLiteral("generic"))
Loads the recent expressions from the given collection.
Definition: qgsexpressionbuilderwidget.cpp:574
QgsExpressionBuilderWidget::saveFunctionFile
void saveFunctionFile(QString fileName)
Saves the current function editor text to the given file.
Definition: qgsexpressionbuilderwidget.cpp:348
QgsExpressionItem::getExpressionText
QString getExpressionText() const
Definition: qgsexpressiontreeview.h:69
QgsExpressionBuilderWidget::loadFunctionCode
void loadFunctionCode(const QString &code)
Loads code into the function editor.
Definition: qgsexpressionbuilderwidget.cpp:481
QgsExpressionItem
An expression item that can be used in the QgsExpressionBuilderWidget tree.
Definition: qgsexpressiontreeview.h:38
QgsExpressionBuilderWidget::setGeomCalculator
void setGeomCalculator(const QgsDistanceArea &da)
Sets geometry calculator used in distance/area calculations.
Definition: qgsexpressionbuilderwidget.cpp:601
QgsExpressionNode::parserLastColumn
int parserLastColumn
Last column in the parser this node was found.
Definition: qgsexpressionnode.h:300
QgsExpressionBuilderWidget::removeSelectedUserExpression
void removeSelectedUserExpression()
Removes the selected expression from the stored user expressions, the selected expression must be a u...
Definition: qgsexpressionbuilderwidget.cpp:939
QgsExpressionBuilderWidget::expectedOutputFormat
QString expectedOutputFormat()
The set expected format string.
Definition: qgsexpressionbuilderwidget.cpp:616
QgsExpressionBuilderWidget::setLayer
void setLayer(QgsVectorLayer *layer)
Sets layer in order to get the fields and values.
Definition: qgsexpressionbuilderwidget.cpp:278
QgsDistanceArea
A general purpose distance and area calculator, capable of performing ellipsoid based calculations.
Definition: qgsdistancearea.h:50
QgsExpressionItem::Header
@ Header
Definition: qgsexpressiontreeview.h:42
QgsExpression::ParserError::FunctionWrongArgs
@ FunctionWrongArgs
Function was called with the wrong number of args.
Definition: qgsexpression.h:119
QgsExpressionNodeInOperator::list
QgsExpressionNode::NodeList * list() const
Returns the list of nodes to search for matching values within.
Definition: qgsexpressionnodeimpl.h:291
QgsExpressionNodeBinaryOperator::opRight
QgsExpressionNode * opRight() const
Returns the node to the right of the operator.
Definition: qgsexpressionnodeimpl.h:158
QgsFieldFormatterContext::setProject
void setProject(QgsProject *project)
Sets the project used in field formatter.
Definition: qgsfieldformatter.h:52
qgslogger.h
QgsExpressionBuilderWidget::isExpressionValid
bool isExpressionValid()
Returns if the expression is valid.
Definition: qgsexpressionbuilderwidget.cpp:564
QgsExpressionBuilderWidget::setExpressionText
void setExpressionText(const QString &expression)
Sets the expression string for the widget.
Definition: qgsexpressionbuilderwidget.cpp:611
QgsFields::lookupField
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:344
QgsExpressionBuilderWidget::parserErrorChanged
void parserErrorChanged()
Will be set to true if the current expression text reported a parser error with the context.
QgsField::editorWidgetSetup
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:559
QgsFields::at
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
QgsPythonRunner::isValid
static bool isValid()
Returns true if the runner has an instance (and thus is able to run commands)
Definition: qgspythonrunner.cpp:23
QgsExpressionBuilderWidget::QgsExpressionBuilderWidget
QgsExpressionBuilderWidget(QWidget *parent=nullptr)
Create a new expression builder widget with an optional parent.
Definition: qgsexpressionbuilderwidget.cpp:73
QgsExpressionPreviewWidget::parserErrorChanged
void parserErrorChanged()
Will be set to true if the current expression text reported a parser error with the context.
QgsExpressionBuilderWidget::expressionText
QString expressionText()
Gets the expression string that has been set in the expression area.
Definition: qgsexpressionbuilderwidget.cpp:606
Q_NOWARN_DEPRECATED_PUSH
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:796
QgsExpressionBuilderWidget::showEvent
void showEvent(QShowEvent *e) override
Definition: qgsexpressionbuilderwidget.cpp:673
qgspythonrunner.h
QgsExpressionBuilderWidget::autosave
void autosave()
Auto save the current Python function code.
Definition: qgsexpressionbuilderwidget.cpp:877
QgsExpressionBuilderWidget::saveToUserExpressions
Q_DECL_DEPRECATED void saveToUserExpressions(const QString &label, const QString expression, const QString &helpText)
Stores the user expression with given label and helpText.
Definition: qgsexpressionbuilderwidget.cpp:590
QgsApplication::reportStyleSheet
static QString reportStyleSheet(QgsApplication::StyleSheetType styleSheetType=QgsApplication::StyleSheetType::Qt)
Returns a css style sheet for reports, the styleSheetType argument determines what type of stylesheet...
Definition: qgsapplication.cpp:1363
QgsFieldFormatter
A field formatter helps to handle and display values for a field.
Definition: qgsfieldformatter.h:73
QgsExpressionTreeView::expressionItemDoubleClicked
void expressionItemDoubleClicked(const QString &text)
Emitted when a expression item is double clicked.
QgsExpressionBuilderWidget::setProject
void setProject(QgsProject *project)
Sets the project currently associated with the widget.
Definition: qgsexpressionbuilderwidget.cpp:667
qgsproject.h
QgsExpressionBuilderWidget::loadFieldsAndValues
Q_DECL_DEPRECATED void loadFieldsAndValues(const QMap< QString, QStringList > &fieldValues)
Loads field names and values from the specified map.
Definition: qgsexpressionbuilderwidget.cpp:493
qgsfieldformatter.h
QgsExpressionBuilderWidget::expressionParsed
void expressionParsed(bool isValid)
Emitted when the user changes the expression in the widget.
QgsExpressionNode::parserFirstColumn
int parserFirstColumn
First column in the parser this node was found.
Definition: qgsexpressionnode.h:286
QgsExpressionItem::getHelpText
QString getHelpText() const
Gets the help text that is associated with this expression item.
Definition: qgsexpressiontreeview.h:76
QgsExpressionBuilderWidget::loadUserExpressions
Q_DECL_DEPRECATED void loadUserExpressions()
Loads the user expressions.
Definition: qgsexpressionbuilderwidget.cpp:585
QgsExpressionBuilderWidget::model
Q_DECL_DEPRECATED QStandardItemModel * model()
Returns a pointer to the dialog's function item model.
Definition: qgsexpressionbuilderwidget.cpp:655
QgsExpressionBuilderWidget::loadAllValues
void loadAllValues()
Load all unique values from the set layer into the sample area.
Definition: qgsexpressionbuilderwidget.cpp:832
QgsExpressionNode::nodeType
virtual QgsExpressionNode::NodeType nodeType() const =0
Gets the type of this node.
QgsExpression::ParserError
Details about any parser errors that were found when parsing the expression.
Definition: qgsexpression.h:114