QGIS API Documentation 3.28.11-Firenze (exported)
Loading...
Searching...
No Matches
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#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
389 myFileStream.setCodec( "UTF-8" );
390#endif
391 myFileStream << txtPython->text() << Qt::endl;
392 myFile.close();
393 }
394}
395
397{
398 mFunctionsPath = path;
399 QDir dir( path );
400 dir.setNameFilters( QStringList() << QStringLiteral( "*.py" ) );
401 QStringList files = dir.entryList( QDir::Files );
402 cmbFileNames->clear();
403 const auto constFiles = files;
404 for ( const QString &name : constFiles )
405 {
406 QFileInfo info( mFunctionsPath + QDir::separator() + name );
407 if ( info.baseName() == QLatin1String( "__init__" ) ) continue;
408 QListWidgetItem *item = new QListWidgetItem( QgsApplication::getThemeIcon( QStringLiteral( "console/iconTabEditorConsole.svg" ) ), info.baseName() );
409 cmbFileNames->addItem( item );
410 }
411 if ( !cmbFileNames->currentItem() )
412 {
413 cmbFileNames->setCurrentRow( 0 );
414 }
415
416 if ( cmbFileNames->count() == 0 )
417 {
418 // Create default sample entry.
419 newFunctionFile( QStringLiteral( "default" ) );
420 txtPython->setText( QStringLiteral( "'''\n#Sample custom function file\n"
421 "#(uncomment to use and customize or Add button to create a new file) \n%1 \n '''" ).arg( txtPython->text() ) );
422 saveFunctionFile( QStringLiteral( "default" ) );
423 }
424}
425
426void QgsExpressionBuilderWidget::newFunctionFile( const QString &fileName )
427{
428 QList<QListWidgetItem *> items = cmbFileNames->findItems( fileName, Qt::MatchExactly );
429 if ( !items.isEmpty() )
430 return;
431
432 QListWidgetItem *item = new QListWidgetItem( QgsApplication::getThemeIcon( QStringLiteral( "console/iconTabEditorConsole.svg" ) ), fileName );
433 cmbFileNames->insertItem( 0, item );
434 cmbFileNames->setCurrentRow( 0 );
435
436 QString templatetxt;
437 QgsPythonRunner::eval( QStringLiteral( "qgis.user.default_expression_template" ), templatetxt );
438 txtPython->setText( templatetxt );
439 saveFunctionFile( fileName );
440}
441
442void QgsExpressionBuilderWidget::btnNewFile_pressed()
443{
444 bool ok;
445 QString text = QInputDialog::getText( this, tr( "New File" ),
446 tr( "New file name:" ), QLineEdit::Normal,
447 QString(), &ok );
448 if ( ok && !text.isEmpty() )
449 {
450 newFunctionFile( text );
451 }
452}
453
454void QgsExpressionBuilderWidget::btnRemoveFile_pressed()
455{
456 if ( QMessageBox::question( this, tr( "Remove File" ),
457 tr( "Are you sure you want to remove current functions file?" ),
458 QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No )
459 return;
460
461 int currentRow = cmbFileNames->currentRow();
462 QString fileName = cmbFileNames->currentItem()->text();
463 if ( QFile::remove( mFunctionsPath + QDir::separator() + fileName.append( ".py" ) ) )
464 {
465 {
466 QListWidgetItem *itemToRemove = whileBlocking( cmbFileNames )->takeItem( currentRow );
467 delete itemToRemove;
468 }
469
470 if ( cmbFileNames->count() > 0 )
471 {
472 whileBlocking( cmbFileNames )->setCurrentRow( currentRow > 0 ? currentRow - 1 : 0 );
473 loadCodeFromFile( mFunctionsPath + QDir::separator() + cmbFileNames->currentItem()->text() );
474 }
475 else
476 {
477 btnRemoveFile->setEnabled( false );
478 txtPython->clear();
479 }
480 }
481 else
482 {
483 QMessageBox::warning( this, tr( "Remove file" ), tr( "Failed to remove function file '%1'." ).arg( fileName ) );
484 }
485}
486
487void QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem )
488{
489 if ( lastitem )
490 {
491 QString filename = lastitem->text();
492 saveFunctionFile( filename );
493 }
494 QString path = mFunctionsPath + QDir::separator() + item->text();
495 loadCodeFromFile( path );
496}
497
499{
500 if ( !path.endsWith( QLatin1String( ".py" ) ) )
501 path.append( ".py" );
502
503 txtPython->loadScript( path );
504}
505
507{
508 txtPython->setText( code );
509}
510
511void QgsExpressionBuilderWidget::insertExpressionText( const QString &text )
512{
513 // Insert the expression text or replace selected text
514 txtExpressionString->insertText( text );
515 txtExpressionString->setFocus();
516}
517
518void QgsExpressionBuilderWidget::loadFieldsAndValues( const QMap<QString, QStringList> &fieldValues )
519{
520 Q_UNUSED( fieldValues )
521 // This is not maintained and setLayer() should be used instead.
522}
523
524void QgsExpressionBuilderWidget::fillFieldValues( const QString &fieldName, QgsVectorLayer *layer, int countLimit, bool forceUsedValues )
525{
526 // TODO We should really return a error the user of the widget that
527 // the there is no layer set.
528 if ( !layer )
529 return;
530
531 // TODO We should thread this so that we don't hold the user up if the layer is massive.
532
533 const QgsFields fields = layer->fields();
534 int fieldIndex = fields.lookupField( fieldName );
535
536 if ( fieldIndex < 0 )
537 return;
538
539 const QgsEditorWidgetSetup setup = fields.at( fieldIndex ).editorWidgetSetup();
541
542 QVariantList values;
543 if ( cbxValuesInUse->isVisible() && !cbxValuesInUse->isChecked() && !forceUsedValues )
544 {
545 QgsFieldFormatterContext fieldFormatterContext;
546 fieldFormatterContext.setProject( mProject );
547 values = formatter->availableValues( setup.config(), countLimit, fieldFormatterContext );
548 }
549 else
550 {
551 values = qgis::setToList( layer->uniqueValues( fieldIndex, countLimit ) );
552 }
553 std::sort( values.begin(), values.end() );
554
555 mValuesModel->clear();
556 for ( const QVariant &value : std::as_const( values ) )
557 {
558 QString strValue;
559 bool forceRepresentedValue = false;
560 if ( QgsVariantUtils::isNull( value ) )
561 strValue = QStringLiteral( "NULL" );
562 else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong )
563 strValue = value.toString();
564 else if ( value.type() == QVariant::StringList )
565 {
566 QString result;
567 const QStringList strList = value.toStringList();
568 for ( QString str : strList )
569 {
570 if ( !result.isEmpty() )
571 result.append( QStringLiteral( ", " ) );
572
573 result.append( '\'' + str.replace( '\'', QLatin1String( "''" ) ) + '\'' );
574 }
575 strValue = QStringLiteral( "array(%1)" ).arg( result );
576 forceRepresentedValue = true;
577 }
578 else if ( value.type() == QVariant::List )
579 {
580 QString result;
581 const QList list = value.toList();
582 for ( const QVariant &item : list )
583 {
584 if ( !result.isEmpty() )
585 result.append( QStringLiteral( ", " ) );
586
587 result.append( item.toString() );
588 }
589 strValue = QStringLiteral( "array(%1)" ).arg( result );
590 forceRepresentedValue = true;
591 }
592 else
593 strValue = '\'' + value.toString().replace( '\'', QLatin1String( "''" ) ) + '\'';
594
595 QString representedValue = formatter->representValue( layer, fieldIndex, setup.config(), QVariant(), value );
596 if ( forceRepresentedValue || representedValue != value.toString() )
597 representedValue = representedValue + QStringLiteral( " [" ) + strValue + ']';
598
599 QStandardItem *item = new QStandardItem( representedValue );
600 item->setData( strValue );
601 mValuesModel->appendRow( item );
602 }
603}
604
605QString QgsExpressionBuilderWidget::getFunctionHelp( QgsExpressionFunction *function )
606{
607 if ( !function )
608 return QString();
609
610 QString helpContents = QgsExpression::helpText( function->name() );
611
612 return QStringLiteral( "<head><style>" ) + helpStylesheet() + QStringLiteral( "</style></head><body>" ) + helpContents + QStringLiteral( "</body>" );
613
614}
615
616
617
619{
620 return mExpressionValid;
621}
622
623void QgsExpressionBuilderWidget::saveToRecent( const QString &collection )
624{
625 mExpressionTreeView->saveToRecent( expressionText(), collection );
626}
627
628void QgsExpressionBuilderWidget::loadRecent( const QString &collection )
629{
630 mExpressionTreeView->loadRecent( collection );
631}
632
634{
635 return mExpressionTreeView;
636}
637
638// this is potentially very slow if there are thousands of user expressions, every time entire cleanup and load
640{
641 mExpressionTreeView->loadUserExpressions();
642}
643
644void QgsExpressionBuilderWidget::saveToUserExpressions( const QString &label, const QString &expression, const QString &helpText )
645{
646 mExpressionTreeView->saveToUserExpressions( label, expression, helpText );
647}
648
650{
651 mExpressionTreeView->removeFromUserExpressions( label );
652}
653
654
656{
657 mExpressionPreviewWidget->setGeomCalculator( da );
658}
659
661{
662 return txtExpressionString->text();
663}
664
665void QgsExpressionBuilderWidget::setExpressionText( const QString &expression )
666{
667 txtExpressionString->setText( expression );
668}
669
671{
672 return lblExpected->text();
673}
674
676{
677 lblExpected->setText( expected );
678 mExpectedOutputFrame->setVisible( !expected.isNull() );
679}
680
682{
683 mExpressionContext = context;
684 expressionContextUpdated();
685}
686
687void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
688{
689 QString text = expressionText();
690
691 btnClearEditor->setEnabled( ! txtExpressionString->text().isEmpty() );
692 btnSaveExpression->setEnabled( false );
693
694 mExpressionPreviewWidget->setExpressionText( text );
695}
696
698{
699 return mExpressionPreviewWidget->parserError();
700}
701
703{
704 mExpressionPreviewWidget->setVisible( isVisible );
705}
706
708{
709 return mExpressionPreviewWidget->evalError();
710}
711
713{
715 return mExpressionTreeView->model();
717}
718
720{
721 return mProject;
722}
723
725{
726 mProject = project;
727 mExpressionTreeView->setProject( project );
728}
729
731{
732 QWidget::showEvent( e );
733 txtExpressionString->setFocus();
734}
735
736void QgsExpressionBuilderWidget::createErrorMarkers( const QList<QgsExpression::ParserError> &errors )
737{
738 clearErrors();
739 for ( const QgsExpression::ParserError &error : errors )
740 {
741 int errorFirstLine = error.firstLine - 1 ;
742 int errorFirstColumn = error.firstColumn - 1;
743 int errorLastColumn = error.lastColumn - 1;
744 int errorLastLine = error.lastLine - 1;
745
746 // If we have a unknown error we just mark the point that hit the error for now
747 // until we can handle others more.
748 if ( error.errorType == QgsExpression::ParserError::Unknown )
749 {
750 errorFirstLine = errorLastLine;
751 errorFirstColumn = errorLastColumn - 1;
752 }
753 txtExpressionString->fillIndicatorRange( errorFirstLine,
754 errorFirstColumn,
755 errorLastLine,
756 errorLastColumn, error.errorType );
757 }
758}
759
760void QgsExpressionBuilderWidget::createMarkers( const QgsExpressionNode *inNode )
761{
762 switch ( inNode->nodeType() )
763 {
765 {
766 const QgsExpressionNodeFunction *node = static_cast<const QgsExpressionNodeFunction *>( inNode );
767 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
768 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->fnIndex() );
769 int start = inNode->parserFirstColumn - 1;
770 int end = inNode->parserLastColumn - 1;
771 int start_pos = txtExpressionString->positionFromLineIndex( inNode->parserFirstLine - 1, start );
772 txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
773 if ( node->args() )
774 {
775 const QList< QgsExpressionNode * > nodeList = node->args()->list();
776 for ( QgsExpressionNode *n : nodeList )
777 {
778 createMarkers( n );
779 }
780 }
781 break;
782 }
784 {
785 break;
786 }
788 {
789 const QgsExpressionNodeUnaryOperator *node = static_cast<const QgsExpressionNodeUnaryOperator *>( inNode );
790 createMarkers( node->operand() );
791 break;
792 }
794 {
795 const QgsExpressionNodeBetweenOperator *node = static_cast<const QgsExpressionNodeBetweenOperator *>( inNode );
796 createMarkers( node->lowerBound() );
797 createMarkers( node->higherBound() );
798 break;
799 }
801 {
802 const QgsExpressionNodeBinaryOperator *node = static_cast<const QgsExpressionNodeBinaryOperator *>( inNode );
803 createMarkers( node->opLeft() );
804 createMarkers( node->opRight() );
805 break;
806 }
808 {
809 break;
810 }
812 {
813 const QgsExpressionNodeInOperator *node = static_cast<const QgsExpressionNodeInOperator *>( inNode );
814 if ( node->list() )
815 {
816 const QList< QgsExpressionNode * > nodeList = node->list()->list();
817 for ( QgsExpressionNode *n : nodeList )
818 {
819 createMarkers( n );
820 }
821 }
822 break;
823 }
825 {
826 const QgsExpressionNodeCondition *node = static_cast<const QgsExpressionNodeCondition *>( inNode );
827 const QList<QgsExpressionNodeCondition::WhenThen *> conditions = node->conditions();
828 for ( QgsExpressionNodeCondition::WhenThen *cond : conditions )
829 {
830 createMarkers( cond->whenExp() );
831 createMarkers( cond->thenExp() );
832 }
833 if ( node->elseExp() )
834 {
835 createMarkers( node->elseExp() );
836 }
837 break;
838 }
840 {
841 break;
842 }
843 }
844}
845
846void QgsExpressionBuilderWidget::clearFunctionMarkers()
847{
848 int lastLine = txtExpressionString->lines() - 1;
849 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
850}
851
852void QgsExpressionBuilderWidget::clearErrors()
853{
854 int lastLine = txtExpressionString->lines() - 1;
855 // Note: -1 here doesn't seem to do the clear all like the other functions. Will need to make this a bit smarter.
856 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::Unknown );
857 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionInvalidParams );
858 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionUnknown );
859 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionWrongArgs );
860 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length(), QgsExpression::ParserError::FunctionNamedArgsError );
861}
862
863void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
864{
865 mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
866 mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
867}
868
869void QgsExpressionBuilderWidget::mValuesListView_doubleClicked( const QModelIndex &index )
870{
871 // Insert the item text or replace selected text
872 txtExpressionString->insertText( ' ' + index.data( Qt::UserRole + 1 ).toString() + ' ' );
873 txtExpressionString->setFocus();
874}
875
876void QgsExpressionBuilderWidget::operatorButtonClicked()
877{
878 QPushButton *button = qobject_cast<QPushButton *>( sender() );
879
880 // Insert the button text or replace selected text
881 txtExpressionString->insertText( ' ' + button->text() + ' ' );
882 txtExpressionString->setFocus();
883}
884
886{
887 QgsExpressionItem *item = mExpressionTreeView->currentItem();
888 if ( ! item )
889 {
890 return;
891 }
892
893 QgsVectorLayer *layer { contextLayer( item ) };
894 // TODO We should really return a error the user of the widget that
895 // the there is no layer set.
896 if ( !layer )
897 {
898 return;
899 }
900
901 mValueGroupBox->show();
902 fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), layer, 10 );
903}
904
906{
907 QgsExpressionItem *item = mExpressionTreeView->currentItem();
908 if ( ! item )
909 {
910 return;
911 }
912
913 QgsVectorLayer *layer { contextLayer( item ) };
914 // TODO We should really return a error the user of the widget that
915 // the there is no layer set.
916 if ( !layer )
917 {
918 return;
919 }
920
921 mValueGroupBox->show();
922 fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), layer, -1 );
923}
924
926{
927 QgsExpressionItem *item = mExpressionTreeView->currentItem();
928 if ( ! item )
929 {
930 return;
931 }
932
933 QgsVectorLayer *layer { contextLayer( item ) };
934 // TODO We should really return a error the user of the widget that
935 // the there is no layer set.
936 if ( !layer )
937 {
938 return;
939 }
940
941 mValueGroupBox->show();
942 fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), layer, 10, true );
943}
944
946{
947 QgsExpressionItem *item = mExpressionTreeView->currentItem();
948 if ( ! item )
949 {
950 return;
951 }
952
953 QgsVectorLayer *layer { contextLayer( item ) };
954 // TODO We should really return a error the user of the widget that
955 // the there is no layer set.
956 if ( !layer )
957 {
958 return;
959 }
960
961 mValueGroupBox->show();
962 fillFieldValues( item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString(), layer, -1, true );
963}
964
965void QgsExpressionBuilderWidget::txtPython_textChanged()
966{
967 lblAutoSave->setText( tr( "Saving…" ) );
968 if ( mAutoSave )
969 {
970 autosave();
971 }
972}
973
975{
976 // Don't auto save if not on function editor that would be silly.
977 if ( tabWidget->currentIndex() != 1 )
978 return;
979
980 QListWidgetItem *item = cmbFileNames->currentItem();
981 if ( !item )
982 return;
983
984 QString file = item->text();
985 saveFunctionFile( file );
986 lblAutoSave->setText( QStringLiteral( "Saved" ) );
987 QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect();
988 lblAutoSave->setGraphicsEffect( effect );
989 QPropertyAnimation *anim = new QPropertyAnimation( effect, "opacity" );
990 anim->setDuration( 2000 );
991 anim->setStartValue( 1.0 );
992 anim->setEndValue( 0.0 );
993 anim->setEasingCurve( QEasingCurve::OutQuad );
994 anim->start( QAbstractAnimation::DeleteWhenStopped );
995}
996
998{
999 const QString expression { this->expressionText() };
1000 QgsExpressionStoreDialog dlg { expression, expression, QString( ), mExpressionTreeView->userExpressionLabels() };
1001 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1002 {
1003 mExpressionTreeView->saveToUserExpressions( dlg.label().simplified(), dlg.expression(), dlg.helpText() );
1004 }
1005}
1006
1008{
1009 // Get the item
1010 QgsExpressionItem *item = mExpressionTreeView->currentItem();
1011 if ( !item )
1012 return;
1013
1014 // Don't handle remove if we are on a header node or the parent
1015 // is not the user group
1016 if ( item->getItemType() == QgsExpressionItem::Header ||
1017 ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) )
1018 return;
1019
1020 QgsSettings settings;
1021 QString helpText = settings.value( QStringLiteral( "user/%1/helpText" ).arg( item->text() ), "", QgsSettings::Section::Expressions ).toString();
1022 QgsExpressionStoreDialog dlg { item->text(), item->getExpressionText(), helpText, mExpressionTreeView->userExpressionLabels() };
1023
1024 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1025 {
1026 // label has changed removed the old one before adding the new one
1027 if ( dlg.isLabelModified() )
1028 {
1029 mExpressionTreeView->removeFromUserExpressions( item->text() );
1030 }
1031
1032 mExpressionTreeView->saveToUserExpressions( dlg.label().simplified(), dlg.expression(), dlg.helpText() );
1033 }
1034}
1035
1037{
1038 // Get the item
1039 QgsExpressionItem *item = mExpressionTreeView->currentItem();
1040
1041 if ( !item )
1042 return;
1043
1044 // Don't handle remove if we are on a header node or the parent
1045 // is not the user group
1046 if ( item->getItemType() == QgsExpressionItem::Header ||
1047 ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) )
1048 return;
1049
1050 if ( QMessageBox::Yes == QMessageBox::question( this, tr( "Remove Stored Expression" ),
1051 tr( "Do you really want to remove stored expressions '%1'?" ).arg( item->text() ),
1052 QMessageBox::Yes | QMessageBox::No ) )
1053 {
1054 mExpressionTreeView->removeFromUserExpressions( item->text() );
1055 }
1056
1057}
1058
1059void QgsExpressionBuilderWidget::exportUserExpressions_pressed()
1060{
1061 QgsSettings settings;
1062 QString lastSaveDir = settings.value( QStringLiteral( "lastExportExpressionsDir" ), QDir::homePath(), QgsSettings::App ).toString();
1063 QString saveFileName = QFileDialog::getSaveFileName(
1064 this,
1065 tr( "Export User Expressions" ),
1066 lastSaveDir,
1067 tr( "User expressions" ) + " (*.json)" );
1068
1069 if ( saveFileName.isEmpty() )
1070 return;
1071
1072 QFileInfo saveFileInfo( saveFileName );
1073
1074 if ( saveFileInfo.suffix().isEmpty() )
1075 {
1076 QString saveFileNameWithSuffix = saveFileName.append( ".json" );
1077 saveFileInfo = QFileInfo( saveFileNameWithSuffix );
1078 }
1079
1080 settings.setValue( QStringLiteral( "lastExportExpressionsDir" ), saveFileInfo.absolutePath(), QgsSettings::App );
1081
1082 QJsonDocument exportJson = mExpressionTreeView->exportUserExpressions();
1083 QFile jsonFile( saveFileName );
1084
1085 if ( !jsonFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
1086 QMessageBox::warning( this, tr( "Export user expressions" ), tr( "Error while creating the expressions file." ) );
1087
1088 if ( ! jsonFile.write( exportJson.toJson() ) )
1089 QMessageBox::warning( this, tr( "Export user expressions" ), tr( "Error while creating the expressions file." ) );
1090 else
1091 jsonFile.close();
1092}
1093
1094void QgsExpressionBuilderWidget::importUserExpressions_pressed()
1095{
1096 QgsSettings settings;
1097 QString lastImportDir = settings.value( QStringLiteral( "lastImportExpressionsDir" ), QDir::homePath(), QgsSettings::App ).toString();
1098 QString loadFileName = QFileDialog::getOpenFileName(
1099 this,
1100 tr( "Import User Expressions" ),
1101 lastImportDir,
1102 tr( "User expressions" ) + " (*.json)" );
1103
1104 if ( loadFileName.isEmpty() )
1105 return;
1106
1107 QFileInfo loadFileInfo( loadFileName );
1108
1109 settings.setValue( QStringLiteral( "lastImportExpressionsDir" ), loadFileInfo.absolutePath(), QgsSettings::App );
1110
1111 QFile jsonFile( loadFileName );
1112
1113 if ( !jsonFile.open( QFile::ReadOnly ) )
1114 QMessageBox::warning( this, tr( "Import User Expressions" ), tr( "Error while reading the expressions file." ) );
1115
1116 QTextStream jsonStream( &jsonFile );
1117 QString jsonString = jsonFile.readAll();
1118 jsonFile.close();
1119
1120 QJsonDocument importJson = QJsonDocument::fromJson( jsonString.toUtf8() );
1121
1122 if ( importJson.isNull() )
1123 {
1124 QMessageBox::warning( this, tr( "Import User Expressions" ), tr( "Error while reading the expressions file." ) );
1125 return;
1126 }
1127
1128 mExpressionTreeView->loadExpressionsFromJson( importJson );
1129}
1130
1131
1132const QList<QgsExpressionItem *> QgsExpressionBuilderWidget::findExpressions( const QString &label )
1133{
1134 return mExpressionTreeView->findExpressions( label );
1135}
1136
1137void QgsExpressionBuilderWidget::indicatorClicked( int line, int index, Qt::KeyboardModifiers state )
1138{
1139 if ( state & Qt::ControlModifier )
1140 {
1141 int position = txtExpressionString->positionFromLineIndex( line, index );
1142 long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID, static_cast<long int>( position ) );
1144 QString help = getFunctionHelp( func );
1145 txtHelpText->setText( help );
1146 }
1147}
1148
1149void QgsExpressionBuilderWidget::onExpressionParsed( bool state )
1150{
1151 clearErrors();
1152
1153 mExpressionValid = state;
1154 if ( state )
1155 {
1156 createMarkers( mExpressionPreviewWidget->rootNode() );
1157 }
1158 else
1159 {
1160 createErrorMarkers( mExpressionPreviewWidget->parserErrors() );
1161 }
1162}
1163
1164QString QgsExpressionBuilderWidget::helpStylesheet() const
1165{
1166 //start with default QGIS report style
1167 QString style = QgsApplication::reportStyleSheet();
1168
1169 //add some tweaks
1170 style += " .functionname {color: #0a6099; font-weight: bold;} "
1171 " .argument {font-family: monospace; color: #bf0c0c; font-style: italic; } "
1172 " td.argument { padding-right: 10px; }";
1173
1174 return style;
1175}
1176
1177QString QgsExpressionBuilderWidget::loadFunctionHelp( QgsExpressionItem *expressionItem )
1178{
1179 if ( !expressionItem )
1180 return QString();
1181
1182 QString helpContents = expressionItem->getHelpText();
1183
1184 // Return the function help that is set for the function if there is one.
1185 if ( helpContents.isEmpty() )
1186 {
1187 QString name = expressionItem->data( Qt::UserRole ).toString();
1188
1189 if ( expressionItem->getItemType() == QgsExpressionItem::Field )
1190 helpContents = QgsExpression::helpText( QStringLiteral( "Field" ) );
1191 else
1192 helpContents = QgsExpression::helpText( name );
1193 }
1194
1195 return "<head><style>" + helpStylesheet() + "</style></head><body>" + helpContents + "</body>";
1196}
1197
1198
1199// *************
1200// Menu provider
1201
1202QMenu *QgsExpressionBuilderWidget::ExpressionTreeMenuProvider::createContextMenu( QgsExpressionItem *item )
1203{
1204 QMenu *menu = nullptr;
1205 QgsVectorLayer *layer = mExpressionBuilderWidget->layer();
1206 if ( item->getItemType() == QgsExpressionItem::Field && layer )
1207 {
1208 menu = new QMenu( mExpressionBuilderWidget );
1209 menu->addAction( tr( "Load First 10 Unique Values" ), mExpressionBuilderWidget, &QgsExpressionBuilderWidget::loadSampleValues );
1210 menu->addAction( tr( "Load All Unique Values" ), mExpressionBuilderWidget, &QgsExpressionBuilderWidget::loadAllValues );
1211
1212 if ( formatterCanProvideAvailableValues( layer, item->data( QgsExpressionItem::ITEM_NAME_ROLE ).toString() ) )
1213 {
1214 menu->addAction( tr( "Load First 10 Unique Used Values" ), mExpressionBuilderWidget, &QgsExpressionBuilderWidget::loadSampleUsedValues );
1215 menu->addAction( tr( "Load All Unique Used Values" ), mExpressionBuilderWidget, &QgsExpressionBuilderWidget::loadAllUsedValues );
1216 }
1217 }
1218 return menu;
1219}
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.
@ ntBetweenOperator
Between operator.
@ ntIndexOperator
Index operator.
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.
void loadUserExpressions()
Loads the user expressions.
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:672
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).
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:104
static QgsProject * instance()
Returns the QgsProject singleton instance.
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:3061
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:3060
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:2453
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.