21#include <QGraphicsOpacityEffect>
22#include <QPropertyAnimation>
24#include <QVersionNumber>
26#include <QJsonDocument>
33#include "moc_qgsexpressionbuilderwidget.cpp"
61 if ( fieldIndex != -1 )
66 return ( formatter->
flags() & QgsFieldFormatter::CanProvideAvailableValues );
81 QVBoxLayout *vl =
new QVBoxLayout();
82 vl->setContentsMargins( 0, 0, 0, 0 );
83 vl->addWidget( codeEditorWidget );
84 mExpressionEditorContainer->setLayout( vl );
86 connect( btnRun, &QToolButton::pressed,
this, &QgsExpressionBuilderWidget::btnRun_pressed );
87 connect( btnNewFile, &QPushButton::clicked,
this, &QgsExpressionBuilderWidget::btnNewFile_pressed );
88 connect( btnRemoveFile, &QPushButton::clicked,
this, &QgsExpressionBuilderWidget::btnRemoveFile_pressed );
89 connect( cmbFileNames, &QListWidget::currentItemChanged,
this, &QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged );
90 connect( txtExpressionString, &QgsCodeEditorExpression::textChanged,
this, &QgsExpressionBuilderWidget::txtExpressionString_textChanged );
91 connect( txtPython, &QgsCodeEditorPython::textChanged,
this, &QgsExpressionBuilderWidget::txtPython_textChanged );
92 connect( txtSearchEditValues, &QgsFilterLineEdit::textChanged,
this, &QgsExpressionBuilderWidget::txtSearchEditValues_textChanged );
93 connect( mValuesListView, &QListView::doubleClicked,
this, &QgsExpressionBuilderWidget::mValuesListView_doubleClicked );
97 connect( btnImportExpressions, &QToolButton::clicked,
this, &QgsExpressionBuilderWidget::importUserExpressions_pressed );
98 connect( btnExportExpressions, &QToolButton::clicked,
this, &QgsExpressionBuilderWidget::exportUserExpressions_pressed );
99 connect( btnClearEditor, &QToolButton::clicked, txtExpressionString, &QgsCodeEditorExpression::clear );
112 mExpressionTreeMenuProvider =
new ExpressionTreeMenuProvider(
this );
113 mExpressionTreeView->setMenuProvider( mExpressionTreeMenuProvider );
115 txtHelpText->setOpenExternalLinks(
true );
116 mValueGroupBox->hide();
133 const auto pushButtons { mOperatorsGroupBox->findChildren<QPushButton *>() };
134 for ( QPushButton *button : pushButtons )
136 connect( button, &QAbstractButton::clicked,
this, &QgsExpressionBuilderWidget::operatorButtonClicked );
139 connect( btnCommentLinePushButton, &QAbstractButton::clicked,
this, &QgsExpressionBuilderWidget::commentLinesClicked );
141 txtSearchEdit->setShowSearchIcon(
true );
142 txtSearchEdit->setPlaceholderText( tr(
"Search…" ) );
144 mValuesModel = std::make_unique<QStandardItemModel>();
145 mProxyValues = std::make_unique<QSortFilterProxyModel>();
146 mProxyValues->setSourceModel( mValuesModel.get() );
147 mValuesListView->setModel( mProxyValues.get() );
148 txtSearchEditValues->setShowSearchIcon(
true );
149 txtSearchEditValues->setPlaceholderText( tr(
"Search…" ) );
151 editorSplit->setSizes( QList<int>( { 175, 300 } ) );
153 functionsplit->setCollapsible( 0,
false );
154 connect( mShowHelpButton, &QPushButton::clicked,
this, [=]() {
155 functionsplit->setSizes( QList<int>( { mOperationListGroup->width() - mHelpAndValuesWidget->minimumWidth(), mHelpAndValuesWidget->minimumWidth() } ) );
156 mShowHelpButton->setEnabled(
false );
158 connect( functionsplit, &QSplitter::splitterMoved,
this, [=](
int,
int ) {
159 mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
163 splitter->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/splitter" ) ).toByteArray() );
164 editorSplit->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/editorsplitter" ) ).toByteArray() );
165 functionsplit->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/functionsplitter" ) ).toByteArray() );
166 mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
172 btnRemoveFile->setEnabled( cmbFileNames->count() > 0 );
179 txtExpressionString->setWrapMode( QsciScintilla::WrapWord );
180 lblAutoSave->clear();
187#if defined( QSCINTILLA_VERSION ) && QSCINTILLA_VERSION >= 0x20a00
194 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::red ), -1 );
195 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::red ), -1 );
196 txtExpressionString->setIndicatorOutlineColor( QColor( Qt::red ), -1 );
199 txtExpressionString->indicatorDefine( QgsCodeEditor::HiddenIndicator, FUNCTION_MARKER_ID );
200 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
201 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
202 txtExpressionString->setIndicatorHoverStyle( QgsCodeEditor::DotsIndicator, FUNCTION_MARKER_ID );
204 connect( txtExpressionString, &QgsCodeEditorExpression::indicatorClicked,
this, &QgsExpressionBuilderWidget::indicatorClicked );
205 txtExpressionString->setAutoCompletionCaseSensitivity(
false );
206 txtExpressionString->setAutoCompletionSource( QsciScintilla::AcsAPIs );
207 txtExpressionString->setCallTipsVisible( 0 );
210 mFunctionBuilderHelp->setLineNumbersVisible(
false );
211 mFunctionBuilderHelp->setFoldingVisible(
false );
212 mFunctionBuilderHelp->setEdgeMode( QsciScintilla::EdgeNone );
213 mFunctionBuilderHelp->setEdgeColumn( 0 );
214 mFunctionBuilderHelp->setReadOnly(
true );
215 mFunctionBuilderHelp->setText( tr(
"\"\"\"Define a new function using the @qgsfunction decorator.\n\
217 Besides its normal arguments, the function may specify the following arguments in its signature\n\
218 Those will not need to be specified when calling the function, but will be automatically injected \n\
220 : param feature: The current feature\n\
221 : param parent: The QgsExpression object\n\
222 : param context: ``QgsExpressionContext`` object, that gives access to various additional information like\n\
223 expression variables. E.g. ``context.variable( 'layer_id' )``\n\
224 : returns: The result of the expression.\n\
228 The @qgsfunction decorator accepts the following arguments:\n\
231 : param group: The name of the group under which this expression function will\n\
233 : param handlesnull: Set this to True if your function has custom handling for NULL values.\n\
234 If False, the result will always be NULL as soon as any parameter is NULL.\n\
235 Defaults to False.\n\
236 : param usesgeometry : Set this to True if your function requires access to\n\
237 feature.geometry(). Defaults to False.\n\
238 : param referenced_columns: An array of attribute names that are required to run\n\
239 this function. Defaults to [QgsFeatureRequest.ALL_ATTRIBUTES].\n\
240 : param params_as_list : Set this to True to pass the function parameters as a list. Can be used to mimic \n\
241 behavior before 3.32, when args was not \"auto\". Defaults to False.\n\
249 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/splitter" ), splitter->saveState() );
250 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/editorsplitter" ), editorSplit->saveState() );
251 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/functionsplitter" ), functionsplit->saveState() );
252 delete mExpressionTreeMenuProvider;
260 mExpressionTreeView->loadRecent( recentCollection );
263 mExpressionTreeView->loadUserExpressions();
268 init( context, recentCollection, flags );
274 init( context, recentCollection, flags );
275 mExpressionTreeView->loadFieldNames( fields );
282 mExpressionTreeView->setLayer( mLayer );
283 mExpressionPreviewWidget->setLayer( mLayer );
290 expressionContextUpdated();
295void QgsExpressionBuilderWidget::expressionContextUpdated()
298 mExpressionTreeView->setExpressionContext( mExpressionContext );
299 mExpressionPreviewWidget->setExpressionContext( mExpressionContext );
307void QgsExpressionBuilderWidget::expressionTreeItemChanged(
QgsExpressionItem *item )
309 txtSearchEditValues->clear();
317 mValuesModel->clear();
320 cbxValuesInUse->setChecked(
false );
322 mValueGroupBox->setVisible( isField );
324 mShowHelpButton->setText( isField ? tr(
"Show Values" ) : tr(
"Show Help" ) );
327 QString help = loadFunctionHelp( item );
328 txtHelpText->setText( help );
330 bool isUserExpression = item->parent() && item->parent()->text() == mUserExpressionsGroupName;
332 btnRemoveExpression->setEnabled( isUserExpression );
333 btnEditExpression->setEnabled( isUserExpression );
336void QgsExpressionBuilderWidget::btnRun_pressed()
338 if ( !cmbFileNames->currentItem() )
341 if ( cmbFileNames->currentItem()->data( Qt::UserRole ) == QLatin1String(
"project" ) )
347 QString file = cmbFileNames->currentItem()->text();
351 runPythonCode( txtPython->text() );
354void QgsExpressionBuilderWidget::runPythonCode(
const QString &code )
358 QString pythontext = code;
361 mExpressionTreeView->refresh();
381 QDir myDir( mFunctionsPath );
382 if ( !myDir.exists() )
384 myDir.mkpath( mFunctionsPath );
387 if ( !fileName.endsWith( QLatin1String(
".py" ) ) )
389 fileName.append(
".py" );
392 fileName = mFunctionsPath + QDir::separator() + fileName;
393 QFile myFile( fileName );
394 if ( myFile.open( QIODevice::WriteOnly | QFile::Truncate ) )
396 QTextStream myFileStream( &myFile );
397#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
398 myFileStream.setCodec(
"UTF-8" );
400 myFileStream << txtPython->text() << Qt::endl;
407 mProject->writeEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ), txtPython->text() );
412 mFunctionsPath = path;
414 dir.setNameFilters( QStringList() << QStringLiteral(
"*.py" ) );
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() == QLatin1String(
"__init__" ) )
423 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"console/iconTabEditorConsole.svg" ) ), info.baseName() );
424 cmbFileNames->addItem( item );
428 mProject->readEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ), QString(), &ok );
431 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"mIconQgsProjectFile.svg" ) ), DEFAULT_PROJECT_FUNCTIONS_ITEM_NAME );
432 item->setData( Qt::UserRole, QStringLiteral(
"project" ) );
433 cmbFileNames->insertItem( 0, item );
436 if ( !cmbFileNames->currentItem() )
438 cmbFileNames->setCurrentRow( 0 );
441 if ( cmbFileNames->count() == 0 )
445 txtPython->setText( QStringLiteral(
"'''\n#Sample custom function file\n"
446 "#(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() )
458 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"console/iconTabEditorConsole.svg" ) ), fileName );
459 cmbFileNames->insertItem( 0, item );
460 cmbFileNames->setCurrentRow( 0 );
462 QString templateText;
464 txtPython->setText( templateText );
468void QgsExpressionBuilderWidget::btnNewFile_pressed()
475 mProject->readEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ), QString(), &ok );
478 if ( dlg.exec() == QDialog::DialogCode::Accepted )
480 if ( dlg.createProjectFunctions() )
482 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"mIconQgsProjectFile.svg" ) ), DEFAULT_PROJECT_FUNCTIONS_ITEM_NAME );
483 item->setData( Qt::UserRole, QStringLiteral(
"project" ) );
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 ) == QLatin1String(
"project" ) )
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 ) == QMessageBox::No )
507 mProject->removeEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ) );
511 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 )
514 QString fileName = cmbFileNames->currentItem()->text();
515 if ( !QFile::remove( mFunctionsPath + QDir::separator() + fileName.append(
".py" ) ) )
517 QMessageBox::warning(
this, tr(
"Remove file" ), tr(
"Failed to remove function file '%1'." ).arg( fileName ) );
522 int currentRow = cmbFileNames->currentRow();
524 QListWidgetItem *itemToRemove =
whileBlocking( cmbFileNames )->takeItem( currentRow );
528 if ( cmbFileNames->count() > 0 )
530 whileBlocking( cmbFileNames )->setCurrentRow( currentRow > 0 ? currentRow - 1 : 0 );
531 if ( cmbFileNames->currentItem()->data( Qt::UserRole ) == QLatin1String(
"project" ) )
537 loadCodeFromFile( mFunctionsPath + QDir::separator() + cmbFileNames->currentItem()->text() );
542 btnRemoveFile->setEnabled(
false );
547void QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem )
551 if ( lastitem->data( Qt::UserRole ) == QLatin1String(
"project" ) )
557 QString filename = lastitem->text();
562 if ( item->data( Qt::UserRole ) == QLatin1String(
"project" ) )
568 QString path = mFunctionsPath + QDir::separator() + item->text();
575 if ( !path.endsWith( QLatin1String(
".py" ) ) )
576 path.append(
".py" );
578 txtPython->loadScript( path );
583 loadFunctionCode( mProject->readEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ) ) );
588 txtPython->setText( code );
591void QgsExpressionBuilderWidget::insertExpressionText(
const QString &text )
595 txtExpressionString->setFocus();
600 Q_UNUSED( fieldValues )
604void QgsExpressionBuilderWidget::fillFieldValues(
const QString &fieldName,
QgsVectorLayer *layer,
int countLimit,
bool forceUsedValues )
616 if ( fieldIndex < 0 )
623 if ( cbxValuesInUse->isVisible() && !cbxValuesInUse->isChecked() && !forceUsedValues )
633 std::sort( values.begin(), values.end() );
635 mValuesModel->clear();
636 for (
const QVariant &value : std::as_const( values ) )
639 bool forceRepresentedValue =
false;
641 strValue = QStringLiteral(
"NULL" );
642 else if ( value.userType() == QMetaType::Type::Int || value.userType() == QMetaType::Type::Double || value.userType() == QMetaType::Type::LongLong || value.userType() == QMetaType::Type::Bool )
643 strValue = value.toString();
644 else if ( value.userType() == QMetaType::Type::QStringList )
647 const QStringList strList = value.toStringList();
648 for ( QString str : strList )
650 if ( !result.isEmpty() )
651 result.append( QStringLiteral(
", " ) );
653 result.append(
'\'' + str.replace(
'\'', QLatin1String(
"''" ) ) +
'\'' );
655 strValue = QStringLiteral(
"array(%1)" ).arg( result );
656 forceRepresentedValue =
true;
658 else if ( value.userType() == QMetaType::Type::QVariantList )
661 const QList list = value.toList();
662 for (
const QVariant &item : list )
664 if ( !result.isEmpty() )
665 result.append( QStringLiteral(
", " ) );
667 result.append( item.toString() );
669 strValue = QStringLiteral(
"array(%1)" ).arg( result );
670 forceRepresentedValue =
true;
673 strValue =
'\'' + value.toString().replace(
'\'', QLatin1String(
"''" ) ) +
'\'';
676 if ( forceRepresentedValue || representedValue != value.toString() )
677 representedValue = representedValue + QStringLiteral(
" [" ) + strValue +
']';
679 QStandardItem *item =
new QStandardItem( representedValue );
680 item->setData( strValue );
681 mValuesModel->appendRow( item );
692 return QStringLiteral(
"<head><style>" ) + helpStylesheet() + QStringLiteral(
"</style></head><body>" ) + helpContents + QStringLiteral(
"</body>" );
698 return mExpressionValid;
703 mExpressionPreviewWidget->setCustomPreviewGenerator( label, choices, previewContextGenerator );
708 mExpressionTreeView->saveToRecent(
expressionText(), collection );
713 mExpressionTreeView->loadRecent( collection );
718 return mExpressionTreeView;
729 mExpressionTreeView->saveToUserExpressions( label, expression, helpText );
734 mExpressionTreeView->removeFromUserExpressions( label );
740 mExpressionPreviewWidget->setGeomCalculator( da );
745 return txtExpressionString->text();
750 txtExpressionString->
setText( expression );
755 return lblExpected->text();
760 lblExpected->setText( expected );
761 mExpectedOutputFrame->setVisible( !expected.isNull() );
766 mExpressionContext = context;
767 expressionContextUpdated();
770void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
774 btnClearEditor->setEnabled( !txtExpressionString->text().isEmpty() );
775 btnSaveExpression->setEnabled(
false );
777 mExpressionPreviewWidget->setExpressionText( text );
782 return mExpressionPreviewWidget->parserError();
787 mExpressionPreviewWidget->setVisible( isVisible );
792 return mExpressionPreviewWidget->evalError();
798 return mExpressionTreeView->model();
810 mExpressionTreeView->setProject(
project );
815 QWidget::showEvent( e );
816 txtExpressionString->setFocus();
819void QgsExpressionBuilderWidget::createErrorMarkers(
const QList<QgsExpression::ParserError> &errors )
824 int errorFirstLine = error.firstLine - 1;
825 int errorFirstColumn = error.firstColumn - 1;
826 int errorLastColumn = error.lastColumn - 1;
827 int errorLastLine = error.lastLine - 1;
833 errorFirstLine = errorLastLine;
834 errorFirstColumn = errorLastColumn - 1;
836 txtExpressionString->fillIndicatorRange( errorFirstLine, errorFirstColumn, errorLastLine, errorLastColumn, error.errorType );
847 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
848 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->
fnIndex() );
851 int start_pos = txtExpressionString->positionFromLineIndex( inNode->
parserFirstLine - 1, start );
852 txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
855 const QList<QgsExpressionNode *> nodeList = node->
args()->
list();
870 createMarkers( node->
operand() );
883 createMarkers( node->
opLeft() );
884 createMarkers( node->
opRight() );
896 const QList<QgsExpressionNode *> nodeList = node->
list()->
list();
907 const QList<QgsExpressionNodeCondition::WhenThen *> conditions = node->
conditions();
910 createMarkers( cond->whenExp() );
911 createMarkers( cond->thenExp() );
915 createMarkers( node->
elseExp() );
926void QgsExpressionBuilderWidget::clearFunctionMarkers()
928 int lastLine = txtExpressionString->lines() - 1;
929 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
932void QgsExpressionBuilderWidget::clearErrors()
934 int lastLine = txtExpressionString->lines() - 1;
943void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
945 mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
946 mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
949void QgsExpressionBuilderWidget::mValuesListView_doubleClicked(
const QModelIndex &index )
952 txtExpressionString->
insertText(
' ' + index.data( Qt::UserRole + 1 ).toString() +
' ' );
953 txtExpressionString->setFocus();
956void QgsExpressionBuilderWidget::operatorButtonClicked()
958 QPushButton *button = qobject_cast<QPushButton *>( sender() );
961 txtExpressionString->
insertText(
' ' + button->text() +
' ' );
962 txtExpressionString->setFocus();
965void QgsExpressionBuilderWidget::commentLinesClicked()
986 mValueGroupBox->show();
1006 mValueGroupBox->show();
1026 mValueGroupBox->show();
1046 mValueGroupBox->show();
1050void QgsExpressionBuilderWidget::txtPython_textChanged()
1052 lblAutoSave->setText( tr(
"Saving…" ) );
1062 if ( tabWidget->currentIndex() != 1 )
1065 QListWidgetItem *item = cmbFileNames->currentItem();
1069 if ( item->data( Qt::UserRole ) == QLatin1String(
"project" ) )
1075 QString file = item->text();
1079 lblAutoSave->setText( QStringLiteral(
"Saved" ) );
1080 QGraphicsOpacityEffect *effect =
new QGraphicsOpacityEffect();
1081 lblAutoSave->setGraphicsEffect( effect );
1082 QPropertyAnimation *anim =
new QPropertyAnimation( effect,
"opacity" );
1083 anim->setDuration( 2000 );
1084 anim->setStartValue( 1.0 );
1085 anim->setEndValue( 0.0 );
1086 anim->setEasingCurve( QEasingCurve::OutQuad );
1087 anim->start( QAbstractAnimation::DeleteWhenStopped );
1094 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1096 mExpressionTreeView->saveToUserExpressions( dlg.label().simplified(), dlg.expression(), dlg.helpText() );
1116 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1119 if ( dlg.isLabelModified() )
1121 mExpressionTreeView->removeFromUserExpressions( item->text() );
1124 mExpressionTreeView->saveToUserExpressions( dlg.label().simplified(), dlg.expression(), dlg.helpText() );
1141 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 ) )
1143 mExpressionTreeView->removeFromUserExpressions( item->text() );
1147void QgsExpressionBuilderWidget::exportUserExpressions_pressed()
1150 QString lastSaveDir = settings.
value( QStringLiteral(
"lastExportExpressionsDir" ), QDir::homePath(),
QgsSettings::App ).toString();
1151 QString saveFileName = QFileDialog::getSaveFileName(
1153 tr(
"Export User Expressions" ),
1155 tr(
"User expressions" ) +
" (*.json)"
1161 if ( saveFileName.isEmpty() )
1164 QFileInfo saveFileInfo( saveFileName );
1166 if ( saveFileInfo.suffix().isEmpty() )
1168 QString saveFileNameWithSuffix = saveFileName.append(
".json" );
1169 saveFileInfo = QFileInfo( saveFileNameWithSuffix );
1174 QJsonDocument exportJson = mExpressionTreeView->exportUserExpressions();
1175 QFile jsonFile( saveFileName );
1177 if ( !jsonFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
1178 QMessageBox::warning(
this, tr(
"Export user expressions" ), tr(
"Error while creating the expressions file." ) );
1180 if ( !jsonFile.write( exportJson.toJson() ) )
1181 QMessageBox::warning(
this, tr(
"Export user expressions" ), tr(
"Error while creating the expressions file." ) );
1186void QgsExpressionBuilderWidget::importUserExpressions_pressed()
1189 QString lastImportDir = settings.
value( QStringLiteral(
"lastImportExpressionsDir" ), QDir::homePath(),
QgsSettings::App ).toString();
1190 QString loadFileName = QFileDialog::getOpenFileName(
1192 tr(
"Import User Expressions" ),
1194 tr(
"User expressions" ) +
" (*.json)"
1197 if ( loadFileName.isEmpty() )
1200 QFileInfo loadFileInfo( loadFileName );
1204 QFile jsonFile( loadFileName );
1206 if ( !jsonFile.open( QFile::ReadOnly ) )
1207 QMessageBox::warning(
this, tr(
"Import User Expressions" ), tr(
"Error while reading the expressions file." ) );
1209 QTextStream jsonStream( &jsonFile );
1210 QString jsonString = jsonFile.readAll();
1213 QJsonDocument importJson = QJsonDocument::fromJson( jsonString.toUtf8() );
1215 if ( importJson.isNull() )
1217 QMessageBox::warning(
this, tr(
"Import User Expressions" ), tr(
"Error while reading the expressions file." ) );
1221 mExpressionTreeView->loadExpressionsFromJson( importJson );
1227 return mExpressionTreeView->findExpressions( label );
1230void QgsExpressionBuilderWidget::indicatorClicked(
int line,
int index, Qt::KeyboardModifiers state )
1232 if ( state & Qt::ControlModifier )
1234 int position = txtExpressionString->positionFromLineIndex( line, index );
1235 long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID,
static_cast<long int>( position ) );
1237 QString help = getFunctionHelp( func );
1238 txtHelpText->setText( help );
1242void QgsExpressionBuilderWidget::onExpressionParsed(
bool state )
1246 mExpressionValid = state;
1249 createMarkers( mExpressionPreviewWidget->rootNode() );
1253 createErrorMarkers( mExpressionPreviewWidget->parserErrors() );
1257QString QgsExpressionBuilderWidget::helpStylesheet()
const
1263 style +=
" .functionname {color: #0a6099; font-weight: bold;} "
1264 " .argument {font-family: monospace; color: #bf0c0c; font-style: italic; } "
1265 " td.argument { padding-right: 10px; }";
1270QString QgsExpressionBuilderWidget::loadFunctionHelp(
QgsExpressionItem *expressionItem )
1272 if ( !expressionItem )
1275 QString helpContents = expressionItem->
getHelpText();
1278 if ( helpContents.isEmpty() )
1280 QString name = expressionItem->data( Qt::UserRole ).toString();
1288 return "<head><style>" + helpStylesheet() +
"</style></head><body>" + helpContents +
"</body>";
1295QMenu *QgsExpressionBuilderWidget::ExpressionTreeMenuProvider::createContextMenu(
QgsExpressionItem *item )
1297 QMenu *menu =
nullptr;
1301 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 toggleComment() override
Toggle comment for the selected text.
void setFields(const QgsFields &fields)
Field names will be added to the API.
void setText(const QString &text) override
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.
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.
SQL-like BETWEEN and NOT BETWEEN predicates.
QgsExpressionNode * lowerBound() const
Returns the lower bound expression node of the range.
QgsExpressionNode * higherBound() const
Returns the higher bound expression node of the range.
A binary expression operator, which operates on two values.
QgsExpressionNode * opLeft() const
Returns the node to the left of the operator.
QgsExpressionNode * opRight() const
Returns the node to the right of the operator.
Represents a "WHEN... THEN..." portation of a CASE WHEN clause in an expression.
An expression node for CASE WHEN clauses.
QgsExpressionNode * elseExp() const
The ELSE expression used for the condition.
WhenThenList conditions() const
The list of WHEN THEN expression parts of the expression.
An expression node for expression functions.
int fnIndex() const
Returns the index of the node's function.
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function.
An expression node for value IN or NOT IN clauses.
QgsExpressionNode::NodeList * list() const
Returns the list of nodes to search for matching values within.
A unary node is either negative as in boolean (not) or as in numbers (minus).
QgsExpressionNode * operand() const
Returns the node the operator will operate upon.
QList< QgsExpressionNode * > list()
Gets a list of all the nodes.
Abstract base class for all nodes that can appear in an expression.
virtual QgsExpressionNode::NodeType nodeType() const =0
Gets the type of this node.
@ ntBetweenOperator
Between operator.
@ ntIndexOperator
Index operator.
int parserFirstLine
First line in the parser this node was found.
int parserLastColumn
Last column in the parser this node was found.
int parserFirstColumn
First column in the parser this node was found.
A generic dialog for editing expression text, label and help text.
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.
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.
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
#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.