QGIS API Documentation  3.2.0-Bonn (bc43194)
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 
17 #include "qgslogger.h"
18 #include "qgsexpression.h"
19 #include "qgsexpressionfunction.h"
20 #include "qgsexpressionnodeimpl.h"
21 #include "qgsmessageviewer.h"
22 #include "qgsapplication.h"
23 #include "qgspythonrunner.h"
24 #include "qgsgeometry.h"
25 #include "qgsfeature.h"
26 #include "qgsfeatureiterator.h"
27 #include "qgsvectorlayer.h"
28 #include "qgssettings.h"
29 #include "qgsproject.h"
30 #include "qgsrelationmanager.h"
31 #include "qgsrelation.h"
32 
33 #include <QMenu>
34 #include <QFile>
35 #include <QTextStream>
36 #include <QDir>
37 #include <QInputDialog>
38 #include <QComboBox>
39 #include <QGraphicsOpacityEffect>
40 #include <QPropertyAnimation>
41 
42 
44  : QWidget( parent )
45  , mProject( QgsProject::instance() )
46 {
47  setupUi( this );
48  connect( btnRun, &QToolButton::pressed, this, &QgsExpressionBuilderWidget::btnRun_pressed );
49  connect( btnNewFile, &QToolButton::pressed, this, &QgsExpressionBuilderWidget::btnNewFile_pressed );
50  connect( cmbFileNames, &QListWidget::currentItemChanged, this, &QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged );
51  connect( expressionTree, &QTreeView::doubleClicked, this, &QgsExpressionBuilderWidget::expressionTree_doubleClicked );
52  connect( txtExpressionString, &QgsCodeEditorSQL::textChanged, this, &QgsExpressionBuilderWidget::txtExpressionString_textChanged );
53  connect( txtPython, &QgsCodeEditorPython::textChanged, this, &QgsExpressionBuilderWidget::txtPython_textChanged );
54  connect( txtSearchEditValues, &QgsFilterLineEdit::textChanged, this, &QgsExpressionBuilderWidget::txtSearchEditValues_textChanged );
55  connect( txtSearchEdit, &QgsFilterLineEdit::textChanged, this, &QgsExpressionBuilderWidget::txtSearchEdit_textChanged );
56  connect( lblPreview, &QLabel::linkActivated, this, &QgsExpressionBuilderWidget::lblPreview_linkActivated );
57  connect( mValuesListView, &QListView::doubleClicked, this, &QgsExpressionBuilderWidget::mValuesListView_doubleClicked );
58 
59  mValueGroupBox->hide();
60  mLoadGroupBox->hide();
61 // highlighter = new QgsExpressionHighlighter( txtExpressionString->document() );
62 
63  mModel = new QStandardItemModel();
64  mProxyModel = new QgsExpressionItemSearchProxy();
65  mProxyModel->setDynamicSortFilter( true );
66  mProxyModel->setSourceModel( mModel );
67  expressionTree->setModel( mProxyModel );
68  expressionTree->setSortingEnabled( true );
69  expressionTree->sortByColumn( 0, Qt::AscendingOrder );
70 
71  expressionTree->setContextMenuPolicy( Qt::CustomContextMenu );
72  connect( this, &QgsExpressionBuilderWidget::expressionParsed, this, &QgsExpressionBuilderWidget::setExpressionState );
73  connect( expressionTree, &QWidget::customContextMenuRequested, this, &QgsExpressionBuilderWidget::showContextMenu );
74  connect( expressionTree->selectionModel(), &QItemSelectionModel::currentChanged,
75  this, &QgsExpressionBuilderWidget::currentChanged );
76 
77  connect( btnLoadAll, &QAbstractButton::pressed, this, &QgsExpressionBuilderWidget::loadAllValues );
78  connect( btnLoadSample, &QAbstractButton::pressed, this, &QgsExpressionBuilderWidget::loadSampleValues );
79 
80  Q_FOREACH ( QPushButton *button, mOperatorsGroupBox->findChildren<QPushButton *>() )
81  {
82  connect( button, &QAbstractButton::pressed, this, &QgsExpressionBuilderWidget::operatorButtonClicked );
83  }
84 
85  txtSearchEdit->setShowSearchIcon( true );
86  txtSearchEdit->setPlaceholderText( tr( "Search" ) );
87 
88  mValuesModel = new QStringListModel();
89  mProxyValues = new QSortFilterProxyModel();
90  mProxyValues->setSourceModel( mValuesModel );
91  mValuesListView->setModel( mProxyValues );
92  txtSearchEditValues->setShowSearchIcon( true );
93  txtSearchEditValues->setPlaceholderText( tr( "Search" ) );
94 
95  QgsSettings settings;
96  splitter->restoreState( settings.value( QStringLiteral( "Windows/QgsExpressionBuilderWidget/splitter" ) ).toByteArray() );
97  editorSplit->restoreState( settings.value( QStringLiteral( "Windows/QgsExpressionBuilderWidget/editorsplitter" ) ).toByteArray() );
98  functionsplit->restoreState( settings.value( QStringLiteral( "Windows/QgsExpressionBuilderWidget/functionsplitter" ) ).toByteArray() );
99 
100  txtExpressionString->setFoldingVisible( false );
101 
102  updateFunctionTree();
103 
104  if ( QgsPythonRunner::isValid() )
105  {
106  QgsPythonRunner::eval( QStringLiteral( "qgis.user.expressionspath" ), mFunctionsPath );
107  updateFunctionFileList( mFunctionsPath );
108  }
109  else
110  {
111  tab_2->hide();
112  }
113 
114  // select the first item in the function list
115  // in order to avoid a blank help widget
116  QModelIndex firstItem = mProxyModel->index( 0, 0, QModelIndex() );
117  expressionTree->setCurrentIndex( firstItem );
118 
119  txtExpressionString->setWrapMode( QsciScintilla::WrapWord );
120  lblAutoSave->clear();
121 
122 
123  // Note: If you add a indicator here you should add it to clearErrors method if you need to clear it on text parse.
124  txtExpressionString->indicatorDefine( QgsCodeEditor::SquiggleIndicator, QgsExpression::ParserError::FunctionUnknown );
125  txtExpressionString->indicatorDefine( QgsCodeEditor::SquiggleIndicator, QgsExpression::ParserError::FunctionWrongArgs );
126  txtExpressionString->indicatorDefine( QgsCodeEditor::SquiggleIndicator, QgsExpression::ParserError::FunctionInvalidParams );
127  txtExpressionString->indicatorDefine( QgsCodeEditor::SquiggleIndicator, QgsExpression::ParserError::FunctionNamedArgsError );
128 #if defined(QSCINTILLA_VERSION) && QSCINTILLA_VERSION >= 0x20a00
129  txtExpressionString->indicatorDefine( QgsCodeEditor::TriangleIndicator, QgsExpression::ParserError::Unknown );
130 #else
131  txtExpressionString->indicatorDefine( QgsCodeEditor::SquiggleIndicator, QgsExpression::ParserError::Unknown );
132 #endif
133 
134  // Set all the error markers as red. -1 is all.
135  txtExpressionString->setIndicatorForegroundColor( QColor( Qt::red ), -1 );
136  txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::red ), -1 );
137  txtExpressionString->setIndicatorOutlineColor( QColor( Qt::red ), -1 );
138 
139  // Hidden function markers.
140  txtExpressionString->indicatorDefine( QgsCodeEditor::HiddenIndicator, FUNCTION_MARKER_ID );
141  txtExpressionString->setIndicatorForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
142  txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
143  txtExpressionString->setIndicatorHoverStyle( QgsCodeEditor::DotsIndicator, FUNCTION_MARKER_ID );
144 
145  connect( txtExpressionString, &QgsCodeEditorSQL::indicatorClicked, this, &QgsExpressionBuilderWidget::indicatorClicked );
146 
147  setExpectedOutputFormat( QString() );
148 }
149 
150 
152 {
153  QgsSettings settings;
154  settings.setValue( QStringLiteral( "Windows/QgsExpressionBuilderWidget/splitter" ), splitter->saveState() );
155  settings.setValue( QStringLiteral( "Windows/QgsExpressionBuilderWidget/editorsplitter" ), editorSplit->saveState() );
156  settings.setValue( QStringLiteral( "Windows/QgsExpressionBuilderWidget/functionsplitter" ), functionsplit->saveState() );
157 
158  delete mModel;
159  delete mProxyModel;
160  delete mValuesModel;
161  delete mProxyValues;
162 }
163 
165 {
166  mLayer = layer;
167 
168  //TODO - remove existing layer scope from context
169 
170  if ( mLayer )
171  mExpressionContext << QgsExpressionContextUtils::layerScope( mLayer );
172 }
173 
174 void QgsExpressionBuilderWidget::currentChanged( const QModelIndex &index, const QModelIndex & )
175 {
176  txtSearchEditValues->clear();
177 
178  // Get the item
179  QModelIndex idx = mProxyModel->mapToSource( index );
180  QgsExpressionItem *item = dynamic_cast<QgsExpressionItem *>( mModel->itemFromIndex( idx ) );
181  if ( !item )
182  return;
183 
184  if ( item->getItemType() == QgsExpressionItem::Field && mFieldValues.contains( item->text() ) )
185  {
186  const QStringList &values = mFieldValues[item->text()];
187  mValuesModel->setStringList( values );
188  }
189 
190  mLoadGroupBox->setVisible( item->getItemType() == QgsExpressionItem::Field && mLayer );
191  mValueGroupBox->setVisible( item->getItemType() == QgsExpressionItem::Field && mLayer );
192 
193  // Show the help for the current item.
194  QString help = loadFunctionHelp( item );
195  txtHelpText->setText( help );
196 }
197 
198 void QgsExpressionBuilderWidget::btnRun_pressed()
199 {
200  if ( !cmbFileNames->currentItem() )
201  return;
202 
203  QString file = cmbFileNames->currentItem()->text();
204  saveFunctionFile( file );
205  runPythonCode( txtPython->text() );
206 }
207 
208 void QgsExpressionBuilderWidget::runPythonCode( const QString &code )
209 {
210  if ( QgsPythonRunner::isValid() )
211  {
212  QString pythontext = code;
213  QgsPythonRunner::run( pythontext );
214  }
215  updateFunctionTree();
216  loadFieldNames();
217  loadRecent( mRecentKey );
218 }
219 
221 {
222  QDir myDir( mFunctionsPath );
223  if ( !myDir.exists() )
224  {
225  myDir.mkpath( mFunctionsPath );
226  }
227 
228  if ( !fileName.endsWith( QLatin1String( ".py" ) ) )
229  {
230  fileName.append( ".py" );
231  }
232 
233  fileName = mFunctionsPath + QDir::separator() + fileName;
234  QFile myFile( fileName );
235  if ( myFile.open( QIODevice::WriteOnly | QFile::Truncate ) )
236  {
237  QTextStream myFileStream( &myFile );
238  myFileStream << txtPython->text() << endl;
239  myFile.close();
240  }
241 }
242 
244 {
245  mFunctionsPath = path;
246  QDir dir( path );
247  dir.setNameFilters( QStringList() << QStringLiteral( "*.py" ) );
248  QStringList files = dir.entryList( QDir::Files );
249  cmbFileNames->clear();
250  Q_FOREACH ( const QString &name, files )
251  {
252  QFileInfo info( mFunctionsPath + QDir::separator() + name );
253  if ( info.baseName() == QLatin1String( "__init__" ) ) continue;
254  QListWidgetItem *item = new QListWidgetItem( QgsApplication::getThemeIcon( QStringLiteral( "console/iconTabEditorConsole.svg" ) ), info.baseName() );
255  cmbFileNames->addItem( item );
256  }
257  if ( !cmbFileNames->currentItem() )
258  cmbFileNames->setCurrentRow( 0 );
259 }
260 
261 void QgsExpressionBuilderWidget::newFunctionFile( const QString &fileName )
262 {
263  QList<QListWidgetItem *> items = cmbFileNames->findItems( fileName, Qt::MatchExactly );
264  if ( !items.isEmpty() )
265  return;
266 
267  QListWidgetItem *item = new QListWidgetItem( QgsApplication::getThemeIcon( QStringLiteral( "console/iconTabEditorConsole.svg" ) ), fileName );
268  cmbFileNames->insertItem( 0, item );
269  cmbFileNames->setCurrentRow( 0 );
270 
271  QString templatetxt;
272  QgsPythonRunner::eval( QStringLiteral( "qgis.user.expressions.template" ), templatetxt );
273  txtPython->setText( templatetxt );
274  saveFunctionFile( fileName );
275 }
276 
277 void QgsExpressionBuilderWidget::btnNewFile_pressed()
278 {
279  bool ok;
280  QString text = QInputDialog::getText( this, tr( "Enter new file name" ),
281  tr( "File name:" ), QLineEdit::Normal,
282  QLatin1String( "" ), &ok );
283  if ( ok && !text.isEmpty() )
284  {
285  newFunctionFile( text );
286  }
287 }
288 
289 void QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem )
290 {
291  if ( lastitem )
292  {
293  QString filename = lastitem->text();
294  saveFunctionFile( filename );
295  }
296  QString path = mFunctionsPath + QDir::separator() + item->text();
297  loadCodeFromFile( path );
298 }
299 
301 {
302  if ( !path.endsWith( QLatin1String( ".py" ) ) )
303  path.append( ".py" );
304 
305  txtPython->loadScript( path );
306 }
307 
309 {
310  txtPython->setText( code );
311 }
312 
313 void QgsExpressionBuilderWidget::expressionTree_doubleClicked( const QModelIndex &index )
314 {
315  QModelIndex idx = mProxyModel->mapToSource( index );
316  QgsExpressionItem *item = dynamic_cast<QgsExpressionItem *>( mModel->itemFromIndex( idx ) );
317  if ( !item )
318  return;
319 
320  // Don't handle the double-click if we are on a header node.
321  if ( item->getItemType() == QgsExpressionItem::Header )
322  return;
323 
324  // Insert the expression text or replace selected text
325  txtExpressionString->insertText( item->getExpressionText() );
326  txtExpressionString->setFocus();
327 }
328 
330 {
331  // TODO We should really return a error the user of the widget that
332  // the there is no layer set.
333  if ( !mLayer )
334  return;
335 
336  loadFieldNames( mLayer->fields() );
337 }
338 
340 {
341  if ( fields.isEmpty() )
342  return;
343 
344  QStringList fieldNames;
345  //Q_FOREACH ( const QgsField& field, fields )
346  fieldNames.reserve( fields.count() );
347  for ( int i = 0; i < fields.count(); ++i )
348  {
349  QString fieldName = fields.at( i ).name();
350  fieldNames << fieldName;
351  registerItem( QStringLiteral( "Fields and Values" ), fieldName, " \"" + fieldName + "\" ", QLatin1String( "" ), QgsExpressionItem::Field, false, i );
352  }
353 // highlighter->addFields( fieldNames );
354 }
355 
356 void QgsExpressionBuilderWidget::loadFieldsAndValues( const QMap<QString, QStringList> &fieldValues )
357 {
358  QgsFields fields;
359  for ( auto it = fieldValues.constBegin(); it != fieldValues.constEnd(); ++it )
360  {
361  fields.append( QgsField( it.key() ) );
362  }
363  loadFieldNames( fields );
364  mFieldValues = fieldValues;
365 }
366 
367 void QgsExpressionBuilderWidget::fillFieldValues( const QString &fieldName, int countLimit )
368 {
369  // TODO We should really return a error the user of the widget that
370  // the there is no layer set.
371  if ( !mLayer )
372  return;
373 
374  // TODO We should thread this so that we don't hold the user up if the layer is massive.
375 
376  int fieldIndex = mLayer->fields().lookupField( fieldName );
377 
378  if ( fieldIndex < 0 )
379  return;
380 
381  QStringList strValues;
382  QList<QVariant> values = mLayer->uniqueValues( fieldIndex, countLimit ).toList();
383  std::sort( values.begin(), values.end() );
384  Q_FOREACH ( const QVariant &value, values )
385  {
386  QString strValue;
387  if ( value.isNull() )
388  strValue = QStringLiteral( "NULL" );
389  else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong )
390  strValue = value.toString();
391  else
392  strValue = '\'' + value.toString().replace( '\'', QLatin1String( "''" ) ) + '\'';
393  strValues.append( strValue );
394  }
395  mValuesModel->setStringList( strValues );
396  mFieldValues[fieldName] = strValues;
397 }
398 
399 QString QgsExpressionBuilderWidget::getFunctionHelp( QgsExpressionFunction *function )
400 {
401  if ( !function )
402  return QString();
403 
404  QString helpContents = QgsExpression::helpText( function->name() );
405 
406  return "<head><style>" + helpStylesheet() + "</style></head><body>" + helpContents + "</body>";
407 
408 }
409 
410 void QgsExpressionBuilderWidget::registerItem( const QString &group,
411  const QString &label,
412  const QString &expressionText,
413  const QString &helpText,
414  QgsExpressionItem::ItemType type, bool highlightedItem, int sortOrder )
415 {
416  QgsExpressionItem *item = new QgsExpressionItem( label, expressionText, helpText, type );
417  item->setData( label, Qt::UserRole );
418  item->setData( sortOrder, QgsExpressionItem::CUSTOM_SORT_ROLE );
419 
420  // Look up the group and insert the new function.
421  if ( mExpressionGroups.contains( group ) )
422  {
423  QgsExpressionItem *groupNode = mExpressionGroups.value( group );
424  groupNode->appendRow( item );
425  }
426  else
427  {
428  // If the group doesn't exist yet we make it first.
429  QgsExpressionItem *newgroupNode = new QgsExpressionItem( QgsExpression::group( group ), QLatin1String( "" ), QgsExpressionItem::Header );
430  newgroupNode->setData( group, Qt::UserRole );
431  //Recent group should always be last group
432  newgroupNode->setData( group.startsWith( QLatin1String( "Recent (" ) ) ? 2 : 1, QgsExpressionItem::CUSTOM_SORT_ROLE );
433  newgroupNode->appendRow( item );
434  newgroupNode->setBackground( QBrush( QColor( 238, 238, 238 ) ) );
435  mModel->appendRow( newgroupNode );
436  mExpressionGroups.insert( group, newgroupNode );
437  }
438 
439  if ( highlightedItem )
440  {
441  //insert a copy as a top level item
442  QgsExpressionItem *topLevelItem = new QgsExpressionItem( label, expressionText, helpText, type );
443  topLevelItem->setData( label, Qt::UserRole );
444  item->setData( 0, QgsExpressionItem::CUSTOM_SORT_ROLE );
445  QFont font = topLevelItem->font();
446  font.setBold( true );
447  topLevelItem->setFont( font );
448  mModel->appendRow( topLevelItem );
449  }
450 
451 }
452 
454 {
455  return mExpressionValid;
456 }
457 
458 void QgsExpressionBuilderWidget::saveToRecent( const QString &collection )
459 {
460  QgsSettings settings;
461  QString location = QStringLiteral( "/expressions/recent/%1" ).arg( collection );
462  QStringList expressions = settings.value( location ).toStringList();
463  expressions.removeAll( this->expressionText() );
464 
465  expressions.prepend( this->expressionText() );
466 
467  while ( expressions.count() > 20 )
468  {
469  expressions.pop_back();
470  }
471 
472  settings.setValue( location, expressions );
473  this->loadRecent( collection );
474 }
475 
476 void QgsExpressionBuilderWidget::loadRecent( const QString &collection )
477 {
478  mRecentKey = collection;
479  QString name = tr( "Recent (%1)" ).arg( collection );
480  if ( mExpressionGroups.contains( name ) )
481  {
482  QgsExpressionItem *node = mExpressionGroups.value( name );
483  node->removeRows( 0, node->rowCount() );
484  }
485 
486  QgsSettings settings;
487  QString location = QStringLiteral( "/expressions/recent/%1" ).arg( collection );
488  QStringList expressions = settings.value( location ).toStringList();
489  int i = 0;
490  Q_FOREACH ( const QString &expression, expressions )
491  {
492  this->registerItem( name, expression, expression, expression, QgsExpressionItem::ExpressionNode, false, i );
493  i++;
494  }
495 }
496 
497 void QgsExpressionBuilderWidget::loadLayers()
498 {
499  if ( !mProject )
500  return;
501 
502  QMap<QString, QgsMapLayer *> layers = mProject->mapLayers();
503  QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
504  for ( ; layerIt != layers.constEnd(); ++layerIt )
505  {
506  registerItemForAllGroups( QStringList() << tr( "Map Layers" ), layerIt.value()->name(), QStringLiteral( "'%1'" ).arg( layerIt.key() ), formatLayerHelp( layerIt.value() ) );
507  }
508 }
509 
510 void QgsExpressionBuilderWidget::loadRelations()
511 {
512  if ( !mProject )
513  return;
514 
515  QMap<QString, QgsRelation> relations = mProject->relationManager()->relations();
516  QMap<QString, QgsRelation>::const_iterator relIt = relations.constBegin();
517  for ( ; relIt != relations.constEnd(); ++relIt )
518  {
519  registerItemForAllGroups( QStringList() << tr( "Relations" ), relIt->name(), QStringLiteral( "'%1'" ).arg( relIt->id() ), formatRelationHelp( relIt.value() ) );
520  }
521 }
522 
523 void QgsExpressionBuilderWidget::updateFunctionTree()
524 {
525  mModel->clear();
526  mExpressionGroups.clear();
527  // TODO Can we move this stuff to QgsExpression, like the functions?
528  registerItem( QStringLiteral( "Operators" ), QStringLiteral( "+" ), QStringLiteral( " + " ) );
529  registerItem( QStringLiteral( "Operators" ), QStringLiteral( "-" ), QStringLiteral( " - " ) );
530  registerItem( QStringLiteral( "Operators" ), QStringLiteral( "*" ), QStringLiteral( " * " ) );
531  registerItem( QStringLiteral( "Operators" ), QStringLiteral( "/" ), QStringLiteral( " / " ) );
532  registerItem( QStringLiteral( "Operators" ), QStringLiteral( "%" ), QStringLiteral( " % " ) );
533  registerItem( QStringLiteral( "Operators" ), QStringLiteral( "^" ), QStringLiteral( " ^ " ) );
534  registerItem( QStringLiteral( "Operators" ), QStringLiteral( "=" ), QStringLiteral( " = " ) );
535  registerItem( QStringLiteral( "Operators" ), QStringLiteral( "~" ), QStringLiteral( " ~ " ) );
536  registerItem( QStringLiteral( "Operators" ), QStringLiteral( ">" ), QStringLiteral( " > " ) );
537  registerItem( QStringLiteral( "Operators" ), QStringLiteral( "<" ), QStringLiteral( " < " ) );
538  registerItem( QStringLiteral( "Operators" ), QStringLiteral( "<>" ), QStringLiteral( " <> " ) );
539  registerItem( QStringLiteral( "Operators" ), QStringLiteral( "<=" ), QStringLiteral( " <= " ) );
540  registerItem( QStringLiteral( "Operators" ), QStringLiteral( ">=" ), QStringLiteral( " >= " ) );
541  registerItem( QStringLiteral( "Operators" ), QStringLiteral( "||" ), QStringLiteral( " || " ) );
542  registerItem( QStringLiteral( "Operators" ), QStringLiteral( "IN" ), QStringLiteral( " IN " ) );
543  registerItem( QStringLiteral( "Operators" ), QStringLiteral( "LIKE" ), QStringLiteral( " LIKE " ) );
544  registerItem( QStringLiteral( "Operators" ), QStringLiteral( "ILIKE" ), QStringLiteral( " ILIKE " ) );
545  registerItem( QStringLiteral( "Operators" ), QStringLiteral( "IS" ), QStringLiteral( " IS " ) );
546  registerItem( QStringLiteral( "Operators" ), QStringLiteral( "OR" ), QStringLiteral( " OR " ) );
547  registerItem( QStringLiteral( "Operators" ), QStringLiteral( "AND" ), QStringLiteral( " AND " ) );
548  registerItem( QStringLiteral( "Operators" ), QStringLiteral( "NOT" ), QStringLiteral( " NOT " ) );
549 
550  QString casestring = QStringLiteral( "CASE WHEN condition THEN result END" );
551  registerItem( QStringLiteral( "Conditionals" ), QStringLiteral( "CASE" ), casestring );
552 
553  registerItem( QStringLiteral( "Fields and Values" ), QStringLiteral( "NULL" ), QStringLiteral( "NULL" ) );
554 
555  // Load the functions from the QgsExpression class
556  int count = QgsExpression::functionCount();
557  for ( int i = 0; i < count; i++ )
558  {
560  QString name = func->name();
561  if ( name.startsWith( '_' ) ) // do not display private functions
562  continue;
563  if ( func->isDeprecated() ) // don't show deprecated functions
564  continue;
565  if ( func->isContextual() )
566  {
567  //don't show contextual functions by default - it's up the the QgsExpressionContext
568  //object to provide them if supported
569  continue;
570  }
571  if ( func->params() != 0 )
572  name += '(';
573  else if ( !name.startsWith( '$' ) )
574  name += QLatin1String( "()" );
575  registerItemForAllGroups( func->groups(), func->name(), ' ' + name + ' ', func->helpText() );
576  }
577 
578  // load relation names
579  loadRelations();
580 
581  // load layer IDs
582  loadLayers();
583 
584  loadExpressionContext();
585 }
586 
588 {
589  mDa = da;
590 }
591 
593 {
594  return txtExpressionString->text();
595 }
596 
597 void QgsExpressionBuilderWidget::setExpressionText( const QString &expression )
598 {
599  txtExpressionString->setText( expression );
600 }
601 
603 {
604  return lblExpected->text();
605 }
606 
608 {
609  lblExpected->setText( expected );
610  mExpectedOutputFrame->setVisible( !expected.isNull() );
611 }
612 
614 {
615  mExpressionContext = context;
616  updateFunctionTree();
617  loadFieldNames();
618  loadRecent( mRecentKey );
619 }
620 
621 void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
622 {
623  QString text = expressionText();
624  clearErrors();
625 
626  // If the string is empty the expression will still "fail" although
627  // we don't show the user an error as it will be confusing.
628  if ( text.isEmpty() )
629  {
630  lblPreview->clear();
631  lblPreview->setStyleSheet( QLatin1String( "" ) );
632  txtExpressionString->setToolTip( QLatin1String( "" ) );
633  lblPreview->setToolTip( QLatin1String( "" ) );
634  emit expressionParsed( false );
635  setParserError( true );
636  setEvalError( true );
637  return;
638  }
639 
640 
641  QgsExpression exp( text );
642 
643  if ( mLayer )
644  {
645  // Only set calculator if we have layer, else use default.
646  exp.setGeomCalculator( &mDa );
647 
648  if ( !mExpressionContext.feature().isValid() )
649  {
650  // no feature passed yet, try to get from layer
651  QgsFeature f;
652  mLayer->getFeatures( QgsFeatureRequest().setLimit( 1 ) ).nextFeature( f );
653  mExpressionContext.setFeature( f );
654  }
655  }
656 
657  QVariant value = exp.evaluate( &mExpressionContext );
658  if ( !exp.hasEvalError() )
659  {
660  lblPreview->setText( QgsExpression::formatPreviewString( value ) );
661  }
662 
663  if ( exp.hasParserError() || exp.hasEvalError() )
664  {
665  QString errorString = exp.parserErrorString().replace( "\n", "<br>" );
666  QString tooltip;
667  if ( exp.hasParserError() )
668  tooltip = QStringLiteral( "<b>%1:</b>"
669  "%2" ).arg( tr( "Parser Errors" ), errorString );
670  // Only show the eval error if there is no parser error.
671  if ( !exp.hasParserError() && exp.hasEvalError() )
672  tooltip += QStringLiteral( "<b>%1:</b> %2" ).arg( tr( "Eval Error" ), exp.evalErrorString() );
673 
674  lblPreview->setText( tr( "Expression is invalid <a href=""more"">(more info)</a>" ) );
675  lblPreview->setStyleSheet( QStringLiteral( "color: rgba(255, 6, 10, 255);" ) );
676  txtExpressionString->setToolTip( tooltip );
677  lblPreview->setToolTip( tooltip );
678  emit expressionParsed( false );
679  setParserError( exp.hasParserError() );
680  setEvalError( exp.hasEvalError() );
681  createErrorMarkers( exp.parserErrors() );
682  return;
683  }
684  else
685  {
686  lblPreview->setStyleSheet( QString() );
687  txtExpressionString->setToolTip( QString() );
688  lblPreview->setToolTip( QString() );
689  emit expressionParsed( true );
690  setParserError( false );
691  setEvalError( false );
692  createMarkers( exp.rootNode() );
693  }
694 
695 }
696 
697 void QgsExpressionBuilderWidget::loadExpressionContext()
698 {
699  QStringList variableNames = mExpressionContext.filteredVariableNames();
700  Q_FOREACH ( const QString &variable, variableNames )
701  {
702  registerItem( QStringLiteral( "Variables" ), variable, " @" + variable + ' ',
703  QgsExpression::formatVariableHelp( mExpressionContext.description( variable ), true, mExpressionContext.variable( variable ) ),
705  mExpressionContext.isHighlightedVariable( variable ) );
706  }
707 
708  // Load the functions from the expression context
709  QStringList contextFunctions = mExpressionContext.functionNames();
710  Q_FOREACH ( const QString &functionName, contextFunctions )
711  {
712  QgsExpressionFunction *func = mExpressionContext.function( functionName );
713  QString name = func->name();
714  if ( name.startsWith( '_' ) ) // do not display private functions
715  continue;
716  if ( func->params() != 0 )
717  name += '(';
718  registerItemForAllGroups( func->groups(), func->name(), ' ' + name + ' ', func->helpText() );
719  }
720 }
721 
722 void QgsExpressionBuilderWidget::registerItemForAllGroups( const QStringList &groups, const QString &label, const QString &expressionText, const QString &helpText, QgsExpressionItem::ItemType type, bool highlightedItem, int sortOrder )
723 {
724  Q_FOREACH ( const QString &group, groups )
725  {
726  registerItem( group, label, expressionText, helpText, type, highlightedItem, sortOrder );
727  }
728 }
729 
730 QString QgsExpressionBuilderWidget::formatRelationHelp( const QgsRelation &relation ) const
731 {
732  QString text = QStringLiteral( "<p>%1</p>" ).arg( tr( "Inserts the relation ID for the relation named '%1'." ).arg( relation.name() ) );
733  text.append( QStringLiteral( "<p>%1</p>" ).arg( tr( "Current value: '%1'" ).arg( relation.id() ) ) );
734  return text;
735 }
736 
737 QString QgsExpressionBuilderWidget::formatLayerHelp( const QgsMapLayer *layer ) const
738 {
739  QString text = QStringLiteral( "<p>%1</p>" ).arg( tr( "Inserts the layer ID for the layer named '%1'." ).arg( layer->name() ) );
740  text.append( QStringLiteral( "<p>%1</p>" ).arg( tr( "Current value: '%1'" ).arg( layer->id() ) ) );
741  return text;
742 }
743 
745 {
746  return mParserError;
747 }
748 
749 void QgsExpressionBuilderWidget::setParserError( bool parserError )
750 {
751  if ( parserError == mParserError )
752  return;
753 
754  mParserError = parserError;
755  emit parserErrorChanged();
756 }
757 
759 {
760  return mEvalError;
761 }
762 
763 void QgsExpressionBuilderWidget::setEvalError( bool evalError )
764 {
765  if ( evalError == mEvalError )
766  return;
767 
768  mEvalError = evalError;
769  emit evalErrorChanged();
770 }
771 
773 {
774  return mModel;
775 }
776 
778 {
779  return mProject;
780 }
781 
783 {
784  mProject = project;
785  updateFunctionTree();
786 }
787 
789 {
790  QWidget::showEvent( e );
791  txtExpressionString->setFocus();
792 }
793 
794 void QgsExpressionBuilderWidget::createErrorMarkers( QList<QgsExpression::ParserError> errors )
795 {
796  clearErrors();
797  for ( const QgsExpression::ParserError &error : errors )
798  {
799  int errorFirstLine = error.firstLine - 1 ;
800  int errorFirstColumn = error.firstColumn - 1;
801  int errorLastColumn = error.lastColumn - 1;
802  int errorLastLine = error.lastLine - 1;
803 
804  // If we have a unknown error we just mark the point that hit the error for now
805  // until we can handle others more.
806  if ( error.errorType == QgsExpression::ParserError::Unknown )
807  {
808  errorFirstLine = errorLastLine;
809  errorFirstColumn = errorLastColumn - 1;
810  }
811  txtExpressionString->fillIndicatorRange( errorFirstLine,
812  errorFirstColumn,
813  errorLastLine,
814  errorLastColumn, error.errorType );
815  }
816 }
817 
818 void QgsExpressionBuilderWidget::createMarkers( const QgsExpressionNode *inNode )
819 {
820  switch ( inNode->nodeType() )
821  {
822  case QgsExpressionNode::NodeType::ntFunction:
823  {
824  const QgsExpressionNodeFunction *node = static_cast<const QgsExpressionNodeFunction *>( inNode );
825  txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
826  txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->fnIndex() );
827  int start = inNode->parserFirstColumn - 1;
828  int end = inNode->parserLastColumn - 1;
829  int start_pos = txtExpressionString->positionFromLineIndex( inNode->parserFirstLine - 1, start );
830  txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
831  if ( node->args() )
832  {
833  const QList< QgsExpressionNode * > nodeList = node->args()->list();
834  for ( QgsExpressionNode *n : nodeList )
835  {
836  createMarkers( n );
837  }
838  }
839  break;
840  }
841  case QgsExpressionNode::NodeType::ntLiteral:
842  {
843  break;
844  }
845  case QgsExpressionNode::NodeType::ntUnaryOperator:
846  {
847  const QgsExpressionNodeUnaryOperator *node = static_cast<const QgsExpressionNodeUnaryOperator *>( inNode );
848  createMarkers( node->operand() );
849  break;
850  }
851  case QgsExpressionNode::NodeType::ntBinaryOperator:
852  {
853  const QgsExpressionNodeBinaryOperator *node = static_cast<const QgsExpressionNodeBinaryOperator *>( inNode );
854  createMarkers( node->opLeft() );
855  createMarkers( node->opRight() );
856  break;
857  }
858  case QgsExpressionNode::NodeType::ntColumnRef:
859  {
860  break;
861  }
862  case QgsExpressionNode::NodeType::ntInOperator:
863  {
864  const QgsExpressionNodeInOperator *node = static_cast<const QgsExpressionNodeInOperator *>( inNode );
865  if ( node->list() )
866  {
867  const QList< QgsExpressionNode * > nodeList = node->list()->list();
868  for ( QgsExpressionNode *n : nodeList )
869  {
870  createMarkers( n );
871  }
872  }
873  break;
874  }
875  case QgsExpressionNode::NodeType::ntCondition:
876  {
877  const QgsExpressionNodeCondition *node = static_cast<const QgsExpressionNodeCondition *>( inNode );
878  for ( QgsExpressionNodeCondition::WhenThen *cond : node->conditions() )
879  {
880  createMarkers( cond->whenExp() );
881  createMarkers( cond->thenExp() );
882  }
883  if ( node->elseExp() )
884  {
885  createMarkers( node->elseExp() );
886  }
887  break;
888  }
889  }
890 }
891 
892 void QgsExpressionBuilderWidget::clearFunctionMarkers()
893 {
894  int lastLine = txtExpressionString->lines() - 1;
895  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
896 }
897 
898 void QgsExpressionBuilderWidget::clearErrors()
899 {
900  int lastLine = txtExpressionString->lines() - 1;
901  // Note: -1 here doesn't seem to do the clear all like the other functions. Will need to make this a bit smarter.
902  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::Unknown );
903  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionInvalidParams );
904  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionUnknown );
905  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionWrongArgs );
906  txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionNamedArgsError );
907 }
908 
909 void QgsExpressionBuilderWidget::txtSearchEdit_textChanged()
910 {
911  mProxyModel->setFilterWildcard( txtSearchEdit->text() );
912  if ( txtSearchEdit->text().isEmpty() )
913  {
914  expressionTree->collapseAll();
915  }
916  else
917  {
918  expressionTree->expandAll();
919  QModelIndex index = mProxyModel->index( 0, 0 );
920  if ( mProxyModel->hasChildren( index ) )
921  {
922  QModelIndex child = mProxyModel->index( 0, 0, index );
923  expressionTree->selectionModel()->setCurrentIndex( child, QItemSelectionModel::ClearAndSelect );
924  }
925  }
926 }
927 
928 void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
929 {
930  mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
931  mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
932 }
933 
934 void QgsExpressionBuilderWidget::lblPreview_linkActivated( const QString &link )
935 {
936  Q_UNUSED( link );
937  QgsMessageViewer *mv = new QgsMessageViewer( this );
938  mv->setWindowTitle( tr( "More Info on Expression Error" ) );
939  mv->setMessageAsHtml( txtExpressionString->toolTip() );
940  mv->exec();
941 }
942 
943 void QgsExpressionBuilderWidget::mValuesListView_doubleClicked( const QModelIndex &index )
944 {
945  // Insert the item text or replace selected text
946  txtExpressionString->insertText( ' ' + index.data( Qt::DisplayRole ).toString() + ' ' );
947  txtExpressionString->setFocus();
948 }
949 
950 void QgsExpressionBuilderWidget::operatorButtonClicked()
951 {
952  QPushButton *button = dynamic_cast<QPushButton *>( sender() );
953 
954  // Insert the button text or replace selected text
955  txtExpressionString->insertText( ' ' + button->text() + ' ' );
956  txtExpressionString->setFocus();
957 }
958 
959 void QgsExpressionBuilderWidget::showContextMenu( QPoint pt )
960 {
961  QModelIndex idx = expressionTree->indexAt( pt );
962  idx = mProxyModel->mapToSource( idx );
963  QgsExpressionItem *item = dynamic_cast<QgsExpressionItem *>( mModel->itemFromIndex( idx ) );
964  if ( !item )
965  return;
966 
967  if ( item->getItemType() == QgsExpressionItem::Field && mLayer )
968  {
969  QMenu *menu = new QMenu( this );
970  menu->addAction( tr( "Load First 10 Unique Values" ), this, SLOT( loadSampleValues() ) );
971  menu->addAction( tr( "Load All Unique Values" ), this, SLOT( loadAllValues() ) );
972  menu->popup( expressionTree->mapToGlobal( pt ) );
973  }
974 }
975 
977 {
978  QModelIndex idx = mProxyModel->mapToSource( expressionTree->currentIndex() );
979  QgsExpressionItem *item = dynamic_cast<QgsExpressionItem *>( mModel->itemFromIndex( idx ) );
980  // TODO We should really return a error the user of the widget that
981  // the there is no layer set.
982  if ( !mLayer || !item )
983  return;
984 
985  mValueGroupBox->show();
986  fillFieldValues( item->text(), 10 );
987 }
988 
990 {
991  QModelIndex idx = mProxyModel->mapToSource( expressionTree->currentIndex() );
992  QgsExpressionItem *item = dynamic_cast<QgsExpressionItem *>( mModel->itemFromIndex( idx ) );
993  // TODO We should really return a error the user of the widget that
994  // the there is no layer set.
995  if ( !mLayer || !item )
996  return;
997 
998  mValueGroupBox->show();
999  fillFieldValues( item->text(), -1 );
1000 }
1001 
1002 void QgsExpressionBuilderWidget::txtPython_textChanged()
1003 {
1004  lblAutoSave->setText( tr( "Saving…" ) );
1005  if ( mAutoSave )
1006  {
1007  autosave();
1008  }
1009 }
1010 
1012 {
1013  // Don't auto save if not on function editor that would be silly.
1014  if ( tabWidget->currentIndex() != 1 )
1015  return;
1016 
1017  QListWidgetItem *item = cmbFileNames->currentItem();
1018  if ( !item )
1019  return;
1020 
1021  QString file = item->text();
1022  saveFunctionFile( file );
1023  lblAutoSave->setText( QStringLiteral( "Saved" ) );
1024  QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect();
1025  lblAutoSave->setGraphicsEffect( effect );
1026  QPropertyAnimation *anim = new QPropertyAnimation( effect, "opacity" );
1027  anim->setDuration( 2000 );
1028  anim->setStartValue( 1.0 );
1029  anim->setEndValue( 0.0 );
1030  anim->setEasingCurve( QEasingCurve::OutQuad );
1031  anim->start( QAbstractAnimation::DeleteWhenStopped );
1032 }
1033 
1034 void QgsExpressionBuilderWidget::indicatorClicked( int line, int index, Qt::KeyboardModifiers state )
1035 {
1036  if ( state & Qt::ControlModifier )
1037  {
1038  int position = txtExpressionString->positionFromLineIndex( line, index );
1039  long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID, ( long int )position );
1040  QgsExpressionFunction *func = QgsExpression::Functions()[fncIndex];
1041  QString help = getFunctionHelp( func );
1042  txtHelpText->setText( help );
1043  }
1044 }
1045 
1046 void QgsExpressionBuilderWidget::setExpressionState( bool state )
1047 {
1048  mExpressionValid = state;
1049 }
1050 
1051 QString QgsExpressionBuilderWidget::helpStylesheet() const
1052 {
1053  //start with default QGIS report style
1054  QString style = QgsApplication::reportStyleSheet();
1055 
1056  //add some tweaks
1057  style += " .functionname {color: #0a6099; font-weight: bold;} "
1058  " .argument {font-family: monospace; color: #bf0c0c; font-style: italic; } "
1059  " td.argument { padding-right: 10px; }";
1060 
1061  return style;
1062 }
1063 
1064 QString QgsExpressionBuilderWidget::loadFunctionHelp( QgsExpressionItem *expressionItem )
1065 {
1066  if ( !expressionItem )
1067  return QLatin1String( "" );
1068 
1069  QString helpContents = expressionItem->getHelpText();
1070 
1071  // Return the function help that is set for the function if there is one.
1072  if ( helpContents.isEmpty() )
1073  {
1074  QString name = expressionItem->data( Qt::UserRole ).toString();
1075 
1076  if ( expressionItem->getItemType() == QgsExpressionItem::Field )
1077  helpContents = QgsExpression::helpText( QStringLiteral( "Field" ) );
1078  else
1079  helpContents = QgsExpression::helpText( name );
1080  }
1081 
1082  return "<head><style>" + helpStylesheet() + "</style></head><body>" + helpContents + "</body>";
1083 }
1084 
1085 
1086 
1087 
1088 
1090 {
1091  setFilterCaseSensitivity( Qt::CaseInsensitive );
1092 }
1093 
1094 bool QgsExpressionItemSearchProxy::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
1095 {
1096  QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
1097  QgsExpressionItem::ItemType itemType = QgsExpressionItem::ItemType( sourceModel()->data( index, QgsExpressionItem::ITEM_TYPE_ROLE ).toInt() );
1098 
1099  int count = sourceModel()->rowCount( index );
1100  bool matchchild = false;
1101  for ( int i = 0; i < count; ++i )
1102  {
1103  if ( filterAcceptsRow( i, index ) )
1104  {
1105  matchchild = true;
1106  break;
1107  }
1108  }
1109 
1110  if ( itemType == QgsExpressionItem::Header && matchchild )
1111  return true;
1112 
1113  if ( itemType == QgsExpressionItem::Header )
1114  return false;
1115 
1116  return QSortFilterProxyModel::filterAcceptsRow( source_row, source_parent );
1117 }
1118 
1119 bool QgsExpressionItemSearchProxy::lessThan( const QModelIndex &left, const QModelIndex &right ) const
1120 {
1121  int leftSort = sourceModel()->data( left, QgsExpressionItem::CUSTOM_SORT_ROLE ).toInt();
1122  int rightSort = sourceModel()->data( right, QgsExpressionItem::CUSTOM_SORT_ROLE ).toInt();
1123  if ( leftSort != rightSort )
1124  return leftSort < rightSort;
1125 
1126  QString leftString = sourceModel()->data( left, Qt::DisplayRole ).toString();
1127  QString rightString = sourceModel()->data( right, Qt::DisplayRole ).toString();
1128 
1129  //ignore $ prefixes when sorting
1130  if ( leftString.startsWith( '$' ) )
1131  leftString = leftString.mid( 1 );
1132  if ( rightString.startsWith( '$' ) )
1133  rightString = rightString.mid( 1 );
1134 
1135  return QString::localeAwareCompare( leftString, rightString ) < 0;
1136 }
int lookupField(const QString &fieldName) const
Look up field&#39;s index from the field name.
Definition: qgsfields.cpp:299
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:176
QgsProject * project()
Returns the project currently associated with the widget.
Class for parsing and evaluation of expressions (formerly called "search strings").
QString name
Definition: qgsrelation.h:45
int parserFirstColumn
First column in the parser this node was found.
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
void setProject(QgsProject *project)
Sets the project currently associated with the widget.
void saveFunctionFile(QString fileName)
Save the current function editor text to the given file.
Base class for all map layer types.
Definition: qgsmaplayer.h:61
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
QStandardItemModel * model()
Returns a pointer to the dialog&#39;s function item model.
QStringList filteredVariableNames() const
Returns a filtered list of variables names set by all scopes in the context.
QString name
Definition: qgsfield.h:57
int params() const
The number of parameters this function takes.
QStringList groups() const
Returns a list of the groups the function belongs to.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
static QString group(const QString &group)
Returns the translated name for a function group.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
QgsExpressionNode * opRight() const
Returns the node to the right of the operator.
WhenThenList conditions() const
The list of WHEN THEN expression parts of the expression.
void setGeomCalculator(const QgsDistanceArea &da)
Sets geometry calculator used in distance/area calculations.
Details about any parser errors that were found when parsing the expression.
void evalErrorChanged()
Will be set to true if the current expression text reported an eval error with the context...
void loadSampleValues()
Load sample values into the sample value area.
void setLayer(QgsVectorLayer *layer)
Sets layer in order to get the fields and values.
QString id
Definition: qgsrelation.h:42
void loadRecent(const QString &collection="generic")
Loads the recent expressions from the given collection.
QVariant evaluate()
Evaluate the feature and return the result.
An expression node for CASE WHEN clauses.
QString evalErrorString() const
Returns evaluation error.
Container of fields for a vector layer.
Definition: qgsfields.h:42
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
QgsExpressionNode::NodeList * list() const
Returns the list of nodes to search for matching values within.
QList< QgsExpression::ParserError > parserErrors() const
Returns parser error details including location of error.
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const override
Calculates a list of unique values contained within an attribute in the layer.
QgsExpressionNode * opLeft() const
Returns the node to the left of the operator.
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:62
static QString formatPreviewString(const QVariant &value)
Formats an expression result for friendly display to the user.
static QString reportStyleSheet()
Returns a standard css style sheet for reports.
int count() const
Returns number of items.
Definition: qgsfields.cpp:115
QString parserErrorString() const
Returns parser error.
void loadFunctionCode(const QString &code)
Load code into the function editor.
QVariant variable(const QString &name) const
Fetches a matching variable from the context.
Function was called with invalid args.
void loadCodeFromFile(QString path)
Load code from the given file into the function editor.
void newFunctionFile(const QString &fileName="scratch")
Create a new file in the function editor.
Non named function arg used after named arg.
QString description(const QString &name) const
Returns a translated description string for the variable with specified name.
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:145
void setExpectedOutputFormat(const QString &expected)
The set expected format string.
void updateFunctionFileList(const QString &path)
Update the list of function files found at the given path.
void setMessageAsHtml(const QString &msg)
static int functionCount()
Returns the number of functions defined in the parser.
Search proxy used to filter the QgsExpressionBuilderWidget tree.
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
QgsFields fields() const override
Returns the list of fields of this layer.
bool parserError() const
Will be set to true if the current expression text reports a parser error with the context...
bool isHighlightedVariable(const QString &name) const
Returns true if the specified variable name is intended to be highlighted to the user.
QString expectedOutputFormat()
The set expected format string.
Function was called with the wrong number of args.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
An expression node for value IN or NOT IN clauses.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Append a field. The field must have unique name, otherwise it is rejected (returns false) ...
Definition: qgsfields.cpp:59
Reads and writes project states.
Definition: qgsproject.h:85
void loadFieldNames()
Loads all the field names from the layer.
Abstract base class for all nodes that can appear in an expression.
static const int ITEM_TYPE_ROLE
Item type role.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:48
void showEvent(QShowEvent *e) override
An expression node for expression functions.
void autosave()
Auto save the current Python function code.
QgsExpressionNode * elseExp() const
The ELSE expression used for the condition.
static const QList< QgsExpressionFunction * > & Functions()
static QString formatVariableHelp(const QString &description, bool showValue=true, const QVariant &value=QVariant())
Returns formatted help text for a variable.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context for the widget.
static bool eval(const QString &command, QString &result)
Eval a Python statement.
bool evalError() const
Will be set to true if the current expression text reported an eval error with the context...
QgsExpressionItem::ItemType getItemType() const
Gets the type of expression item, e.g., header, field, ExpressionNode.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
static const int CUSTOM_SORT_ROLE
Custom sort order role.
QString name() const
The name of the function.
A general purpose distance and area calculator, capable of performing ellipsoid based calculations...
A abstract base class for defining QgsExpression functions.
An expression item that can be used in the QgsExpressionBuilderWidget tree.
void setGeomCalculator(const QgsDistanceArea *calc)
Sets the geometry calculator used for distance and area calculations in expressions.
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
int parserFirstLine
First line in the parser this node was found.
QStringList functionNames() const
Retrieves a list of function names contained in the context.
const QString helpText() const
The help text for the function.
static bool run(const QString &command, const QString &messageOnError=QString())
Execute a Python statement.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
QString getExpressionText() const
const QgsExpressionNode * rootNode() const
Returns root node of the expression. Root node is null is parsing has failed.
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
A unary node is either negative as in boolean (not) or as in numbers (minus).
void loadFieldsAndValues(const QMap< QString, QStringList > &fieldValues)
Loads field names and values from the specified map.
A binary expression operator, which operates on two values.
A generic message view for displaying QGIS messages.
bool isEmpty() const
Check whether the container is empty.
Definition: qgsfields.cpp:110
QString expressionText()
Gets the expression string that has been set in the expression area.
static QString helpText(QString name)
Returns the help text for a specified function.
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set.
void setExpressionText(const QString &expression)
Sets the expression string for the widget.
void parserErrorChanged()
Will be set to true if the current expression text reported a parser error with the context...
QString getHelpText() const
Gets the help text that is associated with this expression item.
void registerItem(const QString &group, const QString &label, const QString &expressionText, const QString &helpText=QString(), QgsExpressionItem::ItemType type=QgsExpressionItem::ExpressionNode, bool highlightedItem=false, int sortOrder=1)
Registers a node item for the expression builder.
QString name
Definition: qgsmaplayer.h:65
QgsExpressionBuilderWidget(QWidget *parent=nullptr)
Create a new expression builder widget with an optional parent.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool nextFeature(QgsFeature &f)
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
Represents a vector layer which manages a vector based data sets.
void expressionParsed(bool isValid)
Emitted when the user changes the expression in the widget.
virtual bool isDeprecated() const
Returns true if the function is deprecated and should not be presented as a valid option to users in ...
virtual QgsExpressionNode::NodeType nodeType() const =0
Gets the type of this node.
QgsExpressionNode * operand() const
Returns the node the operator will operate upon.
void saveToRecent(const QString &collection="generic")
Adds the current expression to the given collection.
static bool isValid()
Returns true if the runner has an instance (and thus is able to run commands)
Represents a "WHEN... THEN..." portation of a CASE WHEN clause in an expression.
void loadAllValues()
Load all unique values from the set layer into the sample area.
QgsExpressionFunction * function(const QString &name) const
Fetches a matching function from the context.
int parserLastColumn
Last column in the parser this node was found.
bool isContextual() const
Returns whether the function is only available if provided by a QgsExpressionContext object...
int fnIndex() const
Returns the index of the node&#39;s function.