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, [=]() {
153 functionsplit->setSizes( QList<int>( { mOperationListGroup->width() - mHelpAndValuesWidget->minimumWidth(), mHelpAndValuesWidget->minimumWidth() } ) );
154 mShowHelpButton->setEnabled(
false );
156 connect( functionsplit, &QSplitter::splitterMoved,
this, [=](
int,
int ) {
157 mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
161 splitter->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/splitter" ) ).toByteArray() );
162 editorSplit->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/editorsplitter" ) ).toByteArray() );
163 functionsplit->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/functionsplitter" ) ).toByteArray() );
164 mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
170 btnRemoveFile->setEnabled( cmbFileNames->count() > 0 );
177 txtExpressionString->setWrapMode( QsciScintilla::WrapWord );
178 lblAutoSave->clear();
185#if defined( QSCINTILLA_VERSION ) && QSCINTILLA_VERSION >= 0x20a00
192 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::red ), -1 );
193 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::red ), -1 );
194 txtExpressionString->setIndicatorOutlineColor( QColor( Qt::red ), -1 );
197 txtExpressionString->indicatorDefine( QgsCodeEditor::HiddenIndicator, FUNCTION_MARKER_ID );
198 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
199 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
200 txtExpressionString->setIndicatorHoverStyle( QgsCodeEditor::DotsIndicator, FUNCTION_MARKER_ID );
202 connect( txtExpressionString, &QgsCodeEditorExpression::indicatorClicked,
this, &QgsExpressionBuilderWidget::indicatorClicked );
203 txtExpressionString->setAutoCompletionCaseSensitivity(
false );
204 txtExpressionString->setAutoCompletionSource( QsciScintilla::AcsAPIs );
205 txtExpressionString->setCallTipsVisible( 0 );
208 mFunctionBuilderHelp->setLineNumbersVisible(
false );
209 mFunctionBuilderHelp->setFoldingVisible(
false );
210 mFunctionBuilderHelp->setEdgeMode( QsciScintilla::EdgeNone );
211 mFunctionBuilderHelp->setEdgeColumn( 0 );
212 mFunctionBuilderHelp->setReadOnly(
true );
213 mFunctionBuilderHelp->setText( tr(
"\"\"\"Define a new function using the @qgsfunction decorator.\n\
215 Besides its normal arguments, the function may specify the following arguments in its signature\n\
216 Those will not need to be specified when calling the function, but will be automatically injected \n\
218 : param feature: The current feature\n\
219 : param parent: The QgsExpression object\n\
220 : param context: ``QgsExpressionContext`` object, that gives access to various additional information like\n\
221 expression variables. E.g. ``context.variable( 'layer_id' )``\n\
222 : returns: The result of the expression.\n\
226 The @qgsfunction decorator accepts the following arguments:\n\
229 : param group: The name of the group under which this expression function will\n\
231 : param handlesnull: Set this to True if your function has custom handling for NULL values.\n\
232 If False, the result will always be NULL as soon as any parameter is NULL.\n\
233 Defaults to False.\n\
234 : param usesgeometry : Set this to True if your function requires access to\n\
235 feature.geometry(). Defaults to False.\n\
236 : param referenced_columns: An array of attribute names that are required to run\n\
237 this function. Defaults to [QgsFeatureRequest.ALL_ATTRIBUTES].\n\
238 : param params_as_list : Set this to True to pass the function parameters as a list. Can be used to mimic \n\
239 behavior before 3.32, when args was not \"auto\". Defaults to False.\n\
247 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/splitter" ), splitter->saveState() );
248 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/editorsplitter" ), editorSplit->saveState() );
249 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/functionsplitter" ), functionsplit->saveState() );
250 delete mExpressionTreeMenuProvider;
258 mExpressionTreeView->loadRecent( recentCollection );
261 mExpressionTreeView->loadUserExpressions();
266 init( context, recentCollection, flags );
272 init( context, recentCollection, flags );
273 mExpressionTreeView->loadFieldNames( fields );
280 mExpressionTreeView->setLayer( mLayer );
281 mExpressionPreviewWidget->setLayer( mLayer );
288 expressionContextUpdated();
293void QgsExpressionBuilderWidget::expressionContextUpdated()
296 mExpressionTreeView->setExpressionContext( mExpressionContext );
297 mExpressionPreviewWidget->setExpressionContext( mExpressionContext );
305void QgsExpressionBuilderWidget::expressionTreeItemChanged(
QgsExpressionItem *item )
307 txtSearchEditValues->clear();
315 mValuesModel->clear();
318 cbxValuesInUse->setChecked(
false );
320 mValueGroupBox->setVisible( isField );
322 mShowHelpButton->setText( isField ? tr(
"Show Values" ) : tr(
"Show Help" ) );
325 QString help = loadFunctionHelp( item );
326 txtHelpText->setText( help );
328 bool isUserExpression = item->parent() && item->parent()->text() == mUserExpressionsGroupName;
330 btnRemoveExpression->setEnabled( isUserExpression );
331 btnEditExpression->setEnabled( isUserExpression );
334void QgsExpressionBuilderWidget::btnRun_pressed()
336 if ( !cmbFileNames->currentItem() )
339 if ( cmbFileNames->currentItem()->data( Qt::UserRole ) == QLatin1String(
"project" ) )
345 QString file = cmbFileNames->currentItem()->text();
349 runPythonCode( txtPython->text() );
352void QgsExpressionBuilderWidget::runPythonCode(
const QString &code )
356 QString pythontext = code;
359 mExpressionTreeView->refresh();
379 QDir myDir( mFunctionsPath );
380 if ( !myDir.exists() )
382 myDir.mkpath( mFunctionsPath );
385 if ( !fileName.endsWith( QLatin1String(
".py" ) ) )
387 fileName.append(
".py" );
390 fileName = mFunctionsPath + QDir::separator() + fileName;
391 QFile myFile( fileName );
392 if ( myFile.open( QIODevice::WriteOnly | QFile::Truncate ) )
394 QTextStream myFileStream( &myFile );
395#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
396 myFileStream.setCodec(
"UTF-8" );
398 myFileStream << txtPython->text() << Qt::endl;
405 mProject->writeEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ), txtPython->text() );
410 mFunctionsPath = path;
412 dir.setNameFilters( QStringList() << QStringLiteral(
"*.py" ) );
413 QStringList files = dir.entryList( QDir::Files );
414 cmbFileNames->clear();
415 const auto constFiles = files;
416 for (
const QString &name : constFiles )
418 QFileInfo info( mFunctionsPath + QDir::separator() + name );
419 if ( info.baseName() == QLatin1String(
"__init__" ) )
421 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"console/iconTabEditorConsole.svg" ) ), info.baseName() );
422 cmbFileNames->addItem( item );
426 mProject->readEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ), QString(), &ok );
429 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"mIconQgsProjectFile.svg" ) ), DEFAULT_PROJECT_FUNCTIONS_ITEM_NAME );
430 item->setData( Qt::UserRole, QStringLiteral(
"project" ) );
431 cmbFileNames->insertItem( 0, item );
434 if ( !cmbFileNames->currentItem() )
436 cmbFileNames->setCurrentRow( 0 );
439 if ( cmbFileNames->count() == 0 )
443 txtPython->setText( QStringLiteral(
"'''\n#Sample custom function file\n"
444 "#(uncomment to use and customize or Add button to create a new file) \n%1 \n '''" )
445 .arg( txtPython->text() ) );
452 QList<QListWidgetItem *> items = cmbFileNames->findItems( fileName, Qt::MatchExactly );
453 if ( !items.isEmpty() )
456 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"console/iconTabEditorConsole.svg" ) ), fileName );
457 cmbFileNames->insertItem( 0, item );
458 cmbFileNames->setCurrentRow( 0 );
460 QString templateText;
462 txtPython->setText( templateText );
466void QgsExpressionBuilderWidget::btnNewFile_pressed()
473 mProject->readEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ), QString(), &ok );
476 if ( dlg.exec() == QDialog::DialogCode::Accepted )
478 if ( dlg.createProjectFunctions() )
480 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"mIconQgsProjectFile.svg" ) ), DEFAULT_PROJECT_FUNCTIONS_ITEM_NAME );
481 item->setData( Qt::UserRole, QStringLiteral(
"project" ) );
482 cmbFileNames->insertItem( 0, item );
483 cmbFileNames->setCurrentRow( 0 );
485 QString templateText;
487 txtPython->setText( templateText );
494 btnRemoveFile->setEnabled( cmbFileNames->count() > 0 );
498void QgsExpressionBuilderWidget::btnRemoveFile_pressed()
500 if ( cmbFileNames->currentItem()->data( Qt::UserRole ) == QLatin1String(
"project" ) )
502 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 )
505 mProject->removeEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ) );
509 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 )
512 QString fileName = cmbFileNames->currentItem()->text();
513 if ( !QFile::remove( mFunctionsPath + QDir::separator() + fileName.append(
".py" ) ) )
515 QMessageBox::warning(
this, tr(
"Remove file" ), tr(
"Failed to remove function file '%1'." ).arg( fileName ) );
520 int currentRow = cmbFileNames->currentRow();
522 QListWidgetItem *itemToRemove =
whileBlocking( cmbFileNames )->takeItem( currentRow );
526 if ( cmbFileNames->count() > 0 )
528 whileBlocking( cmbFileNames )->setCurrentRow( currentRow > 0 ? currentRow - 1 : 0 );
529 if ( cmbFileNames->currentItem()->data( Qt::UserRole ) == QLatin1String(
"project" ) )
535 loadCodeFromFile( mFunctionsPath + QDir::separator() + cmbFileNames->currentItem()->text() );
540 btnRemoveFile->setEnabled(
false );
545void QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem )
549 if ( lastitem->data( Qt::UserRole ) == QLatin1String(
"project" ) )
555 QString filename = lastitem->text();
560 if ( item->data( Qt::UserRole ) == QLatin1String(
"project" ) )
566 QString path = mFunctionsPath + QDir::separator() + item->text();
573 if ( !path.endsWith( QLatin1String(
".py" ) ) )
574 path.append(
".py" );
576 txtPython->loadScript( path );
581 loadFunctionCode( mProject->readEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ) ) );
586 txtPython->setText( code );
589void QgsExpressionBuilderWidget::insertExpressionText(
const QString &text )
593 txtExpressionString->setFocus();
598 Q_UNUSED( fieldValues )
602void QgsExpressionBuilderWidget::fillFieldValues(
const QString &fieldName,
QgsVectorLayer *layer,
int countLimit,
bool forceUsedValues )
614 if ( fieldIndex < 0 )
621 if ( cbxValuesInUse->isVisible() && !cbxValuesInUse->isChecked() && !forceUsedValues )
631 std::sort( values.begin(), values.end() );
633 mValuesModel->clear();
634 for (
const QVariant &value : std::as_const( values ) )
637 bool forceRepresentedValue =
false;
639 strValue = QStringLiteral(
"NULL" );
640 else if ( value.userType() == QMetaType::Type::Int || value.userType() == QMetaType::Type::Double || value.userType() == QMetaType::Type::LongLong || value.userType() == QMetaType::Type::Bool )
641 strValue = value.toString();
642 else if ( value.userType() == QMetaType::Type::QStringList )
645 const QStringList strList = value.toStringList();
646 for ( QString str : strList )
648 if ( !result.isEmpty() )
649 result.append( QStringLiteral(
", " ) );
651 result.append(
'\'' + str.replace(
'\'', QLatin1String(
"''" ) ) +
'\'' );
653 strValue = QStringLiteral(
"array(%1)" ).arg( result );
654 forceRepresentedValue =
true;
656 else if ( value.userType() == QMetaType::Type::QVariantList )
659 const QList list = value.toList();
660 for (
const QVariant &item : list )
662 if ( !result.isEmpty() )
663 result.append( QStringLiteral(
", " ) );
665 result.append( item.toString() );
667 strValue = QStringLiteral(
"array(%1)" ).arg( result );
668 forceRepresentedValue =
true;
671 strValue =
'\'' + value.toString().replace(
'\'', QLatin1String(
"''" ) ) +
'\'';
674 if ( forceRepresentedValue || representedValue != value.toString() )
675 representedValue = representedValue + QStringLiteral(
" [" ) + strValue +
']';
677 QStandardItem *item =
new QStandardItem( representedValue );
678 item->setData( strValue );
679 mValuesModel->appendRow( item );
690 return QStringLiteral(
"<head><style>" ) + helpStylesheet() + QStringLiteral(
"</style></head><body>" ) + helpContents + QStringLiteral(
"</body>" );
696 return mExpressionValid;
701 mExpressionPreviewWidget->setCustomPreviewGenerator( label, choices, previewContextGenerator );
706 mExpressionTreeView->saveToRecent(
expressionText(), collection );
711 mExpressionTreeView->loadRecent( collection );
716 return mExpressionTreeView;
727 mExpressionTreeView->saveToUserExpressions( label, expression, helpText );
732 mExpressionTreeView->removeFromUserExpressions( label );
738 mExpressionPreviewWidget->setGeomCalculator( da );
743 return txtExpressionString->text();
748 txtExpressionString->setText( expression );
753 return lblExpected->text();
758 lblExpected->setText( expected );
759 mExpectedOutputFrame->setVisible( !expected.isNull() );
764 mExpressionContext = context;
765 expressionContextUpdated();
768void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
772 btnClearEditor->setEnabled( !txtExpressionString->text().isEmpty() );
773 btnSaveExpression->setEnabled(
false );
775 mExpressionPreviewWidget->setExpressionText( text );
780 return mExpressionPreviewWidget->parserError();
785 mExpressionPreviewWidget->setVisible( isVisible );
790 return mExpressionPreviewWidget->evalError();
796 return mExpressionTreeView->model();
808 mExpressionTreeView->setProject(
project );
813 QWidget::showEvent( e );
814 txtExpressionString->setFocus();
817void QgsExpressionBuilderWidget::createErrorMarkers(
const QList<QgsExpression::ParserError> &errors )
822 int errorFirstLine = error.firstLine - 1;
823 int errorFirstColumn = error.firstColumn - 1;
824 int errorLastColumn = error.lastColumn - 1;
825 int errorLastLine = error.lastLine - 1;
831 errorFirstLine = errorLastLine;
832 errorFirstColumn = errorLastColumn - 1;
834 txtExpressionString->fillIndicatorRange( errorFirstLine, errorFirstColumn, errorLastLine, errorLastColumn, error.errorType );
845 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
846 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->
fnIndex() );
849 int start_pos = txtExpressionString->positionFromLineIndex( inNode->
parserFirstLine - 1, start );
850 txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
853 const QList<QgsExpressionNode *> nodeList = node->
args()->
list();
868 createMarkers( node->
operand() );
881 createMarkers( node->
opLeft() );
882 createMarkers( node->
opRight() );
894 const QList<QgsExpressionNode *> nodeList = node->
list()->
list();
905 const QList<QgsExpressionNodeCondition::WhenThen *> conditions = node->
conditions();
908 createMarkers( cond->whenExp() );
909 createMarkers( cond->thenExp() );
913 createMarkers( node->
elseExp() );
924void QgsExpressionBuilderWidget::clearFunctionMarkers()
926 int lastLine = txtExpressionString->lines() - 1;
927 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
930void QgsExpressionBuilderWidget::clearErrors()
932 int lastLine = txtExpressionString->lines() - 1;
941void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
943 mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
944 mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
947void QgsExpressionBuilderWidget::mValuesListView_doubleClicked(
const QModelIndex &index )
950 txtExpressionString->
insertText(
' ' + index.data( Qt::UserRole + 1 ).toString() +
' ' );
951 txtExpressionString->setFocus();
954void QgsExpressionBuilderWidget::operatorButtonClicked()
956 QPushButton *button = qobject_cast<QPushButton *>( sender() );
959 txtExpressionString->
insertText(
' ' + button->text() +
' ' );
960 txtExpressionString->setFocus();
979 mValueGroupBox->show();
999 mValueGroupBox->show();
1019 mValueGroupBox->show();
1039 mValueGroupBox->show();
1043void QgsExpressionBuilderWidget::txtPython_textChanged()
1045 lblAutoSave->setText( tr(
"Saving…" ) );
1055 if ( tabWidget->currentIndex() != 1 )
1058 QListWidgetItem *item = cmbFileNames->currentItem();
1062 if ( item->data( Qt::UserRole ) == QLatin1String(
"project" ) )
1068 QString file = item->text();
1072 lblAutoSave->setText( QStringLiteral(
"Saved" ) );
1073 QGraphicsOpacityEffect *effect =
new QGraphicsOpacityEffect();
1074 lblAutoSave->setGraphicsEffect( effect );
1075 QPropertyAnimation *anim =
new QPropertyAnimation( effect,
"opacity" );
1076 anim->setDuration( 2000 );
1077 anim->setStartValue( 1.0 );
1078 anim->setEndValue( 0.0 );
1079 anim->setEasingCurve( QEasingCurve::OutQuad );
1080 anim->start( QAbstractAnimation::DeleteWhenStopped );
1087 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1089 mExpressionTreeView->saveToUserExpressions( dlg.label().simplified(), dlg.expression(), dlg.helpText() );
1109 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1112 if ( dlg.isLabelModified() )
1114 mExpressionTreeView->removeFromUserExpressions( item->text() );
1117 mExpressionTreeView->saveToUserExpressions( dlg.label().simplified(), dlg.expression(), dlg.helpText() );
1134 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 ) )
1136 mExpressionTreeView->removeFromUserExpressions( item->text() );
1140void QgsExpressionBuilderWidget::exportUserExpressions_pressed()
1143 QString lastSaveDir = settings.
value( QStringLiteral(
"lastExportExpressionsDir" ), QDir::homePath(),
QgsSettings::App ).toString();
1144 QString saveFileName = QFileDialog::getSaveFileName(
1146 tr(
"Export User Expressions" ),
1148 tr(
"User expressions" ) +
" (*.json)"
1154 if ( saveFileName.isEmpty() )
1157 QFileInfo saveFileInfo( saveFileName );
1159 if ( saveFileInfo.suffix().isEmpty() )
1161 QString saveFileNameWithSuffix = saveFileName.append(
".json" );
1162 saveFileInfo = QFileInfo( saveFileNameWithSuffix );
1167 QJsonDocument exportJson = mExpressionTreeView->exportUserExpressions();
1168 QFile jsonFile( saveFileName );
1170 if ( !jsonFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
1171 QMessageBox::warning(
this, tr(
"Export user expressions" ), tr(
"Error while creating the expressions file." ) );
1173 if ( !jsonFile.write( exportJson.toJson() ) )
1174 QMessageBox::warning(
this, tr(
"Export user expressions" ), tr(
"Error while creating the expressions file." ) );
1179void QgsExpressionBuilderWidget::importUserExpressions_pressed()
1182 QString lastImportDir = settings.
value( QStringLiteral(
"lastImportExpressionsDir" ), QDir::homePath(),
QgsSettings::App ).toString();
1183 QString loadFileName = QFileDialog::getOpenFileName(
1185 tr(
"Import User Expressions" ),
1187 tr(
"User expressions" ) +
" (*.json)"
1190 if ( loadFileName.isEmpty() )
1193 QFileInfo loadFileInfo( loadFileName );
1197 QFile jsonFile( loadFileName );
1199 if ( !jsonFile.open( QFile::ReadOnly ) )
1200 QMessageBox::warning(
this, tr(
"Import User Expressions" ), tr(
"Error while reading the expressions file." ) );
1202 QTextStream jsonStream( &jsonFile );
1203 QString jsonString = jsonFile.readAll();
1206 QJsonDocument importJson = QJsonDocument::fromJson( jsonString.toUtf8() );
1208 if ( importJson.isNull() )
1210 QMessageBox::warning(
this, tr(
"Import User Expressions" ), tr(
"Error while reading the expressions file." ) );
1214 mExpressionTreeView->loadExpressionsFromJson( importJson );
1220 return mExpressionTreeView->findExpressions( label );
1223void QgsExpressionBuilderWidget::indicatorClicked(
int line,
int index, Qt::KeyboardModifiers state )
1225 if ( state & Qt::ControlModifier )
1227 int position = txtExpressionString->positionFromLineIndex( line, index );
1228 long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID,
static_cast<long int>( position ) );
1230 QString help = getFunctionHelp( func );
1231 txtHelpText->setText( help );
1235void QgsExpressionBuilderWidget::onExpressionParsed(
bool state )
1239 mExpressionValid = state;
1242 createMarkers( mExpressionPreviewWidget->rootNode() );
1246 createErrorMarkers( mExpressionPreviewWidget->parserErrors() );
1250QString QgsExpressionBuilderWidget::helpStylesheet()
const
1256 style +=
" .functionname {color: #0a6099; font-weight: bold;} "
1257 " .argument {font-family: monospace; color: #bf0c0c; font-style: italic; } "
1258 " td.argument { padding-right: 10px; }";
1263QString QgsExpressionBuilderWidget::loadFunctionHelp(
QgsExpressionItem *expressionItem )
1265 if ( !expressionItem )
1268 QString helpContents = expressionItem->
getHelpText();
1271 if ( helpContents.isEmpty() )
1273 QString name = expressionItem->data( Qt::UserRole ).toString();
1281 return "<head><style>" + helpStylesheet() +
"</style></head><body>" + helpContents +
"</body>";
1288QMenu *QgsExpressionBuilderWidget::ExpressionTreeMenuProvider::createContextMenu(
QgsExpressionItem *item )
1290 QMenu *menu =
nullptr;
1294 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.