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