44#include <QGraphicsOpacityEffect>
46#include <QJsonDocument>
50#include <QPropertyAnimation>
53#include <QVersionNumber>
55#include "moc_qgsexpressionbuilderwidget.cpp"
57using namespace Qt::StringLiterals;
65 if ( fieldIndex != -1 )
70 return ( formatter->
flags() & QgsFieldFormatter::CanProvideAvailableValues );
85 QVBoxLayout *vl =
new QVBoxLayout();
86 vl->setContentsMargins( 0, 0, 0, 0 );
87 vl->addWidget( codeEditorWidget );
88 mExpressionEditorContainer->setLayout( vl );
90 connect( btnRun, &QToolButton::pressed,
this, &QgsExpressionBuilderWidget::btnRun_pressed );
91 connect( btnNewFile, &QPushButton::clicked,
this, &QgsExpressionBuilderWidget::btnNewFile_pressed );
92 connect( btnRemoveFile, &QPushButton::clicked,
this, &QgsExpressionBuilderWidget::btnRemoveFile_pressed );
93 connect( cmbFileNames, &QListWidget::currentItemChanged,
this, &QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged );
94 connect( txtExpressionString, &QgsCodeEditorExpression::textChanged,
this, &QgsExpressionBuilderWidget::txtExpressionString_textChanged );
95 connect( txtPython, &QgsCodeEditorPython::textChanged,
this, &QgsExpressionBuilderWidget::txtPython_textChanged );
96 connect( txtSearchEditValues, &QgsFilterLineEdit::textChanged,
this, &QgsExpressionBuilderWidget::txtSearchEditValues_textChanged );
97 connect( mValuesListView, &QListView::doubleClicked,
this, &QgsExpressionBuilderWidget::mValuesListView_doubleClicked );
101 connect( btnImportExpressions, &QToolButton::clicked,
this, &QgsExpressionBuilderWidget::importUserExpressions_pressed );
102 connect( btnExportExpressions, &QToolButton::clicked,
this, &QgsExpressionBuilderWidget::exportUserExpressions_pressed );
103 connect( btnClearEditor, &QToolButton::clicked, txtExpressionString, &QgsCodeEditorExpression::clear );
116 mExpressionTreeMenuProvider =
new ExpressionTreeMenuProvider(
this );
117 mExpressionTreeView->setMenuProvider( mExpressionTreeMenuProvider );
119 txtHelpText->setOpenExternalLinks(
true );
120 mValueGroupBox->hide();
137 const auto pushButtons { mOperatorsGroupBox->findChildren<QPushButton *>() };
138 for ( QPushButton *button : pushButtons )
140 connect( button, &QAbstractButton::clicked,
this, &QgsExpressionBuilderWidget::operatorButtonClicked );
143 connect( btnCommentLinePushButton, &QAbstractButton::clicked,
this, &QgsExpressionBuilderWidget::commentLinesClicked );
145 txtSearchEdit->setShowSearchIcon(
true );
146 txtSearchEdit->setPlaceholderText( tr(
"Search…" ) );
148 mValuesModel = std::make_unique<QStandardItemModel>();
149 mProxyValues = std::make_unique<QSortFilterProxyModel>();
150 mProxyValues->setSourceModel( mValuesModel.get() );
151 mValuesListView->setModel( mProxyValues.get() );
152 txtSearchEditValues->setShowSearchIcon(
true );
153 txtSearchEditValues->setPlaceholderText( tr(
"Search…" ) );
155 editorSplit->setSizes( QList<int>( { 175, 300 } ) );
157 functionsplit->setCollapsible( 0,
false );
158 connect( mShowHelpButton, &QPushButton::clicked,
this, [
this]() {
159 functionsplit->setSizes( QList<int>( { mOperationListGroup->width() - mHelpAndValuesWidget->minimumWidth(), mHelpAndValuesWidget->minimumWidth() } ) );
160 mShowHelpButton->setEnabled(
false );
162 connect( functionsplit, &QSplitter::splitterMoved,
this, [
this](
int,
int ) { mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 ); } );
165 splitter->restoreState( settings.value( u
"Windows/QgsExpressionBuilderWidget/splitter"_s ).toByteArray() );
166 editorSplit->restoreState( settings.value( u
"Windows/QgsExpressionBuilderWidget/editorsplitter"_s ).toByteArray() );
167 functionsplit->restoreState( settings.value( u
"Windows/QgsExpressionBuilderWidget/functionsplitter"_s ).toByteArray() );
168 mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
174 btnRemoveFile->setEnabled( cmbFileNames->count() > 0 );
181 txtExpressionString->setWrapMode( QsciScintilla::WrapWord );
182 lblAutoSave->
clear();
189#if defined( QSCINTILLA_VERSION ) && QSCINTILLA_VERSION >= 0x20a00
196 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::red ), -1 );
197 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::red ), -1 );
198 txtExpressionString->setIndicatorOutlineColor( QColor( Qt::red ), -1 );
201 txtExpressionString->indicatorDefine( QgsCodeEditor::HiddenIndicator, FUNCTION_MARKER_ID );
202 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
203 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
204 txtExpressionString->setIndicatorHoverStyle( QgsCodeEditor::DotsIndicator, FUNCTION_MARKER_ID );
206 connect( txtExpressionString, &QgsCodeEditorExpression::indicatorClicked,
this, &QgsExpressionBuilderWidget::indicatorClicked );
207 txtExpressionString->setAutoCompletionCaseSensitivity(
false );
208 txtExpressionString->setAutoCompletionSource( QsciScintilla::AcsAPIs );
209 txtExpressionString->setCallTipsVisible( 0 );
212 mFunctionBuilderHelp->setLineNumbersVisible(
false );
213 mFunctionBuilderHelp->setFoldingVisible(
false );
214 mFunctionBuilderHelp->setEdgeMode( QsciScintilla::EdgeNone );
215 mFunctionBuilderHelp->setEdgeColumn( 0 );
216 mFunctionBuilderHelp->setReadOnly(
true );
217 mFunctionBuilderHelp->setText( tr(
"\"\"\"Define a new function using the @qgsfunction decorator.\n\
219 Besides its normal arguments, the function may specify the following arguments in its signature\n\
220 Those will not need to be specified when calling the function, but will be automatically injected \n\
222 : param feature: The current feature\n\
223 : param parent: The QgsExpression object\n\
224 : param context: ``QgsExpressionContext`` object, that gives access to various additional information like\n\
225 expression variables. E.g. ``context.variable( 'layer_id' )``\n\
226 : returns: The result of the expression.\n\
230 The @qgsfunction decorator accepts the following arguments:\n\
233 : param group: The name of the group under which this expression function will\n\
235 : param handlesnull: Set this to True if your function has custom handling for NULL values.\n\
236 If False, the result will always be NULL as soon as any parameter is NULL.\n\
237 Defaults to False.\n\
238 : param usesgeometry : Set this to True if your function requires access to\n\
239 feature.geometry(). Defaults to False.\n\
240 : param referenced_columns: An array of attribute names that are required to run\n\
241 this function. Defaults to [QgsFeatureRequest.ALL_ATTRIBUTES].\n\
242 : param params_as_list : Set this to True to pass the function parameters as a list. Can be used to mimic \n\
243 behavior before 3.32, when args was not \"auto\". Defaults to False.\n\
251 settings.
setValue( u
"Windows/QgsExpressionBuilderWidget/splitter"_s, splitter->saveState() );
252 settings.
setValue( u
"Windows/QgsExpressionBuilderWidget/editorsplitter"_s, editorSplit->saveState() );
253 settings.
setValue( u
"Windows/QgsExpressionBuilderWidget/functionsplitter"_s, functionsplit->saveState() );
254 delete mExpressionTreeMenuProvider;
262 mExpressionTreeView->loadRecent( recentCollection );
265 mExpressionTreeView->loadUserExpressions();
270 init( context, recentCollection, flags );
276 init( context, recentCollection, flags );
277 mExpressionTreeView->loadFieldNames( fields );
284 mExpressionTreeView->setLayer( mLayer );
285 mExpressionPreviewWidget->setLayer( mLayer );
292 expressionContextUpdated();
293 txtExpressionString->setFields( mLayer->fields() );
297void QgsExpressionBuilderWidget::expressionContextUpdated()
300 mExpressionTreeView->setExpressionContext( mExpressionContext );
301 mExpressionPreviewWidget->setExpressionContext( mExpressionContext );
309void QgsExpressionBuilderWidget::expressionTreeItemChanged(
QgsExpressionItem *item )
311 txtSearchEditValues->clear();
319 mValuesModel->clear();
322 cbxValuesInUse->setChecked(
false );
324 mValueGroupBox->setVisible( isField );
326 mShowHelpButton->setText( isField ? tr(
"Show Values" ) : tr(
"Show Help" ) );
329 QString help = loadFunctionHelp( item );
330 txtHelpText->setText( help );
332 bool isUserExpression = item->parent() && item->parent()->text() == mUserExpressionsGroupName;
334 btnRemoveExpression->setEnabled( isUserExpression );
335 btnEditExpression->setEnabled( isUserExpression );
338void QgsExpressionBuilderWidget::btnRun_pressed()
340 if ( !cmbFileNames->currentItem() )
343 if ( cmbFileNames->currentItem()->data( Qt::UserRole ) ==
"project"_L1 )
349 QString file = cmbFileNames->currentItem()->text();
353 runPythonCode( txtPython->text() );
356void QgsExpressionBuilderWidget::runPythonCode(
const QString &code )
360 QString pythontext = code;
363 mExpressionTreeView->refresh();
368 QgsVectorLayer *
layer =
nullptr;
383 QDir myDir( mFunctionsPath );
384 if ( !myDir.exists() )
386 myDir.mkpath( mFunctionsPath );
389 if ( !fileName.endsWith(
".py"_L1 ) )
391 fileName.append(
".py" );
394 fileName = mFunctionsPath + QDir::separator() + fileName;
395 QFile myFile( fileName );
396 if ( myFile.open( QIODevice::WriteOnly | QFile::Truncate ) )
398 QTextStream myFileStream( &myFile );
399 myFileStream << txtPython->text() << Qt::endl;
406 mProject->writeEntry( u
"ExpressionFunctions"_s, u
"/pythonCode"_s, txtPython->text() );
411 mFunctionsPath = path;
413 dir.setNameFilters( QStringList() << u
"*.py"_s );
414 QStringList files = dir.entryList( QDir::Files );
415 cmbFileNames->clear();
416 const auto constFiles = files;
417 for (
const QString &name : constFiles )
419 QFileInfo info( mFunctionsPath + QDir::separator() + name );
420 if ( info.baseName() ==
"__init__"_L1 )
423 cmbFileNames->addItem( item );
427 mProject->readEntry( u
"ExpressionFunctions"_s, u
"/pythonCode"_s, QString(), &ok );
430 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( u
"mIconQgsProjectFile.svg"_s ), DEFAULT_PROJECT_FUNCTIONS_ITEM_NAME );
431 item->setData( Qt::UserRole, u
"project"_s );
432 cmbFileNames->insertItem( 0, item );
435 if ( !cmbFileNames->currentItem() )
437 cmbFileNames->setCurrentRow( 0 );
440 if ( cmbFileNames->count() == 0 )
444 txtPython->setText( QStringLiteral(
445 "'''\n#Sample custom function file\n"
446 "#(uncomment to use and customize or Add button to create a new file) \n%1 \n '''"
448 .arg( txtPython->text() ) );
455 QList<QListWidgetItem *> items = cmbFileNames->findItems( fileName, Qt::MatchExactly );
456 if ( !items.isEmpty() )
460 cmbFileNames->insertItem( 0, item );
461 cmbFileNames->setCurrentRow( 0 );
463 QString templateText;
465 txtPython->setText( templateText );
469void QgsExpressionBuilderWidget::btnNewFile_pressed()
476 mProject->readEntry( u
"ExpressionFunctions"_s, u
"/pythonCode"_s, QString(), &ok );
479 if ( dlg.exec() == QDialog::DialogCode::Accepted )
483 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( u
"mIconQgsProjectFile.svg"_s ), DEFAULT_PROJECT_FUNCTIONS_ITEM_NAME );
484 item->setData( Qt::UserRole, u
"project"_s );
485 cmbFileNames->insertItem( 0, item );
486 cmbFileNames->setCurrentRow( 0 );
488 QString templateText;
490 txtPython->setText( templateText );
497 btnRemoveFile->setEnabled( cmbFileNames->count() > 0 );
501void QgsExpressionBuilderWidget::btnRemoveFile_pressed()
503 if ( cmbFileNames->currentItem()->data( Qt::UserRole ) ==
"project"_L1 )
505 if ( QMessageBox::question(
this, tr(
"Remove Project Functions" ), tr(
"Are you sure you want to remove the project functions?" ), QMessageBox::Yes | QMessageBox::No, QMessageBox::No )
509 mProject->removeEntry( u
"ExpressionFunctions"_s, u
"/pythonCode"_s );
513 if ( QMessageBox::question(
this, tr(
"Remove File" ), tr(
"Are you sure you want to remove current functions file?" ), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No )
516 QString fileName = cmbFileNames->currentItem()->text();
517 if ( !QFile::remove( mFunctionsPath + QDir::separator() + fileName.append(
".py" ) ) )
519 QMessageBox::warning(
this, tr(
"Remove file" ), tr(
"Failed to remove function file '%1'." ).arg( fileName ) );
524 int currentRow = cmbFileNames->currentRow();
526 QListWidgetItem *itemToRemove =
whileBlocking( cmbFileNames )->takeItem( currentRow );
530 if ( cmbFileNames->count() > 0 )
532 whileBlocking( cmbFileNames )->setCurrentRow( currentRow > 0 ? currentRow - 1 : 0 );
533 if ( cmbFileNames->currentItem()->data( Qt::UserRole ) ==
"project"_L1 )
539 loadCodeFromFile( mFunctionsPath + QDir::separator() + cmbFileNames->currentItem()->text() );
544 btnRemoveFile->setEnabled(
false );
549void QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem )
553 if ( lastitem->data( Qt::UserRole ) !=
"project"_L1 )
555 QString filename = lastitem->text();
560 if ( item->data( Qt::UserRole ) ==
"project"_L1 )
563 txtPython->blockSignals(
true );
565 txtPython->blockSignals(
false );
567 btnRun->setText( tr(
"Load or update functions" ) );
568 btnRun->setToolTip( tr(
569 "Loads or updates functions from current script file in QGIS.\n"
571 "Note the functions will only be stored when saving the project."
576 QString path = mFunctionsPath + QDir::separator() + item->text();
580 btnRun->setText( tr(
"Load or update functions" ) );
581 btnRun->setToolTip( tr(
582 "Loads or updates functions from current script file in QGIS.\n"
584 "Saved scripts are auto loaded on QGIS startup."
589 btnRun->setText( tr(
"Save and Load Functions" ) );
590 btnRun->setToolTip( tr(
591 "Saves current script file and loads or updates its functions in QGIS.\n"
593 "Saved scripts are auto loaded on QGIS startup."
601 if ( !path.endsWith(
".py"_L1 ) )
602 path.append(
".py" );
604 txtPython->loadScript( path );
609 loadFunctionCode( mProject->readEntry( u
"ExpressionFunctions"_s, u
"/pythonCode"_s ) );
614 txtPython->setText( code );
617void QgsExpressionBuilderWidget::insertExpressionText(
const QString &text )
621 txtExpressionString->setFocus();
626 Q_UNUSED( fieldValues )
630void QgsExpressionBuilderWidget::fillFieldValues(
const QString &fieldName,
QgsVectorLayer *layer,
int countLimit,
bool forceUsedValues )
642 if ( fieldIndex < 0 )
649 if ( cbxValuesInUse->isVisible() && !cbxValuesInUse->isChecked() && !forceUsedValues )
657 values = qgis::setToList(
layer->uniqueValues( fieldIndex, countLimit ) );
659 std::sort( values.begin(), values.end() );
661 mValuesModel->clear();
662 for (
const QVariant &value : std::as_const( values ) )
665 bool forceRepresentedValue =
false;
667 strValue = u
"NULL"_s;
668 else if ( value.userType() == QMetaType::Type::Int || value.userType() == QMetaType::Type::Double || value.userType() == QMetaType::Type::LongLong || value.userType() == QMetaType::Type::Bool )
669 strValue = value.toString();
670 else if ( value.userType() == QMetaType::Type::QStringList )
673 const QStringList strList = value.toStringList();
674 for ( QString str : strList )
676 if ( !result.isEmpty() )
677 result.append( u
", "_s );
679 result.append(
'\'' + str.replace(
'\'',
"''"_L1 ) +
'\'' );
681 strValue = u
"array(%1)"_s.arg( result );
682 forceRepresentedValue =
true;
684 else if ( value.userType() == QMetaType::Type::QVariantList )
687 const QList list = value.toList();
688 for (
const QVariant &item : list )
690 if ( !result.isEmpty() )
691 result.append( u
", "_s );
693 result.append( item.toString() );
695 strValue = u
"array(%1)"_s.arg( result );
696 forceRepresentedValue =
true;
699 strValue =
'\'' + value.toString().replace(
'\'',
"''"_L1 ) +
'\'';
702 if ( forceRepresentedValue || representedValue != value.toString() )
703 representedValue = representedValue + u
" ["_s + strValue +
']';
705 QStandardItem *item =
new QStandardItem( representedValue );
706 item->setData( strValue );
707 mValuesModel->appendRow( item );
718 return u
"<head><style>"_s + helpStylesheet() + u
"</style></head><body>"_s + helpContents + u
"</body>"_s;
724 return mExpressionValid;
728 const QString &label,
const QList<QPair<QString, QVariant>> &choices,
const std::function<
QgsExpressionContext(
const QVariant & )> &previewContextGenerator
731 mExpressionPreviewWidget->setCustomPreviewGenerator( label, choices, previewContextGenerator );
736 mExpressionTreeView->saveToRecent(
expressionText(), collection );
741 mExpressionTreeView->loadRecent( collection );
746 return mExpressionTreeView;
757 mExpressionTreeView->saveToUserExpressions( label, expression, helpText );
762 mExpressionTreeView->removeFromUserExpressions( label );
768 mExpressionPreviewWidget->setGeomCalculator( da );
773 return txtExpressionString->text();
778 txtExpressionString->setText( expression );
783 return lblExpected->text();
788 lblExpected->setText( expected );
789 mExpectedOutputFrame->setVisible( !expected.isNull() );
794 mExpressionContext = context;
795 expressionContextUpdated();
798void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
802 btnClearEditor->setEnabled( !txtExpressionString->text().isEmpty() );
803 btnSaveExpression->setEnabled(
false );
805 mExpressionPreviewWidget->setExpressionText( text );
810 return mExpressionPreviewWidget->parserError();
815 mExpressionPreviewWidget->setVisible( isVisible );
820 return mExpressionPreviewWidget->evalError();
826 return mExpressionTreeView->model();
838 mExpressionTreeView->setProject(
project );
843 QWidget::showEvent( e );
844 txtExpressionString->setFocus();
847void QgsExpressionBuilderWidget::createErrorMarkers(
const QList<QgsExpression::ParserError> &errors )
852 int errorFirstLine = error.firstLine - 1;
853 int errorFirstColumn = error.firstColumn - 1;
854 int errorLastColumn = error.lastColumn - 1;
855 int errorLastLine = error.lastLine - 1;
861 errorFirstLine = errorLastLine;
862 errorFirstColumn = errorLastColumn - 1;
864 txtExpressionString->fillIndicatorRange( errorFirstLine, errorFirstColumn, errorLastLine, errorLastColumn, error.errorType );
874 const QgsExpressionNodeFunction *node =
static_cast<const QgsExpressionNodeFunction *
>( inNode );
875 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
876 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->
fnIndex() );
879 int start_pos = txtExpressionString->positionFromLineIndex( inNode->
parserFirstLine - 1, start );
880 txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
883 const QList<QgsExpressionNode *> nodeList = node->
args()->
list();
884 for ( QgsExpressionNode *n : nodeList )
897 const QgsExpressionNodeUnaryOperator *node =
static_cast<const QgsExpressionNodeUnaryOperator *
>( inNode );
898 createMarkers( node->
operand() );
903 const QgsExpressionNodeBetweenOperator *node =
static_cast<const QgsExpressionNodeBetweenOperator *
>( inNode );
910 const QgsExpressionNodeBinaryOperator *node =
static_cast<const QgsExpressionNodeBinaryOperator *
>( inNode );
911 createMarkers( node->
opLeft() );
912 createMarkers( node->
opRight() );
921 const QgsExpressionNodeInOperator *node =
static_cast<const QgsExpressionNodeInOperator *
>( inNode );
924 const QList<QgsExpressionNode *> nodeList = node->
list()->
list();
925 for ( QgsExpressionNode *n : nodeList )
934 const QgsExpressionNodeCondition *node =
static_cast<const QgsExpressionNodeCondition *
>( inNode );
935 const QList<QgsExpressionNodeCondition::WhenThen *> conditions = node->
conditions();
936 for ( QgsExpressionNodeCondition::WhenThen *cond : conditions )
938 createMarkers( cond->whenExp() );
939 createMarkers( cond->thenExp() );
943 createMarkers( node->
elseExp() );
954void QgsExpressionBuilderWidget::clearFunctionMarkers()
956 int lastLine = txtExpressionString->lines() - 1;
957 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
960void QgsExpressionBuilderWidget::clearErrors()
962 int lastLine = txtExpressionString->lines() - 1;
971void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
973 mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
974 mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
977void QgsExpressionBuilderWidget::mValuesListView_doubleClicked(
const QModelIndex &index )
980 txtExpressionString->insertText(
' ' + index.data( Qt::UserRole + 1 ).toString() +
' ' );
981 txtExpressionString->setFocus();
984void QgsExpressionBuilderWidget::operatorButtonClicked()
986 QPushButton *button = qobject_cast<QPushButton *>( sender() );
989 txtExpressionString->insertText(
' ' + button->text() +
' ' );
990 txtExpressionString->setFocus();
993void QgsExpressionBuilderWidget::commentLinesClicked()
995 txtExpressionString->toggleComment();
1014 mValueGroupBox->show();
1034 mValueGroupBox->show();
1054 mValueGroupBox->show();
1074 mValueGroupBox->show();
1078void QgsExpressionBuilderWidget::txtPython_textChanged()
1080 if ( tabWidget->currentIndex() != 1 )
1083 QListWidgetItem *item = cmbFileNames->currentItem();
1087 if ( item->data( Qt::UserRole ) ==
"project"_L1 )
1090 displayTemporaryLabel( tr(
"Project changed" ) );
1092 else if ( mAutoSave )
1101 if ( tabWidget->currentIndex() != 1 )
1104 QListWidgetItem *item = cmbFileNames->currentItem();
1108 if ( item->data( Qt::UserRole ) !=
"project"_L1 )
1110 QString file = item->text();
1112 displayTemporaryLabel( tr(
"Function file saved" ) );
1116void QgsExpressionBuilderWidget::displayTemporaryLabel(
const QString &text )
1118 lblAutoSave->setText( text );
1119 QGraphicsOpacityEffect *effect =
new QGraphicsOpacityEffect();
1120 lblAutoSave->setGraphicsEffect( effect );
1121 QPropertyAnimation *anim =
new QPropertyAnimation( effect,
"opacity" );
1122 anim->setDuration( 2000 );
1123 anim->setStartValue( 1.0 );
1124 anim->setEndValue( 0.0 );
1125 anim->setEasingCurve( QEasingCurve::OutQuad );
1126 anim->start( QAbstractAnimation::DeleteWhenStopped );
1133 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1155 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1160 mExpressionTreeView->removeFromUserExpressions( item->text() );
1180 if ( QMessageBox::Yes
1181 == QMessageBox::question(
this, tr(
"Remove Stored Expression" ), tr(
"Do you really want to remove stored expressions '%1'?" ).arg( item->text() ), QMessageBox::Yes | QMessageBox::No ) )
1183 mExpressionTreeView->removeFromUserExpressions( item->text() );
1187void QgsExpressionBuilderWidget::exportUserExpressions_pressed()
1190 QString lastSaveDir = settings.
value( u
"lastExportExpressionsDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
1191 QString saveFileName = QFileDialog::getSaveFileName(
this, tr(
"Export User Expressions" ), lastSaveDir, tr(
"User expressions" ) +
" (*.json)" );
1196 if ( saveFileName.isEmpty() )
1199 QFileInfo saveFileInfo( saveFileName );
1201 if ( saveFileInfo.suffix().isEmpty() )
1203 QString saveFileNameWithSuffix = saveFileName.append(
".json" );
1204 saveFileInfo = QFileInfo( saveFileNameWithSuffix );
1209 QJsonDocument exportJson = mExpressionTreeView->exportUserExpressions();
1210 QFile jsonFile( saveFileName );
1212 if ( !jsonFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
1213 QMessageBox::warning(
this, tr(
"Export user expressions" ), tr(
"Error while creating the expressions file." ) );
1215 if ( !jsonFile.write( exportJson.toJson() ) )
1216 QMessageBox::warning(
this, tr(
"Export user expressions" ), tr(
"Error while creating the expressions file." ) );
1221void QgsExpressionBuilderWidget::importUserExpressions_pressed()
1223 QgsSettings settings;
1224 QString lastImportDir = settings.
value( u
"lastImportExpressionsDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
1225 QString loadFileName = QFileDialog::getOpenFileName(
this, tr(
"Import User Expressions" ), lastImportDir, tr(
"User expressions" ) +
" (*.json)" );
1227 if ( loadFileName.isEmpty() )
1230 QFileInfo loadFileInfo( loadFileName );
1234 QFile jsonFile( loadFileName );
1236 if ( !jsonFile.open( QFile::ReadOnly ) )
1237 QMessageBox::warning(
this, tr(
"Import User Expressions" ), tr(
"Error while reading the expressions file." ) );
1239 QTextStream jsonStream( &jsonFile );
1240 QString jsonString = jsonFile.readAll();
1243 QJsonDocument importJson = QJsonDocument::fromJson( jsonString.toUtf8() );
1245 if ( importJson.isNull() )
1247 QMessageBox::warning(
this, tr(
"Import User Expressions" ), tr(
"Error while reading the expressions file." ) );
1251 mExpressionTreeView->loadExpressionsFromJson( importJson );
1257 return mExpressionTreeView->findExpressions( label );
1260void QgsExpressionBuilderWidget::indicatorClicked(
int line,
int index, Qt::KeyboardModifiers state )
1262 if ( state & Qt::ControlModifier )
1264 int position = txtExpressionString->positionFromLineIndex( line, index );
1265 long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID,
static_cast<long int>( position ) );
1267 QString help = getFunctionHelp( func );
1268 txtHelpText->setText( help );
1272void QgsExpressionBuilderWidget::onExpressionParsed(
bool state )
1276 mExpressionValid = state;
1279 createMarkers( mExpressionPreviewWidget->rootNode() );
1283 createErrorMarkers( mExpressionPreviewWidget->parserErrors() );
1287QString QgsExpressionBuilderWidget::helpStylesheet()
const
1293 style +=
" .functionname {color: #0a6099; font-weight: bold;} "
1294 " .argument {font-family: monospace; color: #bf0c0c; font-style: italic; } "
1295 " td.argument { padding-right: 10px; }";
1300QString QgsExpressionBuilderWidget::loadFunctionHelp(
QgsExpressionItem *expressionItem )
1302 if ( !expressionItem )
1305 QString helpContents = expressionItem->
getHelpText();
1308 if ( helpContents.isEmpty() )
1310 QString name = expressionItem->data( Qt::UserRole ).toString();
1318 return "<head><style>" + helpStylesheet() +
"</style></head><body>" + helpContents +
"</body>";
1325QMenu *QgsExpressionBuilderWidget::ExpressionTreeMenuProvider::createContextMenu(
QgsExpressionItem *item )
1327 QMenu *menu =
nullptr;
1328 QgsVectorLayer *
layer = mExpressionBuilderWidget->layer();
1331 menu =
new QMenu( mExpressionBuilderWidget );
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 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.
A dialog to select whether to create a function file or project functions.
QString fileName()
Returns the new file name.
bool createProjectFunctions() const
Returns whether user has selected to create project functions.
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...
An 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.
QgsExpressionNode * lowerBound() const
Returns the lower bound expression node of the range.
QgsExpressionNode * higherBound() const
Returns the higher bound expression node of the range.
QgsExpressionNode * opLeft() const
Returns the node to the left of the operator.
QgsExpressionNode * opRight() const
Returns the node to the right of the operator.
QgsExpressionNode * elseExp() const
The ELSE expression used for the condition.
WhenThenList conditions() const
The list of WHEN THEN expression parts of the expression.
int fnIndex() const
Returns the index of the node's function.
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
QgsExpressionNode::NodeList * list() const
Returns the list of nodes to search for matching values within.
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.
A generic dialog for editing expression text, label and help text.
QString label()
Returns the label text.
QString expression()
Returns the expression text.
QString helpText() const
Returns the help text.
bool isLabelModified() const SIP_SKIP
Returns whether the label text was modified either manually by the user, or automatically because it ...
A tree view to list all expressions functions, variables and fields that can be used in an expression...
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.
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Container of fields for a vector layer.
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,...
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).
Stores settings for use within QGIS.
void clear()
Removes all entries in the user settings.
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 dataset.
#define Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_PUSH
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
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.