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 txtSearchEdit->setShowSearchIcon(
true );
140 txtSearchEdit->setPlaceholderText( tr(
"Search…" ) );
142 mValuesModel = std::make_unique<QStandardItemModel>();
143 mProxyValues = std::make_unique<QSortFilterProxyModel>();
144 mProxyValues->setSourceModel( mValuesModel.get() );
145 mValuesListView->setModel( mProxyValues.get() );
146 txtSearchEditValues->setShowSearchIcon(
true );
147 txtSearchEditValues->setPlaceholderText( tr(
"Search…" ) );
149 editorSplit->setSizes( QList<int>( {175, 300} ) );
151 functionsplit->setCollapsible( 0,
false );
152 connect( mShowHelpButton, &QPushButton::clicked,
this, [ = ]()
154 functionsplit->setSizes( QList<int>( {mOperationListGroup->width() - mHelpAndValuesWidget->minimumWidth(),
155 mHelpAndValuesWidget->minimumWidth()
157 mShowHelpButton->setEnabled(
false );
159 connect( functionsplit, &QSplitter::splitterMoved,
this, [ = ](
int,
int )
161 mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
165 splitter->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/splitter" ) ).toByteArray() );
166 editorSplit->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/editorsplitter" ) ).toByteArray() );
167 functionsplit->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/functionsplitter" ) ).toByteArray() );
168 mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
174 btnRemoveFile->setEnabled( cmbFileNames->count() > 0 );
181 txtExpressionString->setWrapMode( QsciScintilla::WrapWord );
182 lblAutoSave->clear();
189#if defined(QSCINTILLA_VERSION) && QSCINTILLA_VERSION >= 0x20a00
196 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::red ), -1 );
197 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::red ), -1 );
198 txtExpressionString->setIndicatorOutlineColor( QColor( Qt::red ), -1 );
201 txtExpressionString->indicatorDefine( QgsCodeEditor::HiddenIndicator, FUNCTION_MARKER_ID );
202 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
203 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
204 txtExpressionString->setIndicatorHoverStyle( QgsCodeEditor::DotsIndicator, FUNCTION_MARKER_ID );
206 connect( txtExpressionString, &QgsCodeEditorExpression::indicatorClicked,
this, &QgsExpressionBuilderWidget::indicatorClicked );
207 txtExpressionString->setAutoCompletionCaseSensitivity(
false );
208 txtExpressionString->setAutoCompletionSource( QsciScintilla::AcsAPIs );
209 txtExpressionString->setCallTipsVisible( 0 );
212 mFunctionBuilderHelp->setLineNumbersVisible(
false );
213 mFunctionBuilderHelp->setFoldingVisible(
false );
214 mFunctionBuilderHelp->setEdgeMode( QsciScintilla::EdgeNone );
215 mFunctionBuilderHelp->setEdgeColumn( 0 );
216 mFunctionBuilderHelp->setReadOnly(
true );
217 mFunctionBuilderHelp->setText( tr(
"\"\"\"Define a new function using the @qgsfunction decorator.\n\
219 Besides its normal arguments, the function may specify the following arguments in its signature\n\
220 Those will not need to be specified when calling the function, but will be automatically injected \n\
222 : param feature: The current feature\n\
223 : param parent: The QgsExpression object\n\
224 : param context: ``QgsExpressionContext`` object, that gives access to various additional information like\n\
225 expression variables. E.g. ``context.variable( 'layer_id' )``\n\
226 : returns: The result of the expression.\n\
230 The @qgsfunction decorator accepts the following arguments:\n\
233 : param group: The name of the group under which this expression function will\n\
235 : param handlesnull: Set this to True if your function has custom handling for NULL values.\n\
236 If False, the result will always be NULL as soon as any parameter is NULL.\n\
237 Defaults to False.\n\
238 : param usesgeometry : Set this to True if your function requires access to\n\
239 feature.geometry(). Defaults to False.\n\
240 : param referenced_columns: An array of attribute names that are required to run\n\
241 this function. Defaults to [QgsFeatureRequest.ALL_ATTRIBUTES].\n\
242 : param params_as_list : Set this to True to pass the function parameters as a list. Can be used to mimic \n\
243 behavior before 3.32, when args was not \"auto\". Defaults to False.\n\
251 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/splitter" ), splitter->saveState() );
252 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/editorsplitter" ), editorSplit->saveState() );
253 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/functionsplitter" ), functionsplit->saveState() );
254 delete mExpressionTreeMenuProvider;
262 mExpressionTreeView->loadRecent( recentCollection );
265 mExpressionTreeView->loadUserExpressions();
270 init( context, recentCollection, flags );
276 init( context, recentCollection, flags );
277 mExpressionTreeView->loadFieldNames( fields );
284 mExpressionTreeView->setLayer( mLayer );
285 mExpressionPreviewWidget->setLayer( mLayer );
292 expressionContextUpdated();
297void QgsExpressionBuilderWidget::expressionContextUpdated()
300 mExpressionTreeView->setExpressionContext( mExpressionContext );
301 mExpressionPreviewWidget->setExpressionContext( mExpressionContext );
309void QgsExpressionBuilderWidget::expressionTreeItemChanged(
QgsExpressionItem *item )
311 txtSearchEditValues->clear();
319 mValuesModel->clear();
322 cbxValuesInUse->setChecked(
false );
324 mValueGroupBox->setVisible( isField );
326 mShowHelpButton->setText( isField ? tr(
"Show Values" ) : tr(
"Show Help" ) );
329 QString help = loadFunctionHelp( item );
330 txtHelpText->setText( help );
332 bool isUserExpression = item->parent() && item->parent()->text() == mUserExpressionsGroupName;
334 btnRemoveExpression->setEnabled( isUserExpression );
335 btnEditExpression->setEnabled( isUserExpression );
338void QgsExpressionBuilderWidget::btnRun_pressed()
340 if ( !cmbFileNames->currentItem() )
343 if ( cmbFileNames->currentItem()->data( Qt::UserRole ) == QLatin1String(
"project" ) )
349 QString file = cmbFileNames->currentItem()->text();
353 runPythonCode( txtPython->text() );
356void QgsExpressionBuilderWidget::runPythonCode(
const QString &code )
360 QString pythontext = code;
363 mExpressionTreeView->refresh();
383 QDir myDir( mFunctionsPath );
384 if ( !myDir.exists() )
386 myDir.mkpath( mFunctionsPath );
389 if ( !fileName.endsWith( QLatin1String(
".py" ) ) )
391 fileName.append(
".py" );
394 fileName = mFunctionsPath + QDir::separator() + fileName;
395 QFile myFile( fileName );
396 if ( myFile.open( QIODevice::WriteOnly | QFile::Truncate ) )
398 QTextStream myFileStream( &myFile );
399#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
400 myFileStream.setCodec(
"UTF-8" );
402 myFileStream << txtPython->text() << Qt::endl;
409 mProject->writeEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ), txtPython->text() );
414 mFunctionsPath = path;
416 dir.setNameFilters( QStringList() << QStringLiteral(
"*.py" ) );
417 QStringList files = dir.entryList( QDir::Files );
418 cmbFileNames->clear();
419 const auto constFiles = files;
420 for (
const QString &name : constFiles )
422 QFileInfo info( mFunctionsPath + QDir::separator() + name );
423 if ( info.baseName() == QLatin1String(
"__init__" ) )
continue;
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 '''" ).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" ),
505 tr(
"Are you sure you want to remove the project functions?" ),
506 QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No )
509 mProject->removeEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ) );
513 if ( QMessageBox::question(
this, tr(
"Remove File" ),
514 tr(
"Are you sure you want to remove current functions file?" ),
515 QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No )
518 QString fileName = cmbFileNames->currentItem()->text();
519 if ( !QFile::remove( mFunctionsPath + QDir::separator() + fileName.append(
".py" ) ) )
521 QMessageBox::warning(
this, tr(
"Remove file" ), tr(
"Failed to remove function file '%1'." ).arg( fileName ) );
526 int currentRow = cmbFileNames->currentRow();
528 QListWidgetItem *itemToRemove =
whileBlocking( cmbFileNames )->takeItem( currentRow );
532 if ( cmbFileNames->count() > 0 )
534 whileBlocking( cmbFileNames )->setCurrentRow( currentRow > 0 ? currentRow - 1 : 0 );
535 if ( cmbFileNames->currentItem()->data( Qt::UserRole ) == QLatin1String(
"project" ) )
541 loadCodeFromFile( mFunctionsPath + QDir::separator() + cmbFileNames->currentItem()->text() );
546 btnRemoveFile->setEnabled(
false );
552void QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem )
556 if ( lastitem->data( Qt::UserRole ) == QLatin1String(
"project" ) )
562 QString filename = lastitem->text();
567 if ( item->data( Qt::UserRole ) == QLatin1String(
"project" ) )
573 QString path = mFunctionsPath + QDir::separator() + item->text();
580 if ( !path.endsWith( QLatin1String(
".py" ) ) )
581 path.append(
".py" );
583 txtPython->loadScript( path );
588 loadFunctionCode( mProject->readEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ) ) );
593 txtPython->setText( code );
596void QgsExpressionBuilderWidget::insertExpressionText(
const QString &text )
600 txtExpressionString->setFocus();
605 Q_UNUSED( fieldValues )
609void QgsExpressionBuilderWidget::fillFieldValues(
const QString &fieldName,
QgsVectorLayer *layer,
int countLimit,
bool forceUsedValues )
621 if ( fieldIndex < 0 )
628 if ( cbxValuesInUse->isVisible() && !cbxValuesInUse->isChecked() && !forceUsedValues )
632 values =
formatter->availableValues( setup.
config(), countLimit, fieldFormatterContext );
638 std::sort( values.begin(), values.end() );
640 mValuesModel->clear();
641 for (
const QVariant &value : std::as_const( values ) )
644 bool forceRepresentedValue =
false;
646 strValue = QStringLiteral(
"NULL" );
647 else if ( value.userType() == QMetaType::Type::Int || value.userType() == QMetaType::Type::Double || value.userType() == QMetaType::Type::LongLong || value.userType() == QMetaType::Type::Bool )
648 strValue = value.toString();
649 else if ( value.userType() == QMetaType::Type::QStringList )
652 const QStringList strList = value.toStringList();
653 for ( QString
str : strList )
655 if ( !result.isEmpty() )
656 result.append( QStringLiteral(
", " ) );
658 result.append(
'\'' +
str.replace(
'\'', QLatin1String(
"''" ) ) +
'\'' );
660 strValue = QStringLiteral(
"array(%1)" ).arg( result );
661 forceRepresentedValue =
true;
663 else if ( value.userType() == QMetaType::Type::QVariantList )
666 const QList list = value.toList();
667 for (
const QVariant &item : list )
669 if ( !result.isEmpty() )
670 result.append( QStringLiteral(
", " ) );
672 result.append( item.toString() );
674 strValue = QStringLiteral(
"array(%1)" ).arg( result );
675 forceRepresentedValue =
true;
678 strValue =
'\'' + value.toString().replace(
'\'', QLatin1String(
"''" ) ) +
'\'';
680 QString representedValue =
formatter->representValue(
layer, fieldIndex, setup.
config(), QVariant(), value );
681 if ( forceRepresentedValue || representedValue != value.toString() )
682 representedValue = representedValue + QStringLiteral(
" [" ) + strValue +
']';
684 QStandardItem *item =
new QStandardItem( representedValue );
685 item->setData( strValue );
686 mValuesModel->appendRow( item );
697 return QStringLiteral(
"<head><style>" ) + helpStylesheet() + QStringLiteral(
"</style></head><body>" ) + helpContents + QStringLiteral(
"</body>" );
705 return mExpressionValid;
710 mExpressionPreviewWidget->setCustomPreviewGenerator( label, choices, previewContextGenerator );
715 mExpressionTreeView->saveToRecent(
expressionText(), collection );
720 mExpressionTreeView->loadRecent( collection );
725 return mExpressionTreeView;
736 mExpressionTreeView->saveToUserExpressions( label, expression, helpText );
741 mExpressionTreeView->removeFromUserExpressions( label );
747 mExpressionPreviewWidget->setGeomCalculator( da );
752 return txtExpressionString->text();
757 txtExpressionString->setText( expression );
762 return lblExpected->text();
767 lblExpected->setText( expected );
768 mExpectedOutputFrame->setVisible( !expected.isNull() );
773 mExpressionContext = context;
774 expressionContextUpdated();
777void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
781 btnClearEditor->setEnabled( ! txtExpressionString->text().isEmpty() );
782 btnSaveExpression->setEnabled(
false );
784 mExpressionPreviewWidget->setExpressionText( text );
789 return mExpressionPreviewWidget->parserError();
794 mExpressionPreviewWidget->setVisible( isVisible );
799 return mExpressionPreviewWidget->evalError();
805 return mExpressionTreeView->model();
817 mExpressionTreeView->setProject(
project );
822 QWidget::showEvent( e );
823 txtExpressionString->setFocus();
826void QgsExpressionBuilderWidget::createErrorMarkers(
const QList<QgsExpression::ParserError> &errors )
831 int errorFirstLine = error.firstLine - 1 ;
832 int errorFirstColumn = error.firstColumn - 1;
833 int errorLastColumn = error.lastColumn - 1;
834 int errorLastLine = error.lastLine - 1;
840 errorFirstLine = errorLastLine;
841 errorFirstColumn = errorLastColumn - 1;
843 txtExpressionString->fillIndicatorRange( errorFirstLine,
846 errorLastColumn, error.errorType );
857 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
858 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->
fnIndex() );
861 int start_pos = txtExpressionString->positionFromLineIndex( inNode->
parserFirstLine - 1, start );
862 txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
865 const QList< QgsExpressionNode * > nodeList = node->
args()->
list();
880 createMarkers( node->
operand() );
893 createMarkers( node->
opLeft() );
894 createMarkers( node->
opRight() );
906 const QList< QgsExpressionNode * > nodeList = node->
list()->
list();
917 const QList<QgsExpressionNodeCondition::WhenThen *> conditions = node->
conditions();
920 createMarkers( cond->whenExp() );
921 createMarkers( cond->thenExp() );
925 createMarkers( node->
elseExp() );
936void QgsExpressionBuilderWidget::clearFunctionMarkers()
938 int lastLine = txtExpressionString->lines() - 1;
939 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
942void QgsExpressionBuilderWidget::clearErrors()
944 int lastLine = txtExpressionString->lines() - 1;
953void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
955 mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
956 mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
959void QgsExpressionBuilderWidget::mValuesListView_doubleClicked(
const QModelIndex &index )
962 txtExpressionString->
insertText(
' ' + index.data( Qt::UserRole + 1 ).toString() +
' ' );
963 txtExpressionString->setFocus();
966void QgsExpressionBuilderWidget::operatorButtonClicked()
968 QPushButton *button = qobject_cast<QPushButton *>( sender() );
971 txtExpressionString->
insertText(
' ' + button->text() +
' ' );
972 txtExpressionString->setFocus();
991 mValueGroupBox->show();
1011 mValueGroupBox->show();
1031 mValueGroupBox->show();
1051 mValueGroupBox->show();
1055void QgsExpressionBuilderWidget::txtPython_textChanged()
1057 lblAutoSave->setText( tr(
"Saving…" ) );
1067 if ( tabWidget->currentIndex() != 1 )
1070 QListWidgetItem *item = cmbFileNames->currentItem();
1074 if ( item->data( Qt::UserRole ) == QLatin1String(
"project" ) )
1080 QString file = item->text();
1084 lblAutoSave->setText( QStringLiteral(
"Saved" ) );
1085 QGraphicsOpacityEffect *effect =
new QGraphicsOpacityEffect();
1086 lblAutoSave->setGraphicsEffect( effect );
1087 QPropertyAnimation *anim =
new QPropertyAnimation( effect,
"opacity" );
1088 anim->setDuration( 2000 );
1089 anim->setStartValue( 1.0 );
1090 anim->setEndValue( 0.0 );
1091 anim->setEasingCurve( QEasingCurve::OutQuad );
1092 anim->start( QAbstractAnimation::DeleteWhenStopped );
1099 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1101 mExpressionTreeView->saveToUserExpressions( dlg.label().simplified(), dlg.expression(), dlg.helpText() );
1115 ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) )
1122 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1125 if ( dlg.isLabelModified() )
1127 mExpressionTreeView->removeFromUserExpressions( item->text() );
1130 mExpressionTreeView->saveToUserExpressions( dlg.label().simplified(), dlg.expression(), dlg.helpText() );
1145 ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) )
1148 if ( QMessageBox::Yes == QMessageBox::question(
this, tr(
"Remove Stored Expression" ),
1149 tr(
"Do you really want to remove stored expressions '%1'?" ).arg( item->text() ),
1150 QMessageBox::Yes | QMessageBox::No ) )
1152 mExpressionTreeView->removeFromUserExpressions( item->text() );
1157void QgsExpressionBuilderWidget::exportUserExpressions_pressed()
1160 QString lastSaveDir = settings.
value( QStringLiteral(
"lastExportExpressionsDir" ), QDir::homePath(),
QgsSettings::App ).toString();
1161 QString saveFileName = QFileDialog::getSaveFileName(
1163 tr(
"Export User Expressions" ),
1165 tr(
"User expressions" ) +
" (*.json)" );
1170 if ( saveFileName.isEmpty() )
1173 QFileInfo saveFileInfo( saveFileName );
1175 if ( saveFileInfo.suffix().isEmpty() )
1177 QString saveFileNameWithSuffix = saveFileName.append(
".json" );
1178 saveFileInfo = QFileInfo( saveFileNameWithSuffix );
1183 QJsonDocument exportJson = mExpressionTreeView->exportUserExpressions();
1184 QFile jsonFile( saveFileName );
1186 if ( !jsonFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
1187 QMessageBox::warning(
this, tr(
"Export user expressions" ), tr(
"Error while creating the expressions file." ) );
1189 if ( ! jsonFile.write( exportJson.toJson() ) )
1190 QMessageBox::warning(
this, tr(
"Export user expressions" ), tr(
"Error while creating the expressions file." ) );
1195void QgsExpressionBuilderWidget::importUserExpressions_pressed()
1198 QString lastImportDir = settings.
value( QStringLiteral(
"lastImportExpressionsDir" ), QDir::homePath(),
QgsSettings::App ).toString();
1199 QString loadFileName = QFileDialog::getOpenFileName(
1201 tr(
"Import User Expressions" ),
1203 tr(
"User expressions" ) +
" (*.json)" );
1205 if ( loadFileName.isEmpty() )
1208 QFileInfo loadFileInfo( loadFileName );
1212 QFile jsonFile( loadFileName );
1214 if ( !jsonFile.open( QFile::ReadOnly ) )
1215 QMessageBox::warning(
this, tr(
"Import User Expressions" ), tr(
"Error while reading the expressions file." ) );
1217 QTextStream jsonStream( &jsonFile );
1218 QString jsonString = jsonFile.readAll();
1221 QJsonDocument importJson = QJsonDocument::fromJson( jsonString.toUtf8() );
1223 if ( importJson.isNull() )
1225 QMessageBox::warning(
this, tr(
"Import User Expressions" ), tr(
"Error while reading the expressions file." ) );
1229 mExpressionTreeView->loadExpressionsFromJson( importJson );
1235 return mExpressionTreeView->findExpressions( label );
1238void QgsExpressionBuilderWidget::indicatorClicked(
int line,
int index, Qt::KeyboardModifiers state )
1240 if ( state & Qt::ControlModifier )
1242 int position = txtExpressionString->positionFromLineIndex( line, index );
1243 long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID,
static_cast<long int>( position ) );
1245 QString help = getFunctionHelp( func );
1246 txtHelpText->setText( help );
1250void QgsExpressionBuilderWidget::onExpressionParsed(
bool state )
1254 mExpressionValid = state;
1257 createMarkers( mExpressionPreviewWidget->rootNode() );
1261 createErrorMarkers( mExpressionPreviewWidget->parserErrors() );
1265QString QgsExpressionBuilderWidget::helpStylesheet()
const
1271 style +=
" .functionname {color: #0a6099; font-weight: bold;} "
1272 " .argument {font-family: monospace; color: #bf0c0c; font-style: italic; } "
1273 " td.argument { padding-right: 10px; }";
1278QString QgsExpressionBuilderWidget::loadFunctionHelp(
QgsExpressionItem *expressionItem )
1280 if ( !expressionItem )
1283 QString helpContents = expressionItem->
getHelpText();
1286 if ( helpContents.isEmpty() )
1288 QString name = expressionItem->data( Qt::UserRole ).toString();
1296 return "<head><style>" + helpStylesheet() +
"</style></head><body>" + helpContents +
"</body>";
1303QMenu *QgsExpressionBuilderWidget::ExpressionTreeMenuProvider::createContextMenu(
QgsExpressionItem *item )
1305 QMenu *menu =
nullptr;
1309 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 setFields(const QgsFields &fields)
Field names 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.
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...
A 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.
QgsExpressionTreeView is a tree view to list all expressions functions, variables and fields that can...
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)
This class is a composition of two QSettings instances:
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 data sets.
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.