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