QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 
355 QgsVectorLayer *QgsExpressionBuilderWidget::contextLayer( const QgsExpressionItem *item ) const
356 {
357  QgsVectorLayer *layer = nullptr;
358  if ( ! item->data( QgsExpressionItem::LAYER_ID_ROLE ).isNull() )
359  {
360  layer = qobject_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( item->data( QgsExpressionItem::LAYER_ID_ROLE ).toString() ) );
361  }
362  else
363  {
364  layer = mLayer;
365  }
366  return layer;
367 }
368 
369 
371 {
372  QDir myDir( mFunctionsPath );
373  if ( !myDir.exists() )
374  {
375  myDir.mkpath( mFunctionsPath );
376  }
377 
378  if ( !fileName.endsWith( QLatin1String( ".py" ) ) )
379  {
380  fileName.append( ".py" );
381  }
382 
383  fileName = mFunctionsPath + QDir::separator() + fileName;
384  QFile myFile( fileName );
385  if ( myFile.open( QIODevice::WriteOnly | QFile::Truncate ) )
386  {
387  QTextStream myFileStream( &myFile );
388 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
389  myFileStream << txtPython->text() << endl;
390 #else
391  myFileStream << txtPython->text() << Qt::endl;
392 #endif
393  myFile.close();
394  }
395 }
396 
398 {
399  mFunctionsPath = path;
400  QDir dir( path );
401  dir.setNameFilters( QStringList() << QStringLiteral( "*.py" ) );
402  QStringList files = dir.entryList( QDir::Files );
403  cmbFileNames->clear();
404  const auto constFiles = files;
405  for ( const QString &name : constFiles )
406  {
407  QFileInfo info( mFunctionsPath + QDir::separator() + name );
408  if ( info.baseName() == QLatin1String( "__init__" ) ) continue;
409  QListWidgetItem *item = new QListWidgetItem( QgsApplication::getThemeIcon( QStringLiteral( "console/iconTabEditorConsole.svg" ) ), info.baseName() );
410  cmbFileNames->addItem( item );
411  }
412  if ( !cmbFileNames->currentItem() )
413  {
414  cmbFileNames->setCurrentRow( 0 );
415  }
416 
417  if ( cmbFileNames->count() == 0 )
418  {
419  // Create default sample entry.
420  newFunctionFile( QStringLiteral( "default" ) );
421  txtPython->setText( QStringLiteral( "'''\n#Sample custom function file\n"
422  "#(uncomment to use and customize or Add button to create a new file) \n%1 \n '''" ).arg( txtPython->text() ) );
423  saveFunctionFile( QStringLiteral( "default" ) );
424  }
425 }
426 
427 void QgsExpressionBuilderWidget::newFunctionFile( const QString &fileName )
428 {
429  QList<QListWidgetItem *> items = cmbFileNames->findItems( fileName, Qt::MatchExactly );
430  if ( !items.isEmpty() )
431  return;
432 
433  QListWidgetItem *item = new QListWidgetItem( QgsApplication::getThemeIcon( QStringLiteral( "console/iconTabEditorConsole.svg" ) ), fileName );
434  cmbFileNames->insertItem( 0, item );
435  cmbFileNames->setCurrentRow( 0 );
436 
437  QString templatetxt;
438  QgsPythonRunner::eval( QStringLiteral( "qgis.user.default_expression_template" ), templatetxt );
439  txtPython->setText( templatetxt );
440  saveFunctionFile( fileName );
441 }
442 
443 void QgsExpressionBuilderWidget::btnNewFile_pressed()
444 {
445  bool ok;
446  QString text = QInputDialog::getText( this, tr( "New File" ),
447  tr( "New file name:" ), QLineEdit::Normal,
448  QString(), &ok );
449  if ( ok && !text.isEmpty() )
450  {
451  newFunctionFile( text );
452  }
453 }
454 
455 void QgsExpressionBuilderWidget::btnRemoveFile_pressed()
456 {
457  if ( QMessageBox::question( this, tr( "Remove File" ),
458  tr( "Are you sure you want to remove current functions file?" ),
459  QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No )
460  return;
461 
462  int currentRow = cmbFileNames->currentRow();
463  QString fileName = cmbFileNames->currentItem()->text();
464  if ( QFile::remove( mFunctionsPath + QDir::separator() + fileName.append( ".py" ) ) )
465  {
466  {
467  QListWidgetItem *itemToRemove = whileBlocking( cmbFileNames )->takeItem( currentRow );
468  delete itemToRemove;
469  }
470 
471  if ( cmbFileNames->count() > 0 )
472  {
473  cmbFileNames->setCurrentRow( currentRow > 0 ? currentRow - 1 : 0 );
474  loadCodeFromFile( mFunctionsPath + QDir::separator() + cmbFileNames->currentItem()->text() );
475  }
476  else
477  {
478  btnRemoveFile->setEnabled( false );
479  txtPython->clear();
480  }
481  }
482  else
483  {
484  QMessageBox::warning( this, tr( "Remove file" ), tr( "Failed to remove function file '%1'." ).arg( fileName ) );
485  }
486 }
487 
488 void QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem )
489 {
490  if ( lastitem )
491  {
492  QString filename = lastitem->text();
493  saveFunctionFile( filename );
494  }
495  QString path = mFunctionsPath + QDir::separator() + item->text();
496  loadCodeFromFile( path );
497 }
498 
500 {
501  if ( !path.endsWith( QLatin1String( ".py" ) ) )
502  path.append( ".py" );
503 
504  txtPython->loadScript( path );
505 }
506 
508 {
509  txtPython->setText( code );
510 }
511 
512 void QgsExpressionBuilderWidget::insertExpressionText( const QString &text )
513 {
514  // Insert the expression text or replace selected text
515  txtExpressionString->insertText( text );
516  txtExpressionString->setFocus();
517 }
518 
519 void QgsExpressionBuilderWidget::loadFieldsAndValues( const QMap<QString, QStringList> &fieldValues )
520 {
521  Q_UNUSED( fieldValues )
522  // This is not maintained and setLayer() should be used instead.
523 }
524 
525 void QgsExpressionBuilderWidget::fillFieldValues( const QString &fieldName, QgsVectorLayer *layer, int countLimit, bool forceUsedValues )
526 {
527  // TODO We should really return a error the user of the widget that
528  // the there is no layer set.
529  if ( !layer )
530  return;
531 
532  // TODO We should thread this so that we don't hold the user up if the layer is massive.
533 
534  const QgsFields fields = layer->fields();
535  int fieldIndex = fields.lookupField( fieldName );
536 
537  if ( fieldIndex < 0 )
538  return;
539 
540  const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
542 
543  QVariantList values;
544  if ( cbxValuesInUse->isVisible() && !cbxValuesInUse->isChecked() && !forceUsedValues )
545  {
546  QgsFieldFormatterContext fieldFormatterContext;
547  fieldFormatterContext.setProject( mProject );
548  values = formatter->availableValues( setup.config(), countLimit, fieldFormatterContext );
549  }
550  else
551  {
552  values = qgis::setToList( layer->uniqueValues( fieldIndex, countLimit ) );
553  }
554  std::sort( values.begin(), values.end() );
555 
556  mValuesModel->clear();
557  for ( const QVariant &value : std::as_const( values ) )
558  {
559  QString strValue;
560  bool forceRepresentedValue = false;
561  if ( value.isNull() )
562  strValue = QStringLiteral( "NULL" );
563  else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong )
564  strValue = value.toString();
565  else if ( value.type() == QVariant::StringList )
566  {
567  QString result;
568  const QStringList strList = value.toStringList();
569  for ( QString str : strList )
570  {
571  if ( !result.isEmpty() )
572  result.append( QStringLiteral( ", " ) );
573 
574  result.append( '\'' + str.replace( '\'', QLatin1String( "''" ) ) + '\'' );
575  }
576  strValue = QStringLiteral( "array(%1)" ).arg( result );
577  forceRepresentedValue = true;
578  }
579  else if ( value.type() == QVariant::List )
580  {
581  QString result;
582  const QList list = value.toList();
583  for ( const QVariant &item : list )
584  {
585  if ( !result.isEmpty() )
586  result.append( QStringLiteral( ", " ) );
587 
588  result.append( item.toString() );
589  }
590  strValue = QStringLiteral( "array(%1)" ).arg( result );
591  forceRepresentedValue = true;
592  }
593  else
594  strValue = '\'' + value.toString().replace( '\'', QLatin1String( "''" ) ) + '\'';
595 
596  QString representedValue = formatter->representValue( layer, fieldIndex, setup.config(), QVariant(), value );
597  if ( forceRepresentedValue || representedValue != value.toString() )
598  representedValue = representedValue + QStringLiteral( " [" ) + strValue + ']';
599 
600  QStandardItem *item = new QStandardItem( representedValue );
601  item->setData( strValue );
602  mValuesModel->appendRow( item );
603  }
604 }
605 
606 QString QgsExpressionBuilderWidget::getFunctionHelp( QgsExpressionFunction *function )
607 {
608  if ( !function )
609  return QString();
610 
611  QString helpContents = QgsExpression::helpText( function->name() );
612 
613  return QStringLiteral( "<head><style>" ) + helpStylesheet() + QStringLiteral( "</style></head><body>" ) + helpContents + QStringLiteral( "</body>" );
614 
615 }
616 
617 
618 
620 {
621  return mExpressionValid;
622 }
623 
624 void QgsExpressionBuilderWidget::saveToRecent( const QString &collection )
625 {
626  mExpressionTreeView->saveToRecent( expressionText(), collection );
627 }
628 
629 void QgsExpressionBuilderWidget::loadRecent( const QString &collection )
630 {
631  mExpressionTreeView->loadRecent( collection );
632 }
633 
635 {
636  return mExpressionTreeView;
637 }
638 
639 // this is potentially very slow if there are thousands of user expressions, every time entire cleanup and load
641 {
642  mExpressionTreeView->loadUserExpressions();
643 }
644 
645 void QgsExpressionBuilderWidget::saveToUserExpressions( const QString &label, const QString &expression, const QString &helpText )
646 {
647  mExpressionTreeView->saveToUserExpressions( label, expression, helpText );
648 }
649 
651 {
652  mExpressionTreeView->removeFromUserExpressions( label );
653 }
654 
655 
657 {
658  mExpressionPreviewWidget->setGeomCalculator( da );
659 }
660 
662 {
663  return txtExpressionString->text();
664 }
665 
666 void QgsExpressionBuilderWidget::setExpressionText( const QString &expression )
667 {
668  txtExpressionString->setText( expression );
669 }
670 
672 {
673  return lblExpected->text();
674 }
675 
677 {
678  lblExpected->setText( expected );
679  mExpectedOutputFrame->setVisible( !expected.isNull() );
680 }
681 
683 {
684  mExpressionContext = context;
685  expressionContextUpdated();
686 }
687 
688 void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
689 {
690  QString text = expressionText();
691 
692  btnClearEditor->setEnabled( ! txtExpressionString->text().isEmpty() );
693  btnSaveExpression->setEnabled( false );
694 
695  mExpressionPreviewWidget->setExpressionText( text );
696 }
697 
699 {
700  return mExpressionPreviewWidget->parserError();
701 }
702 
704 {
705  mExpressionPreviewWidget->setVisible( isVisible );
706 }
707 
709 {
710  return mExpressionPreviewWidget->evalError();
711 }
712 
714 {
716  return mExpressionTreeView->model();
718 }
719 
721 {
722  return mProject;
723 }
724 
726 {
727  mProject = project;
728  mExpressionTreeView->setProject( project );
729 }
730 
732 {
733  QWidget::showEvent( e );
734  txtExpressionString->setFocus();
735 }
736 
737 void QgsExpressionBuilderWidget::createErrorMarkers( const QList<QgsExpression::ParserError> &errors )
738 {
739  clearErrors();
740  for ( const QgsExpression::ParserError &error : errors )
741  {
742  int errorFirstLine = error.firstLine - 1 ;
743  int errorFirstColumn = error.firstColumn - 1;
744  int errorLastColumn = error.lastColumn - 1;
745  int errorLastLine = error.lastLine - 1;
746 
747  // If we have a unknown error we just mark the point that hit the error for now
748  // until we can handle others more.
749  if ( error.errorType == QgsExpression::ParserError::Unknown )
750  {
751  errorFirstLine = errorLastLine;
752  errorFirstColumn = errorLastColumn - 1;
753  }
754  txtExpressionString->fillIndicatorRange( errorFirstLine,
755  errorFirstColumn,
756  errorLastLine,
757  errorLastColumn, error.errorType );
758  }
759 }
760 
761 void QgsExpressionBuilderWidget::createMarkers( const QgsExpressionNode *inNode )
762 {
763  switch ( inNode->nodeType() )
764  {
765  case QgsExpressionNode::NodeType::ntFunction:
766  {
767  const QgsExpressionNodeFunction *node = static_cast<const QgsExpressionNodeFunction *>( inNode );
768  txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
769  txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->fnIndex() );
770  int start = inNode->parserFirstColumn - 1;
771  int end = inNode->parserLastColumn - 1;
772  int start_pos = txtExpressionString->positionFromLineIndex( inNode->parserFirstLine - 1, start );
773  txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
774  if ( node->args() )
775  {
776  const QList< QgsExpressionNode * > nodeList = node->args()->list();
777  for ( QgsExpressionNode *n : nodeList )
778  {
779  createMarkers( n );
780  }
781  }
782  break;
783  }
784  case QgsExpressionNode::NodeType::ntLiteral:
785  {
786  break;
787  }
788  case QgsExpressionNode::NodeType::ntUnaryOperator:
789  {
790  const QgsExpressionNodeUnaryOperator *node = static_cast<const QgsExpressionNodeUnaryOperator *>( inNode );
791  createMarkers( node->operand() );
792  break;
793  }
794  case QgsExpressionNode::NodeType::ntBetweenOperator:
795  {
796  const QgsExpressionNodeBetweenOperator *node = static_cast<const QgsExpressionNodeBetweenOperator *>( inNode );
797  createMarkers( node->lowerBound() );
798  createMarkers( node->higherBound() );
799  break;
800  }
801  case QgsExpressionNode::NodeType::ntBinaryOperator:
802  {
803  const QgsExpressionNodeBinaryOperator *node = static_cast<const QgsExpressionNodeBinaryOperator *>( inNode );
804  createMarkers( node->opLeft() );
805  createMarkers( node->opRight() );
806  break;
807  }
808  case QgsExpressionNode::NodeType::ntColumnRef:
809  {
810  break;
811  }
812  case QgsExpressionNode::NodeType::ntInOperator:
813  {
814  const QgsExpressionNodeInOperator *node = static_cast<const QgsExpressionNodeInOperator *>( inNode );
815  if ( node->list() )
816  {
817  const QList< QgsExpressionNode * > nodeList = node->list()->list();
818  for ( QgsExpressionNode *n : nodeList )
819  {
820  createMarkers( n );
821  }
822  }
823  break;
824  }
825  case QgsExpressionNode::NodeType::ntCondition:
826  {
827  const QgsExpressionNodeCondition *node = static_cast<const QgsExpressionNodeCondition *>( inNode );
828  const QList<QgsExpressionNodeCondition::WhenThen *> conditions = node->conditions();
829  for ( QgsExpressionNodeCondition::WhenThen *cond : conditions )
830  {
831  createMarkers( cond->whenExp() );
832  createMarkers( cond->thenExp() );
833  }
834  if ( node->elseExp() )
835  {
836  createMarkers( node->elseExp() );
837  }
838  break;
839  }
840  case QgsExpressionNode::NodeType::ntIndexOperator:
841  {
842  break;
843  }
844  }
845 }
846 
847 void QgsExpressionBuilderWidget::clearFunctionMarkers()
848 {
849  int lastLine = txtExpressionString->lines() - 1;
850  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
851 }
852 
853 void QgsExpressionBuilderWidget::clearErrors()
854 {
855  int lastLine = txtExpressionString->lines() - 1;
856  // Note: -1 here doesn't seem to do the clear all like the other functions. Will need to make this a bit smarter.
857  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::Unknown );
858  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionInvalidParams );
859  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionUnknown );
860  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionWrongArgs );
861  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionNamedArgsError );
862 }
863 
864 void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
865 {
866  mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
867  mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
868 }
869 
870 void QgsExpressionBuilderWidget::mValuesListView_doubleClicked( const QModelIndex &index )
871 {
872  // Insert the item text or replace selected text
873  txtExpressionString->insertText( ' ' + index.data( Qt::UserRole + 1 ).toString() + ' ' );
874  txtExpressionString->setFocus();
875 }
876 
877 void QgsExpressionBuilderWidget::operatorButtonClicked()
878 {
879  QPushButton *button = qobject_cast<QPushButton *>( sender() );
880 
881  // Insert the button text or replace selected text
882  txtExpressionString->insertText( ' ' + button->text() + ' ' );
883  txtExpressionString->setFocus();
884 }
885 
887 {
888  QgsExpressionItem *item = mExpressionTreeView->currentItem();
889  if ( ! item )
890  {
891  return;
892  }
893 
894  QgsVectorLayer *layer { contextLayer( item ) };
895  // TODO We should really return a error the user of the widget that
896  // the there is no layer set.
897  if ( !layer )
898  {
899  return;
900  }
901 
902  mValueGroupBox->show();
903  fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), layer, 10 );
904 }
905 
907 {
908  QgsExpressionItem *item = mExpressionTreeView->currentItem();
909  if ( ! item )
910  {
911  return;
912  }
913 
914  QgsVectorLayer *layer { contextLayer( item ) };
915  // TODO We should really return a error the user of the widget that
916  // the there is no layer set.
917  if ( !layer )
918  {
919  return;
920  }
921 
922  mValueGroupBox->show();
923  fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), layer, -1 );
924 }
925 
927 {
928  QgsExpressionItem *item = mExpressionTreeView->currentItem();
929  if ( ! item )
930  {
931  return;
932  }
933 
934  QgsVectorLayer *layer { contextLayer( item ) };
935  // TODO We should really return a error the user of the widget that
936  // the there is no layer set.
937  if ( !layer )
938  {
939  return;
940  }
941 
942  mValueGroupBox->show();
943  fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), layer, 10, true );
944 }
945 
947 {
948  QgsExpressionItem *item = mExpressionTreeView->currentItem();
949  if ( ! item )
950  {
951  return;
952  }
953 
954  QgsVectorLayer *layer { contextLayer( item ) };
955  // TODO We should really return a error the user of the widget that
956  // the there is no layer set.
957  if ( !layer )
958  {
959  return;
960  }
961 
962  mValueGroupBox->show();
963  fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), layer, -1, true );
964 }
965 
966 void QgsExpressionBuilderWidget::txtPython_textChanged()
967 {
968  lblAutoSave->setText( tr( "Saving…" ) );
969  if ( mAutoSave )
970  {
971  autosave();
972  }
973 }
974 
976 {
977  // Don't auto save if not on function editor that would be silly.
978  if ( tabWidget->currentIndex() != 1 )
979  return;
980 
981  QListWidgetItem *item = cmbFileNames->currentItem();
982  if ( !item )
983  return;
984 
985  QString file = item->text();
986  saveFunctionFile( file );
987  lblAutoSave->setText( QStringLiteral( "Saved" ) );
988  QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect();
989  lblAutoSave->setGraphicsEffect( effect );
990  QPropertyAnimation *anim = new QPropertyAnimation( effect, "opacity" );
991  anim->setDuration( 2000 );
992  anim->setStartValue( 1.0 );
993  anim->setEndValue( 0.0 );
994  anim->setEasingCurve( QEasingCurve::OutQuad );
995  anim->start( QAbstractAnimation::DeleteWhenStopped );
996 }
997 
999 {
1000  const QString expression { this->expressionText() };
1001  QgsExpressionStoreDialog dlg { expression, expression, QString( ), mExpressionTreeView->userExpressionLabels() };
1002  if ( dlg.exec() == QDialog::DialogCode::Accepted )
1003  {
1004  mExpressionTreeView->saveToUserExpressions( dlg.label().simplified(), dlg.expression(), dlg.helpText() );
1005  }
1006 }
1007 
1009 {
1010  // Get the item
1011  QgsExpressionItem *item = mExpressionTreeView->currentItem();
1012  if ( !item )
1013  return;
1014 
1015  // Don't handle remove if we are on a header node or the parent
1016  // is not the user group
1017  if ( item->getItemType() == QgsExpressionItem::Header ||
1018  ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) )
1019  return;
1020 
1021  QgsSettings settings;
1022  QString helpText = settings.value( QStringLiteral( "user/%1/helpText" ).arg( item->text() ), "", QgsSettings::Section::Expressions ).toString();
1023  QgsExpressionStoreDialog dlg { item->text(), item->getExpressionText(), helpText, mExpressionTreeView->userExpressionLabels() };
1024 
1025  if ( dlg.exec() == QDialog::DialogCode::Accepted )
1026  {
1027  // label has changed removed the old one before adding the new one
1028  if ( dlg.isLabelModified() )
1029  {
1030  mExpressionTreeView->removeFromUserExpressions( item->text() );
1031  }
1032 
1033  mExpressionTreeView->saveToUserExpressions( dlg.label().simplified(), dlg.expression(), dlg.helpText() );
1034  }
1035 }
1036 
1038 {
1039  // Get the item
1040  QgsExpressionItem *item = mExpressionTreeView->currentItem();
1041 
1042  if ( !item )
1043  return;
1044 
1045  // Don't handle remove if we are on a header node or the parent
1046  // is not the user group
1047  if ( item->getItemType() == QgsExpressionItem::Header ||
1048  ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) )
1049  return;
1050 
1051  if ( QMessageBox::Yes == QMessageBox::question( this, tr( "Remove Stored Expression" ),
1052  tr( "Do you really want to remove stored expressions '%1'?" ).arg( item->text() ),
1053  QMessageBox::Yes | QMessageBox::No ) )
1054  {
1055  mExpressionTreeView->removeFromUserExpressions( item->text() );
1056  }
1057 
1058 }
1059 
1060 void QgsExpressionBuilderWidget::exportUserExpressions_pressed()
1061 {
1062  QgsSettings settings;
1063  QString lastSaveDir = settings.value( QStringLiteral( "lastExportExpressionsDir" ), QDir::homePath(), QgsSettings::App ).toString();
1064  QString saveFileName = QFileDialog::getSaveFileName(
1065  this,
1066  tr( "Export User Expressions" ),
1067  lastSaveDir,
1068  tr( "User expressions" ) + " (*.json)" );
1069 
1070  if ( saveFileName.isEmpty() )
1071  return;
1072 
1073  QFileInfo saveFileInfo( saveFileName );
1074 
1075  if ( saveFileInfo.suffix().isEmpty() )
1076  {
1077  QString saveFileNameWithSuffix = saveFileName.append( ".json" );
1078  saveFileInfo = QFileInfo( saveFileNameWithSuffix );
1079  }
1080 
1081  settings.setValue( QStringLiteral( "lastExportExpressionsDir" ), saveFileInfo.absolutePath(), QgsSettings::App );
1082 
1083  QJsonDocument exportJson = mExpressionTreeView->exportUserExpressions();
1084  QFile jsonFile( saveFileName );
1085 
1086  if ( !jsonFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
1087  QMessageBox::warning( this, tr( "Export user expressions" ), tr( "Error while creating the expressions file." ) );
1088 
1089  if ( ! jsonFile.write( exportJson.toJson() ) )
1090  QMessageBox::warning( this, tr( "Export user expressions" ), tr( "Error while creating the expressions file." ) );
1091  else
1092  jsonFile.close();
1093 }
1094 
1095 void QgsExpressionBuilderWidget::importUserExpressions_pressed()
1096 {
1097  QgsSettings settings;
1098  QString lastImportDir = settings.value( QStringLiteral( "lastImportExpressionsDir" ), QDir::homePath(), QgsSettings::App ).toString();
1099  QString loadFileName = QFileDialog::getOpenFileName(
1100  this,
1101  tr( "Import User Expressions" ),
1102  lastImportDir,
1103  tr( "User expressions" ) + " (*.json)" );
1104 
1105  if ( loadFileName.isEmpty() )
1106  return;
1107 
1108  QFileInfo loadFileInfo( loadFileName );
1109 
1110  settings.setValue( QStringLiteral( "lastImportExpressionsDir" ), loadFileInfo.absolutePath(), QgsSettings::App );
1111 
1112  QFile jsonFile( loadFileName );
1113 
1114  if ( !jsonFile.open( QFile::ReadOnly ) )
1115  QMessageBox::warning( this, tr( "Import User Expressions" ), tr( "Error while reading the expressions file." ) );
1116 
1117  QTextStream jsonStream( &jsonFile );
1118  QString jsonString = jsonFile.readAll();
1119  jsonFile.close();
1120 
1121  QJsonDocument importJson = QJsonDocument::fromJson( jsonString.toUtf8() );
1122 
1123  if ( importJson.isNull() )
1124  {
1125  QMessageBox::warning( this, tr( "Import User Expressions" ), tr( "Error while reading the expressions file." ) );
1126  return;
1127  }
1128 
1129  mExpressionTreeView->loadExpressionsFromJson( importJson );
1130 }
1131 
1132 
1133 const QList<QgsExpressionItem *> QgsExpressionBuilderWidget::findExpressions( const QString &label )
1134 {
1135  return mExpressionTreeView->findExpressions( label );
1136 }
1137 
1138 void QgsExpressionBuilderWidget::indicatorClicked( int line, int index, Qt::KeyboardModifiers state )
1139 {
1140  if ( state & Qt::ControlModifier )
1141  {
1142  int position = txtExpressionString->positionFromLineIndex( line, index );
1143  long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID, static_cast<long int>( position ) );
1144  QgsExpressionFunction *func = QgsExpression::Functions()[fncIndex];
1145  QString help = getFunctionHelp( func );
1146  txtHelpText->setText( help );
1147  }
1148 }
1149 
1150 void QgsExpressionBuilderWidget::onExpressionParsed( bool state )
1151 {
1152  clearErrors();
1153 
1154  mExpressionValid = state;
1155  if ( state )
1156  {
1157  createMarkers( mExpressionPreviewWidget->rootNode() );
1158  }
1159  else
1160  {
1161  createErrorMarkers( mExpressionPreviewWidget->parserErrors() );
1162  }
1163 }
1164 
1165 QString QgsExpressionBuilderWidget::helpStylesheet() const
1166 {
1167  //start with default QGIS report style
1168  QString style = QgsApplication::reportStyleSheet();
1169 
1170  //add some tweaks
1171  style += " .functionname {color: #0a6099; font-weight: bold;} "
1172  " .argument {font-family: monospace; color: #bf0c0c; font-style: italic; } "
1173  " td.argument { padding-right: 10px; }";
1174 
1175  return style;
1176 }
1177 
1178 QString QgsExpressionBuilderWidget::loadFunctionHelp( QgsExpressionItem *expressionItem )
1179 {
1180  if ( !expressionItem )
1181  return QString();
1182 
1183  QString helpContents = expressionItem->getHelpText();
1184 
1185  // Return the function help that is set for the function if there is one.
1186  if ( helpContents.isEmpty() )
1187  {
1188  QString name = expressionItem->data( Qt::UserRole ).toString();
1189 
1190  if ( expressionItem->getItemType() == QgsExpressionItem::Field )
1191  helpContents = QgsExpression::helpText( QStringLiteral( "Field" ) );
1192  else
1193  helpContents = QgsExpression::helpText( name );
1194  }
1195 
1196  return "<head><style>" + helpStylesheet() + "</style></head><body>" + helpContents + "</body>";
1197 }
1198 
1199 
1200 // *************
1201 // Menu provider
1202 
1203 QMenu *QgsExpressionBuilderWidget::ExpressionTreeMenuProvider::createContextMenu( QgsExpressionItem *item )
1204 {
1205  QMenu *menu = nullptr;
1206  QgsVectorLayer *layer = mExpressionBuilderWidget->layer();
1207  if ( item->getItemType() == QgsExpressionItem::Field && layer )
1208  {
1209  menu = new QMenu( mExpressionBuilderWidget );
1210  menu->addAction( tr( "Load First 10 Unique Values" ), mExpressionBuilderWidget, &QgsExpressionBuilderWidget::loadSampleValues );
1211  menu->addAction( tr( "Load All Unique Values" ), mExpressionBuilderWidget, &QgsExpressionBuilderWidget::loadAllValues );
1212 
1213  if ( formatterCanProvideAvailableValues( layer, item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString() ) )
1214  {
1215  menu->addAction( tr( "Load First 10 Unique Used Values" ), mExpressionBuilderWidget, &QgsExpressionBuilderWidget::loadSampleUsedValues );
1216  menu->addAction( tr( "Load All Unique Used Values" ), mExpressionBuilderWidget, &QgsExpressionBuilderWidget::loadAllUsedValues );
1217  }
1218  }
1219  return menu;
1220 }
QgsExpressionBuilderWidget::project
QgsProject * project()
Returns the project currently associated with the widget.
Definition: qgsexpressionbuilderwidget.cpp:720
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:406
formatter
Definition: qgsbasicnumericformat.cpp:24
qgsexpressioncontextutils.h
QgsExpression::ParserError::FunctionInvalidParams
@ FunctionInvalidParams
Function was called with invalid args.
Definition: qgsexpression.h:118
QgsExpressionItem::ITEM_NAME_ROLE
static const int ITEM_NAME_ROLE
Item name role.
Definition: qgsexpressiontreeview.h:100
QgsExpressionNodeCondition::elseExp
QgsExpressionNode * elseExp() const
The ELSE expression used for the condition.
Definition: qgsexpressionnodeimpl.h:642
QgsExpressionNodeBinaryOperator
A binary expression operator, which operates on two values.
Definition: qgsexpressionnodeimpl.h:99
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:161
QgsEditorWidgetSetup
Holder for the widget type and its configuration for a field.
Definition: qgseditorwidgetsetup.h:28
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:1133
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:886
QgsExpressionNodeFunction::fnIndex
int fnIndex() const
Returns the index of the node's function.
Definition: qgsexpressionnodeimpl.h:428
qgsfeatureiterator.h
QgsFields
Container of fields for a vector layer.
Definition: qgsfields.h:44
QgsEditorWidgetSetup::config
QVariantMap config() const
Definition: qgseditorwidgetsetup.h:64
QgsExpressionContextUtils::layerScope
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
Definition: qgsexpressioncontextutils.cpp:334
qgsfeature.h
qgsfieldformatterregistry.h
QgsExpressionNodeUnaryOperator
A unary node is either negative as in boolean (not) or as in numbers (minus).
Definition: qgsexpressionnodeimpl.h:27
QgsFieldFormatterContext
A context for field formatter containing information like the project.
Definition: qgsfieldformatter.h:33
QgsExpressionNodeBetweenOperator::lowerBound
QgsExpressionNode * lowerBound() const
Returns the lower bound expression node of the range.
Definition: qgsexpressionnodeimpl.cpp:1941
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:480
QgsExpressionNodeCondition
An expression node for CASE WHEN clauses.
Definition: qgsexpressionnodeimpl.h:560
QgsExpressionBuilderWidget::loadCodeFromFile
void loadCodeFromFile(QString path)
Loads code from the given file into the function editor.
Definition: qgsexpressionbuilderwidget.cpp:499
QgsExpressionItem::Field
@ Field
Definition: qgsexpressiontreeview.h:44
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
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:698
QgsExpressionTreeView::setSearchText
void setSearchText(const QString &text)
Sets the text to filter the expression tree.
Definition: qgsexpressiontreeview.cpp:196
QgsExpressionBuilderWidget::newFunctionFile
void newFunctionFile(const QString &fileName="scratch")
Creates a new file in the function editor.
Definition: qgsexpressionbuilderwidget.cpp:427
QgsExpressionBuilderWidget::layer
QgsVectorLayer * layer() const
Returns the current layer or a nullptr.
Definition: qgsexpressionbuilderwidget.cpp:301
QgsExpressionBuilderWidget::expressionTree
QgsExpressionTreeView * expressionTree() const
Returns the expression tree.
Definition: qgsexpressionbuilderwidget.cpp:634
QgsProject
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:103
QgsExpressionNodeInOperator
An expression node for value IN or NOT IN clauses.
Definition: qgsexpressionnodeimpl.h:343
QgsExpressionTreeView
QgsExpressionTreeView is a tree view to list all expressions functions, variables and fields that can...
Definition: qgsexpressiontreeview.h:150
qgsapplication.h
QgsExpressionNodeBetweenOperator::higherBound
QgsExpressionNode * higherBound() const
Returns the higher bound expression node of the range.
Definition: qgsexpressionnodeimpl.cpp:1946
QgsVectorLayer::fields
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Definition: qgsvectorlayer.cpp:3436
Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:2820
QgsEditorWidgetSetup::type
QString type() const
Definition: qgseditorwidgetsetup.h:59
QgsExpressionBuilderWidget::setExpressionContext
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context for the widget.
Definition: qgsexpressionbuilderwidget.cpp:682
QgsExpressionBuilderWidget::init
void init(const QgsExpressionContext &context=QgsExpressionContext(), const QString &recentCollection=QStringLiteral("generic"), QgsExpressionBuilderWidget::Flags flags=LoadAll)
Initialize without any layer.
Definition: qgsexpressionbuilderwidget.cpp:254
whileBlocking
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:2191
QgsExpressionNodeFunction::args
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
Definition: qgsexpressionnodeimpl.h:433
QgsExpressionItem::getItemType
QgsExpressionItem::ItemType getItemType() const
Gets the type of expression item, e.g., header, field, ExpressionNode.
Definition: qgsexpressiontreeview.h:91
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::setExpressionPreviewVisible
void setExpressionPreviewVisible(bool isVisible)
Sets whether the expression preview is visible.
Definition: qgsexpressionbuilderwidget.cpp:703
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:636
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:4161
QgsExpressionBuilderWidget::loadAllUsedValues
void loadAllUsedValues()
Load all unique values from the set layer into the sample area.
Definition: qgsexpressionbuilderwidget.cpp:946
QgsExpression::ParserError::Unknown
@ Unknown
Unknown error type.
Definition: qgsexpression.h:115
qgsexpressionfunction.h
QgsExpressionNodeBetweenOperator
SQL-like BETWEEN and NOT BETWEEN predicates.
Definition: qgsexpressionnodeimpl.h:281
QgsExpressionBuilderWidget::loadSampleUsedValues
void loadSampleUsedValues()
Load used sample values into the sample value area.
Definition: qgsexpressionbuilderwidget.cpp:926
QgsExpressionNodeUnaryOperator::operand
QgsExpressionNode * operand() const
Returns the node the operator will operate upon.
Definition: qgsexpressionnodeimpl.h:79
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:945
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:708
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:279
qgsrelation.h
QgsExpression::ParserError::FunctionUnknown
@ FunctionUnknown
Function was unknown.
Definition: qgsexpression.h:116
QgsExpressionNodeFunction
An expression node for expression functions.
Definition: qgsexpressionnodeimpl.h:395
qgsexpressionbuilderwidget.h
QgsExpressionStoreDialog
A generic dialog for editing expression text, label and help text.
Definition: qgsexpressionstoredialog.h:30
QgsExpression::Functions
static const QList< QgsExpressionFunction * > & Functions()
Definition: qgsexpressionfunction.cpp:7316
QgsExpressionBuilderWidget::removeFromUserExpressions
Q_DECL_DEPRECATED void removeFromUserExpressions(const QString &label)
Removes the expression label from the user stored expressions.
Definition: qgsexpressionbuilderwidget.cpp:650
QgsApplication::fieldFormatterRegistry
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
Definition: qgsapplication.cpp:2480
QgsExpressionNode::NodeList::list
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
Definition: qgsexpressionnode.h:148
qgsvectorlayer.h
QgsExpressionNode
Abstract base class for all nodes that can appear in an expression.
Definition: qgsexpressionnode.h:34
QgsPythonRunner::eval
static bool eval(const QString &command, QString &result)
Eval a Python statement.
Definition: qgspythonrunner.cpp:42
QgsExpressionBuilderWidget::saveToRecent
Q_DECL_DEPRECATED void saveToRecent(const QString &collection="generic")
Adds the current expression to the given collection.
Definition: qgsexpressionbuilderwidget.cpp:624
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:998
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:397
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:1008
QgsExpressionFunction
A abstract base class for defining QgsExpression functions.
Definition: qgsexpressionfunction.h:40
str
#define str(x)
Definition: qgis.cpp:37
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
QgsExpressionBuilderWidget::setExpectedOutputFormat
void setExpectedOutputFormat(const QString &expected)
The set expected format string.
Definition: qgsexpressionbuilderwidget.cpp:676
QgsExpression::ParserError::FunctionNamedArgsError
@ FunctionNamedArgsError
Non named function arg used after named arg.
Definition: qgsexpression.h:119
QgsExpressionNodeBinaryOperator::opLeft
QgsExpressionNode * opLeft() const
Returns the node to the left of the operator.
Definition: qgsexpressionnodeimpl.h:168
QgsExpressionNode::parserFirstLine
int parserFirstLine
First line in the parser this node was found.
Definition: qgsexpressionnode.h:297
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:568
QgsFieldFormatterRegistry::fieldFormatter
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
Definition: qgsfieldformatterregistry.cpp:76
qgssettings.h
QgsExpression::helpText
static QString helpText(QString name)
Returns the help text for a specified function.
Definition: qgsexpression.cpp:546
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:629
QgsExpressionBuilderWidget::saveFunctionFile
void saveFunctionFile(QString fileName)
Saves the current function editor text to the given file.
Definition: qgsexpressionbuilderwidget.cpp:370
QgsExpressionItem::getExpressionText
QString getExpressionText() const
Definition: qgsexpressiontreeview.h:70
QgsExpressionBuilderWidget::loadFunctionCode
void loadFunctionCode(const QString &code)
Loads code into the function editor.
Definition: qgsexpressionbuilderwidget.cpp:507
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:656
QgsExpressionNode::parserLastColumn
int parserLastColumn
Last column in the parser this node was found.
Definition: qgsexpressionnode.h:318
QgsExpressionBuilderWidget::removeSelectedUserExpression
void removeSelectedUserExpression()
Removes the selected expression from the stored user expressions, the selected expression must be a u...
Definition: qgsexpressionbuilderwidget.cpp:1037
QgsExpressionBuilderWidget::expectedOutputFormat
QString expectedOutputFormat()
The set expected format string.
Definition: qgsexpressionbuilderwidget.cpp:671
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:52
QgsExpressionItem::Header
@ Header
Definition: qgsexpressiontreeview.h:43
QgsExpression::ParserError::FunctionWrongArgs
@ FunctionWrongArgs
Function was called with the wrong number of args.
Definition: qgsexpression.h:117
QgsExpressionNodeInOperator::list
QgsExpressionNode::NodeList * list() const
Returns the list of nodes to search for matching values within.
Definition: qgsexpressionnodeimpl.h:370
QgsExpressionNodeBinaryOperator::opRight
QgsExpressionNode * opRight() const
Returns the node to the right of the operator.
Definition: qgsexpressionnodeimpl.h:174
QgsExpressionBuilderWidget::initWithFields
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.
Definition: qgsexpressionbuilderwidget.cpp:271
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Definition: qgsapplication.cpp:693
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:619
QgsExpressionBuilderWidget::setExpressionText
void setExpressionText(const QString &expression)
Sets the expression string for the widget.
Definition: qgsexpressionbuilderwidget.cpp:666
QgsFields::lookupField
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:349
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:602
QgsFields::at
QgsField at(int i) const
Returns the 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::initWithLayer
void initWithLayer(QgsVectorLayer *layer, const QgsExpressionContext &context=QgsExpressionContext(), const QString &recentCollection=QStringLiteral("generic"), QgsExpressionBuilderWidget::Flags flags=LoadAll)
Initialize with a layer.
Definition: qgsexpressionbuilderwidget.cpp:265
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:661
Q_NOWARN_DEPRECATED_PUSH
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:2819
QgsExpressionBuilderWidget::showEvent
void showEvent(QShowEvent *e) override
Definition: qgsexpressionbuilderwidget.cpp:731
qgspythonrunner.h
QgsExpressionBuilderWidget::autosave
void autosave()
Auto save the current Python function code.
Definition: qgsexpressionbuilderwidget.cpp:975
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:1517
QgsFieldFormatter
A field formatter helps to handle and display values for a field.
Definition: qgsfieldformatter.h:72
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:725
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:519
QgsExpressionItem::LAYER_ID_ROLE
static const int LAYER_ID_ROLE
Layer ID role.
Definition: qgsexpressiontreeview.h:102
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:304
QgsExpressionItem::getHelpText
QString getHelpText() const
Gets the help text that is associated with this expression item.
Definition: qgsexpressiontreeview.h:77
QgsExpressionBuilderWidget::loadUserExpressions
Q_DECL_DEPRECATED void loadUserExpressions()
Loads the user expressions.
Definition: qgsexpressionbuilderwidget.cpp:640
QgsExpressionBuilderWidget::model
Q_DECL_DEPRECATED QStandardItemModel * model()
Returns a pointer to the dialog's function item model.
Definition: qgsexpressionbuilderwidget.cpp:713
QgsExpressionBuilderWidget::loadAllValues
void loadAllValues()
Load all unique values from the set layer into the sample area.
Definition: qgsexpressionbuilderwidget.cpp:906
QgsExpressionNode::nodeType
virtual QgsExpressionNode::NodeType nodeType() const =0
Gets the type of this node.
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:645
QgsExpression::ParserError
Details about any parser errors that were found when parsing the expression.
Definition: qgsexpression.h:111