38 #include <QTextStream> 40 #include <QInputDialog> 42 #include <QGraphicsOpacityEffect> 43 #include <QPropertyAnimation> 51 connect( btnRun, &QToolButton::pressed,
this, &QgsExpressionBuilderWidget::btnRun_pressed );
52 connect( btnNewFile, &QToolButton::pressed,
this, &QgsExpressionBuilderWidget::btnNewFile_pressed );
53 connect( cmbFileNames, &QListWidget::currentItemChanged,
this, &QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged );
54 connect( expressionTree, &QTreeView::doubleClicked,
this, &QgsExpressionBuilderWidget::expressionTree_doubleClicked );
55 connect( txtExpressionString, &QgsCodeEditorExpression::textChanged,
this, &QgsExpressionBuilderWidget::txtExpressionString_textChanged );
56 connect( txtPython, &QgsCodeEditorPython::textChanged,
this, &QgsExpressionBuilderWidget::txtPython_textChanged );
57 connect( txtSearchEditValues, &QgsFilterLineEdit::textChanged,
this, &QgsExpressionBuilderWidget::txtSearchEditValues_textChanged );
58 connect( txtSearchEdit, &QgsFilterLineEdit::textChanged,
this, &QgsExpressionBuilderWidget::txtSearchEdit_textChanged );
59 connect( lblPreview, &QLabel::linkActivated,
this, &QgsExpressionBuilderWidget::lblPreview_linkActivated );
60 connect( mValuesListView, &QListView::doubleClicked,
this, &QgsExpressionBuilderWidget::mValuesListView_doubleClicked );
62 mValueGroupBox->hide();
65 mModel = qgis::make_unique<QStandardItemModel>();
66 mProxyModel = qgis::make_unique<QgsExpressionItemSearchProxy>();
67 mProxyModel->setDynamicSortFilter(
true );
68 mProxyModel->setSourceModel( mModel.get() );
69 expressionTree->setModel( mProxyModel.get() );
70 expressionTree->setSortingEnabled(
true );
71 expressionTree->sortByColumn( 0, Qt::AscendingOrder );
73 expressionTree->setContextMenuPolicy( Qt::CustomContextMenu );
75 connect( expressionTree, &QWidget::customContextMenuRequested,
this, &QgsExpressionBuilderWidget::showContextMenu );
76 connect( expressionTree->selectionModel(), &QItemSelectionModel::currentChanged,
77 this, &QgsExpressionBuilderWidget::currentChanged );
82 const auto pushButtons { mOperatorsGroupBox->findChildren<QPushButton *>() };
83 for ( QPushButton *button : pushButtons )
85 connect( button, &QAbstractButton::pressed,
this, &QgsExpressionBuilderWidget::operatorButtonClicked );
88 txtSearchEdit->setShowSearchIcon(
true );
89 txtSearchEdit->setPlaceholderText( tr(
"Search…" ) );
91 mValuesModel = qgis::make_unique<QStandardItemModel>();
92 mProxyValues = qgis::make_unique<QSortFilterProxyModel>();
93 mProxyValues->setSourceModel( mValuesModel.get() );
94 mValuesListView->setModel( mProxyValues.get() );
95 txtSearchEditValues->setShowSearchIcon(
true );
96 txtSearchEditValues->setPlaceholderText( tr(
"Search…" ) );
98 editorSplit->setSizes( QList<int>( {175, 300} ) );
100 functionsplit->setCollapsible( 0,
false );
101 connect( mShowHelpButton, &QPushButton::clicked,
this, [ = ]()
103 functionsplit->setSizes( QList<int>( {mOperationListGroup->width() - mHelpAndValuesWidget->minimumWidth(),
104 mHelpAndValuesWidget->minimumWidth()
106 mShowHelpButton->setEnabled(
false );
108 connect( functionsplit, &QSplitter::splitterMoved,
this, [ = ](
int,
int )
110 mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
115 splitter->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/splitter" ) ).toByteArray() );
116 editorSplit->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/editorsplitter" ) ).toByteArray() );
117 functionsplit->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/functionsplitter" ) ).toByteArray() );
119 txtExpressionString->setFoldingVisible(
false );
121 updateFunctionTree();
135 QModelIndex firstItem = mProxyModel->index( 0, 0, QModelIndex() );
136 expressionTree->setCurrentIndex( firstItem );
138 txtExpressionString->setWrapMode( QsciScintilla::WrapWord );
139 lblAutoSave->clear();
147 #if defined(QSCINTILLA_VERSION) && QSCINTILLA_VERSION >= 0x20a00 154 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::red ), -1 );
155 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::red ), -1 );
156 txtExpressionString->setIndicatorOutlineColor( QColor( Qt::red ), -1 );
159 txtExpressionString->indicatorDefine( QgsCodeEditor::HiddenIndicator, FUNCTION_MARKER_ID );
160 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
161 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
162 txtExpressionString->setIndicatorHoverStyle( QgsCodeEditor::DotsIndicator, FUNCTION_MARKER_ID );
164 connect( txtExpressionString, &QgsCodeEditorExpression::indicatorClicked,
this, &QgsExpressionBuilderWidget::indicatorClicked );
165 txtExpressionString->setAutoCompletionCaseSensitivity(
true );
166 txtExpressionString->setAutoCompletionSource( QsciScintilla::AcsAPIs );
167 txtExpressionString->setCallTipsVisible( 0 );
170 mFunctionBuilderHelp->setMarginVisible(
false );
171 mFunctionBuilderHelp->setEdgeMode( QsciScintilla::EdgeNone );
172 mFunctionBuilderHelp->setEdgeColumn( 0 );
173 mFunctionBuilderHelp->setReadOnly(
true );
174 mFunctionBuilderHelp->setText( tr(
"\"\"\"Define a new function using the @qgsfunction decorator.\n\ 176 The function accepts the following parameters\n\ 178 : param [any]: Define any parameters you want to pass to your function before\n\ 179 the following arguments.\n\ 180 : param feature: The current feature\n\ 181 : param parent: The QgsExpression object\n\ 182 : param context: If there is an argument called ``context`` found at the last\n\ 183 position, this variable will contain a ``QgsExpressionContext``\n\ 184 object, that gives access to various additional information like\n\ 185 expression variables. E.g. ``context.variable( 'layer_id' )``\n\ 186 : returns: The result of the expression.\n\ 190 The @qgsfunction decorator accepts the following arguments:\n\ 193 : param args: Defines the number of arguments. With ``args = 'auto'`` the number of\n\ 194 arguments will automatically be extracted from the signature.\n\ 195 With ``args = -1``, any number of arguments are accepted.\n\ 196 : param group: The name of the group under which this expression function will\n\ 198 : param handlesnull: Set this to True if your function has custom handling for NULL values.\n\ 199 If False, the result will always be NULL as soon as any parameter is NULL.\n\ 200 Defaults to False.\n\ 201 : param usesgeometry : Set this to True if your function requires access to\n\ 202 feature.geometry(). Defaults to False.\n\ 203 : param referenced_columns: An array of attribute names that are required to run\n\ 204 this function. Defaults to [QgsFeatureRequest.ALL_ATTRIBUTES].\n\ 212 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/splitter" ), splitter->saveState() );
213 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/editorsplitter" ), editorSplit->saveState() );
214 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/functionsplitter" ), functionsplit->saveState() );
227 void QgsExpressionBuilderWidget::currentChanged(
const QModelIndex &index,
const QModelIndex & )
229 txtSearchEditValues->clear();
232 QModelIndex idx = mProxyModel->mapToSource( index );
240 loadFieldValues( mFieldValues.value( item->text() ) );
242 mValueGroupBox->setVisible( isField );
243 mShowHelpButton->setText( isField ? tr(
"Show Values" ) : tr(
"Show Help" ) );
246 QString help = loadFunctionHelp( item );
247 txtHelpText->setText( help );
250 void QgsExpressionBuilderWidget::btnRun_pressed()
252 if ( !cmbFileNames->currentItem() )
255 QString file = cmbFileNames->currentItem()->text();
257 runPythonCode( txtPython->text() );
260 void QgsExpressionBuilderWidget::runPythonCode(
const QString &code )
264 QString pythontext = code;
267 updateFunctionTree();
274 QDir myDir( mFunctionsPath );
275 if ( !myDir.exists() )
277 myDir.mkpath( mFunctionsPath );
280 if ( !fileName.endsWith( QLatin1String(
".py" ) ) )
282 fileName.append(
".py" );
285 fileName = mFunctionsPath + QDir::separator() + fileName;
286 QFile myFile( fileName );
287 if ( myFile.open( QIODevice::WriteOnly | QFile::Truncate ) )
289 QTextStream myFileStream( &myFile );
290 myFileStream << txtPython->text() << endl;
297 mFunctionsPath = path;
299 dir.setNameFilters( QStringList() << QStringLiteral(
"*.py" ) );
300 QStringList files = dir.entryList( QDir::Files );
301 cmbFileNames->clear();
302 const auto constFiles = files;
303 for (
const QString &name : constFiles )
305 QFileInfo info( mFunctionsPath + QDir::separator() + name );
306 if ( info.baseName() == QLatin1String(
"__init__" ) )
continue;
307 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"console/iconTabEditorConsole.svg" ) ), info.baseName() );
308 cmbFileNames->addItem( item );
310 if ( !cmbFileNames->currentItem() )
312 cmbFileNames->setCurrentRow( 0 );
315 if ( cmbFileNames->count() == 0 )
319 txtPython->setText( QString(
"'''\n#Sample custom function file\n " 320 "(uncomment to use and customize or Add button to create a new file) \n%1 \n '''" ).arg( txtPython->text() ) );
327 QList<QListWidgetItem *> items = cmbFileNames->findItems( fileName, Qt::MatchExactly );
328 if ( !items.isEmpty() )
331 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"console/iconTabEditorConsole.svg" ) ), fileName );
332 cmbFileNames->insertItem( 0, item );
333 cmbFileNames->setCurrentRow( 0 );
337 txtPython->setText( templatetxt );
341 void QgsExpressionBuilderWidget::btnNewFile_pressed()
344 QString text = QInputDialog::getText(
this, tr(
"New File" ),
345 tr(
"New file name:" ), QLineEdit::Normal,
347 if ( ok && !text.isEmpty() )
353 void QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem )
357 QString filename = lastitem->text();
360 QString path = mFunctionsPath + QDir::separator() + item->text();
366 if ( !path.endsWith( QLatin1String(
".py" ) ) )
367 path.append(
".py" );
369 txtPython->loadScript( path );
374 txtPython->setText( code );
377 void QgsExpressionBuilderWidget::expressionTree_doubleClicked(
const QModelIndex &index )
379 QModelIndex idx = mProxyModel->mapToSource( index );
390 txtExpressionString->setFocus();
409 txtExpressionString->setFields( fields );
411 QStringList fieldNames;
412 fieldNames.reserve( fields.
count() );
413 for (
int i = 0; i < fields.
count(); ++i )
416 QString fieldName = field.
name();
417 fieldNames << fieldName;
426 mFieldValues.clear();
428 for (
auto it = fieldValues.constBegin(); it != fieldValues.constEnd(); ++it )
431 const QStringList values = it.value();
433 for (
const QString &value : values )
435 map.insert( value, value );
437 mFieldValues.insert( it.key(), map );
445 for (
auto it = fieldValues.constBegin(); it != fieldValues.constEnd(); ++it )
450 mFieldValues = fieldValues;
453 void QgsExpressionBuilderWidget::fillFieldValues(
const QString &fieldName,
int countLimit )
465 if ( fieldIndex < 0 )
471 QList<QVariant> values = mLayer->
uniqueValues( fieldIndex, countLimit ).toList();
472 std::sort( values.begin(), values.end() );
474 for (
const QVariant &value : qgis::as_const( values ) )
477 if ( value.isNull() )
478 strValue = QStringLiteral(
"NULL" );
479 else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong )
480 strValue = value.toString();
482 strValue =
'\'' + value.toString().replace(
'\'', QLatin1String(
"''" ) ) +
'\'';
484 QString representedValue = formatter->
representValue( mLayer, fieldIndex, setup.
config(), QVariant(), value );
485 if ( representedValue != value.toString() )
486 representedValue = representedValue + QStringLiteral(
" [" ) + strValue +
']';
488 QStandardItem *item =
new QStandardItem( representedValue );
489 item->setData( strValue );
490 mValuesModel->appendRow( item );
501 return QStringLiteral(
"<head><style>" ) + helpStylesheet() + QStringLiteral(
"</style></head><body>" ) + helpContents + QStringLiteral(
"</body>" );
506 const QString &label,
508 const QString &helpText,
512 item->setData( label, Qt::UserRole );
514 item->setIcon( icon );
517 if ( mExpressionGroups.contains( group ) )
520 groupNode->appendRow( item );
526 newgroupNode->setData( group, Qt::UserRole );
529 newgroupNode->appendRow( item );
530 newgroupNode->setBackground( QBrush( QColor( 238, 238, 238 ) ) );
531 mModel->appendRow( newgroupNode );
532 mExpressionGroups.insert( group, newgroupNode );
535 if ( highlightedItem )
539 topLevelItem->setData( label, Qt::UserRole );
541 QFont font = topLevelItem->font();
542 font.setBold(
true );
543 topLevelItem->setFont( font );
544 mModel->appendRow( topLevelItem );
551 return mExpressionValid;
557 QString location = QStringLiteral(
"/expressions/recent/%1" ).arg( collection );
558 QStringList expressions = settings.
value( location ).toStringList();
563 while ( expressions.count() > 20 )
565 expressions.pop_back();
568 settings.
setValue( location, expressions );
574 mRecentKey = collection;
575 QString name = tr(
"Recent (%1)" ).arg( collection );
576 if ( mExpressionGroups.contains( name ) )
579 node->removeRows( 0, node->rowCount() );
583 QString location = QStringLiteral(
"/expressions/recent/%1" ).arg( collection );
584 QStringList expressions = settings.
value( location ).toStringList();
586 const auto constExpressions = expressions;
587 for (
const QString &expression : constExpressions )
594 void QgsExpressionBuilderWidget::loadLayers()
599 QMap<QString, QgsMapLayer *> layers = mProject->mapLayers();
600 QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
601 for ( ; layerIt != layers.constEnd(); ++layerIt )
603 registerItemForAllGroups( QStringList() << tr(
"Map Layers" ), layerIt.value()->name(), QStringLiteral(
"'%1'" ).arg( layerIt.key() ), formatLayerHelp( layerIt.value() ) );
607 void QgsExpressionBuilderWidget::loadRelations()
612 QMap<QString, QgsRelation> relations = mProject->relationManager()->relations();
613 QMap<QString, QgsRelation>::const_iterator relIt = relations.constBegin();
614 for ( ; relIt != relations.constEnd(); ++relIt )
616 registerItemForAllGroups( QStringList() << tr(
"Relations" ), relIt->name(), QStringLiteral(
"'%1'" ).arg( relIt->id() ), formatRelationHelp( relIt.value() ) );
620 void QgsExpressionBuilderWidget::updateFunctionTree()
623 mExpressionGroups.clear();
625 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"+" ), QStringLiteral(
" + " ) );
626 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"-" ), QStringLiteral(
" - " ) );
627 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"*" ), QStringLiteral(
" * " ) );
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(
"IN" ), QStringLiteral(
" IN " ) );
641 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"LIKE" ), QStringLiteral(
" LIKE " ) );
642 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"ILIKE" ), QStringLiteral(
" ILIKE " ) );
643 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"IS" ), QStringLiteral(
" IS " ) );
644 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"OR" ), QStringLiteral(
" OR " ) );
645 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"AND" ), QStringLiteral(
" AND " ) );
646 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"NOT" ), QStringLiteral(
" NOT " ) );
648 QString casestring = QStringLiteral(
"CASE WHEN condition THEN result END" );
649 registerItem( QStringLiteral(
"Conditionals" ), QStringLiteral(
"CASE" ), casestring );
656 for (
int i = 0; i < count; i++ )
659 QString name = func->
name();
660 if ( name.startsWith(
'_' ) )
670 if ( func->
params() != 0 )
672 else if ( !name.startsWith(
'$' ) )
673 name += QLatin1String(
"()" );
683 loadExpressionContext();
693 return txtExpressionString->text();
698 txtExpressionString->setText( expression );
703 return lblExpected->text();
708 lblExpected->setText( expected );
709 mExpectedOutputFrame->setVisible( !expected.isNull() );
714 mExpressionContext = context;
715 updateFunctionTree();
720 void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
727 if ( text.isEmpty() )
730 lblPreview->setStyleSheet( QString() );
731 txtExpressionString->setToolTip( QString() );
732 lblPreview->setToolTip( QString() );
734 setParserError(
true );
735 setEvalError(
true );
756 QVariant value = exp.
evaluate( &mExpressionContext );
767 tooltip = QStringLiteral(
"<b>%1:</b>" 768 "%2" ).arg( tr(
"Parser Errors" ), errorString );
771 tooltip += QStringLiteral(
"<b>%1:</b> %2" ).arg( tr(
"Eval Error" ), exp.
evalErrorString() );
773 lblPreview->setText( tr(
"Expression is invalid <a href=""more"">(more info)</a>" ) );
774 lblPreview->setStyleSheet( QStringLiteral(
"color: rgba(255, 6, 10, 255);" ) );
775 txtExpressionString->setToolTip( tooltip );
776 lblPreview->setToolTip( tooltip );
785 lblPreview->setStyleSheet( QString() );
786 txtExpressionString->setToolTip( QString() );
787 lblPreview->setToolTip( QString() );
789 setParserError(
false );
790 setEvalError(
false );
796 void QgsExpressionBuilderWidget::loadExpressionContext()
798 txtExpressionString->setExpressionContext( mExpressionContext );
800 const auto constVariableNames = variableNames;
801 for (
const QString &variable : constVariableNames )
803 registerItem( QStringLiteral(
"Variables" ), variable,
" @" + variable +
' ',
810 QStringList contextFunctions = mExpressionContext.
functionNames();
811 const auto constContextFunctions = contextFunctions;
812 for (
const QString &functionName : constContextFunctions )
815 QString name = func->
name();
816 if ( name.startsWith(
'_' ) )
818 if ( func->
params() != 0 )
824 void QgsExpressionBuilderWidget::registerItemForAllGroups(
const QStringList &groups,
const QString &label,
const QString &
expressionText,
const QString &helpText,
QgsExpressionItem::ItemType type,
bool highlightedItem,
int sortOrder )
826 const auto constGroups = groups;
827 for (
const QString &group : constGroups )
833 QString QgsExpressionBuilderWidget::formatRelationHelp(
const QgsRelation &relation )
const 835 QString text = QStringLiteral(
"<p>%1</p>" ).arg( tr(
"Inserts the relation ID for the relation named '%1'." ).arg( relation.
name() ) );
836 text.append( QStringLiteral(
"<p>%1</p>" ).arg( tr(
"Current value: '%1'" ).arg( relation.
id() ) ) );
840 QString QgsExpressionBuilderWidget::formatLayerHelp(
const QgsMapLayer *layer )
const 842 QString text = QStringLiteral(
"<p>%1</p>" ).arg( tr(
"Inserts the layer ID for the layer named '%1'." ).arg( layer->
name() ) );
843 text.append( QStringLiteral(
"<p>%1</p>" ).arg( tr(
"Current value: '%1'" ).arg( layer->
id() ) ) );
852 void QgsExpressionBuilderWidget::setParserError(
bool parserError )
854 if ( parserError == mParserError )
861 void QgsExpressionBuilderWidget::loadFieldValues(
const QVariantMap &values )
863 mValuesModel->clear();
864 for ( QVariantMap::ConstIterator it = values.constBegin(); it != values.constEnd(); ++ it )
866 QStandardItem *item =
new QStandardItem( it.key() );
867 item->setData( it.value() );
868 mValuesModel->appendRow( item );
877 void QgsExpressionBuilderWidget::setEvalError(
bool evalError )
879 if ( evalError == mEvalError )
899 updateFunctionTree();
904 QWidget::showEvent( e );
905 txtExpressionString->setFocus();
908 void QgsExpressionBuilderWidget::createErrorMarkers( QList<QgsExpression::ParserError> errors )
913 int errorFirstLine = error.firstLine - 1 ;
914 int errorFirstColumn = error.firstColumn - 1;
915 int errorLastColumn = error.lastColumn - 1;
916 int errorLastLine = error.lastLine - 1;
922 errorFirstLine = errorLastLine;
923 errorFirstColumn = errorLastColumn - 1;
925 txtExpressionString->fillIndicatorRange( errorFirstLine,
928 errorLastColumn, error.errorType );
932 void QgsExpressionBuilderWidget::createMarkers(
const QgsExpressionNode *inNode )
936 case QgsExpressionNode::NodeType::ntFunction:
939 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
940 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->
fnIndex() );
943 int start_pos = txtExpressionString->positionFromLineIndex( inNode->
parserFirstLine - 1, start );
944 txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
947 const QList< QgsExpressionNode * > nodeList = node->
args()->
list();
955 case QgsExpressionNode::NodeType::ntLiteral:
959 case QgsExpressionNode::NodeType::ntUnaryOperator:
962 createMarkers( node->
operand() );
965 case QgsExpressionNode::NodeType::ntBinaryOperator:
968 createMarkers( node->
opLeft() );
969 createMarkers( node->
opRight() );
972 case QgsExpressionNode::NodeType::ntColumnRef:
976 case QgsExpressionNode::NodeType::ntInOperator:
981 const QList< QgsExpressionNode * > nodeList = node->
list()->
list();
989 case QgsExpressionNode::NodeType::ntCondition:
994 createMarkers( cond->whenExp() );
995 createMarkers( cond->thenExp() );
999 createMarkers( node->
elseExp() );
1003 case QgsExpressionNode::NodeType::ntIndexOperator:
1010 void QgsExpressionBuilderWidget::clearFunctionMarkers()
1012 int lastLine = txtExpressionString->lines() - 1;
1013 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
1016 void QgsExpressionBuilderWidget::clearErrors()
1018 int lastLine = txtExpressionString->lines() - 1;
1027 void QgsExpressionBuilderWidget::txtSearchEdit_textChanged()
1029 mProxyModel->setFilterWildcard( txtSearchEdit->text() );
1030 if ( txtSearchEdit->text().isEmpty() )
1032 expressionTree->collapseAll();
1036 expressionTree->expandAll();
1037 QModelIndex index = mProxyModel->index( 0, 0 );
1038 if ( mProxyModel->hasChildren( index ) )
1040 QModelIndex child = mProxyModel->index( 0, 0, index );
1041 expressionTree->selectionModel()->setCurrentIndex( child, QItemSelectionModel::ClearAndSelect );
1046 void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
1048 mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
1049 mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
1052 void QgsExpressionBuilderWidget::lblPreview_linkActivated(
const QString &link )
1056 mv->setWindowTitle( tr(
"More Info on Expression Error" ) );
1061 void QgsExpressionBuilderWidget::mValuesListView_doubleClicked(
const QModelIndex &index )
1064 txtExpressionString->insertText(
' ' + index.data( Qt::UserRole + 1 ).toString() +
' ' );
1065 txtExpressionString->setFocus();
1068 void QgsExpressionBuilderWidget::operatorButtonClicked()
1070 QPushButton *button = qobject_cast<QPushButton *>( sender() );
1073 txtExpressionString->insertText(
' ' + button->text() +
' ' );
1074 txtExpressionString->setFocus();
1077 void QgsExpressionBuilderWidget::showContextMenu( QPoint pt )
1079 QModelIndex idx = expressionTree->indexAt( pt );
1080 idx = mProxyModel->mapToSource( idx );
1087 QMenu *menu =
new QMenu(
this );
1088 menu->addAction( tr(
"Load First 10 Unique Values" ),
this, SLOT(
loadSampleValues() ) );
1089 menu->addAction( tr(
"Load All Unique Values" ),
this, SLOT(
loadAllValues() ) );
1090 menu->popup( expressionTree->mapToGlobal( pt ) );
1096 QModelIndex idx = mProxyModel->mapToSource( expressionTree->currentIndex() );
1100 if ( !mLayer || !item )
1103 mValueGroupBox->show();
1104 fillFieldValues( item->text(), 10 );
1109 QModelIndex idx = mProxyModel->mapToSource( expressionTree->currentIndex() );
1113 if ( !mLayer || !item )
1116 mValueGroupBox->show();
1117 fillFieldValues( item->text(), -1 );
1120 void QgsExpressionBuilderWidget::txtPython_textChanged()
1122 lblAutoSave->setText( tr(
"Saving…" ) );
1132 if ( tabWidget->currentIndex() != 1 )
1135 QListWidgetItem *item = cmbFileNames->currentItem();
1139 QString file = item->text();
1141 lblAutoSave->setText( QStringLiteral(
"Saved" ) );
1142 QGraphicsOpacityEffect *effect =
new QGraphicsOpacityEffect();
1143 lblAutoSave->setGraphicsEffect( effect );
1144 QPropertyAnimation *anim =
new QPropertyAnimation( effect,
"opacity" );
1145 anim->setDuration( 2000 );
1146 anim->setStartValue( 1.0 );
1147 anim->setEndValue( 0.0 );
1148 anim->setEasingCurve( QEasingCurve::OutQuad );
1149 anim->start( QAbstractAnimation::DeleteWhenStopped );
1152 void QgsExpressionBuilderWidget::indicatorClicked(
int line,
int index, Qt::KeyboardModifiers state )
1154 if ( state & Qt::ControlModifier )
1156 int position = txtExpressionString->positionFromLineIndex( line, index );
1157 long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID, static_cast<long int>( position ) );
1159 QString help = getFunctionHelp( func );
1160 txtHelpText->setText( help );
1164 void QgsExpressionBuilderWidget::setExpressionState(
bool state )
1166 mExpressionValid = state;
1169 QString QgsExpressionBuilderWidget::helpStylesheet()
const 1175 style +=
" .functionname {color: #0a6099; font-weight: bold;} " 1176 " .argument {font-family: monospace; color: #bf0c0c; font-style: italic; } " 1177 " td.argument { padding-right: 10px; }";
1182 QString QgsExpressionBuilderWidget::loadFunctionHelp(
QgsExpressionItem *expressionItem )
1184 if ( !expressionItem )
1187 QString helpContents = expressionItem->
getHelpText();
1190 if ( helpContents.isEmpty() )
1192 QString name = expressionItem->data( Qt::UserRole ).toString();
1200 return "<head><style>" + helpStylesheet() +
"</style></head><body>" + helpContents +
"</body>";
1209 setFilterCaseSensitivity( Qt::CaseInsensitive );
1214 QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
1217 int count = sourceModel()->rowCount( index );
1218 bool matchchild =
false;
1219 for (
int i = 0; i < count; ++i )
1221 if ( filterAcceptsRow( i, index ) )
1234 return QSortFilterProxyModel::filterAcceptsRow( source_row, source_parent );
1241 if ( leftSort != rightSort )
1242 return leftSort < rightSort;
1244 QString leftString = sourceModel()->data( left, Qt::DisplayRole ).toString();
1245 QString rightString = sourceModel()->data( right, Qt::DisplayRole ).toString();
1248 if ( leftString.startsWith(
'$' ) )
1249 leftString = leftString.mid( 1 );
1250 if ( rightString.startsWith(
'$' ) )
1251 rightString = rightString.mid( 1 );
1253 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)
void setMessageAsHtml(const QString &msg)
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) ...
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 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.
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
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.