18 #include <QTextStream> 
   20 #include <QInputDialog> 
   22 #include <QGraphicsOpacityEffect> 
   23 #include <QPropertyAnimation> 
   24 #include <QMessageBox> 
   25 #include <QVersionNumber> 
   27 #include <QJsonDocument> 
   28 #include <QJsonObject> 
   30 #include <QFileDialog> 
   61     if ( fieldIndex != -1 )
 
   79   connect( btnRun, &QToolButton::pressed, 
this, &QgsExpressionBuilderWidget::btnRun_pressed );
 
   80   connect( btnNewFile, &QPushButton::clicked, 
this, &QgsExpressionBuilderWidget::btnNewFile_pressed );
 
   81   connect( btnRemoveFile, &QPushButton::clicked, 
this, &QgsExpressionBuilderWidget::btnRemoveFile_pressed );
 
   82   connect( cmbFileNames, &QListWidget::currentItemChanged, 
this, &QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged );
 
   83   connect( txtExpressionString, &QgsCodeEditorExpression::textChanged, 
this, &QgsExpressionBuilderWidget::txtExpressionString_textChanged );
 
   84   connect( txtPython, &QgsCodeEditorPython::textChanged, 
this, &QgsExpressionBuilderWidget::txtPython_textChanged );
 
   85   connect( txtSearchEditValues, &QgsFilterLineEdit::textChanged, 
this, &QgsExpressionBuilderWidget::txtSearchEditValues_textChanged );
 
   86   connect( mValuesListView, &QListView::doubleClicked, 
this, &QgsExpressionBuilderWidget::mValuesListView_doubleClicked );
 
   90   connect( btnImportExpressions, &QToolButton::clicked, 
this, &QgsExpressionBuilderWidget::importUserExpressions_pressed );
 
   91   connect( btnExportExpressions, &QToolButton::clicked, 
this, &QgsExpressionBuilderWidget::exportUserExpressions_pressed );
 
   92   connect( btnClearEditor, &QToolButton::clicked, txtExpressionString, &QgsCodeEditorExpression::clear );
 
  105   mExpressionTreeMenuProvider = 
new ExpressionTreeMenuProvider( 
this );
 
  106   mExpressionTreeView->setMenuProvider( mExpressionTreeMenuProvider );
 
  108   txtHelpText->setOpenExternalLinks( 
true );
 
  109   mValueGroupBox->hide();
 
  126   const auto pushButtons { mOperatorsGroupBox->findChildren<QPushButton *>() };
 
  127   for ( QPushButton *button : pushButtons )
 
  129     connect( button, &QAbstractButton::clicked, 
this, &QgsExpressionBuilderWidget::operatorButtonClicked );
 
  132   txtSearchEdit->setShowSearchIcon( 
true );
 
  133   txtSearchEdit->setPlaceholderText( tr( 
"Search…" ) );
 
  135   mValuesModel = std::make_unique<QStandardItemModel>();
 
  136   mProxyValues = std::make_unique<QSortFilterProxyModel>();
 
  137   mProxyValues->setSourceModel( mValuesModel.get() );
 
  138   mValuesListView->setModel( mProxyValues.get() );
 
  139   txtSearchEditValues->setShowSearchIcon( 
true );
 
  140   txtSearchEditValues->setPlaceholderText( tr( 
"Search…" ) );
 
  142   editorSplit->setSizes( QList<int>( {175, 300} ) );
 
  144   functionsplit->setCollapsible( 0, 
false );
 
  145   connect( mShowHelpButton, &QPushButton::clicked, 
this, [ = ]()
 
  147     functionsplit->setSizes( QList<int>( {mOperationListGroup->width() - mHelpAndValuesWidget->minimumWidth(),
 
  148                                           mHelpAndValuesWidget->minimumWidth()
 
  150     mShowHelpButton->setEnabled( 
false );
 
  152   connect( functionsplit, &QSplitter::splitterMoved, 
this, [ = ]( 
int, 
int )
 
  154     mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
 
  158   splitter->restoreState( settings.value( QStringLiteral( 
"Windows/QgsExpressionBuilderWidget/splitter" ) ).toByteArray() );
 
  159   editorSplit->restoreState( settings.value( QStringLiteral( 
"Windows/QgsExpressionBuilderWidget/editorsplitter" ) ).toByteArray() );
 
  160   functionsplit->restoreState( settings.value( QStringLiteral( 
"Windows/QgsExpressionBuilderWidget/functionsplitter" ) ).toByteArray() );
 
  161   mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
 
  167     btnRemoveFile->setEnabled( cmbFileNames->count() > 0 );
 
  174   txtExpressionString->setWrapMode( QsciScintilla::WrapWord );
 
  175   lblAutoSave->clear();
 
  182 #if defined(QSCINTILLA_VERSION) && QSCINTILLA_VERSION >= 0x20a00 
  189   txtExpressionString->setIndicatorForegroundColor( QColor( Qt::red ), -1 );
 
  190   txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::red ), -1 );
 
  191   txtExpressionString->setIndicatorOutlineColor( QColor( Qt::red ), -1 );
 
  194   txtExpressionString->indicatorDefine( QgsCodeEditor::HiddenIndicator, FUNCTION_MARKER_ID );
 
  195   txtExpressionString->setIndicatorForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
 
  196   txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
 
  197   txtExpressionString->setIndicatorHoverStyle( QgsCodeEditor::DotsIndicator, FUNCTION_MARKER_ID );
 
  199   connect( txtExpressionString, &QgsCodeEditorExpression::indicatorClicked, 
this, &QgsExpressionBuilderWidget::indicatorClicked );
 
  200   txtExpressionString->setAutoCompletionCaseSensitivity( 
false );
 
  201   txtExpressionString->setAutoCompletionSource( QsciScintilla::AcsAPIs );
 
  202   txtExpressionString->setCallTipsVisible( 0 );
 
  205   mFunctionBuilderHelp->setLineNumbersVisible( 
false );
 
  206   mFunctionBuilderHelp->setFoldingVisible( 
false );
 
  207   mFunctionBuilderHelp->setEdgeMode( QsciScintilla::EdgeNone );
 
  208   mFunctionBuilderHelp->setEdgeColumn( 0 );
 
  209   mFunctionBuilderHelp->setReadOnly( 
true );
 
  210   mFunctionBuilderHelp->setText( tr( 
"\"\"\"Define a new function using the @qgsfunction decorator.\n\ 
  212  The function accepts the following parameters\n\ 
  214  : param [any]: Define any parameters you want to pass to your function before\n\ 
  215  the following arguments.\n\ 
  216  : param feature: The current feature\n\ 
  217  : param parent: The QgsExpression object\n\ 
  218  : param context: If there is an argument called ``context`` found at the last\n\ 
  219                    position, this variable will contain a ``QgsExpressionContext``\n\ 
  220                    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 args: Defines the number of arguments. With ``args = 'auto'`` the number of\n\ 
  230                arguments will automatically be extracted from the signature.\n\ 
  231                With ``args = -1``, any number of arguments are accepted.\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\ 
  248   settings.
setValue( QStringLiteral( 
"Windows/QgsExpressionBuilderWidget/splitter" ), splitter->saveState() );
 
  249   settings.
setValue( QStringLiteral( 
"Windows/QgsExpressionBuilderWidget/editorsplitter" ), editorSplit->saveState() );
 
  250   settings.
setValue( QStringLiteral( 
"Windows/QgsExpressionBuilderWidget/functionsplitter" ), functionsplit->saveState() );
 
  251   delete mExpressionTreeMenuProvider;
 
  259     mExpressionTreeView->loadRecent( recentCollection );
 
  262     mExpressionTreeView->loadUserExpressions();
 
  267   init( context, recentCollection, flags );
 
  273   init( context, recentCollection, flags );
 
  274   mExpressionTreeView->loadFieldNames( fields );
 
  281   mExpressionTreeView->setLayer( mLayer );
 
  282   mExpressionPreviewWidget->setLayer( mLayer );
 
  289     expressionContextUpdated();
 
  290     txtExpressionString->setFields( mLayer->
fields() );
 
  294 void QgsExpressionBuilderWidget::expressionContextUpdated()
 
  296   txtExpressionString->setExpressionContext( mExpressionContext );
 
  297   mExpressionTreeView->setExpressionContext( mExpressionContext );
 
  298   mExpressionPreviewWidget->setExpressionContext( mExpressionContext );
 
  306 void QgsExpressionBuilderWidget::expressionTreeItemChanged( 
QgsExpressionItem *item )
 
  308   txtSearchEditValues->clear();
 
  316     mValuesModel->clear();
 
  319     cbxValuesInUse->setChecked( 
false );
 
  321   mValueGroupBox->setVisible( isField );
 
  323   mShowHelpButton->setText( isField ? tr( 
"Show Values" ) : tr( 
"Show Help" ) );
 
  326   QString help = loadFunctionHelp( item );
 
  327   txtHelpText->setText( help );
 
  329   bool isUserExpression = item->parent() && item->parent()->text() == mUserExpressionsGroupName;
 
  331   btnRemoveExpression->setEnabled( isUserExpression );
 
  332   btnEditExpression->setEnabled( isUserExpression );
 
  335 void QgsExpressionBuilderWidget::btnRun_pressed()
 
  337   if ( !cmbFileNames->currentItem() )
 
  340   QString file = cmbFileNames->currentItem()->text();
 
  342   runPythonCode( txtPython->text() );
 
  345 void QgsExpressionBuilderWidget::runPythonCode( 
const QString &code )
 
  349     QString pythontext = code;
 
  352   mExpressionTreeView->refresh();
 
  357   QDir myDir( mFunctionsPath );
 
  358   if ( !myDir.exists() )
 
  360     myDir.mkpath( mFunctionsPath );
 
  363   if ( !fileName.endsWith( QLatin1String( 
".py" ) ) )
 
  365     fileName.append( 
".py" );
 
  368   fileName = mFunctionsPath + QDir::separator() + fileName;
 
  369   QFile myFile( fileName );
 
  370   if ( myFile.open( QIODevice::WriteOnly | QFile::Truncate ) )
 
  372     QTextStream myFileStream( &myFile );
 
  373 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) 
  374     myFileStream << txtPython->text() << endl;
 
  376     myFileStream << txtPython->text() << Qt::endl;
 
  384   mFunctionsPath = path;
 
  386   dir.setNameFilters( QStringList() << QStringLiteral( 
"*.py" ) );
 
  387   QStringList files = dir.entryList( QDir::Files );
 
  388   cmbFileNames->clear();
 
  389   const auto constFiles = files;
 
  390   for ( 
const QString &name : constFiles )
 
  392     QFileInfo info( mFunctionsPath + QDir::separator() + name );
 
  393     if ( info.baseName() == QLatin1String( 
"__init__" ) ) 
continue;
 
  394     QListWidgetItem *item = 
new QListWidgetItem( 
QgsApplication::getThemeIcon( QStringLiteral( 
"console/iconTabEditorConsole.svg" ) ), info.baseName() );
 
  395     cmbFileNames->addItem( item );
 
  397   if ( !cmbFileNames->currentItem() )
 
  399     cmbFileNames->setCurrentRow( 0 );
 
  402   if ( cmbFileNames->count() == 0 )
 
  406     txtPython->setText( QStringLiteral( 
"'''\n#Sample custom function file\n" 
  407                                         "#(uncomment to use and customize or Add button to create a new file) \n%1 \n '''" ).arg( txtPython->text() ) );
 
  414   QList<QListWidgetItem *> items = cmbFileNames->findItems( fileName, Qt::MatchExactly );
 
  415   if ( !items.isEmpty() )
 
  418   QListWidgetItem *item = 
new QListWidgetItem( 
QgsApplication::getThemeIcon( QStringLiteral( 
"console/iconTabEditorConsole.svg" ) ), fileName );
 
  419   cmbFileNames->insertItem( 0, item );
 
  420   cmbFileNames->setCurrentRow( 0 );
 
  424   txtPython->setText( templatetxt );
 
  428 void QgsExpressionBuilderWidget::btnNewFile_pressed()
 
  431   QString text = QInputDialog::getText( 
this, tr( 
"New File" ),
 
  432                                         tr( 
"New file name:" ), QLineEdit::Normal,
 
  434   if ( ok && !text.isEmpty() )
 
  440 void QgsExpressionBuilderWidget::btnRemoveFile_pressed()
 
  442   if ( QMessageBox::question( 
this, tr( 
"Remove File" ),
 
  443                               tr( 
"Are you sure you want to remove current functions file?" ),
 
  444                               QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No )
 
  447   int currentRow = cmbFileNames->currentRow();
 
  448   QString fileName = cmbFileNames->currentItem()->text();
 
  449   if ( QFile::remove( mFunctionsPath + QDir::separator() + fileName.append( 
".py" ) ) )
 
  452       QListWidgetItem *itemToRemove = 
whileBlocking( cmbFileNames )->takeItem( currentRow );
 
  456     if ( cmbFileNames->count() > 0 )
 
  458       cmbFileNames->setCurrentRow( currentRow > 0 ? currentRow - 1 : 0 );
 
  459       loadCodeFromFile( mFunctionsPath + QDir::separator() + cmbFileNames->currentItem()->text() );
 
  463       btnRemoveFile->setEnabled( 
false );
 
  469     QMessageBox::warning( 
this, tr( 
"Remove file" ), tr( 
"Failed to remove function file '%1'." ).arg( fileName ) );
 
  473 void QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem )
 
  477     QString filename = lastitem->text();
 
  480   QString path = mFunctionsPath + QDir::separator() + item->text();
 
  486   if ( !path.endsWith( QLatin1String( 
".py" ) ) )
 
  487     path.append( 
".py" );
 
  489   txtPython->loadScript( path );
 
  494   txtPython->setText( code );
 
  497 void QgsExpressionBuilderWidget::insertExpressionText( 
const QString &text )
 
  500   txtExpressionString->insertText( text );
 
  501   txtExpressionString->setFocus();
 
  506   Q_UNUSED( fieldValues )
 
  510 void QgsExpressionBuilderWidget::fillFieldValues( 
const QString &fieldName, 
int countLimit, 
bool forceUsedValues )
 
  522   if ( fieldIndex < 0 )
 
  529   if ( cbxValuesInUse->isVisible() && !cbxValuesInUse->isChecked() && !forceUsedValues )
 
  533     values = 
formatter->availableValues( setup.
config(), countLimit, fieldFormatterContext );
 
  537     values = qgis::setToList( mLayer->
uniqueValues( fieldIndex, countLimit ) );
 
  539   std::sort( values.begin(), values.end() );
 
  541   mValuesModel->clear();
 
  542   for ( 
const QVariant &value : std::as_const( values ) )
 
  545     bool forceRepresentedValue = 
false;
 
  546     if ( value.isNull() )
 
  547       strValue = QStringLiteral( 
"NULL" );
 
  548     else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong )
 
  549       strValue = value.toString();
 
  550     else if ( value.type() == QVariant::StringList )
 
  553       const QStringList strList = value.toStringList();
 
  554       for ( QString 
str : strList )
 
  556         if ( !result.isEmpty() )
 
  557           result.append( QStringLiteral( 
", " ) );
 
  559         result.append( 
'\'' + 
str.replace( 
'\'', QLatin1String( 
"''" ) ) + 
'\'' );
 
  561       strValue = QStringLiteral( 
"array(%1)" ).arg( result );
 
  562       forceRepresentedValue = 
true;
 
  565       strValue = 
'\'' + value.toString().replace( 
'\'', QLatin1String( 
"''" ) ) + 
'\'';
 
  567     QString representedValue = 
formatter->representValue( mLayer, fieldIndex, setup.
config(), QVariant(), value );
 
  568     if ( forceRepresentedValue || representedValue != value.toString() )
 
  569       representedValue = representedValue + QStringLiteral( 
" [" ) + strValue + 
']';
 
  571     QStandardItem *item = 
new QStandardItem( representedValue );
 
  572     item->setData( strValue );
 
  573     mValuesModel->appendRow( item );
 
  584   return QStringLiteral( 
"<head><style>" ) + helpStylesheet() + QStringLiteral( 
"</style></head><body>" ) + helpContents + QStringLiteral( 
"</body>" );
 
  592   return mExpressionValid;
 
  597   mExpressionTreeView->saveToRecent( 
expressionText(), collection );
 
  602   mExpressionTreeView->loadRecent( collection );
 
  607   return mExpressionTreeView;
 
  613   mExpressionTreeView->loadUserExpressions();
 
  618   mExpressionTreeView->saveToUserExpressions( label, expression, helpText );
 
  623   mExpressionTreeView->removeFromUserExpressions( label );
 
  629   mExpressionPreviewWidget->setGeomCalculator( da );
 
  634   return txtExpressionString->text();
 
  639   txtExpressionString->setText( expression );
 
  644   return lblExpected->text();
 
  649   lblExpected->setText( expected );
 
  650   mExpectedOutputFrame->setVisible( !expected.isNull() );
 
  655   mExpressionContext = context;
 
  656   expressionContextUpdated();
 
  659 void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
 
  663   btnClearEditor->setEnabled( ! txtExpressionString->text().isEmpty() );
 
  664   btnSaveExpression->setEnabled( 
false );
 
  666   mExpressionPreviewWidget->setExpressionText( text );
 
  671   return mExpressionPreviewWidget->parserError();
 
  676   mExpressionPreviewWidget->setVisible( isVisible );
 
  681   return mExpressionPreviewWidget->evalError();
 
  687   return mExpressionTreeView->model();
 
  699   mExpressionTreeView->setProject( 
project );
 
  704   QWidget::showEvent( e );
 
  705   txtExpressionString->setFocus();
 
  708 void QgsExpressionBuilderWidget::createErrorMarkers( 
const QList<QgsExpression::ParserError> &errors )
 
  713     int errorFirstLine = error.firstLine - 1 ;
 
  714     int errorFirstColumn = error.firstColumn - 1;
 
  715     int errorLastColumn = error.lastColumn - 1;
 
  716     int errorLastLine = error.lastLine - 1;
 
  722       errorFirstLine = errorLastLine;
 
  723       errorFirstColumn = errorLastColumn - 1;
 
  725     txtExpressionString->fillIndicatorRange( errorFirstLine,
 
  728         errorLastColumn, error.errorType );
 
  732 void QgsExpressionBuilderWidget::createMarkers( 
const QgsExpressionNode *inNode )
 
  736     case QgsExpressionNode::NodeType::ntFunction:
 
  739       txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
 
  740       txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->
fnIndex() );
 
  743       int start_pos = txtExpressionString->positionFromLineIndex( inNode->
parserFirstLine - 1, start );
 
  744       txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
 
  747         const QList< QgsExpressionNode * > nodeList = node->
args()->
list();
 
  755     case QgsExpressionNode::NodeType::ntLiteral:
 
  759     case QgsExpressionNode::NodeType::ntUnaryOperator:
 
  762       createMarkers( node->
operand() );
 
  765     case QgsExpressionNode::NodeType::ntBinaryOperator:
 
  768       createMarkers( node->
opLeft() );
 
  769       createMarkers( node->
opRight() );
 
  772     case QgsExpressionNode::NodeType::ntColumnRef:
 
  776     case QgsExpressionNode::NodeType::ntInOperator:
 
  781         const QList< QgsExpressionNode * > nodeList = node->
list()->
list();
 
  789     case QgsExpressionNode::NodeType::ntCondition:
 
  792       const QList<QgsExpressionNodeCondition::WhenThen *> conditions = node->
conditions();
 
  795         createMarkers( cond->whenExp() );
 
  796         createMarkers( cond->thenExp() );
 
  800         createMarkers( node->
elseExp() );
 
  804     case QgsExpressionNode::NodeType::ntIndexOperator:
 
  811 void QgsExpressionBuilderWidget::clearFunctionMarkers()
 
  813   int lastLine = txtExpressionString->lines() - 1;
 
  814   txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
 
  817 void QgsExpressionBuilderWidget::clearErrors()
 
  819   int lastLine = txtExpressionString->lines() - 1;
 
  828 void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
 
  830   mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
 
  831   mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
 
  834 void QgsExpressionBuilderWidget::mValuesListView_doubleClicked( 
const QModelIndex &index )
 
  837   txtExpressionString->insertText( 
' ' + index.data( Qt::UserRole + 1 ).toString() + 
' ' );
 
  838   txtExpressionString->setFocus();
 
  841 void QgsExpressionBuilderWidget::operatorButtonClicked()
 
  843   QPushButton *button = qobject_cast<QPushButton *>( sender() );
 
  846   txtExpressionString->insertText( 
' ' + button->text() + 
' ' );
 
  847   txtExpressionString->setFocus();
 
  855   if ( !mLayer || !item )
 
  858   mValueGroupBox->show();
 
  867   if ( !mLayer || !item )
 
  870   mValueGroupBox->show();
 
  879   if ( !mLayer || !item )
 
  882   mValueGroupBox->show();
 
  891   if ( !mLayer || !item )
 
  894   mValueGroupBox->show();
 
  898 void QgsExpressionBuilderWidget::txtPython_textChanged()
 
  900   lblAutoSave->setText( tr( 
"Saving…" ) );
 
  910   if ( tabWidget->currentIndex() != 1 )
 
  913   QListWidgetItem *item = cmbFileNames->currentItem();
 
  917   QString file = item->text();
 
  919   lblAutoSave->setText( QStringLiteral( 
"Saved" ) );
 
  920   QGraphicsOpacityEffect *effect = 
new QGraphicsOpacityEffect();
 
  921   lblAutoSave->setGraphicsEffect( effect );
 
  922   QPropertyAnimation *anim = 
new QPropertyAnimation( effect, 
"opacity" );
 
  923   anim->setDuration( 2000 );
 
  924   anim->setStartValue( 1.0 );
 
  925   anim->setEndValue( 0.0 );
 
  926   anim->setEasingCurve( QEasingCurve::OutQuad );
 
  927   anim->start( QAbstractAnimation::DeleteWhenStopped );
 
  934   if ( dlg.exec() == QDialog::DialogCode::Accepted )
 
  936     mExpressionTreeView->saveToUserExpressions( dlg.label().simplified(), dlg.expression(), dlg.helpText() );
 
  950        ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) )
 
  954   QString helpText = settings.
value( QStringLiteral( 
"user/%1/helpText" ).arg( item->text() ), 
"", QgsSettings::Section::Expressions ).toString();
 
  957   if ( dlg.exec() == QDialog::DialogCode::Accepted )
 
  960     if ( dlg.isLabelModified() )
 
  962       mExpressionTreeView->removeFromUserExpressions( item->text() );
 
  965     mExpressionTreeView->saveToUserExpressions( dlg.label().simplified(), dlg.expression(), dlg.helpText() );
 
  980        ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) )
 
  983   if ( QMessageBox::Yes == QMessageBox::question( 
this, tr( 
"Remove Stored Expression" ),
 
  984        tr( 
"Do you really want to remove stored expressions '%1'?" ).arg( item->text() ),
 
  985        QMessageBox::Yes | QMessageBox::No ) )
 
  987     mExpressionTreeView->removeFromUserExpressions( item->text() );
 
  992 void QgsExpressionBuilderWidget::exportUserExpressions_pressed()
 
  995   QString lastSaveDir = settings.
value( QStringLiteral( 
"lastExportExpressionsDir" ), QDir::homePath(), 
QgsSettings::App ).toString();
 
  996   QString saveFileName = QFileDialog::getSaveFileName(
 
  998                            tr( 
"Export User Expressions" ),
 
 1000                            tr( 
"User expressions" ) + 
" (*.json)" );
 
 1002   if ( saveFileName.isEmpty() )
 
 1005   QFileInfo saveFileInfo( saveFileName );
 
 1007   if ( saveFileInfo.suffix().isEmpty() )
 
 1009     QString saveFileNameWithSuffix = saveFileName.append( 
".json" );
 
 1010     saveFileInfo = QFileInfo( saveFileNameWithSuffix );
 
 1015   QJsonDocument exportJson = mExpressionTreeView->exportUserExpressions();
 
 1016   QFile jsonFile( saveFileName );
 
 1018   if ( !jsonFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
 
 1019     QMessageBox::warning( 
this, tr( 
"Export user expressions" ), tr( 
"Error while creating the expressions file." ) );
 
 1021   if ( ! jsonFile.write( exportJson.toJson() ) )
 
 1022     QMessageBox::warning( 
this, tr( 
"Export user expressions" ), tr( 
"Error while creating the expressions file." ) );
 
 1027 void QgsExpressionBuilderWidget::importUserExpressions_pressed()
 
 1030   QString lastImportDir = settings.
value( QStringLiteral( 
"lastImportExpressionsDir" ), QDir::homePath(), 
QgsSettings::App ).toString();
 
 1031   QString loadFileName = QFileDialog::getOpenFileName(
 
 1033                            tr( 
"Import User Expressions" ),
 
 1035                            tr( 
"User expressions" ) + 
" (*.json)" );
 
 1037   if ( loadFileName.isEmpty() )
 
 1040   QFileInfo loadFileInfo( loadFileName );
 
 1044   QFile jsonFile( loadFileName );
 
 1046   if ( !jsonFile.open( QFile::ReadOnly ) )
 
 1047     QMessageBox::warning( 
this, tr( 
"Import User Expressions" ), tr( 
"Error while reading the expressions file." ) );
 
 1049   QTextStream jsonStream( &jsonFile );
 
 1050   QString jsonString = jsonFile.readAll();
 
 1053   QJsonDocument importJson = QJsonDocument::fromJson( jsonString.toUtf8() );
 
 1055   if ( importJson.isNull() )
 
 1057     QMessageBox::warning( 
this, tr( 
"Import User Expressions" ), tr( 
"Error while reading the expressions file." ) );
 
 1061   mExpressionTreeView->loadExpressionsFromJson( importJson );
 
 1067   return mExpressionTreeView->findExpressions( label );
 
 1070 void QgsExpressionBuilderWidget::indicatorClicked( 
int line, 
int index, Qt::KeyboardModifiers state )
 
 1072   if ( state & Qt::ControlModifier )
 
 1074     int position = txtExpressionString->positionFromLineIndex( line, index );
 
 1075     long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID, 
static_cast<long int>( position ) );
 
 1077     QString help = getFunctionHelp( func );
 
 1078     txtHelpText->setText( help );
 
 1082 void QgsExpressionBuilderWidget::onExpressionParsed( 
bool state )
 
 1086   mExpressionValid = state;
 
 1089     createMarkers( mExpressionPreviewWidget->rootNode() );
 
 1093     createErrorMarkers( mExpressionPreviewWidget->parserErrors() );
 
 1097 QString QgsExpressionBuilderWidget::helpStylesheet()
 const 
 1103   style += 
" .functionname {color: #0a6099; font-weight: bold;} " 
 1104            " .argument {font-family: monospace; color: #bf0c0c; font-style: italic; } " 
 1105            " td.argument { padding-right: 10px; }";
 
 1110 QString QgsExpressionBuilderWidget::loadFunctionHelp( 
QgsExpressionItem *expressionItem )
 
 1112   if ( !expressionItem )
 
 1115   QString helpContents = expressionItem->
getHelpText();
 
 1118   if ( helpContents.isEmpty() )
 
 1120     QString name = expressionItem->data( Qt::UserRole ).toString();
 
 1128   return "<head><style>" + helpStylesheet() + 
"</style></head><body>" + helpContents + 
"</body>";
 
 1135 QMenu *QgsExpressionBuilderWidget::ExpressionTreeMenuProvider::createContextMenu( 
QgsExpressionItem *item )
 
 1137   QMenu *menu = 
nullptr;
 
 1141     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 general purpose distance and area calculator, capable of performing ellipsoid based calculations.
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.
An expression item that can be used in the QgsExpressionBuilderWidget tree.
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.
A binary expression operator, which operates on two values.
QgsExpressionNode * opRight() const
Returns the node to the right of the operator.
QgsExpressionNode * opLeft() const
Returns the node to the left 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.
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.
static const QList< QgsExpressionFunction * > & Functions()
static 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).
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 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.
Represents a vector layer which manages a vector based data sets.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
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.