36 #include <QTextStream>    38 #include <QInputDialog>    40 #include <QGraphicsOpacityEffect>    41 #include <QPropertyAnimation>    49   connect( btnRun, &QToolButton::pressed, 
this, &QgsExpressionBuilderWidget::btnRun_pressed );
    50   connect( btnNewFile, &QToolButton::pressed, 
this, &QgsExpressionBuilderWidget::btnNewFile_pressed );
    51   connect( cmbFileNames, &QListWidget::currentItemChanged, 
this, &QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged );
    52   connect( expressionTree, &QTreeView::doubleClicked, 
this, &QgsExpressionBuilderWidget::expressionTree_doubleClicked );
    53   connect( txtExpressionString, &QgsCodeEditorExpression::textChanged, 
this, &QgsExpressionBuilderWidget::txtExpressionString_textChanged );
    54   connect( txtPython, &QgsCodeEditorPython::textChanged, 
this, &QgsExpressionBuilderWidget::txtPython_textChanged );
    55   connect( txtSearchEditValues, &QgsFilterLineEdit::textChanged, 
this, &QgsExpressionBuilderWidget::txtSearchEditValues_textChanged );
    56   connect( txtSearchEdit, &QgsFilterLineEdit::textChanged, 
this, &QgsExpressionBuilderWidget::txtSearchEdit_textChanged );
    57   connect( lblPreview, &QLabel::linkActivated, 
this, &QgsExpressionBuilderWidget::lblPreview_linkActivated );
    58   connect( mValuesListView, &QListView::doubleClicked, 
this, &QgsExpressionBuilderWidget::mValuesListView_doubleClicked );
    60   mValueGroupBox->hide();
    63   mModel = 
new QStandardItemModel();
    65   mProxyModel->setDynamicSortFilter( 
true );
    66   mProxyModel->setSourceModel( mModel );
    67   expressionTree->setModel( mProxyModel );
    68   expressionTree->setSortingEnabled( 
true );
    69   expressionTree->sortByColumn( 0, Qt::AscendingOrder );
    71   expressionTree->setContextMenuPolicy( Qt::CustomContextMenu );
    73   connect( expressionTree, &QWidget::customContextMenuRequested, 
this, &QgsExpressionBuilderWidget::showContextMenu );
    74   connect( expressionTree->selectionModel(), &QItemSelectionModel::currentChanged,
    75            this, &QgsExpressionBuilderWidget::currentChanged );
    80   Q_FOREACH ( QPushButton *button, mOperatorsGroupBox->findChildren<QPushButton *>() )
    82     connect( button, &QAbstractButton::pressed, 
this, &QgsExpressionBuilderWidget::operatorButtonClicked );
    85   txtSearchEdit->setShowSearchIcon( 
true );
    86   txtSearchEdit->setPlaceholderText( tr( 
"Search…" ) );
    88   mValuesModel = 
new QStringListModel();
    89   mProxyValues = 
new QSortFilterProxyModel();
    90   mProxyValues->setSourceModel( mValuesModel );
    91   mValuesListView->setModel( mProxyValues );
    92   txtSearchEditValues->setShowSearchIcon( 
true );
    93   txtSearchEditValues->setPlaceholderText( tr( 
"Search…" ) );
    95   editorSplit->setSizes( QList<int>( {175, 300} ) );
    97   functionsplit->setCollapsible( 0, 
false );
    98   connect( mShowHelpButton, &QPushButton::clicked, 
this, [ = ]()
   100     functionsplit->setSizes( QList<int>( {mOperationListGroup->width() - mHelpAndValuesWidget->minimumWidth(),
   101                                           mHelpAndValuesWidget->minimumWidth()
   103     mShowHelpButton->setEnabled( 
false );
   105   connect( functionsplit, &QSplitter::splitterMoved, 
this, [ = ]( 
int, 
int )
   107     mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
   112   splitter->restoreState( settings.value( QStringLiteral( 
"Windows/QgsExpressionBuilderWidget/splitter" ) ).toByteArray() );
   113   editorSplit->restoreState( settings.value( QStringLiteral( 
"Windows/QgsExpressionBuilderWidget/editorsplitter" ) ).toByteArray() );
   114   functionsplit->restoreState( settings.value( QStringLiteral( 
"Windows/QgsExpressionBuilderWidget/functionsplitter" ) ).toByteArray() );
   116   txtExpressionString->setFoldingVisible( 
false );
   118   updateFunctionTree();
   132   QModelIndex firstItem = mProxyModel->index( 0, 0, QModelIndex() );
   133   expressionTree->setCurrentIndex( firstItem );
   135   txtExpressionString->setWrapMode( QsciScintilla::WrapWord );
   136   lblAutoSave->clear();
   144 #if defined(QSCINTILLA_VERSION) && QSCINTILLA_VERSION >= 0x20a00   151   txtExpressionString->setIndicatorForegroundColor( QColor( Qt::red ), -1 );
   152   txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::red ), -1 );
   153   txtExpressionString->setIndicatorOutlineColor( QColor( Qt::red ), -1 );
   156   txtExpressionString->indicatorDefine( QgsCodeEditor::HiddenIndicator, FUNCTION_MARKER_ID );
   157   txtExpressionString->setIndicatorForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
   158   txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
   159   txtExpressionString->setIndicatorHoverStyle( QgsCodeEditor::DotsIndicator, FUNCTION_MARKER_ID );
   161   connect( txtExpressionString, &QgsCodeEditorExpression::indicatorClicked, 
this, &QgsExpressionBuilderWidget::indicatorClicked );
   162   txtExpressionString->setAutoCompletionCaseSensitivity( 
true );
   163   txtExpressionString->setAutoCompletionSource( QsciScintilla::AcsAPIs );
   164   txtExpressionString->setCallTipsVisible( 0 );
   167   mFunctionBuilderHelp->setMarginVisible( 
false );
   168   mFunctionBuilderHelp->setEdgeMode( QsciScintilla::EdgeNone );
   169   mFunctionBuilderHelp->setEdgeColumn( 0 );
   170   mFunctionBuilderHelp->setReadOnly( 
true );
   171   mFunctionBuilderHelp->setText( tr( 
"\"\"\"Define a new function using the @qgsfunction decorator.\n\   173  The function accepts the following parameters\n\   175  : param [any]: Define any parameters you want to pass to your function before\n\   176  the following arguments.\n\   177  : param feature: The current feature\n\   178  : param parent: The QgsExpression object\n\   179  : param context: If there is an argument called ``context`` found at the last\n\   180                    position, this variable will contain a ``QgsExpressionContext``\n\   181                    object, that gives access to various additional information like\n\   182                    expression variables. E.g. ``context.variable( 'layer_id' )``\n\   183  : returns: The result of the expression.\n\   187  The @qgsfunction decorator accepts the following arguments:\n\   190  : param args: Defines the number of arguments. With ``args = 'auto'`` the number of\n\   191                arguments will automatically be extracted from the signature.\n\   192                With ``args = -1``, any number of arguments are accepted.\n\   193  : param group: The name of the group under which this expression function will\n\   195  : param handlesnull: Set this to True if your function has custom handling for NULL values.\n\   196                      If False, the result will always be NULL as soon as any parameter is NULL.\n\   197                      Defaults to False.\n\   198  : param usesgeometry : Set this to True if your function requires access to\n\   199                         feature.geometry(). Defaults to False.\n\   200  : param referenced_columns: An array of attribute names that are required to run\n\   201                              this function. Defaults to [QgsFeatureRequest.ALL_ATTRIBUTES].\n\   209   settings.
setValue( QStringLiteral( 
"Windows/QgsExpressionBuilderWidget/splitter" ), splitter->saveState() );
   210   settings.
setValue( QStringLiteral( 
"Windows/QgsExpressionBuilderWidget/editorsplitter" ), editorSplit->saveState() );
   211   settings.
setValue( QStringLiteral( 
"Windows/QgsExpressionBuilderWidget/functionsplitter" ), functionsplit->saveState() );
   229 void QgsExpressionBuilderWidget::currentChanged( 
const QModelIndex &index, 
const QModelIndex & )
   231   txtSearchEditValues->clear();
   234   QModelIndex idx = mProxyModel->mapToSource( index );
   242     if ( mFieldValues.contains( item->text() ) )
   244       const QStringList &values = mFieldValues[item->text()];
   245       mValuesModel->setStringList( values );
   249       mValuesModel->setStringList( QStringList() );
   252   mValueGroupBox->setVisible( isField );
   253   mShowHelpButton->setText( isField ? tr( 
"Show Values" ) : tr( 
"Show Help" ) );
   256   QString help = loadFunctionHelp( item );
   257   txtHelpText->setText( help );
   260 void QgsExpressionBuilderWidget::btnRun_pressed()
   262   if ( !cmbFileNames->currentItem() )
   265   QString file = cmbFileNames->currentItem()->text();
   267   runPythonCode( txtPython->text() );
   270 void QgsExpressionBuilderWidget::runPythonCode( 
const QString &code )
   274     QString pythontext = code;
   277   updateFunctionTree();
   284   QDir myDir( mFunctionsPath );
   285   if ( !myDir.exists() )
   287     myDir.mkpath( mFunctionsPath );
   290   if ( !fileName.endsWith( QLatin1String( 
".py" ) ) )
   292     fileName.append( 
".py" );
   295   fileName = mFunctionsPath + QDir::separator() + fileName;
   296   QFile myFile( fileName );
   297   if ( myFile.open( QIODevice::WriteOnly | QFile::Truncate ) )
   299     QTextStream myFileStream( &myFile );
   300     myFileStream << txtPython->text() << endl;
   307   mFunctionsPath = path;
   309   dir.setNameFilters( QStringList() << QStringLiteral( 
"*.py" ) );
   310   QStringList files = dir.entryList( QDir::Files );
   311   cmbFileNames->clear();
   312   Q_FOREACH ( 
const QString &name, files )
   314     QFileInfo info( mFunctionsPath + QDir::separator() + name );
   315     if ( info.baseName() == QLatin1String( 
"__init__" ) ) 
continue;
   316     QListWidgetItem *item = 
new QListWidgetItem( 
QgsApplication::getThemeIcon( QStringLiteral( 
"console/iconTabEditorConsole.svg" ) ), info.baseName() );
   317     cmbFileNames->addItem( item );
   319   if ( !cmbFileNames->currentItem() )
   321     cmbFileNames->setCurrentRow( 0 );
   324   if ( cmbFileNames->count() == 0 )
   328     txtPython->setText( QString( 
"'''\n#Sample custom function file\n "   329                                  "(uncomment to use and customize or Add button to create a new file) \n%1 \n '''" ).arg( txtPython->text() ) );
   336   QList<QListWidgetItem *> items = cmbFileNames->findItems( fileName, Qt::MatchExactly );
   337   if ( !items.isEmpty() )
   340   QListWidgetItem *item = 
new QListWidgetItem( 
QgsApplication::getThemeIcon( QStringLiteral( 
"console/iconTabEditorConsole.svg" ) ), fileName );
   341   cmbFileNames->insertItem( 0, item );
   342   cmbFileNames->setCurrentRow( 0 );
   346   txtPython->setText( templatetxt );
   350 void QgsExpressionBuilderWidget::btnNewFile_pressed()
   353   QString text = QInputDialog::getText( 
this, tr( 
"New File" ),
   354                                         tr( 
"New file name:" ), QLineEdit::Normal,
   356   if ( ok && !text.isEmpty() )
   362 void QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem )
   366     QString filename = lastitem->text();
   369   QString path = mFunctionsPath + QDir::separator() + item->text();
   375   if ( !path.endsWith( QLatin1String( 
".py" ) ) )
   376     path.append( 
".py" );
   378   txtPython->loadScript( path );
   383   txtPython->setText( code );
   386 void QgsExpressionBuilderWidget::expressionTree_doubleClicked( 
const QModelIndex &index )
   388   QModelIndex idx = mProxyModel->mapToSource( index );
   399   txtExpressionString->setFocus();
   418   txtExpressionString->setFields( fields );
   420   QStringList fieldNames;
   421   fieldNames.reserve( fields.
count() );
   422   for ( 
int i = 0; i < fields.
count(); ++i )
   425     QString fieldName = field.
name();
   426     fieldNames << fieldName;
   436   for ( 
auto it = fieldValues.constBegin(); it != fieldValues.constEnd(); ++it )
   441   mFieldValues = fieldValues;
   444 void QgsExpressionBuilderWidget::fillFieldValues( 
const QString &fieldName, 
int countLimit )
   455   if ( fieldIndex < 0 )
   458   QStringList strValues;
   459   QList<QVariant> values = mLayer->
uniqueValues( fieldIndex, countLimit ).toList();
   460   std::sort( values.begin(), values.end() );
   461   Q_FOREACH ( 
const QVariant &value, values )
   464     if ( value.isNull() )
   465       strValue = QStringLiteral( 
"NULL" );
   466     else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong )
   467       strValue = value.toString();
   469       strValue = 
'\'' + value.toString().replace( 
'\'', QLatin1String( 
"''" ) ) + 
'\'';
   470     strValues.append( strValue );
   472   mValuesModel->setStringList( strValues );
   473   mFieldValues[fieldName] = strValues;
   483   return "<head><style>" + helpStylesheet() + 
"</style></head><body>" + helpContents + 
"</body>";
   488     const QString &label,
   490     const QString &helpText,
   494   item->setData( label, Qt::UserRole );
   496   item->setIcon( icon );
   499   if ( mExpressionGroups.contains( group ) )
   502     groupNode->appendRow( item );
   508     newgroupNode->setData( group, Qt::UserRole );
   511     newgroupNode->appendRow( item );
   512     newgroupNode->setBackground( QBrush( QColor( 238, 238, 238 ) ) );
   513     mModel->appendRow( newgroupNode );
   514     mExpressionGroups.insert( group, newgroupNode );
   517   if ( highlightedItem )
   521     topLevelItem->setData( label, Qt::UserRole );
   523     QFont font = topLevelItem->font();
   524     font.setBold( 
true );
   525     topLevelItem->setFont( font );
   526     mModel->appendRow( topLevelItem );
   533   return mExpressionValid;
   539   QString location = QStringLiteral( 
"/expressions/recent/%1" ).arg( collection );
   540   QStringList expressions = settings.
value( location ).toStringList();
   545   while ( expressions.count() > 20 )
   547     expressions.pop_back();
   550   settings.
setValue( location, expressions );
   556   mRecentKey = collection;
   557   QString name = tr( 
"Recent (%1)" ).arg( collection );
   558   if ( mExpressionGroups.contains( name ) )
   561     node->removeRows( 0, node->rowCount() );
   565   QString location = QStringLiteral( 
"/expressions/recent/%1" ).arg( collection );
   566   QStringList expressions = settings.
value( location ).toStringList();
   568   Q_FOREACH ( 
const QString &expression, expressions )
   575 void QgsExpressionBuilderWidget::loadLayers()
   580   QMap<QString, QgsMapLayer *> layers = mProject->mapLayers();
   581   QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
   582   for ( ; layerIt != layers.constEnd(); ++layerIt )
   584     registerItemForAllGroups( QStringList() << tr( 
"Map Layers" ), layerIt.value()->name(), QStringLiteral( 
"'%1'" ).arg( layerIt.key() ), formatLayerHelp( layerIt.value() ) );
   588 void QgsExpressionBuilderWidget::loadRelations()
   593   QMap<QString, QgsRelation> relations = mProject->relationManager()->relations();
   594   QMap<QString, QgsRelation>::const_iterator relIt = relations.constBegin();
   595   for ( ; relIt != relations.constEnd(); ++relIt )
   597     registerItemForAllGroups( QStringList() << tr( 
"Relations" ), relIt->name(), QStringLiteral( 
"'%1'" ).arg( relIt->id() ), formatRelationHelp( relIt.value() ) );
   601 void QgsExpressionBuilderWidget::updateFunctionTree()
   604   mExpressionGroups.clear();
   606   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
"+" ), QStringLiteral( 
" + " ) );
   607   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
"-" ), QStringLiteral( 
" - " ) );
   608   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
"*" ), QStringLiteral( 
" * " ) );
   609   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
"/" ), QStringLiteral( 
" / " ) );
   610   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
"%" ), QStringLiteral( 
" % " ) );
   611   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
"^" ), QStringLiteral( 
" ^ " ) );
   612   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
"=" ), QStringLiteral( 
" = " ) );
   613   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
"~" ), QStringLiteral( 
" ~ " ) );
   614   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
">" ), QStringLiteral( 
" > " ) );
   615   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
"<" ), QStringLiteral( 
" < " ) );
   616   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
"<>" ), QStringLiteral( 
" <> " ) );
   617   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
"<=" ), QStringLiteral( 
" <= " ) );
   618   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
">=" ), QStringLiteral( 
" >= " ) );
   619   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
"[]" ), QStringLiteral( 
"[ ]" ) );
   620   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
"||" ), QStringLiteral( 
" || " ) );
   621   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
"IN" ), QStringLiteral( 
" IN " ) );
   622   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
"LIKE" ), QStringLiteral( 
" LIKE " ) );
   623   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
"ILIKE" ), QStringLiteral( 
" ILIKE " ) );
   624   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
"IS" ), QStringLiteral( 
" IS " ) );
   625   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
"OR" ), QStringLiteral( 
" OR " ) );
   626   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
"AND" ), QStringLiteral( 
" AND " ) );
   627   registerItem( QStringLiteral( 
"Operators" ), QStringLiteral( 
"NOT" ), QStringLiteral( 
" NOT " ) );
   629   QString casestring = QStringLiteral( 
"CASE WHEN condition THEN result END" );
   630   registerItem( QStringLiteral( 
"Conditionals" ), QStringLiteral( 
"CASE" ), casestring );
   632   registerItem( QStringLiteral( 
"Fields and Values" ), QStringLiteral( 
"NULL" ), QStringLiteral( 
"NULL" ) );
   636   for ( 
int i = 0; i < count; i++ )
   639     QString name = func->
name();
   640     if ( name.startsWith( 
'_' ) ) 
   650     if ( func->
params() != 0 )
   652     else if ( !name.startsWith( 
'$' ) )
   653       name += QLatin1String( 
"()" );
   663   loadExpressionContext();
   673   return txtExpressionString->text();
   678   txtExpressionString->setText( expression );
   683   return lblExpected->text();
   688   lblExpected->setText( expected );
   689   mExpectedOutputFrame->setVisible( !expected.isNull() );
   694   mExpressionContext = context;
   695   updateFunctionTree();
   700 void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
   707   if ( text.isEmpty() )
   710     lblPreview->setStyleSheet( QString() );
   711     txtExpressionString->setToolTip( QString() );
   712     lblPreview->setToolTip( QString() );
   714     setParserError( 
true );
   715     setEvalError( 
true );
   736   QVariant value = exp.
evaluate( &mExpressionContext );
   747       tooltip = QStringLiteral( 
"<b>%1:</b>"   748                                 "%2" ).arg( tr( 
"Parser Errors" ), errorString );
   751       tooltip += QStringLiteral( 
"<b>%1:</b> %2" ).arg( tr( 
"Eval Error" ), exp.
evalErrorString() );
   753     lblPreview->setText( tr( 
"Expression is invalid <a href=""more"">(more info)</a>" ) );
   754     lblPreview->setStyleSheet( QStringLiteral( 
"color: rgba(255, 6, 10,  255);" ) );
   755     txtExpressionString->setToolTip( tooltip );
   756     lblPreview->setToolTip( tooltip );
   765     lblPreview->setStyleSheet( QString() );
   766     txtExpressionString->setToolTip( QString() );
   767     lblPreview->setToolTip( QString() );
   769     setParserError( 
false );
   770     setEvalError( 
false );
   776 void QgsExpressionBuilderWidget::loadExpressionContext()
   778   txtExpressionString->setExpressionContext( mExpressionContext );
   780   Q_FOREACH ( 
const QString &variable, variableNames )
   782     registerItem( QStringLiteral( 
"Variables" ), variable, 
" @" + variable + 
' ',
   789   QStringList contextFunctions = mExpressionContext.
functionNames();
   790   Q_FOREACH ( 
const QString &functionName, contextFunctions )
   793     QString name = func->
name();
   794     if ( name.startsWith( 
'_' ) ) 
   796     if ( func->
params() != 0 )
   802 void QgsExpressionBuilderWidget::registerItemForAllGroups( 
const QStringList &groups, 
const QString &label, 
const QString &
expressionText, 
const QString &helpText, 
QgsExpressionItem::ItemType type, 
bool highlightedItem, 
int sortOrder )
   804   Q_FOREACH ( 
const QString &group, groups )
   810 QString QgsExpressionBuilderWidget::formatRelationHelp( 
const QgsRelation &relation )
 const   812   QString text = QStringLiteral( 
"<p>%1</p>" ).arg( tr( 
"Inserts the relation ID for the relation named '%1'." ).arg( relation.
name() ) );
   813   text.append( QStringLiteral( 
"<p>%1</p>" ).arg( tr( 
"Current value: '%1'" ).arg( relation.
id() ) ) );
   817 QString QgsExpressionBuilderWidget::formatLayerHelp( 
const QgsMapLayer *layer )
 const   819   QString text = QStringLiteral( 
"<p>%1</p>" ).arg( tr( 
"Inserts the layer ID for the layer named '%1'." ).arg( layer->
name() ) );
   820   text.append( QStringLiteral( 
"<p>%1</p>" ).arg( tr( 
"Current value: '%1'" ).arg( layer->
id() ) ) );
   829 void QgsExpressionBuilderWidget::setParserError( 
bool parserError )
   831   if ( parserError == mParserError )
   843 void QgsExpressionBuilderWidget::setEvalError( 
bool evalError )
   845   if ( evalError == mEvalError )
   865   updateFunctionTree();
   870   QWidget::showEvent( e );
   871   txtExpressionString->setFocus();
   874 void QgsExpressionBuilderWidget::createErrorMarkers( QList<QgsExpression::ParserError> errors )
   879     int errorFirstLine = error.firstLine - 1 ;
   880     int errorFirstColumn = error.firstColumn - 1;
   881     int errorLastColumn = error.lastColumn - 1;
   882     int errorLastLine = error.lastLine - 1;
   888       errorFirstLine = errorLastLine;
   889       errorFirstColumn = errorLastColumn - 1;
   891     txtExpressionString->fillIndicatorRange( errorFirstLine,
   894         errorLastColumn, error.errorType );
   898 void QgsExpressionBuilderWidget::createMarkers( 
const QgsExpressionNode *inNode )
   902     case QgsExpressionNode::NodeType::ntFunction:
   905       txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
   906       txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->
fnIndex() );
   909       int start_pos = txtExpressionString->positionFromLineIndex( inNode->
parserFirstLine - 1, start );
   910       txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
   913         const QList< QgsExpressionNode * > nodeList = node->
args()->
list();
   921     case QgsExpressionNode::NodeType::ntLiteral:
   925     case QgsExpressionNode::NodeType::ntUnaryOperator:
   928       createMarkers( node->
operand() );
   931     case QgsExpressionNode::NodeType::ntBinaryOperator:
   934       createMarkers( node->
opLeft() );
   935       createMarkers( node->
opRight() );
   938     case QgsExpressionNode::NodeType::ntColumnRef:
   942     case QgsExpressionNode::NodeType::ntInOperator:
   947         const QList< QgsExpressionNode * > nodeList = node->
list()->
list();
   955     case QgsExpressionNode::NodeType::ntCondition:
   960         createMarkers( cond->whenExp() );
   961         createMarkers( cond->thenExp() );
   965         createMarkers( node->
elseExp() );
   969     case QgsExpressionNode::NodeType::ntIndexOperator:
   976 void QgsExpressionBuilderWidget::clearFunctionMarkers()
   978   int lastLine = txtExpressionString->lines() - 1;
   979   txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
   982 void QgsExpressionBuilderWidget::clearErrors()
   984   int lastLine = txtExpressionString->lines() - 1;
   993 void QgsExpressionBuilderWidget::txtSearchEdit_textChanged()
   995   mProxyModel->setFilterWildcard( txtSearchEdit->text() );
   996   if ( txtSearchEdit->text().isEmpty() )
   998     expressionTree->collapseAll();
  1002     expressionTree->expandAll();
  1003     QModelIndex index = mProxyModel->index( 0, 0 );
  1004     if ( mProxyModel->hasChildren( index ) )
  1006       QModelIndex child = mProxyModel->index( 0, 0, index );
  1007       expressionTree->selectionModel()->setCurrentIndex( child, QItemSelectionModel::ClearAndSelect );
  1012 void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
  1014   mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
  1015   mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
  1018 void QgsExpressionBuilderWidget::lblPreview_linkActivated( 
const QString &link )
  1022   mv->setWindowTitle( tr( 
"More Info on Expression Error" ) );
  1027 void QgsExpressionBuilderWidget::mValuesListView_doubleClicked( 
const QModelIndex &index )
  1030   txtExpressionString->insertText( 
' ' + index.data( Qt::DisplayRole ).toString() + 
' ' );
  1031   txtExpressionString->setFocus();
  1034 void QgsExpressionBuilderWidget::operatorButtonClicked()
  1036   QPushButton *button = 
dynamic_cast<QPushButton *
>( sender() );
  1039   txtExpressionString->insertText( 
' ' + button->text() + 
' ' );
  1040   txtExpressionString->setFocus();
  1043 void QgsExpressionBuilderWidget::showContextMenu( QPoint pt )
  1045   QModelIndex idx = expressionTree->indexAt( pt );
  1046   idx = mProxyModel->mapToSource( idx );
  1053     QMenu *menu = 
new QMenu( 
this );
  1054     menu->addAction( tr( 
"Load First 10 Unique Values" ), 
this, SLOT( 
loadSampleValues() ) );
  1055     menu->addAction( tr( 
"Load All Unique Values" ), 
this, SLOT( 
loadAllValues() ) );
  1056     menu->popup( expressionTree->mapToGlobal( pt ) );
  1062   QModelIndex idx = mProxyModel->mapToSource( expressionTree->currentIndex() );
  1066   if ( !mLayer || !item )
  1069   mValueGroupBox->show();
  1070   fillFieldValues( item->text(), 10 );
  1075   QModelIndex idx = mProxyModel->mapToSource( expressionTree->currentIndex() );
  1079   if ( !mLayer || !item )
  1082   mValueGroupBox->show();
  1083   fillFieldValues( item->text(), -1 );
  1086 void QgsExpressionBuilderWidget::txtPython_textChanged()
  1088   lblAutoSave->setText( tr( 
"Saving…" ) );
  1098   if ( tabWidget->currentIndex() != 1 )
  1101   QListWidgetItem *item = cmbFileNames->currentItem();
  1105   QString file = item->text();
  1107   lblAutoSave->setText( QStringLiteral( 
"Saved" ) );
  1108   QGraphicsOpacityEffect *effect = 
new QGraphicsOpacityEffect();
  1109   lblAutoSave->setGraphicsEffect( effect );
  1110   QPropertyAnimation *anim = 
new QPropertyAnimation( effect, 
"opacity" );
  1111   anim->setDuration( 2000 );
  1112   anim->setStartValue( 1.0 );
  1113   anim->setEndValue( 0.0 );
  1114   anim->setEasingCurve( QEasingCurve::OutQuad );
  1115   anim->start( QAbstractAnimation::DeleteWhenStopped );
  1118 void QgsExpressionBuilderWidget::indicatorClicked( 
int line, 
int index, Qt::KeyboardModifiers state )
  1120   if ( state & Qt::ControlModifier )
  1122     int position = txtExpressionString->positionFromLineIndex( line, index );
  1123     long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID, static_cast<long int>( position ) );
  1125     QString help = getFunctionHelp( func );
  1126     txtHelpText->setText( help );
  1130 void QgsExpressionBuilderWidget::setExpressionState( 
bool state )
  1132   mExpressionValid = state;
  1135 QString QgsExpressionBuilderWidget::helpStylesheet()
 const  1141   style += 
" .functionname {color: #0a6099; font-weight: bold;} "  1142            " .argument {font-family: monospace; color: #bf0c0c; font-style: italic; } "  1143            " td.argument { padding-right: 10px; }";
  1148 QString QgsExpressionBuilderWidget::loadFunctionHelp( 
QgsExpressionItem *expressionItem )
  1150   if ( !expressionItem )
  1153   QString helpContents = expressionItem->
getHelpText();
  1156   if ( helpContents.isEmpty() )
  1158     QString name = expressionItem->data( Qt::UserRole ).toString();
  1166   return "<head><style>" + helpStylesheet() + 
"</style></head><body>" + helpContents + 
"</body>";
  1175   setFilterCaseSensitivity( Qt::CaseInsensitive );
  1180   QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
  1183   int count = sourceModel()->rowCount( index );
  1184   bool matchchild = 
false;
  1185   for ( 
int i = 0; i < count; ++i )
  1187     if ( filterAcceptsRow( i, index ) )
  1200   return QSortFilterProxyModel::filterAcceptsRow( source_row, source_parent );
  1207   if ( leftSort != rightSort )
  1208     return leftSort < rightSort;
  1210   QString leftString = sourceModel()->data( left, Qt::DisplayRole ).toString();
  1211   QString rightString = sourceModel()->data( right, Qt::DisplayRole ).toString();
  1214   if ( leftString.startsWith( 
'$' ) )
  1215     leftString = leftString.mid( 1 );
  1216   if ( rightString.startsWith( 
'$' ) )
  1217     rightString = rightString.mid( 1 );
  1219   return QString::localeAwareCompare( leftString, rightString ) < 0;
 int lookupField(const QString &fieldName) const
Looks up field's index from the field name. 
 
bool isValid() const
Returns the validity of this feature. 
 
Class for parsing and evaluation of expressions (formerly called "search strings"). 
 
int parserFirstColumn
First column in the parser this node was found. 
 
bool hasParserError() const
Returns true if an error occurred when parsing the input expression. 
 
static QString formatPreviewString(const QVariant &value, bool htmlOutput=true)
Formats an expression result for friendly display to the user. 
 
Base class for all map layer types. 
 
bool isHighlightedFunction(const QString &name) const
Returns true if the specified function name is intended to be highlighted to the user. 
 
QgsExpressionNode::NodeList * args() const
Returns a list of arguments specified for the function. 
 
QStringList filteredVariableNames() const
Returns a filtered list of variables names set by all scopes in the context. 
 
int params() const
The number of parameters this function takes. 
 
QStringList groups() const
Returns a list of the groups the function belongs to. 
 
This class is a composition of two QSettings instances: 
 
static QString group(const QString &group)
Returns the translated name for a function group. 
 
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key. 
 
QIcon iconForField(int fieldIdx) const
Returns an icon corresponding to a field index, based on the field's type and source. 
 
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context. 
 
QgsExpressionNode * opRight() const
Returns the node to the right of the operator. 
 
WhenThenList conditions() const
The list of WHEN THEN expression parts of the expression. 
 
Details about any parser errors that were found when parsing the expression. 
 
QVariant evaluate()
Evaluate the feature and return the result. 
 
An expression node for CASE WHEN clauses. 
 
QString evalErrorString() const
Returns evaluation error. 
 
Container of fields for a vector layer. 
 
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon. 
 
QgsExpressionNode::NodeList * list() const
Returns the list of nodes to search for matching values within. 
 
QList< QgsExpression::ParserError > parserErrors() const
Returns parser error details including location of error. 
 
QgsExpressionNode * opLeft() const
Returns the node to the left of the operator. 
 
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
 
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
 
static QString reportStyleSheet()
Returns a standard css style sheet for reports. 
 
QgsExpressionItemSearchProxy()
 
int count() const
Returns number of items. 
 
QString parserErrorString() const
Returns parser error. 
 
QVariant variable(const QString &name) const
Fetches a matching variable from the context. 
 
Function was called with invalid args. 
 
Non named function arg used after named arg. 
 
QString description(const QString &name) const
Returns a translated description string for the variable with specified name. 
 
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1) 
 
void setMessageAsHtml(const QString &msg)
 
static int functionCount()
Returns the number of functions defined in the parser. 
 
Search proxy used to filter the QgsExpressionBuilderWidget tree. 
 
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject. 
 
QgsFields fields() const FINAL
Returns the list of fields of this layer. 
 
bool isHighlightedVariable(const QString &name) const
Returns true if the specified variable name is intended to be highlighted to the user. 
 
Function was called with the wrong number of args. 
 
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
 
An expression node for value IN or NOT IN clauses. 
 
This class wraps a request for features to a vector layer (or directly its vector data provider)...
 
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false) ...
 
Reads and writes project states. 
 
Abstract base class for all nodes that can appear in an expression. 
 
static const int ITEM_TYPE_ROLE
Item type role. 
 
Encapsulate a field in an attribute table or data source. 
 
An expression node for expression functions. 
 
QgsExpressionNode * elseExp() const
The ELSE expression used for the condition. 
 
static const QList< QgsExpressionFunction * > & Functions()
 
static QString formatVariableHelp(const QString &description, bool showValue=true, const QVariant &value=QVariant())
Returns formatted help text for a variable. 
 
static bool eval(const QString &command, QString &result)
Eval a Python statement. 
 
QgsExpressionItem::ItemType getItemType() const
Gets the type of expression item, e.g., header, field, ExpressionNode. 
 
static const int CUSTOM_SORT_ROLE
Custom sort order role. 
 
QString name() const
The name of the function. 
 
A general purpose distance and area calculator, capable of performing ellipsoid based calculations...
 
A abstract base class for defining QgsExpression functions. 
 
An expression item that can be used in the QgsExpressionBuilderWidget tree. 
 
void setGeomCalculator(const QgsDistanceArea *calc)
Sets the geometry calculator used for distance and area calculations in expressions. 
 
QList< QgsExpressionNode * > list()
Gets a list of all the nodes. 
 
int parserFirstLine
First line in the parser this node was found. 
 
QStringList functionNames() const
Retrieves a list of function names contained in the context. 
 
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer. 
 
const QString helpText() const
The help text for the function. 
 
static bool run(const QString &command, const QString &messageOnError=QString())
Execute a Python statement. 
 
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value. 
 
QString getExpressionText() const
 
const QgsExpressionNode * rootNode() const
Returns root node of the expression. Root node is null is parsing has failed. 
 
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
 
A unary node is either negative as in boolean (not) or as in numbers (minus). 
 
A binary expression operator, which operates on two values. 
 
A generic message view for displaying QGIS messages. 
 
bool isEmpty() const
Checks whether the container is empty. 
 
static QString helpText(QString name)
Returns the help text for a specified function. 
 
QgsFeature feature() const
Convenience function for retrieving the feature for the context, if set. 
 
QString getHelpText() const
Gets the help text that is associated with this expression item. 
 
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Query the layer for features specified in request. 
 
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer. 
 
bool nextFeature(QgsFeature &f)
 
bool hasEvalError() const
Returns true if an error occurred when evaluating last input. 
 
Represents a vector layer which manages a vector based data sets. 
 
virtual bool isDeprecated() const
Returns true if the function is deprecated and should not be presented as a valid option to users in ...
 
virtual QgsExpressionNode::NodeType nodeType() const =0
Gets the type of this node. 
 
QgsExpressionNode * operand() const
Returns the node the operator will operate upon. 
 
static bool isValid()
Returns true if the runner has an instance (and thus is able to run commands) 
 
Represents a "WHEN... THEN..." portation of a CASE WHEN clause in an expression. 
 
QgsExpressionFunction * function(const QString &name) const
Fetches a matching function from the context. 
 
int parserLastColumn
Last column in the parser this node was found. 
 
bool isContextual() const
Returns whether the function is only available if provided by a QgsExpressionContext object...
 
int fnIndex() const
Returns the index of the node's function.