38 #include <QTextStream> 40 #include <QInputDialog> 42 #include <QGraphicsOpacityEffect> 43 #include <QPropertyAnimation> 52 connect( btnRun, &QToolButton::pressed,
this, &QgsExpressionBuilderWidget::btnRun_pressed );
53 connect( btnNewFile, &QToolButton::pressed,
this, &QgsExpressionBuilderWidget::btnNewFile_pressed );
54 connect( cmbFileNames, &QListWidget::currentItemChanged,
this, &QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged );
55 connect( expressionTree, &QTreeView::doubleClicked,
this, &QgsExpressionBuilderWidget::expressionTree_doubleClicked );
56 connect( txtExpressionString, &QgsCodeEditorExpression::textChanged,
this, &QgsExpressionBuilderWidget::txtExpressionString_textChanged );
57 connect( txtPython, &QgsCodeEditorPython::textChanged,
this, &QgsExpressionBuilderWidget::txtPython_textChanged );
58 connect( txtSearchEditValues, &QgsFilterLineEdit::textChanged,
this, &QgsExpressionBuilderWidget::txtSearchEditValues_textChanged );
59 connect( txtSearchEdit, &QgsFilterLineEdit::textChanged,
this, &QgsExpressionBuilderWidget::txtSearchEdit_textChanged );
60 connect( lblPreview, &QLabel::linkActivated,
this, &QgsExpressionBuilderWidget::lblPreview_linkActivated );
61 connect( mValuesListView, &QListView::doubleClicked,
this, &QgsExpressionBuilderWidget::mValuesListView_doubleClicked );
63 txtHelpText->setOpenExternalLinks(
true );
65 mValueGroupBox->hide();
68 mModel = qgis::make_unique<QStandardItemModel>();
69 mProxyModel = qgis::make_unique<QgsExpressionItemSearchProxy>();
70 mProxyModel->setDynamicSortFilter(
true );
71 mProxyModel->setSourceModel( mModel.get() );
72 expressionTree->setModel( mProxyModel.get() );
73 expressionTree->setSortingEnabled(
true );
74 expressionTree->sortByColumn( 0, Qt::AscendingOrder );
76 expressionTree->setContextMenuPolicy( Qt::CustomContextMenu );
78 connect( expressionTree, &QWidget::customContextMenuRequested,
this, &QgsExpressionBuilderWidget::showContextMenu );
79 connect( expressionTree->selectionModel(), &QItemSelectionModel::currentChanged,
80 this, &QgsExpressionBuilderWidget::currentChanged );
85 const auto pushButtons { mOperatorsGroupBox->findChildren<QPushButton *>() };
86 for ( QPushButton *button : pushButtons )
88 connect( button, &QAbstractButton::pressed,
this, &QgsExpressionBuilderWidget::operatorButtonClicked );
91 txtSearchEdit->setShowSearchIcon(
true );
92 txtSearchEdit->setPlaceholderText( tr(
"Search…" ) );
94 mValuesModel = qgis::make_unique<QStandardItemModel>();
95 mProxyValues = qgis::make_unique<QSortFilterProxyModel>();
96 mProxyValues->setSourceModel( mValuesModel.get() );
97 mValuesListView->setModel( mProxyValues.get() );
98 txtSearchEditValues->setShowSearchIcon(
true );
99 txtSearchEditValues->setPlaceholderText( tr(
"Search…" ) );
101 editorSplit->setSizes( QList<int>( {175, 300} ) );
103 functionsplit->setCollapsible( 0,
false );
104 connect( mShowHelpButton, &QPushButton::clicked,
this, [ = ]()
106 functionsplit->setSizes( QList<int>( {mOperationListGroup->width() - mHelpAndValuesWidget->minimumWidth(),
107 mHelpAndValuesWidget->minimumWidth()
109 mShowHelpButton->setEnabled(
false );
111 connect( functionsplit, &QSplitter::splitterMoved,
this, [ = ](
int,
int )
113 mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
118 splitter->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/splitter" ) ).toByteArray() );
119 editorSplit->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/editorsplitter" ) ).toByteArray() );
120 functionsplit->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/functionsplitter" ) ).toByteArray() );
122 txtExpressionString->setFoldingVisible(
false );
124 updateFunctionTree();
138 QModelIndex firstItem = mProxyModel->index( 0, 0, QModelIndex() );
139 expressionTree->setCurrentIndex( firstItem );
141 txtExpressionString->setWrapMode( QsciScintilla::WrapWord );
142 lblAutoSave->clear();
150 #if defined(QSCINTILLA_VERSION) && QSCINTILLA_VERSION >= 0x20a00 157 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::red ), -1 );
158 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::red ), -1 );
159 txtExpressionString->setIndicatorOutlineColor( QColor( Qt::red ), -1 );
162 txtExpressionString->indicatorDefine( QgsCodeEditor::HiddenIndicator, FUNCTION_MARKER_ID );
163 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
164 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
165 txtExpressionString->setIndicatorHoverStyle( QgsCodeEditor::DotsIndicator, FUNCTION_MARKER_ID );
167 connect( txtExpressionString, &QgsCodeEditorExpression::indicatorClicked,
this, &QgsExpressionBuilderWidget::indicatorClicked );
168 txtExpressionString->setAutoCompletionCaseSensitivity(
true );
169 txtExpressionString->setAutoCompletionSource( QsciScintilla::AcsAPIs );
170 txtExpressionString->setCallTipsVisible( 0 );
173 mFunctionBuilderHelp->setMarginVisible(
false );
174 mFunctionBuilderHelp->setEdgeMode( QsciScintilla::EdgeNone );
175 mFunctionBuilderHelp->setEdgeColumn( 0 );
176 mFunctionBuilderHelp->setReadOnly(
true );
177 mFunctionBuilderHelp->setText( tr(
"\"\"\"Define a new function using the @qgsfunction decorator.\n\ 179 The function accepts the following parameters\n\ 181 : param [any]: Define any parameters you want to pass to your function before\n\ 182 the following arguments.\n\ 183 : param feature: The current feature\n\ 184 : param parent: The QgsExpression object\n\ 185 : param context: If there is an argument called ``context`` found at the last\n\ 186 position, this variable will contain a ``QgsExpressionContext``\n\ 187 object, that gives access to various additional information like\n\ 188 expression variables. E.g. ``context.variable( 'layer_id' )``\n\ 189 : returns: The result of the expression.\n\ 193 The @qgsfunction decorator accepts the following arguments:\n\ 196 : param args: Defines the number of arguments. With ``args = 'auto'`` the number of\n\ 197 arguments will automatically be extracted from the signature.\n\ 198 With ``args = -1``, any number of arguments are accepted.\n\ 199 : param group: The name of the group under which this expression function will\n\ 201 : param handlesnull: Set this to True if your function has custom handling for NULL values.\n\ 202 If False, the result will always be NULL as soon as any parameter is NULL.\n\ 203 Defaults to False.\n\ 204 : param usesgeometry : Set this to True if your function requires access to\n\ 205 feature.geometry(). Defaults to False.\n\ 206 : param referenced_columns: An array of attribute names that are required to run\n\ 207 this function. Defaults to [QgsFeatureRequest.ALL_ATTRIBUTES].\n\ 215 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/splitter" ), splitter->saveState() );
216 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/editorsplitter" ), editorSplit->saveState() );
217 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/functionsplitter" ), functionsplit->saveState() );
230 void QgsExpressionBuilderWidget::currentChanged(
const QModelIndex &index,
const QModelIndex & )
232 txtSearchEditValues->clear();
235 QModelIndex idx = mProxyModel->mapToSource( index );
243 loadFieldValues( mFieldValues.value( item->text() ) );
245 mValueGroupBox->setVisible( isField );
246 mShowHelpButton->setText( isField ? tr(
"Show Values" ) : tr(
"Show Help" ) );
249 QString help = loadFunctionHelp( item );
250 txtHelpText->setText( help );
253 void QgsExpressionBuilderWidget::btnRun_pressed()
255 if ( !cmbFileNames->currentItem() )
258 QString file = cmbFileNames->currentItem()->text();
260 runPythonCode( txtPython->text() );
263 void QgsExpressionBuilderWidget::runPythonCode(
const QString &code )
267 QString pythontext = code;
270 updateFunctionTree();
277 QDir myDir( mFunctionsPath );
278 if ( !myDir.exists() )
280 myDir.mkpath( mFunctionsPath );
283 if ( !fileName.endsWith( QLatin1String(
".py" ) ) )
285 fileName.append(
".py" );
288 fileName = mFunctionsPath + QDir::separator() + fileName;
289 QFile myFile( fileName );
290 if ( myFile.open( QIODevice::WriteOnly | QFile::Truncate ) )
292 QTextStream myFileStream( &myFile );
293 myFileStream << txtPython->text() << endl;
300 mFunctionsPath = path;
302 dir.setNameFilters( QStringList() << QStringLiteral(
"*.py" ) );
303 QStringList files = dir.entryList( QDir::Files );
304 cmbFileNames->clear();
305 const auto constFiles = files;
306 for (
const QString &name : constFiles )
308 QFileInfo info( mFunctionsPath + QDir::separator() + name );
309 if ( info.baseName() == QLatin1String(
"__init__" ) )
continue;
310 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"console/iconTabEditorConsole.svg" ) ), info.baseName() );
311 cmbFileNames->addItem( item );
313 if ( !cmbFileNames->currentItem() )
315 cmbFileNames->setCurrentRow( 0 );
318 if ( cmbFileNames->count() == 0 )
322 txtPython->setText( QString(
"'''\n#Sample custom function file\n " 323 "(uncomment to use and customize or Add button to create a new file) \n%1 \n '''" ).arg( txtPython->text() ) );
330 QList<QListWidgetItem *> items = cmbFileNames->findItems( fileName, Qt::MatchExactly );
331 if ( !items.isEmpty() )
334 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"console/iconTabEditorConsole.svg" ) ), fileName );
335 cmbFileNames->insertItem( 0, item );
336 cmbFileNames->setCurrentRow( 0 );
340 txtPython->setText( templatetxt );
344 void QgsExpressionBuilderWidget::btnNewFile_pressed()
347 QString text = QInputDialog::getText(
this, tr(
"New File" ),
348 tr(
"New file name:" ), QLineEdit::Normal,
350 if ( ok && !text.isEmpty() )
356 void QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem )
360 QString filename = lastitem->text();
363 QString path = mFunctionsPath + QDir::separator() + item->text();
369 if ( !path.endsWith( QLatin1String(
".py" ) ) )
370 path.append(
".py" );
372 txtPython->loadScript( path );
377 txtPython->setText( code );
380 void QgsExpressionBuilderWidget::expressionTree_doubleClicked(
const QModelIndex &index )
382 QModelIndex idx = mProxyModel->mapToSource( index );
393 txtExpressionString->setFocus();
412 txtExpressionString->setFields( fields );
414 QStringList fieldNames;
415 fieldNames.reserve( fields.
count() );
416 for (
int i = 0; i < fields.
count(); ++i )
419 QString fieldName = field.
name();
420 fieldNames << fieldName;
429 mFieldValues.clear();
431 for (
auto it = fieldValues.constBegin(); it != fieldValues.constEnd(); ++it )
434 const QStringList values = it.value();
436 for (
const QString &value : values )
438 map.insert( value, value );
440 mFieldValues.insert( it.key(), map );
448 for (
auto it = fieldValues.constBegin(); it != fieldValues.constEnd(); ++it )
453 mFieldValues = fieldValues;
456 void QgsExpressionBuilderWidget::fillFieldValues(
const QString &fieldName,
int countLimit )
468 if ( fieldIndex < 0 )
474 QList<QVariant> values = mLayer->
uniqueValues( fieldIndex, countLimit ).toList();
475 std::sort( values.begin(), values.end() );
477 for (
const QVariant &value : qgis::as_const( values ) )
480 if ( value.isNull() )
481 strValue = QStringLiteral(
"NULL" );
482 else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong )
483 strValue = value.toString();
485 strValue =
'\'' + value.toString().replace(
'\'', QLatin1String(
"''" ) ) +
'\'';
487 QString representedValue = formatter->
representValue( mLayer, fieldIndex, setup.
config(), QVariant(), value );
488 if ( representedValue != value.toString() )
489 representedValue = representedValue + QStringLiteral(
" [" ) + strValue +
']';
491 QStandardItem *item =
new QStandardItem( representedValue );
492 item->setData( strValue );
493 mValuesModel->appendRow( item );
504 return QStringLiteral(
"<head><style>" ) + helpStylesheet() + QStringLiteral(
"</style></head><body>" ) + helpContents + QStringLiteral(
"</body>" );
509 const QString &label,
511 const QString &helpText,
515 item->setData( label, Qt::UserRole );
517 item->setIcon( icon );
520 if ( mExpressionGroups.contains( group ) )
523 groupNode->appendRow( item );
529 newgroupNode->setData( group, Qt::UserRole );
532 newgroupNode->appendRow( item );
533 newgroupNode->setBackground( QBrush( QColor( 238, 238, 238 ) ) );
534 mModel->appendRow( newgroupNode );
535 mExpressionGroups.insert( group, newgroupNode );
538 if ( highlightedItem )
542 topLevelItem->setData( label, Qt::UserRole );
544 QFont font = topLevelItem->font();
545 font.setBold(
true );
546 topLevelItem->setFont( font );
547 mModel->appendRow( topLevelItem );
554 return mExpressionValid;
560 QString location = QStringLiteral(
"/expressions/recent/%1" ).arg( collection );
561 QStringList expressions = settings.
value( location ).toStringList();
566 while ( expressions.count() > 20 )
568 expressions.pop_back();
571 settings.
setValue( location, expressions );
577 mRecentKey = collection;
578 QString name = tr(
"Recent (%1)" ).arg( collection );
579 if ( mExpressionGroups.contains( name ) )
582 node->removeRows( 0, node->rowCount() );
586 QString location = QStringLiteral(
"/expressions/recent/%1" ).arg( collection );
587 QStringList expressions = settings.
value( location ).toStringList();
589 const auto constExpressions = expressions;
590 for (
const QString &expression : constExpressions )
597 void QgsExpressionBuilderWidget::loadLayers()
602 QMap<QString, QgsMapLayer *> layers = mProject->mapLayers();
603 QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
604 for ( ; layerIt != layers.constEnd(); ++layerIt )
606 registerItemForAllGroups( QStringList() << tr(
"Map Layers" ), layerIt.value()->name(), QStringLiteral(
"'%1'" ).arg( layerIt.key() ), formatLayerHelp( layerIt.value() ) );
610 void QgsExpressionBuilderWidget::loadRelations()
615 QMap<QString, QgsRelation> relations = mProject->relationManager()->relations();
616 QMap<QString, QgsRelation>::const_iterator relIt = relations.constBegin();
617 for ( ; relIt != relations.constEnd(); ++relIt )
619 registerItemForAllGroups( QStringList() << tr(
"Relations" ), relIt->name(), QStringLiteral(
"'%1'" ).arg( relIt->id() ), formatRelationHelp( relIt.value() ) );
623 void QgsExpressionBuilderWidget::updateFunctionTree()
626 mExpressionGroups.clear();
628 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"+" ), QStringLiteral(
" + " ) );
629 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"-" ), QStringLiteral(
" - " ) );
630 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"*" ), QStringLiteral(
" * " ) );
631 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"/" ), QStringLiteral(
" / " ) );
632 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"%" ), QStringLiteral(
" % " ) );
633 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"^" ), QStringLiteral(
" ^ " ) );
634 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"=" ), QStringLiteral(
" = " ) );
635 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"~" ), QStringLiteral(
" ~ " ) );
636 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
">" ), QStringLiteral(
" > " ) );
637 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"<" ), QStringLiteral(
" < " ) );
638 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"<>" ), QStringLiteral(
" <> " ) );
639 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"<=" ), QStringLiteral(
" <= " ) );
640 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
">=" ), QStringLiteral(
" >= " ) );
641 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"[]" ), QStringLiteral(
"[ ]" ) );
642 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"||" ), QStringLiteral(
" || " ) );
643 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"IN" ), QStringLiteral(
" IN " ) );
644 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"LIKE" ), QStringLiteral(
" LIKE " ) );
645 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"ILIKE" ), QStringLiteral(
" ILIKE " ) );
646 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"IS" ), QStringLiteral(
" IS " ) );
647 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"OR" ), QStringLiteral(
" OR " ) );
648 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"AND" ), QStringLiteral(
" AND " ) );
649 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"NOT" ), QStringLiteral(
" NOT " ) );
651 QString casestring = QStringLiteral(
"CASE WHEN condition THEN result END" );
652 registerItem( QStringLiteral(
"Conditionals" ), QStringLiteral(
"CASE" ), casestring );
659 for (
int i = 0; i < count; i++ )
662 QString name = func->
name();
663 if ( name.startsWith(
'_' ) )
673 if ( func->
params() != 0 )
675 else if ( !name.startsWith(
'$' ) )
676 name += QLatin1String(
"()" );
686 loadExpressionContext();
696 return txtExpressionString->text();
701 txtExpressionString->setText( expression );
706 return lblExpected->text();
711 lblExpected->setText( expected );
712 mExpectedOutputFrame->setVisible( !expected.isNull() );
717 mExpressionContext = context;
718 updateFunctionTree();
723 void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
730 if ( text.isEmpty() )
733 lblPreview->setStyleSheet( QString() );
734 txtExpressionString->setToolTip( QString() );
735 lblPreview->setToolTip( QString() );
737 setParserError(
true );
738 setEvalError(
true );
759 QVariant value = exp.
evaluate( &mExpressionContext );
770 tooltip = QStringLiteral(
"<b>%1:</b>" 771 "%2" ).arg( tr(
"Parser Errors" ), errorString );
774 tooltip += QStringLiteral(
"<b>%1:</b> %2" ).arg( tr(
"Eval Error" ), exp.
evalErrorString() );
776 lblPreview->setText( tr(
"Expression is invalid <a href=""more"">(more info)</a>" ) );
777 lblPreview->setStyleSheet( QStringLiteral(
"color: rgba(255, 6, 10, 255);" ) );
778 txtExpressionString->setToolTip( tooltip );
779 lblPreview->setToolTip( tooltip );
788 lblPreview->setStyleSheet( QString() );
789 txtExpressionString->setToolTip( QString() );
790 lblPreview->setToolTip( QString() );
792 setParserError(
false );
793 setEvalError(
false );
799 void QgsExpressionBuilderWidget::loadExpressionContext()
801 txtExpressionString->setExpressionContext( mExpressionContext );
803 const auto constVariableNames = variableNames;
804 for (
const QString &variable : constVariableNames )
806 registerItem( QStringLiteral(
"Variables" ), variable,
" @" + variable +
' ',
813 QStringList contextFunctions = mExpressionContext.
functionNames();
814 const auto constContextFunctions = contextFunctions;
815 for (
const QString &functionName : constContextFunctions )
818 QString name = func->
name();
819 if ( name.startsWith(
'_' ) )
821 if ( func->
params() != 0 )
827 void QgsExpressionBuilderWidget::registerItemForAllGroups(
const QStringList &groups,
const QString &label,
const QString &
expressionText,
const QString &helpText,
QgsExpressionItem::ItemType type,
bool highlightedItem,
int sortOrder )
829 const auto constGroups = groups;
830 for (
const QString &group : constGroups )
836 QString QgsExpressionBuilderWidget::formatRelationHelp(
const QgsRelation &relation )
const 838 QString text = QStringLiteral(
"<p>%1</p>" ).arg( tr(
"Inserts the relation ID for the relation named '%1'." ).arg( relation.
name() ) );
839 text.append( QStringLiteral(
"<p>%1</p>" ).arg( tr(
"Current value: '%1'" ).arg( relation.
id() ) ) );
843 QString QgsExpressionBuilderWidget::formatLayerHelp(
const QgsMapLayer *layer )
const 845 QString text = QStringLiteral(
"<p>%1</p>" ).arg( tr(
"Inserts the layer ID for the layer named '%1'." ).arg( layer->
name() ) );
846 text.append( QStringLiteral(
"<p>%1</p>" ).arg( tr(
"Current value: '%1'" ).arg( layer->
id() ) ) );
855 void QgsExpressionBuilderWidget::setParserError(
bool parserError )
857 if ( parserError == mParserError )
864 void QgsExpressionBuilderWidget::loadFieldValues(
const QVariantMap &values )
866 mValuesModel->clear();
867 for ( QVariantMap::ConstIterator it = values.constBegin(); it != values.constEnd(); ++ it )
869 QStandardItem *item =
new QStandardItem( it.key() );
870 item->setData( it.value() );
871 mValuesModel->appendRow( item );
880 void QgsExpressionBuilderWidget::setEvalError(
bool evalError )
882 if ( evalError == mEvalError )
902 updateFunctionTree();
907 QWidget::showEvent( e );
908 txtExpressionString->setFocus();
911 void QgsExpressionBuilderWidget::createErrorMarkers( QList<QgsExpression::ParserError> errors )
916 int errorFirstLine = error.firstLine - 1 ;
917 int errorFirstColumn = error.firstColumn - 1;
918 int errorLastColumn = error.lastColumn - 1;
919 int errorLastLine = error.lastLine - 1;
925 errorFirstLine = errorLastLine;
926 errorFirstColumn = errorLastColumn - 1;
928 txtExpressionString->fillIndicatorRange( errorFirstLine,
931 errorLastColumn, error.errorType );
935 void QgsExpressionBuilderWidget::createMarkers(
const QgsExpressionNode *inNode )
939 case QgsExpressionNode::NodeType::ntFunction:
942 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
943 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->
fnIndex() );
946 int start_pos = txtExpressionString->positionFromLineIndex( inNode->
parserFirstLine - 1, start );
947 txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
950 const QList< QgsExpressionNode * > nodeList = node->
args()->
list();
958 case QgsExpressionNode::NodeType::ntLiteral:
962 case QgsExpressionNode::NodeType::ntUnaryOperator:
965 createMarkers( node->
operand() );
968 case QgsExpressionNode::NodeType::ntBinaryOperator:
971 createMarkers( node->
opLeft() );
972 createMarkers( node->
opRight() );
975 case QgsExpressionNode::NodeType::ntColumnRef:
979 case QgsExpressionNode::NodeType::ntInOperator:
984 const QList< QgsExpressionNode * > nodeList = node->
list()->
list();
992 case QgsExpressionNode::NodeType::ntCondition:
997 createMarkers( cond->whenExp() );
998 createMarkers( cond->thenExp() );
1002 createMarkers( node->
elseExp() );
1006 case QgsExpressionNode::NodeType::ntIndexOperator:
1013 void QgsExpressionBuilderWidget::clearFunctionMarkers()
1015 int lastLine = txtExpressionString->lines() - 1;
1016 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
1019 void QgsExpressionBuilderWidget::clearErrors()
1021 int lastLine = txtExpressionString->lines() - 1;
1030 void QgsExpressionBuilderWidget::txtSearchEdit_textChanged()
1032 mProxyModel->setFilterWildcard( txtSearchEdit->text() );
1033 if ( txtSearchEdit->text().isEmpty() )
1035 expressionTree->collapseAll();
1039 expressionTree->expandAll();
1040 QModelIndex index = mProxyModel->index( 0, 0 );
1041 if ( mProxyModel->hasChildren( index ) )
1043 QModelIndex child = mProxyModel->index( 0, 0, index );
1044 expressionTree->selectionModel()->setCurrentIndex( child, QItemSelectionModel::ClearAndSelect );
1049 void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
1051 mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
1052 mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
1055 void QgsExpressionBuilderWidget::lblPreview_linkActivated(
const QString &link )
1058 QgsMessageViewer *mv =
new QgsMessageViewer(
this );
1059 mv->setWindowTitle( tr(
"More Info on Expression Error" ) );
1060 mv->setMessageAsHtml( txtExpressionString->toolTip() );
1064 void QgsExpressionBuilderWidget::mValuesListView_doubleClicked(
const QModelIndex &index )
1067 txtExpressionString->insertText(
' ' + index.data( Qt::UserRole + 1 ).toString() +
' ' );
1068 txtExpressionString->setFocus();
1071 void QgsExpressionBuilderWidget::operatorButtonClicked()
1073 QPushButton *button = qobject_cast<QPushButton *>( sender() );
1076 txtExpressionString->insertText(
' ' + button->text() +
' ' );
1077 txtExpressionString->setFocus();
1080 void QgsExpressionBuilderWidget::showContextMenu( QPoint pt )
1082 QModelIndex idx = expressionTree->indexAt( pt );
1083 idx = mProxyModel->mapToSource( idx );
1090 QMenu *menu =
new QMenu(
this );
1091 menu->addAction( tr(
"Load First 10 Unique Values" ),
this, SLOT(
loadSampleValues() ) );
1092 menu->addAction( tr(
"Load All Unique Values" ),
this, SLOT(
loadAllValues() ) );
1093 menu->popup( expressionTree->mapToGlobal( pt ) );
1099 QModelIndex idx = mProxyModel->mapToSource( expressionTree->currentIndex() );
1103 if ( !mLayer || !item )
1106 mValueGroupBox->show();
1107 fillFieldValues( item->text(), 10 );
1112 QModelIndex idx = mProxyModel->mapToSource( expressionTree->currentIndex() );
1116 if ( !mLayer || !item )
1119 mValueGroupBox->show();
1120 fillFieldValues( item->text(), -1 );
1123 void QgsExpressionBuilderWidget::txtPython_textChanged()
1125 lblAutoSave->setText( tr(
"Saving…" ) );
1135 if ( tabWidget->currentIndex() != 1 )
1138 QListWidgetItem *item = cmbFileNames->currentItem();
1142 QString file = item->text();
1144 lblAutoSave->setText( QStringLiteral(
"Saved" ) );
1145 QGraphicsOpacityEffect *effect =
new QGraphicsOpacityEffect();
1146 lblAutoSave->setGraphicsEffect( effect );
1147 QPropertyAnimation *anim =
new QPropertyAnimation( effect,
"opacity" );
1148 anim->setDuration( 2000 );
1149 anim->setStartValue( 1.0 );
1150 anim->setEndValue( 0.0 );
1151 anim->setEasingCurve( QEasingCurve::OutQuad );
1152 anim->start( QAbstractAnimation::DeleteWhenStopped );
1155 void QgsExpressionBuilderWidget::indicatorClicked(
int line,
int index, Qt::KeyboardModifiers state )
1157 if ( state & Qt::ControlModifier )
1159 int position = txtExpressionString->positionFromLineIndex( line, index );
1160 long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID, static_cast<long int>( position ) );
1162 QString help = getFunctionHelp( func );
1163 txtHelpText->setText( help );
1167 void QgsExpressionBuilderWidget::setExpressionState(
bool state )
1169 mExpressionValid = state;
1172 QString QgsExpressionBuilderWidget::helpStylesheet()
const 1178 style +=
" .functionname {color: #0a6099; font-weight: bold;} " 1179 " .argument {font-family: monospace; color: #bf0c0c; font-style: italic; } " 1180 " td.argument { padding-right: 10px; }";
1185 QString QgsExpressionBuilderWidget::loadFunctionHelp(
QgsExpressionItem *expressionItem )
1187 if ( !expressionItem )
1190 QString helpContents = expressionItem->
getHelpText();
1193 if ( helpContents.isEmpty() )
1195 QString name = expressionItem->data( Qt::UserRole ).toString();
1203 return "<head><style>" + helpStylesheet() +
"</style></head><body>" + helpContents +
"</body>";
1212 setFilterCaseSensitivity( Qt::CaseInsensitive );
1217 QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
1220 int count = sourceModel()->rowCount( index );
1221 bool matchchild =
false;
1222 for (
int i = 0; i < count; ++i )
1224 if ( filterAcceptsRow( i, index ) )
1237 return QSortFilterProxyModel::filterAcceptsRow( source_row, source_parent );
1244 if ( leftSort != rightSort )
1245 return leftSort < rightSort;
1247 QString leftString = sourceModel()->data( left, Qt::DisplayRole ).toString();
1248 QString rightString = sourceModel()->data( right, Qt::DisplayRole ).toString();
1251 if ( leftString.startsWith(
'$' ) )
1252 leftString = leftString.mid( 1 );
1253 if ( rightString.startsWith(
'$' ) )
1254 rightString = rightString.mid( 1 );
1256 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.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
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.
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
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)
static int functionCount()
Returns the number of functions defined in the parser.
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) ...
Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc.
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 the root node of the expression.
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.
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
Queries 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.