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 );
79 , mExpressionTreeMenuProvider( this )
86 QVBoxLayout *vl =
new QVBoxLayout();
87 vl->setContentsMargins( 0, 0, 0, 0 );
88 vl->addWidget( codeEditorWidget );
89 mExpressionEditorContainer->setLayout( vl );
91 connect( btnRun, &QToolButton::pressed,
this, &QgsExpressionBuilderWidget::btnRun_pressed );
92 connect( btnNewFile, &QPushButton::clicked,
this, &QgsExpressionBuilderWidget::btnNewFile_pressed );
93 connect( btnRemoveFile, &QPushButton::clicked,
this, &QgsExpressionBuilderWidget::btnRemoveFile_pressed );
94 connect( cmbFileNames, &QListWidget::currentItemChanged,
this, &QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged );
95 connect( txtExpressionString, &QgsCodeEditorExpression::textChanged,
this, &QgsExpressionBuilderWidget::txtExpressionString_textChanged );
96 connect( txtPython, &QgsCodeEditorPython::textChanged,
this, &QgsExpressionBuilderWidget::txtPython_textChanged );
97 connect( txtSearchEditValues, &QgsFilterLineEdit::textChanged,
this, &QgsExpressionBuilderWidget::txtSearchEditValues_textChanged );
98 connect( mValuesListView, &QListView::doubleClicked,
this, &QgsExpressionBuilderWidget::mValuesListView_doubleClicked );
102 connect( btnImportExpressions, &QToolButton::clicked,
this, &QgsExpressionBuilderWidget::importUserExpressions_pressed );
103 connect( btnExportExpressions, &QToolButton::clicked,
this, &QgsExpressionBuilderWidget::exportUserExpressions_pressed );
104 connect( btnClearEditor, &QToolButton::clicked, txtExpressionString, &QgsCodeEditorExpression::clear );
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() );
261 mExpressionTreeView->loadRecent( recentCollection );
264 mExpressionTreeView->loadUserExpressions();
269 init( context, recentCollection, flags );
275 init( context, recentCollection, flags );
276 mExpressionTreeView->loadFieldNames( fields );
283 mExpressionTreeView->setLayer( mLayer );
284 mExpressionPreviewWidget->setLayer( mLayer );
291 expressionContextUpdated();
292 txtExpressionString->setFields( mLayer->fields() );
296void QgsExpressionBuilderWidget::expressionContextUpdated()
299 mExpressionTreeView->setExpressionContext( mExpressionContext );
300 mExpressionPreviewWidget->setExpressionContext( mExpressionContext );
308void QgsExpressionBuilderWidget::expressionTreeItemChanged(
QgsExpressionItem *item )
310 txtSearchEditValues->clear();
318 mValuesModel->clear();
321 cbxValuesInUse->setChecked(
false );
323 mValueGroupBox->setVisible( isField );
325 mShowHelpButton->setText( isField ? tr(
"Show Values" ) : tr(
"Show Help" ) );
328 QString help = loadFunctionHelp( item );
329 txtHelpText->setText( help );
331 bool isUserExpression = item->parent() && item->parent()->text() == mUserExpressionsGroupName;
333 btnRemoveExpression->setEnabled( isUserExpression );
334 btnEditExpression->setEnabled( isUserExpression );
337void QgsExpressionBuilderWidget::btnRun_pressed()
339 if ( !cmbFileNames->currentItem() )
342 if ( cmbFileNames->currentItem()->data( Qt::UserRole ) ==
"project"_L1 )
348 QString file = cmbFileNames->currentItem()->text();
352 runPythonCode( txtPython->text() );
355void QgsExpressionBuilderWidget::runPythonCode(
const QString &code )
359 QString pythontext = code;
362 mExpressionTreeView->refresh();
367 QgsVectorLayer *
layer =
nullptr;
382 QDir myDir( mFunctionsPath );
383 if ( !myDir.exists() )
385 myDir.mkpath( mFunctionsPath );
388 if ( !fileName.endsWith(
".py"_L1 ) )
390 fileName.append(
".py" );
393 fileName = mFunctionsPath + QDir::separator() + fileName;
394 QFile myFile( fileName );
395 if ( myFile.open( QIODevice::WriteOnly | QFile::Truncate ) )
397 QTextStream myFileStream( &myFile );
398 myFileStream << txtPython->text() << Qt::endl;
405 mProject->writeEntry( u
"ExpressionFunctions"_s, u
"/pythonCode"_s, txtPython->text() );
410 mFunctionsPath = path;
412 dir.setNameFilters( QStringList() << u
"*.py"_s );
413 QStringList files = dir.entryList( QDir::Files );
414 cmbFileNames->clear();
415 const auto constFiles = files;
416 for (
const QString &name : constFiles )
418 QFileInfo info( mFunctionsPath + QDir::separator() + name );
419 if ( info.baseName() ==
"__init__"_L1 )
422 cmbFileNames->addItem( item );
426 mProject->readEntry( u
"ExpressionFunctions"_s, u
"/pythonCode"_s, QString(), &ok );
429 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( u
"mIconQgsProjectFile.svg"_s ), DEFAULT_PROJECT_FUNCTIONS_ITEM_NAME );
430 item->setData( Qt::UserRole, u
"project"_s );
431 cmbFileNames->insertItem( 0, item );
434 if ( !cmbFileNames->currentItem() )
436 cmbFileNames->setCurrentRow( 0 );
439 if ( cmbFileNames->count() == 0 )
443 txtPython->setText( QStringLiteral(
444 "'''\n#Sample custom function file\n"
445 "#(uncomment to use and customize or Add button to create a new file) \n%1 \n '''"
447 .arg( txtPython->text() ) );
454 QList<QListWidgetItem *> items = cmbFileNames->findItems( fileName, Qt::MatchExactly );
455 if ( !items.isEmpty() )
459 cmbFileNames->insertItem( 0, item );
460 cmbFileNames->setCurrentRow( 0 );
462 QString templateText;
464 txtPython->setText( templateText );
468void QgsExpressionBuilderWidget::btnNewFile_pressed()
475 mProject->readEntry( u
"ExpressionFunctions"_s, u
"/pythonCode"_s, QString(), &ok );
478 if ( dlg.exec() == QDialog::DialogCode::Accepted )
482 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( u
"mIconQgsProjectFile.svg"_s ), DEFAULT_PROJECT_FUNCTIONS_ITEM_NAME );
483 item->setData( Qt::UserRole, u
"project"_s );
484 cmbFileNames->insertItem( 0, item );
485 cmbFileNames->setCurrentRow( 0 );
487 QString templateText;
489 txtPython->setText( templateText );
496 btnRemoveFile->setEnabled( cmbFileNames->count() > 0 );
500void QgsExpressionBuilderWidget::btnRemoveFile_pressed()
502 if ( cmbFileNames->currentItem()->data( Qt::UserRole ) ==
"project"_L1 )
504 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 )
508 mProject->removeEntry( u
"ExpressionFunctions"_s, u
"/pythonCode"_s );
512 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 )
515 QString fileName = cmbFileNames->currentItem()->text();
516 if ( !QFile::remove( mFunctionsPath + QDir::separator() + fileName.append(
".py" ) ) )
518 QMessageBox::warning(
this, tr(
"Remove file" ), tr(
"Failed to remove function file '%1'." ).arg( fileName ) );
523 int currentRow = cmbFileNames->currentRow();
525 QListWidgetItem *itemToRemove =
whileBlocking( cmbFileNames )->takeItem( currentRow );
529 if ( cmbFileNames->count() > 0 )
531 whileBlocking( cmbFileNames )->setCurrentRow( currentRow > 0 ? currentRow - 1 : 0 );
532 if ( cmbFileNames->currentItem()->data( Qt::UserRole ) ==
"project"_L1 )
538 loadCodeFromFile( mFunctionsPath + QDir::separator() + cmbFileNames->currentItem()->text() );
543 btnRemoveFile->setEnabled(
false );
548void QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem )
552 if ( lastitem->data( Qt::UserRole ) !=
"project"_L1 )
554 QString filename = lastitem->text();
559 if ( item->data( Qt::UserRole ) ==
"project"_L1 )
562 txtPython->blockSignals(
true );
564 txtPython->blockSignals(
false );
566 btnRun->setText( tr(
"Load or update functions" ) );
567 btnRun->setToolTip( tr(
568 "Loads or updates functions from current script file in QGIS.\n"
570 "Note the functions will only be stored when saving the project."
575 QString path = mFunctionsPath + QDir::separator() + item->text();
579 btnRun->setText( tr(
"Load or update functions" ) );
580 btnRun->setToolTip( tr(
581 "Loads or updates functions from current script file in QGIS.\n"
583 "Saved scripts are auto loaded on QGIS startup."
588 btnRun->setText( tr(
"Save and Load Functions" ) );
589 btnRun->setToolTip( tr(
590 "Saves current script file and loads or updates its functions in QGIS.\n"
592 "Saved scripts are auto loaded on QGIS startup."
600 if ( !path.endsWith(
".py"_L1 ) )
601 path.append(
".py" );
603 txtPython->loadScript( path );
608 loadFunctionCode( mProject->readEntry( u
"ExpressionFunctions"_s, u
"/pythonCode"_s ) );
613 txtPython->setText( code );
616void QgsExpressionBuilderWidget::insertExpressionText(
const QString &text )
620 txtExpressionString->setFocus();
625 Q_UNUSED( fieldValues )
629void QgsExpressionBuilderWidget::fillFieldValues(
const QString &fieldName,
QgsVectorLayer *layer,
int countLimit,
bool forceUsedValues )
641 if ( fieldIndex < 0 )
648 if ( cbxValuesInUse->isVisible() && !cbxValuesInUse->isChecked() && !forceUsedValues )
656 values = qgis::setToList(
layer->uniqueValues( fieldIndex, countLimit ) );
658 std::sort( values.begin(), values.end() );
660 mValuesModel->clear();
661 for (
const QVariant &value : std::as_const( values ) )
664 bool forceRepresentedValue =
false;
666 strValue = u
"NULL"_s;
667 else if ( value.userType() == QMetaType::Type::Int || value.userType() == QMetaType::Type::Double || value.userType() == QMetaType::Type::LongLong || value.userType() == QMetaType::Type::Bool )
668 strValue = value.toString();
669 else if ( value.userType() == QMetaType::Type::QStringList )
672 const QStringList strList = value.toStringList();
673 for ( QString str : strList )
675 if ( !result.isEmpty() )
676 result.append( u
", "_s );
678 result.append(
'\'' + str.replace(
'\'',
"''"_L1 ) +
'\'' );
680 strValue = u
"array(%1)"_s.arg( result );
681 forceRepresentedValue =
true;
683 else if ( value.userType() == QMetaType::Type::QVariantList )
686 const QList list = value.toList();
687 for (
const QVariant &item : list )
689 if ( !result.isEmpty() )
690 result.append( u
", "_s );
692 result.append( item.toString() );
694 strValue = u
"array(%1)"_s.arg( result );
695 forceRepresentedValue =
true;
698 strValue =
'\'' + value.toString().replace(
'\'',
"''"_L1 ) +
'\'';
701 if ( forceRepresentedValue || representedValue != value.toString() )
702 representedValue = representedValue + u
" ["_s + strValue +
']';
704 QStandardItem *item =
new QStandardItem( representedValue );
705 item->setData( strValue );
706 mValuesModel->appendRow( item );
717 return u
"<head><style>"_s + helpStylesheet() + u
"</style></head><body>"_s + helpContents + u
"</body>"_s;
723 return mExpressionValid;
727 const QString &label,
const QList<QPair<QString, QVariant>> &choices,
const std::function<
QgsExpressionContext(
const QVariant & )> &previewContextGenerator
730 mExpressionPreviewWidget->setCustomPreviewGenerator( label, choices, previewContextGenerator );
735 mExpressionTreeView->saveToRecent(
expressionText(), collection );
740 mExpressionTreeView->loadRecent( collection );
745 return mExpressionTreeView;
756 mExpressionTreeView->saveToUserExpressions( label, expression, helpText );
761 mExpressionTreeView->removeFromUserExpressions( label );
767 mExpressionPreviewWidget->setGeomCalculator( da );
772 return txtExpressionString->text();
777 txtExpressionString->setText( expression );
782 return lblExpected->text();
787 lblExpected->setText( expected );
788 mExpectedOutputFrame->setVisible( !expected.isNull() );
793 mExpressionContext = context;
794 expressionContextUpdated();
797void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
801 btnClearEditor->setEnabled( !txtExpressionString->text().isEmpty() );
802 btnSaveExpression->setEnabled(
false );
804 mExpressionPreviewWidget->setExpressionText( text );
809 return mExpressionPreviewWidget->parserError();
814 mExpressionPreviewWidget->setVisible( isVisible );
819 return mExpressionPreviewWidget->evalError();
825 return mExpressionTreeView->model();
837 mExpressionTreeView->setProject(
project );
842 QWidget::showEvent( e );
843 txtExpressionString->setFocus();
846void QgsExpressionBuilderWidget::createErrorMarkers(
const QList<QgsExpression::ParserError> &errors )
851 int errorFirstLine = error.firstLine - 1;
852 int errorFirstColumn = error.firstColumn - 1;
853 int errorLastColumn = error.lastColumn - 1;
854 int errorLastLine = error.lastLine - 1;
860 errorFirstLine = errorLastLine;
861 errorFirstColumn = errorLastColumn - 1;
863 txtExpressionString->fillIndicatorRange( errorFirstLine, errorFirstColumn, errorLastLine, errorLastColumn, error.errorType );
873 const QgsExpressionNodeFunction *node =
static_cast<const QgsExpressionNodeFunction *
>( inNode );
874 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
875 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->
fnIndex() );
878 int start_pos = txtExpressionString->positionFromLineIndex( inNode->
parserFirstLine - 1, start );
879 txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
882 const QList<QgsExpressionNode *> nodeList = node->
args()->
list();
883 for ( QgsExpressionNode *n : nodeList )
896 const QgsExpressionNodeUnaryOperator *node =
static_cast<const QgsExpressionNodeUnaryOperator *
>( inNode );
897 createMarkers( node->
operand() );
902 const QgsExpressionNodeBetweenOperator *node =
static_cast<const QgsExpressionNodeBetweenOperator *
>( inNode );
909 const QgsExpressionNodeBinaryOperator *node =
static_cast<const QgsExpressionNodeBinaryOperator *
>( inNode );
910 createMarkers( node->
opLeft() );
911 createMarkers( node->
opRight() );
920 const QgsExpressionNodeInOperator *node =
static_cast<const QgsExpressionNodeInOperator *
>( inNode );
923 const QList<QgsExpressionNode *> nodeList = node->
list()->
list();
924 for ( QgsExpressionNode *n : nodeList )
933 const QgsExpressionNodeCondition *node =
static_cast<const QgsExpressionNodeCondition *
>( inNode );
934 const QList<QgsExpressionNodeCondition::WhenThen *> conditions = node->
conditions();
935 for ( QgsExpressionNodeCondition::WhenThen *cond : conditions )
937 createMarkers( cond->whenExp() );
938 createMarkers( cond->thenExp() );
942 createMarkers( node->
elseExp() );
953void QgsExpressionBuilderWidget::clearFunctionMarkers()
955 int lastLine = txtExpressionString->lines() - 1;
956 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
959void QgsExpressionBuilderWidget::clearErrors()
961 int lastLine = txtExpressionString->lines() - 1;
970void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
972 mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
973 mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
976void QgsExpressionBuilderWidget::mValuesListView_doubleClicked(
const QModelIndex &index )
979 txtExpressionString->insertText(
' ' + index.data( Qt::UserRole + 1 ).toString() +
' ' );
980 txtExpressionString->setFocus();
983void QgsExpressionBuilderWidget::operatorButtonClicked()
985 QPushButton *button = qobject_cast<QPushButton *>( sender() );
988 txtExpressionString->insertText(
' ' + button->text() +
' ' );
989 txtExpressionString->setFocus();
992void QgsExpressionBuilderWidget::commentLinesClicked()
994 txtExpressionString->toggleComment();
1013 mValueGroupBox->show();
1033 mValueGroupBox->show();
1053 mValueGroupBox->show();
1073 mValueGroupBox->show();
1077void QgsExpressionBuilderWidget::txtPython_textChanged()
1079 if ( tabWidget->currentIndex() != 1 )
1082 QListWidgetItem *item = cmbFileNames->currentItem();
1086 if ( item->data( Qt::UserRole ) ==
"project"_L1 )
1089 displayTemporaryLabel( tr(
"Project changed" ) );
1091 else if ( mAutoSave )
1100 if ( tabWidget->currentIndex() != 1 )
1103 QListWidgetItem *item = cmbFileNames->currentItem();
1107 if ( item->data( Qt::UserRole ) !=
"project"_L1 )
1109 QString file = item->text();
1111 displayTemporaryLabel( tr(
"Function file saved" ) );
1115void QgsExpressionBuilderWidget::displayTemporaryLabel(
const QString &text )
1117 lblAutoSave->setText( text );
1118 QGraphicsOpacityEffect *effect =
new QGraphicsOpacityEffect();
1119 lblAutoSave->setGraphicsEffect( effect );
1120 QPropertyAnimation *anim =
new QPropertyAnimation( effect,
"opacity" );
1121 anim->setDuration( 2000 );
1122 anim->setStartValue( 1.0 );
1123 anim->setEndValue( 0.0 );
1124 anim->setEasingCurve( QEasingCurve::OutQuad );
1125 anim->start( QAbstractAnimation::DeleteWhenStopped );
1132 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1154 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1159 mExpressionTreeView->removeFromUserExpressions( item->text() );
1179 if ( QMessageBox::Yes
1180 == QMessageBox::question(
this, tr(
"Remove Stored Expression" ), tr(
"Do you really want to remove stored expressions '%1'?" ).arg( item->text() ), QMessageBox::Yes | QMessageBox::No ) )
1182 mExpressionTreeView->removeFromUserExpressions( item->text() );
1186void QgsExpressionBuilderWidget::exportUserExpressions_pressed()
1189 QString lastSaveDir = settings.
value( u
"lastExportExpressionsDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
1190 QString saveFileName = QFileDialog::getSaveFileName(
this, tr(
"Export User Expressions" ), lastSaveDir, tr(
"User expressions" ) +
" (*.json)" );
1195 if ( saveFileName.isEmpty() )
1198 QFileInfo saveFileInfo( saveFileName );
1200 if ( saveFileInfo.suffix().isEmpty() )
1202 QString saveFileNameWithSuffix = saveFileName.append(
".json" );
1203 saveFileInfo = QFileInfo( saveFileNameWithSuffix );
1208 QJsonDocument exportJson = mExpressionTreeView->exportUserExpressions();
1209 QFile jsonFile( saveFileName );
1211 if ( !jsonFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
1212 QMessageBox::warning(
this, tr(
"Export user expressions" ), tr(
"Error while creating the expressions file." ) );
1214 if ( !jsonFile.write( exportJson.toJson() ) )
1215 QMessageBox::warning(
this, tr(
"Export user expressions" ), tr(
"Error while creating the expressions file." ) );
1220void QgsExpressionBuilderWidget::importUserExpressions_pressed()
1222 QgsSettings settings;
1223 QString lastImportDir = settings.
value( u
"lastImportExpressionsDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
1224 QString loadFileName = QFileDialog::getOpenFileName(
this, tr(
"Import User Expressions" ), lastImportDir, tr(
"User expressions" ) +
" (*.json)" );
1226 if ( loadFileName.isEmpty() )
1229 QFileInfo loadFileInfo( loadFileName );
1233 QFile jsonFile( loadFileName );
1235 if ( !jsonFile.open( QFile::ReadOnly ) )
1236 QMessageBox::warning(
this, tr(
"Import User Expressions" ), tr(
"Error while reading the expressions file." ) );
1238 QTextStream jsonStream( &jsonFile );
1239 QString jsonString = jsonFile.readAll();
1242 QJsonDocument importJson = QJsonDocument::fromJson( jsonString.toUtf8() );
1244 if ( importJson.isNull() )
1246 QMessageBox::warning(
this, tr(
"Import User Expressions" ), tr(
"Error while reading the expressions file." ) );
1250 mExpressionTreeView->loadExpressionsFromJson( importJson );
1256 return mExpressionTreeView->findExpressions( label );
1259void QgsExpressionBuilderWidget::indicatorClicked(
int line,
int index, Qt::KeyboardModifiers state )
1261 if ( state & Qt::ControlModifier )
1263 int position = txtExpressionString->positionFromLineIndex( line, index );
1264 long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID,
static_cast<long int>( position ) );
1266 QString help = getFunctionHelp( func );
1267 txtHelpText->setText( help );
1271void QgsExpressionBuilderWidget::onExpressionParsed(
bool state )
1275 mExpressionValid = state;
1278 createMarkers( mExpressionPreviewWidget->rootNode() );
1282 createErrorMarkers( mExpressionPreviewWidget->parserErrors() );
1286QString QgsExpressionBuilderWidget::helpStylesheet()
const
1292 style +=
" .functionname {color: #0a6099; font-weight: bold;} "
1293 " .argument {font-family: monospace; color: #bf0c0c; font-style: italic; } "
1294 " td.argument { padding-right: 10px; }";
1299QString QgsExpressionBuilderWidget::loadFunctionHelp(
QgsExpressionItem *expressionItem )
1301 if ( !expressionItem )
1304 QString helpContents = expressionItem->
getHelpText();
1307 if ( helpContents.isEmpty() )
1309 QString name = expressionItem->data( Qt::UserRole ).toString();
1317 return "<head><style>" + helpStylesheet() +
"</style></head><body>" + helpContents +
"</body>";
1324QMenu *QgsExpressionBuilderWidget::ExpressionTreeMenuProvider::createContextMenu(
QgsExpressionItem *item )
1326 QMenu *menu =
nullptr;
1327 QgsVectorLayer *
layer = mExpressionBuilderWidget->layer();
1330 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.