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