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