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