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\
246 txtExpressionString->setFocus();
253 settings.
setValue( u
"Windows/QgsExpressionBuilderWidget/splitter"_s, splitter->saveState() );
254 settings.
setValue( u
"Windows/QgsExpressionBuilderWidget/editorsplitter"_s, editorSplit->saveState() );
255 settings.
setValue( u
"Windows/QgsExpressionBuilderWidget/functionsplitter"_s, functionsplit->saveState() );
263 mExpressionTreeView->loadRecent( recentCollection );
266 mExpressionTreeView->loadUserExpressions();
271 init( context, recentCollection, flags );
277 init( context, recentCollection, flags );
278 mExpressionTreeView->loadFieldNames( fields );
285 mExpressionTreeView->setLayer( mLayer );
286 mExpressionPreviewWidget->setLayer( mLayer );
293 expressionContextUpdated();
294 txtExpressionString->setFields( mLayer->fields() );
298void QgsExpressionBuilderWidget::expressionContextUpdated()
301 mExpressionTreeView->setExpressionContext( mExpressionContext );
302 mExpressionPreviewWidget->setExpressionContext( mExpressionContext );
310void QgsExpressionBuilderWidget::expressionTreeItemChanged(
QgsExpressionItem *item )
312 txtSearchEditValues->clear();
320 mValuesModel->clear();
323 cbxValuesInUse->setChecked(
false );
325 mValueGroupBox->setVisible( isField );
327 mShowHelpButton->setText( isField ? tr(
"Show Values" ) : tr(
"Show Help" ) );
330 QString help = loadFunctionHelp( item );
331 txtHelpText->setText( help );
333 bool isUserExpression = item->parent() && item->parent()->text() == mUserExpressionsGroupName;
335 btnRemoveExpression->setEnabled( isUserExpression );
336 btnEditExpression->setEnabled( isUserExpression );
339void QgsExpressionBuilderWidget::btnRun_pressed()
341 if ( !cmbFileNames->currentItem() )
344 if ( cmbFileNames->currentItem()->data( Qt::UserRole ) ==
"project"_L1 )
350 QString file = cmbFileNames->currentItem()->text();
354 runPythonCode( txtPython->text() );
357void QgsExpressionBuilderWidget::runPythonCode(
const QString &code )
361 QString pythontext = code;
364 mExpressionTreeView->refresh();
369 QgsVectorLayer *
layer =
nullptr;
384 QDir myDir( mFunctionsPath );
385 if ( !myDir.exists() )
387 myDir.mkpath( mFunctionsPath );
390 if ( !fileName.endsWith(
".py"_L1 ) )
392 fileName.append(
".py" );
395 fileName = mFunctionsPath + QDir::separator() + fileName;
396 QFile myFile( fileName );
397 if ( myFile.open( QIODevice::WriteOnly | QFile::Truncate ) )
399 QTextStream myFileStream( &myFile );
400 myFileStream << txtPython->text() << Qt::endl;
407 mProject->writeEntry( u
"ExpressionFunctions"_s, u
"/pythonCode"_s, txtPython->text() );
412 mFunctionsPath = path;
414 dir.setNameFilters( QStringList() << u
"*.py"_s );
415 QStringList files = dir.entryList( QDir::Files );
416 cmbFileNames->clear();
417 const auto constFiles = files;
418 for (
const QString &name : constFiles )
420 QFileInfo info( mFunctionsPath + QDir::separator() + name );
421 if ( info.baseName() ==
"__init__"_L1 )
424 cmbFileNames->addItem( item );
428 mProject->readEntry( u
"ExpressionFunctions"_s, u
"/pythonCode"_s, QString(), &ok );
431 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( u
"mIconQgsProjectFile.svg"_s ), DEFAULT_PROJECT_FUNCTIONS_ITEM_NAME );
432 item->setData( Qt::UserRole, u
"project"_s );
433 cmbFileNames->insertItem( 0, item );
436 if ( !cmbFileNames->currentItem() )
438 cmbFileNames->setCurrentRow( 0 );
441 if ( cmbFileNames->count() == 0 )
445 txtPython->setText( QStringLiteral(
446 "'''\n#Sample custom function file\n"
447 "#(uncomment to use and customize or Add button to create a new file) \n%1 \n '''"
449 .arg( txtPython->text() ) );
456 QList<QListWidgetItem *> items = cmbFileNames->findItems( fileName, Qt::MatchExactly );
457 if ( !items.isEmpty() )
461 cmbFileNames->insertItem( 0, item );
462 cmbFileNames->setCurrentRow( 0 );
464 QString templateText;
466 txtPython->setText( templateText );
470void QgsExpressionBuilderWidget::btnNewFile_pressed()
477 mProject->readEntry( u
"ExpressionFunctions"_s, u
"/pythonCode"_s, QString(), &ok );
480 if ( dlg.exec() == QDialog::DialogCode::Accepted )
484 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( u
"mIconQgsProjectFile.svg"_s ), DEFAULT_PROJECT_FUNCTIONS_ITEM_NAME );
485 item->setData( Qt::UserRole, u
"project"_s );
486 cmbFileNames->insertItem( 0, item );
487 cmbFileNames->setCurrentRow( 0 );
489 QString templateText;
491 txtPython->setText( templateText );
498 btnRemoveFile->setEnabled( cmbFileNames->count() > 0 );
502void QgsExpressionBuilderWidget::btnRemoveFile_pressed()
504 if ( cmbFileNames->currentItem()->data( Qt::UserRole ) ==
"project"_L1 )
506 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 )
510 mProject->removeEntry( u
"ExpressionFunctions"_s, u
"/pythonCode"_s );
514 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 )
517 QString fileName = cmbFileNames->currentItem()->text();
518 if ( !QFile::remove( mFunctionsPath + QDir::separator() + fileName.append(
".py" ) ) )
520 QMessageBox::warning(
this, tr(
"Remove file" ), tr(
"Failed to remove function file '%1'." ).arg( fileName ) );
525 int currentRow = cmbFileNames->currentRow();
527 QListWidgetItem *itemToRemove =
whileBlocking( cmbFileNames )->takeItem( currentRow );
531 if ( cmbFileNames->count() > 0 )
533 whileBlocking( cmbFileNames )->setCurrentRow( currentRow > 0 ? currentRow - 1 : 0 );
534 if ( cmbFileNames->currentItem()->data( Qt::UserRole ) ==
"project"_L1 )
540 loadCodeFromFile( mFunctionsPath + QDir::separator() + cmbFileNames->currentItem()->text() );
545 btnRemoveFile->setEnabled(
false );
550void QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem )
554 if ( lastitem->data( Qt::UserRole ) !=
"project"_L1 )
556 QString filename = lastitem->text();
561 if ( item->data( Qt::UserRole ) ==
"project"_L1 )
564 txtPython->blockSignals(
true );
566 txtPython->blockSignals(
false );
568 btnRun->setText( tr(
"Load or update functions" ) );
569 btnRun->setToolTip( tr(
570 "Loads or updates functions from current script file in QGIS.\n"
572 "Note the functions will only be stored when saving the project."
577 QString path = mFunctionsPath + QDir::separator() + item->text();
581 btnRun->setText( tr(
"Load or update functions" ) );
582 btnRun->setToolTip( tr(
583 "Loads or updates functions from current script file in QGIS.\n"
585 "Saved scripts are auto loaded on QGIS startup."
590 btnRun->setText( tr(
"Save and Load Functions" ) );
591 btnRun->setToolTip( tr(
592 "Saves current script file and loads or updates its functions in QGIS.\n"
594 "Saved scripts are auto loaded on QGIS startup."
602 if ( !path.endsWith(
".py"_L1 ) )
603 path.append(
".py" );
605 txtPython->loadScript( path );
610 loadFunctionCode( mProject->readEntry( u
"ExpressionFunctions"_s, u
"/pythonCode"_s ) );
615 txtPython->setText( code );
618void QgsExpressionBuilderWidget::insertExpressionText(
const QString &text )
622 txtExpressionString->setFocus();
627 Q_UNUSED( fieldValues )
631void QgsExpressionBuilderWidget::fillFieldValues(
const QString &fieldName,
QgsVectorLayer *layer,
int countLimit,
bool forceUsedValues )
643 if ( fieldIndex < 0 )
650 if ( cbxValuesInUse->isVisible() && !cbxValuesInUse->isChecked() && !forceUsedValues )
658 values = qgis::setToList(
layer->uniqueValues( fieldIndex, countLimit ) );
660 std::sort( values.begin(), values.end() );
662 mValuesModel->clear();
663 for (
const QVariant &value : std::as_const( values ) )
666 bool forceRepresentedValue =
false;
668 strValue = u
"NULL"_s;
669 else if ( value.userType() == QMetaType::Type::Int || value.userType() == QMetaType::Type::Double || value.userType() == QMetaType::Type::LongLong || value.userType() == QMetaType::Type::Bool )
670 strValue = value.toString();
671 else if ( value.userType() == QMetaType::Type::QStringList )
674 const QStringList strList = value.toStringList();
675 for ( QString str : strList )
677 if ( !result.isEmpty() )
678 result.append( u
", "_s );
680 result.append(
'\'' + str.replace(
'\'',
"''"_L1 ) +
'\'' );
682 strValue = u
"array(%1)"_s.arg( result );
683 forceRepresentedValue =
true;
685 else if ( value.userType() == QMetaType::Type::QVariantList )
688 const QList list = value.toList();
689 for (
const QVariant &item : list )
691 if ( !result.isEmpty() )
692 result.append( u
", "_s );
694 result.append( item.toString() );
696 strValue = u
"array(%1)"_s.arg( result );
697 forceRepresentedValue =
true;
700 strValue =
'\'' + value.toString().replace(
'\'',
"''"_L1 ) +
'\'';
703 if ( forceRepresentedValue || representedValue != value.toString() )
704 representedValue = representedValue + u
" ["_s + strValue +
']';
706 QStandardItem *item =
new QStandardItem( representedValue );
707 item->setData( strValue );
708 mValuesModel->appendRow( item );
719 return u
"<head><style>"_s + helpStylesheet() + u
"</style></head><body>"_s + helpContents + u
"</body>"_s;
725 return mExpressionValid;
729 const QString &label,
const QList<QPair<QString, QVariant>> &choices,
const std::function<
QgsExpressionContext(
const QVariant & )> &previewContextGenerator
732 mExpressionPreviewWidget->setCustomPreviewGenerator( label, choices, previewContextGenerator );
737 mExpressionTreeView->saveToRecent(
expressionText(), collection );
742 mExpressionTreeView->loadRecent( collection );
747 return mExpressionTreeView;
758 mExpressionTreeView->saveToUserExpressions( label, expression, helpText );
763 mExpressionTreeView->removeFromUserExpressions( label );
769 mExpressionPreviewWidget->setGeomCalculator( da );
774 return txtExpressionString->text();
779 txtExpressionString->setText( expression );
784 return lblExpected->text();
789 lblExpected->setText( expected );
790 mExpectedOutputFrame->setVisible( !expected.isNull() );
795 mExpressionContext = context;
796 expressionContextUpdated();
799void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
803 btnClearEditor->setEnabled( !txtExpressionString->text().isEmpty() );
804 btnSaveExpression->setEnabled(
false );
806 mExpressionPreviewWidget->setExpressionText( text );
811 return mExpressionPreviewWidget->parserError();
816 mExpressionPreviewWidget->setVisible( isVisible );
821 return mExpressionPreviewWidget->evalError();
827 return mExpressionTreeView->model();
839 mExpressionTreeView->setProject(
project );
842void QgsExpressionBuilderWidget::createErrorMarkers(
const QList<QgsExpression::ParserError> &errors )
847 int errorFirstLine = error.firstLine - 1;
848 int errorFirstColumn = error.firstColumn - 1;
849 int errorLastColumn = error.lastColumn - 1;
850 int errorLastLine = error.lastLine - 1;
856 errorFirstLine = errorLastLine;
857 errorFirstColumn = errorLastColumn - 1;
859 txtExpressionString->fillIndicatorRange( errorFirstLine, errorFirstColumn, errorLastLine, errorLastColumn, error.errorType );
869 const QgsExpressionNodeFunction *node =
static_cast<const QgsExpressionNodeFunction *
>( inNode );
870 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
871 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->
fnIndex() );
874 int start_pos = txtExpressionString->positionFromLineIndex( inNode->
parserFirstLine - 1, start );
875 txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
878 const QList<QgsExpressionNode *> nodeList = node->
args()->
list();
879 for ( QgsExpressionNode *n : nodeList )
892 const QgsExpressionNodeUnaryOperator *node =
static_cast<const QgsExpressionNodeUnaryOperator *
>( inNode );
893 createMarkers( node->
operand() );
898 const QgsExpressionNodeBetweenOperator *node =
static_cast<const QgsExpressionNodeBetweenOperator *
>( inNode );
905 const QgsExpressionNodeBinaryOperator *node =
static_cast<const QgsExpressionNodeBinaryOperator *
>( inNode );
906 createMarkers( node->
opLeft() );
907 createMarkers( node->
opRight() );
916 const QgsExpressionNodeInOperator *node =
static_cast<const QgsExpressionNodeInOperator *
>( inNode );
919 const QList<QgsExpressionNode *> nodeList = node->
list()->
list();
920 for ( QgsExpressionNode *n : nodeList )
929 const QgsExpressionNodeCondition *node =
static_cast<const QgsExpressionNodeCondition *
>( inNode );
930 const QList<QgsExpressionNodeCondition::WhenThen *> conditions = node->
conditions();
931 for ( QgsExpressionNodeCondition::WhenThen *cond : conditions )
933 createMarkers( cond->whenExp() );
934 createMarkers( cond->thenExp() );
938 createMarkers( node->
elseExp() );
949void QgsExpressionBuilderWidget::clearFunctionMarkers()
951 int lastLine = txtExpressionString->lines() - 1;
952 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
955void QgsExpressionBuilderWidget::clearErrors()
957 int lastLine = txtExpressionString->lines() - 1;
966void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
968 mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
969 mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
972void QgsExpressionBuilderWidget::mValuesListView_doubleClicked(
const QModelIndex &index )
975 txtExpressionString->insertText(
' ' + index.data( Qt::UserRole + 1 ).toString() +
' ' );
976 txtExpressionString->setFocus();
979void QgsExpressionBuilderWidget::operatorButtonClicked()
981 QPushButton *button = qobject_cast<QPushButton *>( sender() );
984 txtExpressionString->insertText(
' ' + button->text() +
' ' );
985 txtExpressionString->setFocus();
988void QgsExpressionBuilderWidget::commentLinesClicked()
990 txtExpressionString->toggleComment();
1009 mValueGroupBox->show();
1029 mValueGroupBox->show();
1049 mValueGroupBox->show();
1069 mValueGroupBox->show();
1073void QgsExpressionBuilderWidget::txtPython_textChanged()
1075 if ( tabWidget->currentIndex() != 1 )
1078 QListWidgetItem *item = cmbFileNames->currentItem();
1082 if ( item->data( Qt::UserRole ) ==
"project"_L1 )
1085 displayTemporaryLabel( tr(
"Project changed" ) );
1087 else if ( mAutoSave )
1096 if ( tabWidget->currentIndex() != 1 )
1099 QListWidgetItem *item = cmbFileNames->currentItem();
1103 if ( item->data( Qt::UserRole ) !=
"project"_L1 )
1105 QString file = item->text();
1107 displayTemporaryLabel( tr(
"Function file saved" ) );
1111void QgsExpressionBuilderWidget::displayTemporaryLabel(
const QString &text )
1113 lblAutoSave->setText( text );
1114 QGraphicsOpacityEffect *effect =
new QGraphicsOpacityEffect();
1115 lblAutoSave->setGraphicsEffect( effect );
1116 QPropertyAnimation *anim =
new QPropertyAnimation( effect,
"opacity" );
1117 anim->setDuration( 2000 );
1118 anim->setStartValue( 1.0 );
1119 anim->setEndValue( 0.0 );
1120 anim->setEasingCurve( QEasingCurve::OutQuad );
1121 anim->start( QAbstractAnimation::DeleteWhenStopped );
1128 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1150 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1155 mExpressionTreeView->removeFromUserExpressions( item->text() );
1175 if ( QMessageBox::Yes
1176 == QMessageBox::question(
this, tr(
"Remove Stored Expression" ), tr(
"Do you really want to remove stored expressions '%1'?" ).arg( item->text() ), QMessageBox::Yes | QMessageBox::No ) )
1178 mExpressionTreeView->removeFromUserExpressions( item->text() );
1182void QgsExpressionBuilderWidget::exportUserExpressions_pressed()
1185 QString lastSaveDir = settings.
value( u
"lastExportExpressionsDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
1186 QString saveFileName = QFileDialog::getSaveFileName(
this, tr(
"Export User Expressions" ), lastSaveDir, tr(
"User expressions" ) +
" (*.json)" );
1191 if ( saveFileName.isEmpty() )
1194 QFileInfo saveFileInfo( saveFileName );
1196 if ( saveFileInfo.suffix().isEmpty() )
1198 QString saveFileNameWithSuffix = saveFileName.append(
".json" );
1199 saveFileInfo = QFileInfo( saveFileNameWithSuffix );
1204 QJsonDocument exportJson = mExpressionTreeView->exportUserExpressions();
1205 QFile jsonFile( saveFileName );
1207 if ( !jsonFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
1208 QMessageBox::warning(
this, tr(
"Export user expressions" ), tr(
"Error while creating the expressions file." ) );
1210 if ( !jsonFile.write( exportJson.toJson() ) )
1211 QMessageBox::warning(
this, tr(
"Export user expressions" ), tr(
"Error while creating the expressions file." ) );
1216void QgsExpressionBuilderWidget::importUserExpressions_pressed()
1218 QgsSettings settings;
1219 QString lastImportDir = settings.
value( u
"lastImportExpressionsDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
1220 QString loadFileName = QFileDialog::getOpenFileName(
this, tr(
"Import User Expressions" ), lastImportDir, tr(
"User expressions" ) +
" (*.json)" );
1222 if ( loadFileName.isEmpty() )
1225 QFileInfo loadFileInfo( loadFileName );
1229 QFile jsonFile( loadFileName );
1231 if ( !jsonFile.open( QFile::ReadOnly ) )
1232 QMessageBox::warning(
this, tr(
"Import User Expressions" ), tr(
"Error while reading the expressions file." ) );
1234 QTextStream jsonStream( &jsonFile );
1235 QString jsonString = jsonFile.readAll();
1238 QJsonDocument importJson = QJsonDocument::fromJson( jsonString.toUtf8() );
1240 if ( importJson.isNull() )
1242 QMessageBox::warning(
this, tr(
"Import User Expressions" ), tr(
"Error while reading the expressions file." ) );
1246 mExpressionTreeView->loadExpressionsFromJson( importJson );
1252 return mExpressionTreeView->findExpressions( label );
1255void QgsExpressionBuilderWidget::indicatorClicked(
int line,
int index, Qt::KeyboardModifiers state )
1257 if ( state & Qt::ControlModifier )
1259 int position = txtExpressionString->positionFromLineIndex( line, index );
1260 long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID,
static_cast<long int>( position ) );
1262 QString help = getFunctionHelp( func );
1263 txtHelpText->setText( help );
1267void QgsExpressionBuilderWidget::onExpressionParsed(
bool state )
1271 mExpressionValid = state;
1274 createMarkers( mExpressionPreviewWidget->rootNode() );
1278 createErrorMarkers( mExpressionPreviewWidget->parserErrors() );
1282QString QgsExpressionBuilderWidget::helpStylesheet()
const
1288 style +=
" .functionname {color: #0a6099; font-weight: bold;} "
1289 " .argument {font-family: monospace; color: #bf0c0c; font-style: italic; } "
1290 " td.argument { padding-right: 10px; }";
1295QString QgsExpressionBuilderWidget::loadFunctionHelp(
QgsExpressionItem *expressionItem )
1297 if ( !expressionItem )
1300 QString helpContents = expressionItem->
getHelpText();
1303 if ( helpContents.isEmpty() )
1305 QString name = expressionItem->data( Qt::UserRole ).toString();
1313 return "<head><style>" + helpStylesheet() +
"</style></head><body>" + helpContents +
"</body>";
1320QMenu *QgsExpressionBuilderWidget::ExpressionTreeMenuProvider::createContextMenu(
QgsExpressionItem *item )
1322 QMenu *menu =
nullptr;
1323 QgsVectorLayer *
layer = mExpressionBuilderWidget->layer();
1326 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.