21#include <QGraphicsOpacityEffect>
22#include <QPropertyAnimation>
24#include <QVersionNumber>
26#include <QJsonDocument>
60 if ( fieldIndex != -1 )
65 return (
formatter->flags() & QgsFieldFormatter::CanProvideAvailableValues );
80 QVBoxLayout *vl =
new QVBoxLayout();
81 vl->setContentsMargins( 0, 0, 0, 0 );
82 vl->addWidget( codeEditorWidget );
83 mExpressionEditorContainer->setLayout( vl );
85 connect( btnRun, &QToolButton::pressed,
this, &QgsExpressionBuilderWidget::btnRun_pressed );
86 connect( btnNewFile, &QPushButton::clicked,
this, &QgsExpressionBuilderWidget::btnNewFile_pressed );
87 connect( btnRemoveFile, &QPushButton::clicked,
this, &QgsExpressionBuilderWidget::btnRemoveFile_pressed );
88 connect( cmbFileNames, &QListWidget::currentItemChanged,
this, &QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged );
89 connect( txtExpressionString, &QgsCodeEditorExpression::textChanged,
this, &QgsExpressionBuilderWidget::txtExpressionString_textChanged );
90 connect( txtPython, &QgsCodeEditorPython::textChanged,
this, &QgsExpressionBuilderWidget::txtPython_textChanged );
91 connect( txtSearchEditValues, &QgsFilterLineEdit::textChanged,
this, &QgsExpressionBuilderWidget::txtSearchEditValues_textChanged );
92 connect( mValuesListView, &QListView::doubleClicked,
this, &QgsExpressionBuilderWidget::mValuesListView_doubleClicked );
96 connect( btnImportExpressions, &QToolButton::clicked,
this, &QgsExpressionBuilderWidget::importUserExpressions_pressed );
97 connect( btnExportExpressions, &QToolButton::clicked,
this, &QgsExpressionBuilderWidget::exportUserExpressions_pressed );
98 connect( btnClearEditor, &QToolButton::clicked, txtExpressionString, &QgsCodeEditorExpression::clear );
111 mExpressionTreeMenuProvider =
new ExpressionTreeMenuProvider(
this );
112 mExpressionTreeView->setMenuProvider( mExpressionTreeMenuProvider );
114 txtHelpText->setOpenExternalLinks(
true );
115 mValueGroupBox->hide();
132 const auto pushButtons { mOperatorsGroupBox->findChildren<QPushButton *>() };
133 for ( QPushButton *button : pushButtons )
135 connect( button, &QAbstractButton::clicked,
this, &QgsExpressionBuilderWidget::operatorButtonClicked );
138 txtSearchEdit->setShowSearchIcon(
true );
139 txtSearchEdit->setPlaceholderText( tr(
"Search…" ) );
141 mValuesModel = std::make_unique<QStandardItemModel>();
142 mProxyValues = std::make_unique<QSortFilterProxyModel>();
143 mProxyValues->setSourceModel( mValuesModel.get() );
144 mValuesListView->setModel( mProxyValues.get() );
145 txtSearchEditValues->setShowSearchIcon(
true );
146 txtSearchEditValues->setPlaceholderText( tr(
"Search…" ) );
148 editorSplit->setSizes( QList<int>( {175, 300} ) );
150 functionsplit->setCollapsible( 0,
false );
151 connect( mShowHelpButton, &QPushButton::clicked,
this, [ = ]()
153 functionsplit->setSizes( QList<int>( {mOperationListGroup->width() - mHelpAndValuesWidget->minimumWidth(),
154 mHelpAndValuesWidget->minimumWidth()
156 mShowHelpButton->setEnabled(
false );
158 connect( functionsplit, &QSplitter::splitterMoved,
this, [ = ](
int,
int )
160 mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
164 splitter->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/splitter" ) ).toByteArray() );
165 editorSplit->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/editorsplitter" ) ).toByteArray() );
166 functionsplit->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/functionsplitter" ) ).toByteArray() );
167 mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
173 btnRemoveFile->setEnabled( cmbFileNames->count() > 0 );
180 txtExpressionString->setWrapMode( QsciScintilla::WrapWord );
181 lblAutoSave->clear();
188#if defined(QSCINTILLA_VERSION) && QSCINTILLA_VERSION >= 0x20a00
195 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::red ), -1 );
196 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::red ), -1 );
197 txtExpressionString->setIndicatorOutlineColor( QColor( Qt::red ), -1 );
200 txtExpressionString->indicatorDefine( QgsCodeEditor::HiddenIndicator, FUNCTION_MARKER_ID );
201 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
202 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
203 txtExpressionString->setIndicatorHoverStyle( QgsCodeEditor::DotsIndicator, FUNCTION_MARKER_ID );
205 connect( txtExpressionString, &QgsCodeEditorExpression::indicatorClicked,
this, &QgsExpressionBuilderWidget::indicatorClicked );
206 txtExpressionString->setAutoCompletionCaseSensitivity(
false );
207 txtExpressionString->setAutoCompletionSource( QsciScintilla::AcsAPIs );
208 txtExpressionString->setCallTipsVisible( 0 );
211 mFunctionBuilderHelp->setLineNumbersVisible(
false );
212 mFunctionBuilderHelp->setFoldingVisible(
false );
213 mFunctionBuilderHelp->setEdgeMode( QsciScintilla::EdgeNone );
214 mFunctionBuilderHelp->setEdgeColumn( 0 );
215 mFunctionBuilderHelp->setReadOnly(
true );
216 mFunctionBuilderHelp->setText( tr(
"\"\"\"Define a new function using the @qgsfunction decorator.\n\
218 Besides its normal arguments, the function may specify the following arguments in its signature\n\
219 Those will not need to be specified when calling the function, but will be automatically injected \n\
221 : param feature: The current feature\n\
222 : param parent: The QgsExpression object\n\
223 : param context: ``QgsExpressionContext`` object, that gives access to various additional information like\n\
224 expression variables. E.g. ``context.variable( 'layer_id' )``\n\
225 : returns: The result of the expression.\n\
229 The @qgsfunction decorator accepts the following arguments:\n\
232 : param group: The name of the group under which this expression function will\n\
234 : param handlesnull: Set this to True if your function has custom handling for NULL values.\n\
235 If False, the result will always be NULL as soon as any parameter is NULL.\n\
236 Defaults to False.\n\
237 : param usesgeometry : Set this to True if your function requires access to\n\
238 feature.geometry(). Defaults to False.\n\
239 : param referenced_columns: An array of attribute names that are required to run\n\
240 this function. Defaults to [QgsFeatureRequest.ALL_ATTRIBUTES].\n\
241 : param params_as_list : Set this to True to pass the function parameters as a list. Can be used to mimic \n\
242 behavior before 3.32, when args was not \"auto\". Defaults to False.\n\
250 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/splitter" ), splitter->saveState() );
251 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/editorsplitter" ), editorSplit->saveState() );
252 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/functionsplitter" ), functionsplit->saveState() );
253 delete mExpressionTreeMenuProvider;
261 mExpressionTreeView->loadRecent( recentCollection );
264 mExpressionTreeView->loadUserExpressions();
269 init( context, recentCollection, flags );
275 init( context, recentCollection, flags );
276 mExpressionTreeView->loadFieldNames( fields );
283 mExpressionTreeView->setLayer( mLayer );
284 mExpressionPreviewWidget->setLayer( mLayer );
291 expressionContextUpdated();
296void QgsExpressionBuilderWidget::expressionContextUpdated()
299 mExpressionTreeView->setExpressionContext( mExpressionContext );
300 mExpressionPreviewWidget->setExpressionContext( mExpressionContext );
308void QgsExpressionBuilderWidget::expressionTreeItemChanged(
QgsExpressionItem *item )
310 txtSearchEditValues->clear();
318 mValuesModel->clear();
321 cbxValuesInUse->setChecked(
false );
323 mValueGroupBox->setVisible( isField );
325 mShowHelpButton->setText( isField ? tr(
"Show Values" ) : tr(
"Show Help" ) );
328 QString help = loadFunctionHelp( item );
329 txtHelpText->setText( help );
331 bool isUserExpression = item->parent() && item->parent()->text() == mUserExpressionsGroupName;
333 btnRemoveExpression->setEnabled( isUserExpression );
334 btnEditExpression->setEnabled( isUserExpression );
337void QgsExpressionBuilderWidget::btnRun_pressed()
339 if ( !cmbFileNames->currentItem() )
342 if ( cmbFileNames->currentItem()->data( Qt::UserRole ) == QStringLiteral(
"project" ) )
348 QString file = cmbFileNames->currentItem()->text();
352 runPythonCode( txtPython->text() );
355void QgsExpressionBuilderWidget::runPythonCode(
const QString &code )
359 QString pythontext = code;
362 mExpressionTreeView->refresh();
382 QDir myDir( mFunctionsPath );
383 if ( !myDir.exists() )
385 myDir.mkpath( mFunctionsPath );
388 if ( !fileName.endsWith( QLatin1String(
".py" ) ) )
390 fileName.append(
".py" );
393 fileName = mFunctionsPath + QDir::separator() + fileName;
394 QFile myFile( fileName );
395 if ( myFile.open( QIODevice::WriteOnly | QFile::Truncate ) )
397 QTextStream myFileStream( &myFile );
398#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
399 myFileStream.setCodec(
"UTF-8" );
401 myFileStream << txtPython->text() << Qt::endl;
408 mProject->writeEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ), txtPython->text() );
413 mFunctionsPath = path;
415 dir.setNameFilters( QStringList() << QStringLiteral(
"*.py" ) );
416 QStringList files = dir.entryList( QDir::Files );
417 cmbFileNames->clear();
418 const auto constFiles = files;
419 for (
const QString &name : constFiles )
421 QFileInfo info( mFunctionsPath + QDir::separator() + name );
422 if ( info.baseName() == QLatin1String(
"__init__" ) )
continue;
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 '''" ).arg( txtPython->text() ) );
453 QList<QListWidgetItem *> items = cmbFileNames->findItems( fileName, Qt::MatchExactly );
454 if ( !items.isEmpty() )
457 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"console/iconTabEditorConsole.svg" ) ), fileName );
458 cmbFileNames->insertItem( 0, item );
459 cmbFileNames->setCurrentRow( 0 );
461 QString templateText;
463 txtPython->setText( templateText );
467void QgsExpressionBuilderWidget::btnNewFile_pressed()
474 mProject->readEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ), QString(), &ok );
477 if ( dlg.exec() == QDialog::DialogCode::Accepted )
479 if ( dlg.createProjectFunctions() )
481 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"mIconQgsProjectFile.svg" ) ), DEFAULT_PROJECT_FUNCTIONS_ITEM_NAME );
482 item->setData( Qt::UserRole, QStringLiteral(
"project" ) );
483 cmbFileNames->insertItem( 0, item );
484 cmbFileNames->setCurrentRow( 0 );
486 QString templateText;
488 txtPython->setText( templateText );
495 btnRemoveFile->setEnabled( cmbFileNames->count() > 0 );
499void QgsExpressionBuilderWidget::btnRemoveFile_pressed()
501 if ( cmbFileNames->currentItem()->data( Qt::UserRole ) == QStringLiteral(
"project" ) )
503 if ( QMessageBox::question(
this, tr(
"Remove Project Functions" ),
504 tr(
"Are you sure you want to remove the project functions?" ),
505 QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No )
508 mProject->removeEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ) );
512 if ( QMessageBox::question(
this, tr(
"Remove File" ),
513 tr(
"Are you sure you want to remove current functions file?" ),
514 QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No )
517 QString fileName = cmbFileNames->currentItem()->text();
518 if ( !QFile::remove( mFunctionsPath + QDir::separator() + fileName.append(
".py" ) ) )
520 QMessageBox::warning(
this, tr(
"Remove file" ), tr(
"Failed to remove function file '%1'." ).arg( fileName ) );
525 int currentRow = cmbFileNames->currentRow();
527 QListWidgetItem *itemToRemove =
whileBlocking( cmbFileNames )->takeItem( currentRow );
531 if ( cmbFileNames->count() > 0 )
533 whileBlocking( cmbFileNames )->setCurrentRow( currentRow > 0 ? currentRow - 1 : 0 );
534 if ( cmbFileNames->currentItem()->data( Qt::UserRole ) == QStringLiteral(
"project" ) )
540 loadCodeFromFile( mFunctionsPath + QDir::separator() + cmbFileNames->currentItem()->text() );
545 btnRemoveFile->setEnabled(
false );
551void QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem )
555 if ( lastitem->data( Qt::UserRole ) == QStringLiteral(
"project" ) )
561 QString filename = lastitem->text();
566 if ( item->data( Qt::UserRole ) == QStringLiteral(
"project" ) )
572 QString path = mFunctionsPath + QDir::separator() + item->text();
579 if ( !path.endsWith( QLatin1String(
".py" ) ) )
580 path.append(
".py" );
582 txtPython->loadScript( path );
587 loadFunctionCode( mProject->readEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ) ) );
592 txtPython->setText( code );
595void QgsExpressionBuilderWidget::insertExpressionText(
const QString &text )
599 txtExpressionString->setFocus();
604 Q_UNUSED( fieldValues )
608void QgsExpressionBuilderWidget::fillFieldValues(
const QString &fieldName,
QgsVectorLayer *layer,
int countLimit,
bool forceUsedValues )
620 if ( fieldIndex < 0 )
627 if ( cbxValuesInUse->isVisible() && !cbxValuesInUse->isChecked() && !forceUsedValues )
631 values =
formatter->availableValues( setup.
config(), countLimit, fieldFormatterContext );
637 std::sort( values.begin(), values.end() );
639 mValuesModel->clear();
640 for (
const QVariant &value : std::as_const( values ) )
643 bool forceRepresentedValue =
false;
645 strValue = QStringLiteral(
"NULL" );
646 else if ( value.userType() == QMetaType::Type::Int || value.userType() == QMetaType::Type::Double || value.userType() == QMetaType::Type::LongLong || value.userType() == QMetaType::Type::Bool )
647 strValue = value.toString();
648 else if ( value.userType() == QMetaType::Type::QStringList )
651 const QStringList strList = value.toStringList();
652 for ( QString
str : strList )
654 if ( !result.isEmpty() )
655 result.append( QStringLiteral(
", " ) );
657 result.append(
'\'' +
str.replace(
'\'', QLatin1String(
"''" ) ) +
'\'' );
659 strValue = QStringLiteral(
"array(%1)" ).arg( result );
660 forceRepresentedValue =
true;
662 else if ( value.userType() == QMetaType::Type::QVariantList )
665 const QList list = value.toList();
666 for (
const QVariant &item : list )
668 if ( !result.isEmpty() )
669 result.append( QStringLiteral(
", " ) );
671 result.append( item.toString() );
673 strValue = QStringLiteral(
"array(%1)" ).arg( result );
674 forceRepresentedValue =
true;
677 strValue =
'\'' + value.toString().replace(
'\'', QLatin1String(
"''" ) ) +
'\'';
679 QString representedValue =
formatter->representValue(
layer, fieldIndex, setup.
config(), QVariant(), value );
680 if ( forceRepresentedValue || representedValue != value.toString() )
681 representedValue = representedValue + QStringLiteral(
" [" ) + strValue +
']';
683 QStandardItem *item =
new QStandardItem( representedValue );
684 item->setData( strValue );
685 mValuesModel->appendRow( item );
696 return QStringLiteral(
"<head><style>" ) + helpStylesheet() + QStringLiteral(
"</style></head><body>" ) + helpContents + QStringLiteral(
"</body>" );
704 return mExpressionValid;
709 mExpressionPreviewWidget->setCustomPreviewGenerator( label, choices, previewContextGenerator );
714 mExpressionTreeView->saveToRecent(
expressionText(), collection );
719 mExpressionTreeView->loadRecent( collection );
724 return mExpressionTreeView;
735 mExpressionTreeView->saveToUserExpressions( label, expression, helpText );
740 mExpressionTreeView->removeFromUserExpressions( label );
746 mExpressionPreviewWidget->setGeomCalculator( da );
751 return txtExpressionString->text();
756 txtExpressionString->setText( expression );
761 return lblExpected->text();
766 lblExpected->setText( expected );
767 mExpectedOutputFrame->setVisible( !expected.isNull() );
772 mExpressionContext = context;
773 expressionContextUpdated();
776void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
780 btnClearEditor->setEnabled( ! txtExpressionString->text().isEmpty() );
781 btnSaveExpression->setEnabled(
false );
783 mExpressionPreviewWidget->setExpressionText( text );
788 return mExpressionPreviewWidget->parserError();
793 mExpressionPreviewWidget->setVisible( isVisible );
798 return mExpressionPreviewWidget->evalError();
804 return mExpressionTreeView->model();
816 mExpressionTreeView->setProject(
project );
821 QWidget::showEvent( e );
822 txtExpressionString->setFocus();
825void QgsExpressionBuilderWidget::createErrorMarkers(
const QList<QgsExpression::ParserError> &errors )
830 int errorFirstLine = error.firstLine - 1 ;
831 int errorFirstColumn = error.firstColumn - 1;
832 int errorLastColumn = error.lastColumn - 1;
833 int errorLastLine = error.lastLine - 1;
839 errorFirstLine = errorLastLine;
840 errorFirstColumn = errorLastColumn - 1;
842 txtExpressionString->fillIndicatorRange( errorFirstLine,
845 errorLastColumn, error.errorType );
856 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
857 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->
fnIndex() );
860 int start_pos = txtExpressionString->positionFromLineIndex( inNode->
parserFirstLine - 1, start );
861 txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
864 const QList< QgsExpressionNode * > nodeList = node->
args()->
list();
879 createMarkers( node->
operand() );
892 createMarkers( node->
opLeft() );
893 createMarkers( node->
opRight() );
905 const QList< QgsExpressionNode * > nodeList = node->
list()->
list();
916 const QList<QgsExpressionNodeCondition::WhenThen *> conditions = node->
conditions();
919 createMarkers( cond->whenExp() );
920 createMarkers( cond->thenExp() );
924 createMarkers( node->
elseExp() );
935void QgsExpressionBuilderWidget::clearFunctionMarkers()
937 int lastLine = txtExpressionString->lines() - 1;
938 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
941void QgsExpressionBuilderWidget::clearErrors()
943 int lastLine = txtExpressionString->lines() - 1;
952void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
954 mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
955 mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
958void QgsExpressionBuilderWidget::mValuesListView_doubleClicked(
const QModelIndex &index )
961 txtExpressionString->
insertText(
' ' + index.data( Qt::UserRole + 1 ).toString() +
' ' );
962 txtExpressionString->setFocus();
965void QgsExpressionBuilderWidget::operatorButtonClicked()
967 QPushButton *button = qobject_cast<QPushButton *>( sender() );
970 txtExpressionString->
insertText(
' ' + button->text() +
' ' );
971 txtExpressionString->setFocus();
990 mValueGroupBox->show();
1010 mValueGroupBox->show();
1030 mValueGroupBox->show();
1050 mValueGroupBox->show();
1054void QgsExpressionBuilderWidget::txtPython_textChanged()
1056 lblAutoSave->setText( tr(
"Saving…" ) );
1066 if ( tabWidget->currentIndex() != 1 )
1069 QListWidgetItem *item = cmbFileNames->currentItem();
1073 if ( item->data( Qt::UserRole ) == QStringLiteral(
"project" ) )
1079 QString file = item->text();
1083 lblAutoSave->setText( QStringLiteral(
"Saved" ) );
1084 QGraphicsOpacityEffect *effect =
new QGraphicsOpacityEffect();
1085 lblAutoSave->setGraphicsEffect( effect );
1086 QPropertyAnimation *anim =
new QPropertyAnimation( effect,
"opacity" );
1087 anim->setDuration( 2000 );
1088 anim->setStartValue( 1.0 );
1089 anim->setEndValue( 0.0 );
1090 anim->setEasingCurve( QEasingCurve::OutQuad );
1091 anim->start( QAbstractAnimation::DeleteWhenStopped );
1098 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1100 mExpressionTreeView->saveToUserExpressions( dlg.label().simplified(), dlg.expression(), dlg.helpText() );
1114 ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) )
1121 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1124 if ( dlg.isLabelModified() )
1126 mExpressionTreeView->removeFromUserExpressions( item->text() );
1129 mExpressionTreeView->saveToUserExpressions( dlg.label().simplified(), dlg.expression(), dlg.helpText() );
1144 ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) )
1147 if ( QMessageBox::Yes == QMessageBox::question(
this, tr(
"Remove Stored Expression" ),
1148 tr(
"Do you really want to remove stored expressions '%1'?" ).arg( item->text() ),
1149 QMessageBox::Yes | QMessageBox::No ) )
1151 mExpressionTreeView->removeFromUserExpressions( item->text() );
1156void QgsExpressionBuilderWidget::exportUserExpressions_pressed()
1159 QString lastSaveDir = settings.
value( QStringLiteral(
"lastExportExpressionsDir" ), QDir::homePath(),
QgsSettings::App ).toString();
1160 QString saveFileName = QFileDialog::getSaveFileName(
1162 tr(
"Export User Expressions" ),
1164 tr(
"User expressions" ) +
" (*.json)" );
1169 if ( saveFileName.isEmpty() )
1172 QFileInfo saveFileInfo( saveFileName );
1174 if ( saveFileInfo.suffix().isEmpty() )
1176 QString saveFileNameWithSuffix = saveFileName.append(
".json" );
1177 saveFileInfo = QFileInfo( saveFileNameWithSuffix );
1182 QJsonDocument exportJson = mExpressionTreeView->exportUserExpressions();
1183 QFile jsonFile( saveFileName );
1185 if ( !jsonFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
1186 QMessageBox::warning(
this, tr(
"Export user expressions" ), tr(
"Error while creating the expressions file." ) );
1188 if ( ! jsonFile.write( exportJson.toJson() ) )
1189 QMessageBox::warning(
this, tr(
"Export user expressions" ), tr(
"Error while creating the expressions file." ) );
1194void QgsExpressionBuilderWidget::importUserExpressions_pressed()
1197 QString lastImportDir = settings.
value( QStringLiteral(
"lastImportExpressionsDir" ), QDir::homePath(),
QgsSettings::App ).toString();
1198 QString loadFileName = QFileDialog::getOpenFileName(
1200 tr(
"Import User Expressions" ),
1202 tr(
"User expressions" ) +
" (*.json)" );
1204 if ( loadFileName.isEmpty() )
1207 QFileInfo loadFileInfo( loadFileName );
1211 QFile jsonFile( loadFileName );
1213 if ( !jsonFile.open( QFile::ReadOnly ) )
1214 QMessageBox::warning(
this, tr(
"Import User Expressions" ), tr(
"Error while reading the expressions file." ) );
1216 QTextStream jsonStream( &jsonFile );
1217 QString jsonString = jsonFile.readAll();
1220 QJsonDocument importJson = QJsonDocument::fromJson( jsonString.toUtf8() );
1222 if ( importJson.isNull() )
1224 QMessageBox::warning(
this, tr(
"Import User Expressions" ), tr(
"Error while reading the expressions file." ) );
1228 mExpressionTreeView->loadExpressionsFromJson( importJson );
1234 return mExpressionTreeView->findExpressions( label );
1237void QgsExpressionBuilderWidget::indicatorClicked(
int line,
int index, Qt::KeyboardModifiers state )
1239 if ( state & Qt::ControlModifier )
1241 int position = txtExpressionString->positionFromLineIndex( line, index );
1242 long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID,
static_cast<long int>( position ) );
1244 QString help = getFunctionHelp( func );
1245 txtHelpText->setText( help );
1249void QgsExpressionBuilderWidget::onExpressionParsed(
bool state )
1253 mExpressionValid = state;
1256 createMarkers( mExpressionPreviewWidget->rootNode() );
1260 createErrorMarkers( mExpressionPreviewWidget->parserErrors() );
1264QString QgsExpressionBuilderWidget::helpStylesheet()
const
1270 style +=
" .functionname {color: #0a6099; font-weight: bold;} "
1271 " .argument {font-family: monospace; color: #bf0c0c; font-style: italic; } "
1272 " td.argument { padding-right: 10px; }";
1277QString QgsExpressionBuilderWidget::loadFunctionHelp(
QgsExpressionItem *expressionItem )
1279 if ( !expressionItem )
1282 QString helpContents = expressionItem->
getHelpText();
1285 if ( helpContents.isEmpty() )
1287 QString name = expressionItem->data( Qt::UserRole ).toString();
1295 return "<head><style>" + helpStylesheet() +
"</style></head><body>" + helpContents +
"</body>";
1302QMenu *QgsExpressionBuilderWidget::ExpressionTreeMenuProvider::createContextMenu(
QgsExpressionItem *item )
1304 QMenu *menu =
nullptr;
1308 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.