QGIS API Documentation 3.29.0-Master (006c3c0232)
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 The function accepts the following parameters\n\
213\n\
214 : param [any]: Define any parameters you want to pass to your function before\n\
215 the following arguments.\n\
216 : param feature: The current feature\n\
217 : param parent: The QgsExpression object\n\
218 : param context: If there is an argument called ``context`` found at the last\n\
219 position, this variable will contain a ``QgsExpressionContext``\n\
220 object, that gives access to various additional information like\n\
221 expression variables. E.g. ``context.variable( 'layer_id' )``\n\
222 : returns: The result of the expression.\n\
223\n\
224\n\
225\n\
226 The @qgsfunction decorator accepts the following arguments:\n\
227\n\
228\n\
229 : param args: Defines the number of arguments. With ``args = 'auto'`` the number of\n\
230 arguments will automatically be extracted from the signature.\n\
231 With ``args = -1``, any number of arguments are accepted.\n\
232 : param group: The name of the group under which this expression function will\n\
233 be listed.\n\
234 : param handlesnull: Set this to True if your function has custom handling for NULL values.\n\
235 If False, the result will always be NULL as soon as any parameter is NULL.\n\
236 Defaults to False.\n\
237 : param usesgeometry : Set this to True if your function requires access to\n\
238 feature.geometry(). Defaults to False.\n\
239 : param referenced_columns: An array of attribute names that are required to run\n\
240 this function. Defaults to [QgsFeatureRequest.ALL_ATTRIBUTES].\n\
241 \"\"\"" ) );
242}
243
244
246{
247 QgsSettings settings;
248 settings.setValue( QStringLiteral( "Windows/QgsExpressionBuilderWidget/splitter" ), splitter->saveState() );
249 settings.setValue( QStringLiteral( "Windows/QgsExpressionBuilderWidget/editorsplitter" ), editorSplit->saveState() );
250 settings.setValue( QStringLiteral( "Windows/QgsExpressionBuilderWidget/functionsplitter" ), functionsplit->saveState() );
251 delete mExpressionTreeMenuProvider;
252}
253
254void QgsExpressionBuilderWidget::init( const QgsExpressionContext &context, const QString &recentCollection, QgsExpressionBuilderWidget::Flags flags )
255{
256 setExpressionContext( context );
257
258 if ( flags.testFlag( LoadRecent ) )
259 mExpressionTreeView->loadRecent( recentCollection );
260
261 if ( flags.testFlag( LoadUserExpressions ) )
262 mExpressionTreeView->loadUserExpressions();
263}
264
265void QgsExpressionBuilderWidget::initWithLayer( QgsVectorLayer *layer, const QgsExpressionContext &context, const QString &recentCollection, QgsExpressionBuilderWidget::Flags flags )
266{
267 init( context, recentCollection, flags );
268 setLayer( layer );
269}
270
271void QgsExpressionBuilderWidget::initWithFields( const QgsFields &fields, const QgsExpressionContext &context, const QString &recentCollection, QgsExpressionBuilderWidget::Flags flags )
272{
273 init( context, recentCollection, flags );
274 mExpressionTreeView->loadFieldNames( fields );
275}
276
277
279{
280 mLayer = layer;
281 mExpressionTreeView->setLayer( mLayer );
282 mExpressionPreviewWidget->setLayer( mLayer );
283
284 //TODO - remove existing layer scope from context
285
286 if ( mLayer )
287 {
288 mExpressionContext << QgsExpressionContextUtils::layerScope( mLayer );
289 expressionContextUpdated();
290 txtExpressionString->setFields( mLayer->fields() );
291 }
292}
293
294void QgsExpressionBuilderWidget::expressionContextUpdated()
295{
296 txtExpressionString->setExpressionContext( mExpressionContext );
297 mExpressionTreeView->setExpressionContext( mExpressionContext );
298 mExpressionPreviewWidget->setExpressionContext( mExpressionContext );
299}
300
302{
303 return mLayer;
304}
305
306void QgsExpressionBuilderWidget::expressionTreeItemChanged( QgsExpressionItem *item )
307{
308 txtSearchEditValues->clear();
309
310 if ( !item )
311 return;
312
313 bool isField = mLayer && item->getItemType() == QgsExpressionItem::Field;
314 if ( isField )
315 {
316 mValuesModel->clear();
317
318 cbxValuesInUse->setVisible( formatterCanProvideAvailableValues( mLayer, item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString() ) );
319 cbxValuesInUse->setChecked( false );
320 }
321 mValueGroupBox->setVisible( isField );
322
323 mShowHelpButton->setText( isField ? tr( "Show Values" ) : tr( "Show Help" ) );
324
325 // Show the help for the current item.
326 QString help = loadFunctionHelp( item );
327 txtHelpText->setText( help );
328
329 bool isUserExpression = item->parent() && item->parent()->text() == mUserExpressionsGroupName;
330
331 btnRemoveExpression->setEnabled( isUserExpression );
332 btnEditExpression->setEnabled( isUserExpression );
333}
334
335void QgsExpressionBuilderWidget::btnRun_pressed()
336{
337 if ( !cmbFileNames->currentItem() )
338 return;
339
340 QString file = cmbFileNames->currentItem()->text();
341 saveFunctionFile( file );
342 runPythonCode( txtPython->text() );
343}
344
345void QgsExpressionBuilderWidget::runPythonCode( const QString &code )
346{
348 {
349 QString pythontext = code;
350 QgsPythonRunner::run( pythontext );
351 }
352 mExpressionTreeView->refresh();
353}
354
355QgsVectorLayer *QgsExpressionBuilderWidget::contextLayer( const QgsExpressionItem *item ) const
356{
357 QgsVectorLayer *layer = nullptr;
359 {
360 layer = qobject_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( item->data( QgsExpressionItem::LAYER_ID_ROLE ).toString() ) );
361 }
362 else
363 {
364 layer = mLayer;
365 }
366 return layer;
367}
368
369
371{
372 QDir myDir( mFunctionsPath );
373 if ( !myDir.exists() )
374 {
375 myDir.mkpath( mFunctionsPath );
376 }
377
378 if ( !fileName.endsWith( QLatin1String( ".py" ) ) )
379 {
380 fileName.append( ".py" );
381 }
382
383 fileName = mFunctionsPath + QDir::separator() + fileName;
384 QFile myFile( fileName );
385 if ( myFile.open( QIODevice::WriteOnly | QFile::Truncate ) )
386 {
387 QTextStream myFileStream( &myFile );
388 myFileStream << txtPython->text() << Qt::endl;
389 myFile.close();
390 }
391}
392
394{
395 mFunctionsPath = path;
396 QDir dir( path );
397 dir.setNameFilters( QStringList() << QStringLiteral( "*.py" ) );
398 QStringList files = dir.entryList( QDir::Files );
399 cmbFileNames->clear();
400 const auto constFiles = files;
401 for ( const QString &name : constFiles )
402 {
403 QFileInfo info( mFunctionsPath + QDir::separator() + name );
404 if ( info.baseName() == QLatin1String( "__init__" ) ) continue;
405 QListWidgetItem *item = new QListWidgetItem( QgsApplication::getThemeIcon( QStringLiteral( "console/iconTabEditorConsole.svg" ) ), info.baseName() );
406 cmbFileNames->addItem( item );
407 }
408 if ( !cmbFileNames->currentItem() )
409 {
410 cmbFileNames->setCurrentRow( 0 );
411 }
412
413 if ( cmbFileNames->count() == 0 )
414 {
415 // Create default sample entry.
416 newFunctionFile( QStringLiteral( "default" ) );
417 txtPython->setText( QStringLiteral( "'''\n#Sample custom function file\n"
418 "#(uncomment to use and customize or Add button to create a new file) \n%1 \n '''" ).arg( txtPython->text() ) );
419 saveFunctionFile( QStringLiteral( "default" ) );
420 }
421}
422
423void QgsExpressionBuilderWidget::newFunctionFile( const QString &fileName )
424{
425 QList<QListWidgetItem *> items = cmbFileNames->findItems( fileName, Qt::MatchExactly );
426 if ( !items.isEmpty() )
427 return;
428
429 QListWidgetItem *item = new QListWidgetItem( QgsApplication::getThemeIcon( QStringLiteral( "console/iconTabEditorConsole.svg" ) ), fileName );
430 cmbFileNames->insertItem( 0, item );
431 cmbFileNames->setCurrentRow( 0 );
432
433 QString templatetxt;
434 QgsPythonRunner::eval( QStringLiteral( "qgis.user.default_expression_template" ), templatetxt );
435 txtPython->setText( templatetxt );
436 saveFunctionFile( fileName );
437}
438
439void QgsExpressionBuilderWidget::btnNewFile_pressed()
440{
441 bool ok;
442 QString text = QInputDialog::getText( this, tr( "New File" ),
443 tr( "New file name:" ), QLineEdit::Normal,
444 QString(), &ok );
445 if ( ok && !text.isEmpty() )
446 {
447 newFunctionFile( text );
448 }
449}
450
451void QgsExpressionBuilderWidget::btnRemoveFile_pressed()
452{
453 if ( QMessageBox::question( this, tr( "Remove File" ),
454 tr( "Are you sure you want to remove current functions file?" ),
455 QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No )
456 return;
457
458 int currentRow = cmbFileNames->currentRow();
459 QString fileName = cmbFileNames->currentItem()->text();
460 if ( QFile::remove( mFunctionsPath + QDir::separator() + fileName.append( ".py" ) ) )
461 {
462 {
463 QListWidgetItem *itemToRemove = whileBlocking( cmbFileNames )->takeItem( currentRow );
464 delete itemToRemove;
465 }
466
467 if ( cmbFileNames->count() > 0 )
468 {
469 cmbFileNames->setCurrentRow( currentRow > 0 ? currentRow - 1 : 0 );
470 loadCodeFromFile( mFunctionsPath + QDir::separator() + cmbFileNames->currentItem()->text() );
471 }
472 else
473 {
474 btnRemoveFile->setEnabled( false );
475 txtPython->clear();
476 }
477 }
478 else
479 {
480 QMessageBox::warning( this, tr( "Remove file" ), tr( "Failed to remove function file '%1'." ).arg( fileName ) );
481 }
482}
483
484void QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem )
485{
486 if ( lastitem )
487 {
488 QString filename = lastitem->text();
489 saveFunctionFile( filename );
490 }
491 QString path = mFunctionsPath + QDir::separator() + item->text();
492 loadCodeFromFile( path );
493}
494
496{
497 if ( !path.endsWith( QLatin1String( ".py" ) ) )
498 path.append( ".py" );
499
500 txtPython->loadScript( path );
501}
502
504{
505 txtPython->setText( code );
506}
507
508void QgsExpressionBuilderWidget::insertExpressionText( const QString &text )
509{
510 // Insert the expression text or replace selected text
511 txtExpressionString->insertText( text );
512 txtExpressionString->setFocus();
513}
514
515void QgsExpressionBuilderWidget::loadFieldsAndValues( const QMap<QString, QStringList> &fieldValues )
516{
517 Q_UNUSED( fieldValues )
518 // This is not maintained and setLayer() should be used instead.
519}
520
521void QgsExpressionBuilderWidget::fillFieldValues( const QString &fieldName, QgsVectorLayer *layer, int countLimit, bool forceUsedValues )
522{
523 // TODO We should really return a error the user of the widget that
524 // the there is no layer set.
525 if ( !layer )
526 return;
527
528 // TODO We should thread this so that we don't hold the user up if the layer is massive.
529
530 const QgsFields fields = layer->fields();
531 int fieldIndex = fields.lookupField( fieldName );
532
533 if ( fieldIndex < 0 )
534 return;
535
536 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
538
539 QVariantList values;
540 if ( cbxValuesInUse->isVisible() && !cbxValuesInUse->isChecked() && !forceUsedValues )
541 {
542 QgsFieldFormatterContext fieldFormatterContext;
543 fieldFormatterContext.setProject( mProject );
544 values = formatter->availableValues( setup.config(), countLimit, fieldFormatterContext );
545 }
546 else
547 {
548 values = qgis::setToList( layer->uniqueValues( fieldIndex, countLimit ) );
549 }
550 std::sort( values.begin(), values.end() );
551
552 mValuesModel->clear();
553 for ( const QVariant &value : std::as_const( values ) )
554 {
555 QString strValue;
556 bool forceRepresentedValue = false;
557 if ( QgsVariantUtils::isNull( value ) )
558 strValue = QStringLiteral( "NULL" );
559 else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong )
560 strValue = value.toString();
561 else if ( value.type() == QVariant::StringList )
562 {
563 QString result;
564 const QStringList strList = value.toStringList();
565 for ( QString str : strList )
566 {
567 if ( !result.isEmpty() )
568 result.append( QStringLiteral( ", " ) );
569
570 result.append( '\'' + str.replace( '\'', QLatin1String( "''" ) ) + '\'' );
571 }
572 strValue = QStringLiteral( "array(%1)" ).arg( result );
573 forceRepresentedValue = true;
574 }
575 else if ( value.type() == QVariant::List )
576 {
577 QString result;
578 const QList list = value.toList();
579 for ( const QVariant &item : list )
580 {
581 if ( !result.isEmpty() )
582 result.append( QStringLiteral( ", " ) );
583
584 result.append( item.toString() );
585 }
586 strValue = QStringLiteral( "array(%1)" ).arg( result );
587 forceRepresentedValue = true;
588 }
589 else
590 strValue = '\'' + value.toString().replace( '\'', QLatin1String( "''" ) ) + '\'';
591
592 QString representedValue = formatter->representValue( layer, fieldIndex, setup.config(), QVariant(), value );
593 if ( forceRepresentedValue || representedValue != value.toString() )
594 representedValue = representedValue + QStringLiteral( " [" ) + strValue + ']';
595
596 QStandardItem *item = new QStandardItem( representedValue );
597 item->setData( strValue );
598 mValuesModel->appendRow( item );
599 }
600}
601
602QString QgsExpressionBuilderWidget::getFunctionHelp( QgsExpressionFunction *function )
603{
604 if ( !function )
605 return QString();
606
607 QString helpContents = QgsExpression::helpText( function->name() );
608
609 return QStringLiteral( "<head><style>" ) + helpStylesheet() + QStringLiteral( "</style></head><body>" ) + helpContents + QStringLiteral( "</body>" );
610
611}
612
613
614
616{
617 return mExpressionValid;
618}
619
620void QgsExpressionBuilderWidget::saveToRecent( const QString &collection )
621{
622 mExpressionTreeView->saveToRecent( expressionText(), collection );
623}
624
625void QgsExpressionBuilderWidget::loadRecent( const QString &collection )
626{
627 mExpressionTreeView->loadRecent( collection );
628}
629
631{
632 return mExpressionTreeView;
633}
634
635// this is potentially very slow if there are thousands of user expressions, every time entire cleanup and load
637{
638 mExpressionTreeView->loadUserExpressions();
639}
640
641void QgsExpressionBuilderWidget::saveToUserExpressions( const QString &label, const QString &expression, const QString &helpText )
642{
643 mExpressionTreeView->saveToUserExpressions( label, expression, helpText );
644}
645
647{
648 mExpressionTreeView->removeFromUserExpressions( label );
649}
650
651
653{
654 mExpressionPreviewWidget->setGeomCalculator( da );
655}
656
658{
659 return txtExpressionString->text();
660}
661
662void QgsExpressionBuilderWidget::setExpressionText( const QString &expression )
663{
664 txtExpressionString->setText( expression );
665}
666
668{
669 return lblExpected->text();
670}
671
673{
674 lblExpected->setText( expected );
675 mExpectedOutputFrame->setVisible( !expected.isNull() );
676}
677
679{
680 mExpressionContext = context;
681 expressionContextUpdated();
682}
683
684void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
685{
686 QString text = expressionText();
687
688 btnClearEditor->setEnabled( ! txtExpressionString->text().isEmpty() );
689 btnSaveExpression->setEnabled( false );
690
691 mExpressionPreviewWidget->setExpressionText( text );
692}
693
695{
696 return mExpressionPreviewWidget->parserError();
697}
698
700{
701 mExpressionPreviewWidget->setVisible( isVisible );
702}
703
705{
706 return mExpressionPreviewWidget->evalError();
707}
708
710{
712 return mExpressionTreeView->model();
714}
715
717{
718 return mProject;
719}
720
722{
723 mProject = project;
724 mExpressionTreeView->setProject( project );
725}
726
728{
729 QWidget::showEvent( e );
730 txtExpressionString->setFocus();
731}
732
733void QgsExpressionBuilderWidget::createErrorMarkers( const QList<QgsExpression::ParserError> &errors )
734{
735 clearErrors();
736 for ( const QgsExpression::ParserError &error : errors )
737 {
738 int errorFirstLine = error.firstLine - 1 ;
739 int errorFirstColumn = error.firstColumn - 1;
740 int errorLastColumn = error.lastColumn - 1;
741 int errorLastLine = error.lastLine - 1;
742
743 // If we have a unknown error we just mark the point that hit the error for now
744 // until we can handle others more.
745 if ( error.errorType == QgsExpression::ParserError::Unknown )
746 {
747 errorFirstLine = errorLastLine;
748 errorFirstColumn = errorLastColumn - 1;
749 }
750 txtExpressionString->fillIndicatorRange( errorFirstLine,
751 errorFirstColumn,
752 errorLastLine,
753 errorLastColumn, error.errorType );
754 }
755}
756
757void QgsExpressionBuilderWidget::createMarkers( const QgsExpressionNode *inNode )
758{
759 switch ( inNode->nodeType() )
760 {
761 case QgsExpressionNode::NodeType::ntFunction:
762 {
763 const QgsExpressionNodeFunction *node = static_cast<const QgsExpressionNodeFunction *>( inNode );
764 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
765 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->fnIndex() );
766 int start = inNode->parserFirstColumn - 1;
767 int end = inNode->parserLastColumn - 1;
768 int start_pos = txtExpressionString->positionFromLineIndex( inNode->parserFirstLine - 1, start );
769 txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
770 if ( node->args() )
771 {
772 const QList< QgsExpressionNode * > nodeList = node->args()->list();
773 for ( QgsExpressionNode *n : nodeList )
774 {
775 createMarkers( n );
776 }
777 }
778 break;
779 }
780 case QgsExpressionNode::NodeType::ntLiteral:
781 {
782 break;
783 }
784 case QgsExpressionNode::NodeType::ntUnaryOperator:
785 {
786 const QgsExpressionNodeUnaryOperator *node = static_cast<const QgsExpressionNodeUnaryOperator *>( inNode );
787 createMarkers( node->operand() );
788 break;
789 }
790 case QgsExpressionNode::NodeType::ntBetweenOperator:
791 {
792 const QgsExpressionNodeBetweenOperator *node = static_cast<const QgsExpressionNodeBetweenOperator *>( inNode );
793 createMarkers( node->lowerBound() );
794 createMarkers( node->higherBound() );
795 break;
796 }
797 case QgsExpressionNode::NodeType::ntBinaryOperator:
798 {
799 const QgsExpressionNodeBinaryOperator *node = static_cast<const QgsExpressionNodeBinaryOperator *>( inNode );
800 createMarkers( node->opLeft() );
801 createMarkers( node->opRight() );
802 break;
803 }
804 case QgsExpressionNode::NodeType::ntColumnRef:
805 {
806 break;
807 }
808 case QgsExpressionNode::NodeType::ntInOperator:
809 {
810 const QgsExpressionNodeInOperator *node = static_cast<const QgsExpressionNodeInOperator *>( inNode );
811 if ( node->list() )
812 {
813 const QList< QgsExpressionNode * > nodeList = node->list()->list();
814 for ( QgsExpressionNode *n : nodeList )
815 {
816 createMarkers( n );
817 }
818 }
819 break;
820 }
821 case QgsExpressionNode::NodeType::ntCondition:
822 {
823 const QgsExpressionNodeCondition *node = static_cast<const QgsExpressionNodeCondition *>( inNode );
824 const QList<QgsExpressionNodeCondition::WhenThen *> conditions = node->conditions();
825 for ( QgsExpressionNodeCondition::WhenThen *cond : conditions )
826 {
827 createMarkers( cond->whenExp() );
828 createMarkers( cond->thenExp() );
829 }
830 if ( node->elseExp() )
831 {
832 createMarkers( node->elseExp() );
833 }
834 break;
835 }
836 case QgsExpressionNode::NodeType::ntIndexOperator:
837 {
838 break;
839 }
840 }
841}
842
843void QgsExpressionBuilderWidget::clearFunctionMarkers()
844{
845 int lastLine = txtExpressionString->lines() - 1;
846 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
847}
848
849void QgsExpressionBuilderWidget::clearErrors()
850{
851 int lastLine = txtExpressionString->lines() - 1;
852 // Note: -1 here doesn't seem to do the clear all like the other functions. Will need to make this a bit smarter.
853 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::Unknown );
854 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionInvalidParams );
855 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionUnknown );
856 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionWrongArgs );
857 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionNamedArgsError );
858}
859
860void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
861{
862 mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
863 mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
864}
865
866void QgsExpressionBuilderWidget::mValuesListView_doubleClicked( const QModelIndex &index )
867{
868 // Insert the item text or replace selected text
869 txtExpressionString->insertText( ' ' + index.data( Qt::UserRole + 1 ).toString() + ' ' );
870 txtExpressionString->setFocus();
871}
872
873void QgsExpressionBuilderWidget::operatorButtonClicked()
874{
875 QPushButton *button = qobject_cast<QPushButton *>( sender() );
876
877 // Insert the button text or replace selected text
878 txtExpressionString->insertText( ' ' + button->text() + ' ' );
879 txtExpressionString->setFocus();
880}
881
883{
884 QgsExpressionItem *item = mExpressionTreeView->currentItem();
885 if ( ! item )
886 {
887 return;
888 }
889
890 QgsVectorLayer *layer { contextLayer( item ) };
891 // TODO We should really return a error the user of the widget that
892 // the there is no layer set.
893 if ( !layer )
894 {
895 return;
896 }
897
898 mValueGroupBox->show();
899 fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), layer, 10 );
900}
901
903{
904 QgsExpressionItem *item = mExpressionTreeView->currentItem();
905 if ( ! item )
906 {
907 return;
908 }
909
910 QgsVectorLayer *layer { contextLayer( item ) };
911 // TODO We should really return a error the user of the widget that
912 // the there is no layer set.
913 if ( !layer )
914 {
915 return;
916 }
917
918 mValueGroupBox->show();
919 fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), layer, -1 );
920}
921
923{
924 QgsExpressionItem *item = mExpressionTreeView->currentItem();
925 if ( ! item )
926 {
927 return;
928 }
929
930 QgsVectorLayer *layer { contextLayer( item ) };
931 // TODO We should really return a error the user of the widget that
932 // the there is no layer set.
933 if ( !layer )
934 {
935 return;
936 }
937
938 mValueGroupBox->show();
939 fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), layer, 10, true );
940}
941
943{
944 QgsExpressionItem *item = mExpressionTreeView->currentItem();
945 if ( ! item )
946 {
947 return;
948 }
949
950 QgsVectorLayer *layer { contextLayer( item ) };
951 // TODO We should really return a error the user of the widget that
952 // the there is no layer set.
953 if ( !layer )
954 {
955 return;
956 }
957
958 mValueGroupBox->show();
959 fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), layer, -1, true );
960}
961
962void QgsExpressionBuilderWidget::txtPython_textChanged()
963{
964 lblAutoSave->setText( tr( "Saving…" ) );
965 if ( mAutoSave )
966 {
967 autosave();
968 }
969}
970
972{
973 // Don't auto save if not on function editor that would be silly.
974 if ( tabWidget->currentIndex() != 1 )
975 return;
976
977 QListWidgetItem *item = cmbFileNames->currentItem();
978 if ( !item )
979 return;
980
981 QString file = item->text();
982 saveFunctionFile( file );
983 lblAutoSave->setText( QStringLiteral( "Saved" ) );
984 QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect();
985 lblAutoSave->setGraphicsEffect( effect );
986 QPropertyAnimation *anim = new QPropertyAnimation( effect, "opacity" );
987 anim->setDuration( 2000 );
988 anim->setStartValue( 1.0 );
989 anim->setEndValue( 0.0 );
990 anim->setEasingCurve( QEasingCurve::OutQuad );
991 anim->start( QAbstractAnimation::DeleteWhenStopped );
992}
993
995{
996 const QString expression { this->expressionText() };
997 QgsExpressionStoreDialog dlg { expression, expression, QString( ), mExpressionTreeView->userExpressionLabels() };
998 if ( dlg.exec() == QDialog::DialogCode::Accepted )
999 {
1000 mExpressionTreeView->saveToUserExpressions( dlg.label().simplified(), dlg.expression(), dlg.helpText() );
1001 }
1002}
1003
1005{
1006 // Get the item
1007 QgsExpressionItem *item = mExpressionTreeView->currentItem();
1008 if ( !item )
1009 return;
1010
1011 // Don't handle remove if we are on a header node or the parent
1012 // is not the user group
1013 if ( item->getItemType() == QgsExpressionItem::Header ||
1014 ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) )
1015 return;
1016
1017 QgsSettings settings;
1018 QString helpText = settings.value( QStringLiteral( "user/%1/helpText" ).arg( item->text() ), "", QgsSettings::Section::Expressions ).toString();
1019 QgsExpressionStoreDialog dlg { item->text(), item->getExpressionText(), helpText, mExpressionTreeView->userExpressionLabels() };
1020
1021 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1022 {
1023 // label has changed removed the old one before adding the new one
1024 if ( dlg.isLabelModified() )
1025 {
1026 mExpressionTreeView->removeFromUserExpressions( item->text() );
1027 }
1028
1029 mExpressionTreeView->saveToUserExpressions( dlg.label().simplified(), dlg.expression(), dlg.helpText() );
1030 }
1031}
1032
1034{
1035 // Get the item
1036 QgsExpressionItem *item = mExpressionTreeView->currentItem();
1037
1038 if ( !item )
1039 return;
1040
1041 // Don't handle remove if we are on a header node or the parent
1042 // is not the user group
1043 if ( item->getItemType() == QgsExpressionItem::Header ||
1044 ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) )
1045 return;
1046
1047 if ( QMessageBox::Yes == QMessageBox::question( this, tr( "Remove Stored Expression" ),
1048 tr( "Do you really want to remove stored expressions '%1'?" ).arg( item->text() ),
1049 QMessageBox::Yes | QMessageBox::No ) )
1050 {
1051 mExpressionTreeView->removeFromUserExpressions( item->text() );
1052 }
1053
1054}
1055
1056void QgsExpressionBuilderWidget::exportUserExpressions_pressed()
1057{
1058 QgsSettings settings;
1059 QString lastSaveDir = settings.value( QStringLiteral( "lastExportExpressionsDir" ), QDir::homePath(), QgsSettings::App ).toString();
1060 QString saveFileName = QFileDialog::getSaveFileName(
1061 this,
1062 tr( "Export User Expressions" ),
1063 lastSaveDir,
1064 tr( "User expressions" ) + " (*.json)" );
1065
1066 if ( saveFileName.isEmpty() )
1067 return;
1068
1069 QFileInfo saveFileInfo( saveFileName );
1070
1071 if ( saveFileInfo.suffix().isEmpty() )
1072 {
1073 QString saveFileNameWithSuffix = saveFileName.append( ".json" );
1074 saveFileInfo = QFileInfo( saveFileNameWithSuffix );
1075 }
1076
1077 settings.setValue( QStringLiteral( "lastExportExpressionsDir" ), saveFileInfo.absolutePath(), QgsSettings::App );
1078
1079 QJsonDocument exportJson = mExpressionTreeView->exportUserExpressions();
1080 QFile jsonFile( saveFileName );
1081
1082 if ( !jsonFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
1083 QMessageBox::warning( this, tr( "Export user expressions" ), tr( "Error while creating the expressions file." ) );
1084
1085 if ( ! jsonFile.write( exportJson.toJson() ) )
1086 QMessageBox::warning( this, tr( "Export user expressions" ), tr( "Error while creating the expressions file." ) );
1087 else
1088 jsonFile.close();
1089}
1090
1091void QgsExpressionBuilderWidget::importUserExpressions_pressed()
1092{
1093 QgsSettings settings;
1094 QString lastImportDir = settings.value( QStringLiteral( "lastImportExpressionsDir" ), QDir::homePath(), QgsSettings::App ).toString();
1095 QString loadFileName = QFileDialog::getOpenFileName(
1096 this,
1097 tr( "Import User Expressions" ),
1098 lastImportDir,
1099 tr( "User expressions" ) + " (*.json)" );
1100
1101 if ( loadFileName.isEmpty() )
1102 return;
1103
1104 QFileInfo loadFileInfo( loadFileName );
1105
1106 settings.setValue( QStringLiteral( "lastImportExpressionsDir" ), loadFileInfo.absolutePath(), QgsSettings::App );
1107
1108 QFile jsonFile( loadFileName );
1109
1110 if ( !jsonFile.open( QFile::ReadOnly ) )
1111 QMessageBox::warning( this, tr( "Import User Expressions" ), tr( "Error while reading the expressions file." ) );
1112
1113 QTextStream jsonStream( &jsonFile );
1114 QString jsonString = jsonFile.readAll();
1115 jsonFile.close();
1116
1117 QJsonDocument importJson = QJsonDocument::fromJson( jsonString.toUtf8() );
1118
1119 if ( importJson.isNull() )
1120 {
1121 QMessageBox::warning( this, tr( "Import User Expressions" ), tr( "Error while reading the expressions file." ) );
1122 return;
1123 }
1124
1125 mExpressionTreeView->loadExpressionsFromJson( importJson );
1126}
1127
1128
1129const QList<QgsExpressionItem *> QgsExpressionBuilderWidget::findExpressions( const QString &label )
1130{
1131 return mExpressionTreeView->findExpressions( label );
1132}
1133
1134void QgsExpressionBuilderWidget::indicatorClicked( int line, int index, Qt::KeyboardModifiers state )
1135{
1136 if ( state & Qt::ControlModifier )
1137 {
1138 int position = txtExpressionString->positionFromLineIndex( line, index );
1139 long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID, static_cast<long int>( position ) );
1141 QString help = getFunctionHelp( func );
1142 txtHelpText->setText( help );
1143 }
1144}
1145
1146void QgsExpressionBuilderWidget::onExpressionParsed( bool state )
1147{
1148 clearErrors();
1149
1150 mExpressionValid = state;
1151 if ( state )
1152 {
1153 createMarkers( mExpressionPreviewWidget->rootNode() );
1154 }
1155 else
1156 {
1157 createErrorMarkers( mExpressionPreviewWidget->parserErrors() );
1158 }
1159}
1160
1161QString QgsExpressionBuilderWidget::helpStylesheet() const
1162{
1163 //start with default QGIS report style
1164 QString style = QgsApplication::reportStyleSheet();
1165
1166 //add some tweaks
1167 style += " .functionname {color: #0a6099; font-weight: bold;} "
1168 " .argument {font-family: monospace; color: #bf0c0c; font-style: italic; } "
1169 " td.argument { padding-right: 10px; }";
1170
1171 return style;
1172}
1173
1174QString QgsExpressionBuilderWidget::loadFunctionHelp( QgsExpressionItem *expressionItem )
1175{
1176 if ( !expressionItem )
1177 return QString();
1178
1179 QString helpContents = expressionItem->getHelpText();
1180
1181 // Return the function help that is set for the function if there is one.
1182 if ( helpContents.isEmpty() )
1183 {
1184 QString name = expressionItem->data( Qt::UserRole ).toString();
1185
1186 if ( expressionItem->getItemType() == QgsExpressionItem::Field )
1187 helpContents = QgsExpression::helpText( QStringLiteral( "Field" ) );
1188 else
1189 helpContents = QgsExpression::helpText( name );
1190 }
1191
1192 return "<head><style>" + helpStylesheet() + "</style></head><body>" + helpContents + "</body>";
1193}
1194
1195
1196// *************
1197// Menu provider
1198
1199QMenu *QgsExpressionBuilderWidget::ExpressionTreeMenuProvider::createContextMenu( QgsExpressionItem *item )
1200{
1201 QMenu *menu = nullptr;
1202 QgsVectorLayer *layer = mExpressionBuilderWidget->layer();
1203 if ( item->getItemType() == QgsExpressionItem::Field && layer )
1204 {
1205 menu = new QMenu( mExpressionBuilderWidget );
1206 menu->addAction( tr( "Load First 10 Unique Values" ), mExpressionBuilderWidget, &QgsExpressionBuilderWidget::loadSampleValues );
1207 menu->addAction( tr( "Load All Unique Values" ), mExpressionBuilderWidget, &QgsExpressionBuilderWidget::loadAllValues );
1208
1209 if ( formatterCanProvideAvailableValues( layer, item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString() ) )
1210 {
1211 menu->addAction( tr( "Load First 10 Unique Used Values" ), mExpressionBuilderWidget, &QgsExpressionBuilderWidget::loadSampleUsedValues );
1212 menu->addAction( tr( "Load All Unique Used Values" ), mExpressionBuilderWidget, &QgsExpressionBuilderWidget::loadAllUsedValues );
1213 }
1214 }
1215 return menu;
1216}
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 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:631
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:349
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:105
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:476
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:62
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.
#define str(x)
Definition: qgis.cpp:37
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:3499
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:3498
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:2841
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.