44#include <QGraphicsOpacityEffect>
46#include <QJsonDocument>
50#include <QPropertyAnimation>
52#include <QVersionNumber>
54#include "moc_qgsexpressionbuilderwidget.cpp"
62 if ( fieldIndex != -1 )
67 return ( formatter->
flags() & QgsFieldFormatter::CanProvideAvailableValues );
82 QVBoxLayout *vl =
new QVBoxLayout();
83 vl->setContentsMargins( 0, 0, 0, 0 );
84 vl->addWidget( codeEditorWidget );
85 mExpressionEditorContainer->setLayout( vl );
87 connect( btnRun, &QToolButton::pressed,
this, &QgsExpressionBuilderWidget::btnRun_pressed );
88 connect( btnNewFile, &QPushButton::clicked,
this, &QgsExpressionBuilderWidget::btnNewFile_pressed );
89 connect( btnRemoveFile, &QPushButton::clicked,
this, &QgsExpressionBuilderWidget::btnRemoveFile_pressed );
90 connect( cmbFileNames, &QListWidget::currentItemChanged,
this, &QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged );
91 connect( txtExpressionString, &QgsCodeEditorExpression::textChanged,
this, &QgsExpressionBuilderWidget::txtExpressionString_textChanged );
92 connect( txtPython, &QgsCodeEditorPython::textChanged,
this, &QgsExpressionBuilderWidget::txtPython_textChanged );
93 connect( txtSearchEditValues, &QgsFilterLineEdit::textChanged,
this, &QgsExpressionBuilderWidget::txtSearchEditValues_textChanged );
94 connect( mValuesListView, &QListView::doubleClicked,
this, &QgsExpressionBuilderWidget::mValuesListView_doubleClicked );
98 connect( btnImportExpressions, &QToolButton::clicked,
this, &QgsExpressionBuilderWidget::importUserExpressions_pressed );
99 connect( btnExportExpressions, &QToolButton::clicked,
this, &QgsExpressionBuilderWidget::exportUserExpressions_pressed );
100 connect( btnClearEditor, &QToolButton::clicked, txtExpressionString, &QgsCodeEditorExpression::clear );
113 mExpressionTreeMenuProvider =
new ExpressionTreeMenuProvider(
this );
114 mExpressionTreeView->setMenuProvider( mExpressionTreeMenuProvider );
116 txtHelpText->setOpenExternalLinks(
true );
117 mValueGroupBox->hide();
134 const auto pushButtons { mOperatorsGroupBox->findChildren<QPushButton *>() };
135 for ( QPushButton *button : pushButtons )
137 connect( button, &QAbstractButton::clicked,
this, &QgsExpressionBuilderWidget::operatorButtonClicked );
140 connect( btnCommentLinePushButton, &QAbstractButton::clicked,
this, &QgsExpressionBuilderWidget::commentLinesClicked );
142 txtSearchEdit->setShowSearchIcon(
true );
143 txtSearchEdit->setPlaceholderText( tr(
"Search…" ) );
145 mValuesModel = std::make_unique<QStandardItemModel>();
146 mProxyValues = std::make_unique<QSortFilterProxyModel>();
147 mProxyValues->setSourceModel( mValuesModel.get() );
148 mValuesListView->setModel( mProxyValues.get() );
149 txtSearchEditValues->setShowSearchIcon(
true );
150 txtSearchEditValues->setPlaceholderText( tr(
"Search…" ) );
152 editorSplit->setSizes( QList<int>( { 175, 300 } ) );
154 functionsplit->setCollapsible( 0,
false );
155 connect( mShowHelpButton, &QPushButton::clicked,
this, [
this]() {
156 functionsplit->setSizes( QList<int>( { mOperationListGroup->width() - mHelpAndValuesWidget->minimumWidth(), mHelpAndValuesWidget->minimumWidth() } ) );
157 mShowHelpButton->setEnabled(
false );
159 connect( functionsplit, &QSplitter::splitterMoved,
this, [
this](
int,
int ) {
160 mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
164 splitter->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/splitter" ) ).toByteArray() );
165 editorSplit->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/editorsplitter" ) ).toByteArray() );
166 functionsplit->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/functionsplitter" ) ).toByteArray() );
167 mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
173 btnRemoveFile->setEnabled( cmbFileNames->count() > 0 );
180 txtExpressionString->setWrapMode( QsciScintilla::WrapWord );
181 lblAutoSave->
clear();
188#if defined( QSCINTILLA_VERSION ) && QSCINTILLA_VERSION >= 0x20a00
195 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::red ), -1 );
196 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::red ), -1 );
197 txtExpressionString->setIndicatorOutlineColor( QColor( Qt::red ), -1 );
200 txtExpressionString->indicatorDefine( QgsCodeEditor::HiddenIndicator, FUNCTION_MARKER_ID );
201 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
202 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
203 txtExpressionString->setIndicatorHoverStyle( QgsCodeEditor::DotsIndicator, FUNCTION_MARKER_ID );
205 connect( txtExpressionString, &QgsCodeEditorExpression::indicatorClicked,
this, &QgsExpressionBuilderWidget::indicatorClicked );
206 txtExpressionString->setAutoCompletionCaseSensitivity(
false );
207 txtExpressionString->setAutoCompletionSource( QsciScintilla::AcsAPIs );
208 txtExpressionString->setCallTipsVisible( 0 );
211 mFunctionBuilderHelp->setLineNumbersVisible(
false );
212 mFunctionBuilderHelp->setFoldingVisible(
false );
213 mFunctionBuilderHelp->setEdgeMode( QsciScintilla::EdgeNone );
214 mFunctionBuilderHelp->setEdgeColumn( 0 );
215 mFunctionBuilderHelp->setReadOnly(
true );
216 mFunctionBuilderHelp->setText( tr(
"\"\"\"Define a new function using the @qgsfunction decorator.\n\
218 Besides its normal arguments, the function may specify the following arguments in its signature\n\
219 Those will not need to be specified when calling the function, but will be automatically injected \n\
221 : param feature: The current feature\n\
222 : param parent: The QgsExpression object\n\
223 : param context: ``QgsExpressionContext`` object, that gives access to various additional information like\n\
224 expression variables. E.g. ``context.variable( 'layer_id' )``\n\
225 : returns: The result of the expression.\n\
229 The @qgsfunction decorator accepts the following arguments:\n\
232 : param group: The name of the group under which this expression function will\n\
234 : param handlesnull: Set this to True if your function has custom handling for NULL values.\n\
235 If False, the result will always be NULL as soon as any parameter is NULL.\n\
236 Defaults to False.\n\
237 : param usesgeometry : Set this to True if your function requires access to\n\
238 feature.geometry(). Defaults to False.\n\
239 : param referenced_columns: An array of attribute names that are required to run\n\
240 this function. Defaults to [QgsFeatureRequest.ALL_ATTRIBUTES].\n\
241 : param params_as_list : Set this to True to pass the function parameters as a list. Can be used to mimic \n\
242 behavior before 3.32, when args was not \"auto\". Defaults to False.\n\
250 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/splitter" ), splitter->saveState() );
251 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/editorsplitter" ), editorSplit->saveState() );
252 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/functionsplitter" ), functionsplit->saveState() );
253 delete mExpressionTreeMenuProvider;
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 ) == QLatin1String(
"project" ) )
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( QLatin1String(
".py" ) ) )
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#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
399 myFileStream.setCodec(
"UTF-8" );
401 myFileStream << txtPython->text() << Qt::endl;
408 mProject->writeEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ), txtPython->text() );
413 mFunctionsPath = path;
415 dir.setNameFilters( QStringList() << QStringLiteral(
"*.py" ) );
416 QStringList files = dir.entryList( QDir::Files );
417 cmbFileNames->clear();
418 const auto constFiles = files;
419 for (
const QString &name : constFiles )
421 QFileInfo info( mFunctionsPath + QDir::separator() + name );
422 if ( info.baseName() == QLatin1String(
"__init__" ) )
424 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"console/iconTabEditorConsole.svg" ) ), info.baseName() );
425 cmbFileNames->addItem( item );
429 mProject->readEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ), QString(), &ok );
432 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"mIconQgsProjectFile.svg" ) ), DEFAULT_PROJECT_FUNCTIONS_ITEM_NAME );
433 item->setData( Qt::UserRole, QStringLiteral(
"project" ) );
434 cmbFileNames->insertItem( 0, item );
437 if ( !cmbFileNames->currentItem() )
439 cmbFileNames->setCurrentRow( 0 );
442 if ( cmbFileNames->count() == 0 )
446 txtPython->setText( QStringLiteral(
"'''\n#Sample custom function file\n"
447 "#(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() )
459 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"console/iconTabEditorConsole.svg" ) ), fileName );
460 cmbFileNames->insertItem( 0, item );
461 cmbFileNames->setCurrentRow( 0 );
463 QString templateText;
465 txtPython->setText( templateText );
469void QgsExpressionBuilderWidget::btnNewFile_pressed()
476 mProject->readEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ), QString(), &ok );
479 if ( dlg.exec() == QDialog::DialogCode::Accepted )
483 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"mIconQgsProjectFile.svg" ) ), DEFAULT_PROJECT_FUNCTIONS_ITEM_NAME );
484 item->setData( Qt::UserRole, QStringLiteral(
"project" ) );
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 ) == QLatin1String(
"project" ) )
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 ) == QMessageBox::No )
508 mProject->removeEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ) );
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 ) == QLatin1String(
"project" ) )
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 ) != QLatin1String(
"project" ) )
554 QString filename = lastitem->text();
559 if ( item->data( Qt::UserRole ) == QLatin1String(
"project" ) )
562 txtPython->blockSignals(
true );
564 txtPython->blockSignals(
false );
566 btnRun->setText( tr(
"Load or update functions" ) );
567 btnRun->setToolTip( tr(
"Loads or updates functions from current script file in QGIS.\n"
569 "Note the functions will only be stored when saving the project." ) );
573 QString path = mFunctionsPath + QDir::separator() + item->text();
577 btnRun->setText( tr(
"Load or update functions" ) );
578 btnRun->setToolTip( tr(
"Loads or updates functions from current script file in QGIS.\n"
580 "Saved scripts are auto loaded on QGIS startup." ) );
584 btnRun->setText( tr(
"Save and Load Functions" ) );
585 btnRun->setToolTip( tr(
"Saves current script file and loads or updates its functions in QGIS.\n"
587 "Saved scripts are auto loaded on QGIS startup." ) );
594 if ( !path.endsWith( QLatin1String(
".py" ) ) )
595 path.append(
".py" );
597 txtPython->loadScript( path );
602 loadFunctionCode( mProject->readEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ) ) );
607 txtPython->setText( code );
610void QgsExpressionBuilderWidget::insertExpressionText(
const QString &text )
614 txtExpressionString->setFocus();
619 Q_UNUSED( fieldValues )
623void QgsExpressionBuilderWidget::fillFieldValues(
const QString &fieldName,
QgsVectorLayer *layer,
int countLimit,
bool forceUsedValues )
635 if ( fieldIndex < 0 )
642 if ( cbxValuesInUse->isVisible() && !cbxValuesInUse->isChecked() && !forceUsedValues )
650 values = qgis::setToList(
layer->uniqueValues( fieldIndex, countLimit ) );
652 std::sort( values.begin(), values.end() );
654 mValuesModel->clear();
655 for (
const QVariant &value : std::as_const( values ) )
658 bool forceRepresentedValue =
false;
660 strValue = QStringLiteral(
"NULL" );
661 else if ( value.userType() == QMetaType::Type::Int || value.userType() == QMetaType::Type::Double || value.userType() == QMetaType::Type::LongLong || value.userType() == QMetaType::Type::Bool )
662 strValue = value.toString();
663 else if ( value.userType() == QMetaType::Type::QStringList )
666 const QStringList strList = value.toStringList();
667 for ( QString str : strList )
669 if ( !result.isEmpty() )
670 result.append( QStringLiteral(
", " ) );
672 result.append(
'\'' + str.replace(
'\'', QLatin1String(
"''" ) ) +
'\'' );
674 strValue = QStringLiteral(
"array(%1)" ).arg( result );
675 forceRepresentedValue =
true;
677 else if ( value.userType() == QMetaType::Type::QVariantList )
680 const QList list = value.toList();
681 for (
const QVariant &item : list )
683 if ( !result.isEmpty() )
684 result.append( QStringLiteral(
", " ) );
686 result.append( item.toString() );
688 strValue = QStringLiteral(
"array(%1)" ).arg( result );
689 forceRepresentedValue =
true;
692 strValue =
'\'' + value.toString().replace(
'\'', QLatin1String(
"''" ) ) +
'\'';
695 if ( forceRepresentedValue || representedValue != value.toString() )
696 representedValue = representedValue + QStringLiteral(
" [" ) + strValue +
']';
698 QStandardItem *item =
new QStandardItem( representedValue );
699 item->setData( strValue );
700 mValuesModel->appendRow( item );
711 return QStringLiteral(
"<head><style>" ) + helpStylesheet() + QStringLiteral(
"</style></head><body>" ) + helpContents + QStringLiteral(
"</body>" );
717 return mExpressionValid;
722 mExpressionPreviewWidget->setCustomPreviewGenerator( label, choices, previewContextGenerator );
727 mExpressionTreeView->saveToRecent(
expressionText(), collection );
732 mExpressionTreeView->loadRecent( collection );
737 return mExpressionTreeView;
748 mExpressionTreeView->saveToUserExpressions( label, expression, helpText );
753 mExpressionTreeView->removeFromUserExpressions( label );
759 mExpressionPreviewWidget->setGeomCalculator( da );
764 return txtExpressionString->text();
769 txtExpressionString->setText( expression );
774 return lblExpected->text();
779 lblExpected->setText( expected );
780 mExpectedOutputFrame->setVisible( !expected.isNull() );
785 mExpressionContext = context;
786 expressionContextUpdated();
789void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
793 btnClearEditor->setEnabled( !txtExpressionString->text().isEmpty() );
794 btnSaveExpression->setEnabled(
false );
796 mExpressionPreviewWidget->setExpressionText( text );
801 return mExpressionPreviewWidget->parserError();
806 mExpressionPreviewWidget->setVisible( isVisible );
811 return mExpressionPreviewWidget->evalError();
817 return mExpressionTreeView->model();
829 mExpressionTreeView->setProject(
project );
834 QWidget::showEvent( e );
835 txtExpressionString->setFocus();
838void QgsExpressionBuilderWidget::createErrorMarkers(
const QList<QgsExpression::ParserError> &errors )
843 int errorFirstLine = error.firstLine - 1;
844 int errorFirstColumn = error.firstColumn - 1;
845 int errorLastColumn = error.lastColumn - 1;
846 int errorLastLine = error.lastLine - 1;
852 errorFirstLine = errorLastLine;
853 errorFirstColumn = errorLastColumn - 1;
855 txtExpressionString->fillIndicatorRange( errorFirstLine, errorFirstColumn, errorLastLine, errorLastColumn, error.errorType );
865 const QgsExpressionNodeFunction *node =
static_cast<const QgsExpressionNodeFunction *
>( inNode );
866 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
867 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->
fnIndex() );
870 int start_pos = txtExpressionString->positionFromLineIndex( inNode->
parserFirstLine - 1, start );
871 txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
874 const QList<QgsExpressionNode *> nodeList = node->
args()->
list();
875 for ( QgsExpressionNode *n : nodeList )
888 const QgsExpressionNodeUnaryOperator *node =
static_cast<const QgsExpressionNodeUnaryOperator *
>( inNode );
889 createMarkers( node->
operand() );
894 const QgsExpressionNodeBetweenOperator *node =
static_cast<const QgsExpressionNodeBetweenOperator *
>( inNode );
901 const QgsExpressionNodeBinaryOperator *node =
static_cast<const QgsExpressionNodeBinaryOperator *
>( inNode );
902 createMarkers( node->
opLeft() );
903 createMarkers( node->
opRight() );
912 const QgsExpressionNodeInOperator *node =
static_cast<const QgsExpressionNodeInOperator *
>( inNode );
915 const QList<QgsExpressionNode *> nodeList = node->
list()->
list();
916 for ( QgsExpressionNode *n : nodeList )
925 const QgsExpressionNodeCondition *node =
static_cast<const QgsExpressionNodeCondition *
>( inNode );
926 const QList<QgsExpressionNodeCondition::WhenThen *> conditions = node->
conditions();
927 for ( QgsExpressionNodeCondition::WhenThen *cond : conditions )
929 createMarkers( cond->whenExp() );
930 createMarkers( cond->thenExp() );
934 createMarkers( node->
elseExp() );
945void QgsExpressionBuilderWidget::clearFunctionMarkers()
947 int lastLine = txtExpressionString->lines() - 1;
948 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
951void QgsExpressionBuilderWidget::clearErrors()
953 int lastLine = txtExpressionString->lines() - 1;
962void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
964 mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
965 mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
968void QgsExpressionBuilderWidget::mValuesListView_doubleClicked(
const QModelIndex &index )
971 txtExpressionString->insertText(
' ' + index.data( Qt::UserRole + 1 ).toString() +
' ' );
972 txtExpressionString->setFocus();
975void QgsExpressionBuilderWidget::operatorButtonClicked()
977 QPushButton *button = qobject_cast<QPushButton *>( sender() );
980 txtExpressionString->insertText(
' ' + button->text() +
' ' );
981 txtExpressionString->setFocus();
984void QgsExpressionBuilderWidget::commentLinesClicked()
986 txtExpressionString->toggleComment();
1005 mValueGroupBox->show();
1025 mValueGroupBox->show();
1045 mValueGroupBox->show();
1065 mValueGroupBox->show();
1069void QgsExpressionBuilderWidget::txtPython_textChanged()
1071 if ( tabWidget->currentIndex() != 1 )
1074 QListWidgetItem *item = cmbFileNames->currentItem();
1078 if ( item->data( Qt::UserRole ) == QLatin1String(
"project" ) )
1081 displayTemporaryLabel( tr(
"Project changed" ) );
1083 else if ( mAutoSave )
1092 if ( tabWidget->currentIndex() != 1 )
1095 QListWidgetItem *item = cmbFileNames->currentItem();
1099 if ( item->data( Qt::UserRole ) != QLatin1String(
"project" ) )
1101 QString file = item->text();
1103 displayTemporaryLabel( tr(
"Function file saved" ) );
1107void QgsExpressionBuilderWidget::displayTemporaryLabel(
const QString &text )
1109 lblAutoSave->setText( text );
1110 QGraphicsOpacityEffect *effect =
new QGraphicsOpacityEffect();
1111 lblAutoSave->setGraphicsEffect( effect );
1112 QPropertyAnimation *anim =
new QPropertyAnimation( effect,
"opacity" );
1113 anim->setDuration( 2000 );
1114 anim->setStartValue( 1.0 );
1115 anim->setEndValue( 0.0 );
1116 anim->setEasingCurve( QEasingCurve::OutQuad );
1117 anim->start( QAbstractAnimation::DeleteWhenStopped );
1124 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1146 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1151 mExpressionTreeView->removeFromUserExpressions( item->text() );
1171 if ( QMessageBox::Yes == QMessageBox::question(
this, tr(
"Remove Stored Expression" ), tr(
"Do you really want to remove stored expressions '%1'?" ).arg( item->text() ), QMessageBox::Yes | QMessageBox::No ) )
1173 mExpressionTreeView->removeFromUserExpressions( item->text() );
1177void QgsExpressionBuilderWidget::exportUserExpressions_pressed()
1180 QString lastSaveDir = settings.
value( QStringLiteral(
"lastExportExpressionsDir" ), QDir::homePath(),
QgsSettings::App ).toString();
1181 QString saveFileName = QFileDialog::getSaveFileName(
1183 tr(
"Export User Expressions" ),
1185 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( QStringLiteral(
"lastImportExpressionsDir" ), QDir::homePath(),
QgsSettings::App ).toString();
1220 QString loadFileName = QFileDialog::getOpenFileName(
1222 tr(
"Import User Expressions" ),
1224 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.