39 #include <QTextStream> 41 #include <QInputDialog> 43 #include <QGraphicsOpacityEffect> 44 #include <QPropertyAnimation> 45 #include <QMessageBox> 53 connect( btnRun, &QToolButton::pressed,
this, &QgsExpressionBuilderWidget::btnRun_pressed );
54 connect( btnNewFile, &QToolButton::pressed,
this, &QgsExpressionBuilderWidget::btnNewFile_pressed );
55 connect( cmbFileNames, &QListWidget::currentItemChanged,
this, &QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged );
56 connect( expressionTree, &QTreeView::doubleClicked,
this, &QgsExpressionBuilderWidget::expressionTree_doubleClicked );
57 connect( txtExpressionString, &QgsCodeEditorExpression::textChanged,
this, &QgsExpressionBuilderWidget::txtExpressionString_textChanged );
58 connect( txtPython, &QgsCodeEditorPython::textChanged,
this, &QgsExpressionBuilderWidget::txtPython_textChanged );
59 connect( txtSearchEditValues, &QgsFilterLineEdit::textChanged,
this, &QgsExpressionBuilderWidget::txtSearchEditValues_textChanged );
60 connect( txtSearchEdit, &QgsFilterLineEdit::textChanged,
this, &QgsExpressionBuilderWidget::txtSearchEdit_textChanged );
61 connect( lblPreview, &QLabel::linkActivated,
this, &QgsExpressionBuilderWidget::lblPreview_linkActivated );
62 connect( mValuesListView, &QListView::doubleClicked,
this, &QgsExpressionBuilderWidget::mValuesListView_doubleClicked );
65 connect( btnClearEditor, &QPushButton::pressed, txtExpressionString, &QgsCodeEditorExpression::clear );
67 txtHelpText->setOpenExternalLinks(
true );
68 mValueGroupBox->hide();
71 mModel = qgis::make_unique<QStandardItemModel>();
72 mProxyModel = qgis::make_unique<QgsExpressionItemSearchProxy>();
73 mProxyModel->setDynamicSortFilter(
true );
74 mProxyModel->setSourceModel( mModel.get() );
75 expressionTree->setModel( mProxyModel.get() );
76 expressionTree->setSortingEnabled(
true );
77 expressionTree->sortByColumn( 0, Qt::AscendingOrder );
79 expressionTree->setSelectionMode( QAbstractItemView::SelectionMode::SingleSelection );
89 expressionTree->setContextMenuPolicy( Qt::CustomContextMenu );
91 connect( expressionTree, &QWidget::customContextMenuRequested,
this, &QgsExpressionBuilderWidget::showContextMenu );
92 connect( expressionTree->selectionModel(), &QItemSelectionModel::currentChanged,
93 this, &QgsExpressionBuilderWidget::currentChanged );
98 const auto pushButtons { mOperatorsGroupBox->findChildren<QPushButton *>() };
99 for ( QPushButton *button : pushButtons )
101 connect( button, &QAbstractButton::pressed,
this, &QgsExpressionBuilderWidget::operatorButtonClicked );
104 txtSearchEdit->setShowSearchIcon(
true );
105 txtSearchEdit->setPlaceholderText( tr(
"Search…" ) );
107 mValuesModel = qgis::make_unique<QStandardItemModel>();
108 mProxyValues = qgis::make_unique<QSortFilterProxyModel>();
109 mProxyValues->setSourceModel( mValuesModel.get() );
110 mValuesListView->setModel( mProxyValues.get() );
111 txtSearchEditValues->setShowSearchIcon(
true );
112 txtSearchEditValues->setPlaceholderText( tr(
"Search…" ) );
114 editorSplit->setSizes( QList<int>( {175, 300} ) );
116 functionsplit->setCollapsible( 0,
false );
117 connect( mShowHelpButton, &QPushButton::clicked,
this, [ = ]()
119 functionsplit->setSizes( QList<int>( {mOperationListGroup->width() - mHelpAndValuesWidget->minimumWidth(),
120 mHelpAndValuesWidget->minimumWidth()
122 mShowHelpButton->setEnabled(
false );
124 connect( functionsplit, &QSplitter::splitterMoved,
this, [ = ](
int,
int )
126 mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
131 splitter->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/splitter" ) ).toByteArray() );
132 editorSplit->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/editorsplitter" ) ).toByteArray() );
133 functionsplit->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/functionsplitter" ) ).toByteArray() );
134 mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
136 txtExpressionString->setFoldingVisible(
false );
138 updateFunctionTree();
153 QModelIndex firstItem = mProxyModel->index( 0, 0, QModelIndex() );
154 expressionTree->setCurrentIndex( firstItem );
156 txtExpressionString->setWrapMode( QsciScintilla::WrapWord );
157 lblAutoSave->clear();
164 #if defined(QSCINTILLA_VERSION) && QSCINTILLA_VERSION >= 0x20a00 171 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::red ), -1 );
172 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::red ), -1 );
173 txtExpressionString->setIndicatorOutlineColor( QColor( Qt::red ), -1 );
176 txtExpressionString->indicatorDefine( QgsCodeEditor::HiddenIndicator, FUNCTION_MARKER_ID );
177 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
178 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
179 txtExpressionString->setIndicatorHoverStyle( QgsCodeEditor::DotsIndicator, FUNCTION_MARKER_ID );
181 connect( txtExpressionString, &QgsCodeEditorExpression::indicatorClicked,
this, &QgsExpressionBuilderWidget::indicatorClicked );
182 txtExpressionString->setAutoCompletionCaseSensitivity(
true );
183 txtExpressionString->setAutoCompletionSource( QsciScintilla::AcsAPIs );
184 txtExpressionString->setCallTipsVisible( 0 );
187 mFunctionBuilderHelp->setMarginVisible(
false );
188 mFunctionBuilderHelp->setEdgeMode( QsciScintilla::EdgeNone );
189 mFunctionBuilderHelp->setEdgeColumn( 0 );
190 mFunctionBuilderHelp->setReadOnly(
true );
191 mFunctionBuilderHelp->setText( tr(
"\"\"\"Define a new function using the @qgsfunction decorator.\n\ 193 The function accepts the following parameters\n\ 195 : param [any]: Define any parameters you want to pass to your function before\n\ 196 the following arguments.\n\ 197 : param feature: The current feature\n\ 198 : param parent: The QgsExpression object\n\ 199 : param context: If there is an argument called ``context`` found at the last\n\ 200 position, this variable will contain a ``QgsExpressionContext``\n\ 201 object, that gives access to various additional information like\n\ 202 expression variables. E.g. ``context.variable( 'layer_id' )``\n\ 203 : returns: The result of the expression.\n\ 207 The @qgsfunction decorator accepts the following arguments:\n\ 210 : param args: Defines the number of arguments. With ``args = 'auto'`` the number of\n\ 211 arguments will automatically be extracted from the signature.\n\ 212 With ``args = -1``, any number of arguments are accepted.\n\ 213 : param group: The name of the group under which this expression function will\n\ 215 : param handlesnull: Set this to True if your function has custom handling for NULL values.\n\ 216 If False, the result will always be NULL as soon as any parameter is NULL.\n\ 217 Defaults to False.\n\ 218 : param usesgeometry : Set this to True if your function requires access to\n\ 219 feature.geometry(). Defaults to False.\n\ 220 : param referenced_columns: An array of attribute names that are required to run\n\ 221 this function. Defaults to [QgsFeatureRequest.ALL_ATTRIBUTES].\n\ 229 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/splitter" ), splitter->saveState() );
230 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/editorsplitter" ), editorSplit->saveState() );
231 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/functionsplitter" ), functionsplit->saveState() );
244 void QgsExpressionBuilderWidget::currentChanged(
const QModelIndex &index,
const QModelIndex & )
246 txtSearchEditValues->clear();
249 QModelIndex idx = mProxyModel->mapToSource( index );
257 loadFieldValues( mFieldValues.value( item->text() ) );
259 cbxValuesInUse->setVisible( formatterCanProvideAvailableValues( item->text() ) );
260 cbxValuesInUse->setChecked(
false );
262 mValueGroupBox->setVisible( isField );
264 mShowHelpButton->setText( isField ? tr(
"Show Values" ) : tr(
"Show Help" ) );
267 QString help = loadFunctionHelp( item );
268 txtHelpText->setText( help );
270 btnRemoveExpression->setEnabled( item->parent() &&
271 item->parent()->text() == mUserExpressionsGroupName );
275 void QgsExpressionBuilderWidget::btnRun_pressed()
277 if ( !cmbFileNames->currentItem() )
280 QString file = cmbFileNames->currentItem()->text();
282 runPythonCode( txtPython->text() );
285 void QgsExpressionBuilderWidget::runPythonCode(
const QString &code )
289 QString pythontext = code;
292 updateFunctionTree();
300 QDir myDir( mFunctionsPath );
301 if ( !myDir.exists() )
303 myDir.mkpath( mFunctionsPath );
306 if ( !fileName.endsWith( QLatin1String(
".py" ) ) )
308 fileName.append(
".py" );
311 fileName = mFunctionsPath + QDir::separator() + fileName;
312 QFile myFile( fileName );
313 if ( myFile.open( QIODevice::WriteOnly | QFile::Truncate ) )
315 QTextStream myFileStream( &myFile );
316 myFileStream << txtPython->text() << endl;
323 mFunctionsPath = path;
325 dir.setNameFilters( QStringList() << QStringLiteral(
"*.py" ) );
326 QStringList files = dir.entryList( QDir::Files );
327 cmbFileNames->clear();
328 const auto constFiles = files;
329 for (
const QString &name : constFiles )
331 QFileInfo info( mFunctionsPath + QDir::separator() + name );
332 if ( info.baseName() == QLatin1String(
"__init__" ) )
continue;
333 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"console/iconTabEditorConsole.svg" ) ), info.baseName() );
334 cmbFileNames->addItem( item );
336 if ( !cmbFileNames->currentItem() )
338 cmbFileNames->setCurrentRow( 0 );
341 if ( cmbFileNames->count() == 0 )
345 txtPython->setText( QString(
"'''\n#Sample custom function file\n " 346 "(uncomment to use and customize or Add button to create a new file) \n%1 \n '''" ).arg( txtPython->text() ) );
353 QList<QListWidgetItem *> items = cmbFileNames->findItems( fileName, Qt::MatchExactly );
354 if ( !items.isEmpty() )
357 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"console/iconTabEditorConsole.svg" ) ), fileName );
358 cmbFileNames->insertItem( 0, item );
359 cmbFileNames->setCurrentRow( 0 );
363 txtPython->setText( templatetxt );
367 void QgsExpressionBuilderWidget::btnNewFile_pressed()
370 QString text = QInputDialog::getText(
this, tr(
"New File" ),
371 tr(
"New file name:" ), QLineEdit::Normal,
373 if ( ok && !text.isEmpty() )
379 void QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem )
383 QString filename = lastitem->text();
386 QString path = mFunctionsPath + QDir::separator() + item->text();
392 if ( !path.endsWith( QLatin1String(
".py" ) ) )
393 path.append(
".py" );
395 txtPython->loadScript( path );
400 txtPython->setText( code );
403 void QgsExpressionBuilderWidget::expressionTree_doubleClicked(
const QModelIndex &index )
405 QModelIndex idx = mProxyModel->mapToSource( index );
416 txtExpressionString->setFocus();
435 txtExpressionString->setFields( fields );
437 for (
int i = 0; i < fields.
count(); ++i )
448 mFieldValues.clear();
450 for (
auto it = fieldValues.constBegin(); it != fieldValues.constEnd(); ++it )
453 const QStringList values = it.value();
455 for (
const QString &value : values )
457 map.insert( value, value );
459 mFieldValues.insert( it.key(), map );
467 for (
auto it = fieldValues.constBegin(); it != fieldValues.constEnd(); ++it )
472 mFieldValues = fieldValues;
475 void QgsExpressionBuilderWidget::fillFieldValues(
const QString &fieldName,
int countLimit,
bool forceUsedValues )
487 if ( fieldIndex < 0 )
494 if ( cbxValuesInUse->isVisible() && !cbxValuesInUse->isChecked() && !forceUsedValues )
502 values = mLayer->
uniqueValues( fieldIndex, countLimit ).toList();
504 std::sort( values.begin(), values.end() );
506 mValuesModel->clear();
507 for (
const QVariant &value : qgis::as_const( values ) )
510 if ( value.isNull() )
511 strValue = QStringLiteral(
"NULL" );
512 else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong )
513 strValue = value.toString();
515 strValue =
'\'' + value.toString().replace(
'\'', QLatin1String(
"''" ) ) +
'\'';
517 QString representedValue = formatter->
representValue( mLayer, fieldIndex, setup.
config(), QVariant(), value );
518 if ( representedValue != value.toString() )
519 representedValue = representedValue + QStringLiteral(
" [" ) + strValue +
']';
521 QStandardItem *item =
new QStandardItem( representedValue );
522 item->setData( strValue );
523 mValuesModel->appendRow( item );
527 bool QgsExpressionBuilderWidget::formatterCanProvideAvailableValues(
const QString &fieldName )
531 if ( fieldIndex != -1 )
548 return QStringLiteral(
"<head><style>" ) + helpStylesheet() + QStringLiteral(
"</style></head><body>" ) + helpContents + QStringLiteral(
"</body>" );
553 const QString &label,
555 const QString &helpText,
559 item->setData( label, Qt::UserRole );
562 item->setIcon( icon );
565 if ( mExpressionGroups.contains( group ) )
568 groupNode->appendRow( item );
574 newgroupNode->setData( group, Qt::UserRole );
577 newgroupNode->appendRow( item );
578 newgroupNode->setBackground( QBrush( QColor( 238, 238, 238 ) ) );
579 mModel->appendRow( newgroupNode );
580 mExpressionGroups.insert( group, newgroupNode );
583 if ( highlightedItem )
587 topLevelItem->setData( label, Qt::UserRole );
589 QFont font = topLevelItem->font();
590 font.setBold(
true );
591 topLevelItem->setFont( font );
592 mModel->appendRow( topLevelItem );
599 return mExpressionValid;
605 QString location = QStringLiteral(
"/expressions/recent/%1" ).arg( collection );
606 QStringList expressions = settings.
value( location ).toStringList();
611 while ( expressions.count() > 20 )
613 expressions.pop_back();
616 settings.
setValue( location, expressions );
622 mRecentKey = collection;
623 QString name = tr(
"Recent (%1)" ).arg( collection );
624 if ( mExpressionGroups.contains( name ) )
627 node->removeRows( 0, node->rowCount() );
631 const QString location = QStringLiteral(
"/expressions/recent/%1" ).arg( collection );
632 const QStringList expressions = settings.
value( location ).toStringList();
634 for (
const QString &expression : expressions )
644 if ( mExpressionGroups.contains( QStringLiteral(
"UserGroup" ) ) )
646 QgsExpressionItem *node = mExpressionGroups.value( QStringLiteral(
"UserGroup" ) );
647 node->removeRows( 0, node->rowCount() );
651 const QString location = QStringLiteral(
"user" );
652 settings.
beginGroup( location, QgsSettings::Section::Expressions );
658 for (
const auto &label : qgis::as_const( mUserExpressionLabels ) )
661 expression = settings.
value( QStringLiteral(
"expression" ) ).toString();
662 helpText = settings.
value( QStringLiteral(
"helpText" ) ).toString();
671 const QString location = QStringLiteral(
"user" );
672 settings.
beginGroup( location, QgsSettings::Section::Expressions );
674 settings.
setValue( QStringLiteral(
"expression" ), expression );
675 settings.
setValue( QStringLiteral(
"helpText" ), helpText );
678 const QModelIndexList idxs { expressionTree->model()->match( expressionTree->model()->index( 0, 0 ),
679 Qt::DisplayRole, label, 1,
680 Qt::MatchFlag::MatchRecursive ) };
681 if ( ! idxs.isEmpty() )
683 expressionTree->scrollTo( idxs.first() );
690 settings.
remove( QStringLiteral(
"user/%1" ).arg( label ), QgsSettings::Section::Expressions );
694 void QgsExpressionBuilderWidget::loadLayers()
699 QMap<QString, QgsMapLayer *> layers = mProject->mapLayers();
700 QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
701 for ( ; layerIt != layers.constEnd(); ++layerIt )
703 registerItemForAllGroups( QStringList() << tr(
"Map Layers" ), layerIt.value()->name(), QStringLiteral(
"'%1'" ).arg( layerIt.key() ), formatLayerHelp( layerIt.value() ) );
707 void QgsExpressionBuilderWidget::loadRelations()
712 QMap<QString, QgsRelation> relations = mProject->relationManager()->relations();
713 QMap<QString, QgsRelation>::const_iterator relIt = relations.constBegin();
714 for ( ; relIt != relations.constEnd(); ++relIt )
716 registerItemForAllGroups( QStringList() << tr(
"Relations" ), relIt->name(), QStringLiteral(
"'%1'" ).arg( relIt->id() ), formatRelationHelp( relIt.value() ) );
720 void QgsExpressionBuilderWidget::updateFunctionTree()
723 mExpressionGroups.clear();
725 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"+" ), QStringLiteral(
" + " ) );
726 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"-" ), QStringLiteral(
" - " ) );
727 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"*" ), QStringLiteral(
" * " ) );
728 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"/" ), QStringLiteral(
" / " ) );
729 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"%" ), QStringLiteral(
" % " ) );
730 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"^" ), QStringLiteral(
" ^ " ) );
731 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"=" ), QStringLiteral(
" = " ) );
732 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"~" ), QStringLiteral(
" ~ " ) );
733 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
">" ), QStringLiteral(
" > " ) );
734 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"<" ), QStringLiteral(
" < " ) );
735 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"<>" ), QStringLiteral(
" <> " ) );
736 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"<=" ), QStringLiteral(
" <= " ) );
737 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
">=" ), QStringLiteral(
" >= " ) );
738 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"[]" ), QStringLiteral(
"[ ]" ) );
739 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"||" ), QStringLiteral(
" || " ) );
740 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"IN" ), QStringLiteral(
" IN " ) );
741 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"LIKE" ), QStringLiteral(
" LIKE " ) );
742 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"ILIKE" ), QStringLiteral(
" ILIKE " ) );
743 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"IS" ), QStringLiteral(
" IS " ) );
744 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"OR" ), QStringLiteral(
" OR " ) );
745 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"AND" ), QStringLiteral(
" AND " ) );
746 registerItem( QStringLiteral(
"Operators" ), QStringLiteral(
"NOT" ), QStringLiteral(
" NOT " ) );
748 QString casestring = QStringLiteral(
"CASE WHEN condition THEN result END" );
749 registerItem( QStringLiteral(
"Conditionals" ), QStringLiteral(
"CASE" ), casestring );
756 for (
int i = 0; i < count; i++ )
759 QString name = func->
name();
760 if ( name.startsWith(
'_' ) )
770 if ( func->
params() != 0 )
772 else if ( !name.startsWith(
'$' ) )
773 name += QLatin1String(
"()" );
783 loadExpressionContext();
793 return txtExpressionString->text();
798 txtExpressionString->setText( expression );
803 return lblExpected->text();
808 lblExpected->setText( expected );
809 mExpectedOutputFrame->setVisible( !expected.isNull() );
814 mExpressionContext = context;
815 updateFunctionTree();
821 void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
826 btnClearEditor->setEnabled( ! txtExpressionString->text().isEmpty() );
827 btnSaveExpression->setEnabled(
false );
831 if ( text.isEmpty() )
834 lblPreview->setStyleSheet( QString() );
835 txtExpressionString->setToolTip( QString() );
836 lblPreview->setToolTip( QString() );
838 setParserError(
true );
839 setEvalError(
true );
860 QVariant value = exp.
evaluate( &mExpressionContext );
871 tooltip = QStringLiteral(
"<b>%1:</b>" 872 "%2" ).arg( tr(
"Parser Errors" ), errorString );
875 tooltip += QStringLiteral(
"<b>%1:</b> %2" ).arg( tr(
"Eval Error" ), exp.
evalErrorString() );
877 lblPreview->setText( tr(
"Expression is invalid <a href=""more"">(more info)</a>" ) );
878 lblPreview->setStyleSheet( QStringLiteral(
"color: rgba(255, 6, 10, 255);" ) );
879 txtExpressionString->setToolTip( tooltip );
880 lblPreview->setToolTip( tooltip );
889 lblPreview->setStyleSheet( QString() );
890 txtExpressionString->setToolTip( QString() );
891 lblPreview->setToolTip( QString() );
893 setParserError(
false );
894 setEvalError(
false );
896 btnSaveExpression->setEnabled(
true );
901 void QgsExpressionBuilderWidget::loadExpressionContext()
903 txtExpressionString->setExpressionContext( mExpressionContext );
905 const auto constVariableNames = variableNames;
906 for (
const QString &variable : constVariableNames )
908 registerItem( QStringLiteral(
"Variables" ), variable,
" @" + variable +
' ',
915 QStringList contextFunctions = mExpressionContext.
functionNames();
916 const auto constContextFunctions = contextFunctions;
917 for (
const QString &functionName : constContextFunctions )
920 QString name = func->
name();
921 if ( name.startsWith(
'_' ) )
923 if ( func->
params() != 0 )
929 void QgsExpressionBuilderWidget::registerItemForAllGroups(
const QStringList &groups,
const QString &label,
const QString &
expressionText,
const QString &helpText,
QgsExpressionItem::ItemType type,
bool highlightedItem,
int sortOrder,
const QStringList &tags )
931 const auto constGroups = groups;
932 for (
const QString &group : constGroups )
938 QString QgsExpressionBuilderWidget::formatRelationHelp(
const QgsRelation &relation )
const 940 QString text = QStringLiteral(
"<p>%1</p>" ).arg( tr(
"Inserts the relation ID for the relation named '%1'." ).arg( relation.
name() ) );
941 text.append( QStringLiteral(
"<p>%1</p>" ).arg( tr(
"Current value: '%1'" ).arg( relation.
id() ) ) );
945 QString QgsExpressionBuilderWidget::formatLayerHelp(
const QgsMapLayer *layer )
const 947 QString text = QStringLiteral(
"<p>%1</p>" ).arg( tr(
"Inserts the layer ID for the layer named '%1'." ).arg( layer->
name() ) );
948 text.append( QStringLiteral(
"<p>%1</p>" ).arg( tr(
"Current value: '%1'" ).arg( layer->
id() ) ) );
957 void QgsExpressionBuilderWidget::setParserError(
bool parserError )
959 if ( parserError == mParserError )
966 void QgsExpressionBuilderWidget::loadFieldValues(
const QVariantMap &values )
968 mValuesModel->clear();
969 for ( QVariantMap::ConstIterator it = values.constBegin(); it != values.constEnd(); ++ it )
971 QStandardItem *item =
new QStandardItem( it.key() );
972 item->setData( it.value() );
973 mValuesModel->appendRow( item );
982 void QgsExpressionBuilderWidget::setEvalError(
bool evalError )
984 if ( evalError == mEvalError )
1004 updateFunctionTree();
1009 QWidget::showEvent( e );
1010 txtExpressionString->setFocus();
1013 void QgsExpressionBuilderWidget::createErrorMarkers( QList<QgsExpression::ParserError> errors )
1018 int errorFirstLine = error.firstLine - 1 ;
1019 int errorFirstColumn = error.firstColumn - 1;
1020 int errorLastColumn = error.lastColumn - 1;
1021 int errorLastLine = error.lastLine - 1;
1027 errorFirstLine = errorLastLine;
1028 errorFirstColumn = errorLastColumn - 1;
1030 txtExpressionString->fillIndicatorRange( errorFirstLine,
1033 errorLastColumn, error.errorType );
1037 void QgsExpressionBuilderWidget::createMarkers(
const QgsExpressionNode *inNode )
1041 case QgsExpressionNode::NodeType::ntFunction:
1044 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
1045 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->
fnIndex() );
1048 int start_pos = txtExpressionString->positionFromLineIndex( inNode->
parserFirstLine - 1, start );
1049 txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
1052 const QList< QgsExpressionNode * > nodeList = node->
args()->
list();
1060 case QgsExpressionNode::NodeType::ntLiteral:
1064 case QgsExpressionNode::NodeType::ntUnaryOperator:
1067 createMarkers( node->
operand() );
1070 case QgsExpressionNode::NodeType::ntBinaryOperator:
1073 createMarkers( node->
opLeft() );
1074 createMarkers( node->
opRight() );
1077 case QgsExpressionNode::NodeType::ntColumnRef:
1081 case QgsExpressionNode::NodeType::ntInOperator:
1086 const QList< QgsExpressionNode * > nodeList = node->
list()->
list();
1094 case QgsExpressionNode::NodeType::ntCondition:
1099 createMarkers( cond->whenExp() );
1100 createMarkers( cond->thenExp() );
1104 createMarkers( node->
elseExp() );
1108 case QgsExpressionNode::NodeType::ntIndexOperator:
1115 void QgsExpressionBuilderWidget::clearFunctionMarkers()
1117 int lastLine = txtExpressionString->lines() - 1;
1118 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
1121 void QgsExpressionBuilderWidget::clearErrors()
1123 int lastLine = txtExpressionString->lines() - 1;
1132 void QgsExpressionBuilderWidget::txtSearchEdit_textChanged()
1134 mProxyModel->setFilterWildcard( txtSearchEdit->text() );
1135 if ( txtSearchEdit->text().isEmpty() )
1137 expressionTree->collapseAll();
1141 expressionTree->expandAll();
1142 QModelIndex index = mProxyModel->index( 0, 0 );
1143 if ( mProxyModel->hasChildren( index ) )
1145 QModelIndex child = mProxyModel->index( 0, 0, index );
1146 expressionTree->selectionModel()->setCurrentIndex( child, QItemSelectionModel::ClearAndSelect );
1151 void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
1153 mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
1154 mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
1157 void QgsExpressionBuilderWidget::lblPreview_linkActivated(
const QString &link )
1160 QgsMessageViewer *mv =
new QgsMessageViewer(
this );
1161 mv->setWindowTitle( tr(
"More Info on Expression Error" ) );
1162 mv->setMessageAsHtml( txtExpressionString->toolTip() );
1166 void QgsExpressionBuilderWidget::mValuesListView_doubleClicked(
const QModelIndex &index )
1169 txtExpressionString->insertText(
' ' + index.data( Qt::UserRole + 1 ).toString() +
' ' );
1170 txtExpressionString->setFocus();
1173 void QgsExpressionBuilderWidget::operatorButtonClicked()
1175 QPushButton *button = qobject_cast<QPushButton *>( sender() );
1178 txtExpressionString->insertText(
' ' + button->text() +
' ' );
1179 txtExpressionString->setFocus();
1182 void QgsExpressionBuilderWidget::showContextMenu( QPoint pt )
1184 QModelIndex idx = expressionTree->indexAt( pt );
1185 idx = mProxyModel->mapToSource( idx );
1192 QMenu *menu =
new QMenu(
this );
1193 menu->addAction( tr(
"Load First 10 Unique Values" ),
this, SLOT(
loadSampleValues() ) );
1194 menu->addAction( tr(
"Load All Unique Values" ),
this, SLOT(
loadAllValues() ) );
1196 if ( formatterCanProvideAvailableValues( item->text() ) )
1198 menu->addAction( tr(
"Load First 10 Unique Used Values" ),
this, SLOT(
loadSampleUsedValues() ) );
1199 menu->addAction( tr(
"Load All Unique Used Values" ),
this, SLOT(
loadAllUsedValues() ) );
1201 menu->popup( expressionTree->mapToGlobal( pt ) );
1207 QModelIndex idx = mProxyModel->mapToSource( expressionTree->currentIndex() );
1211 if ( !mLayer || !item )
1214 mValueGroupBox->show();
1215 fillFieldValues( item->text(), 10 );
1220 QModelIndex idx = mProxyModel->mapToSource( expressionTree->currentIndex() );
1224 if ( !mLayer || !item )
1227 mValueGroupBox->show();
1228 fillFieldValues( item->text(), -1 );
1233 QModelIndex idx = mProxyModel->mapToSource( expressionTree->currentIndex() );
1237 if ( !mLayer || !item )
1240 mValueGroupBox->show();
1241 fillFieldValues( item->text(), 10, true );
1246 QModelIndex idx = mProxyModel->mapToSource( expressionTree->currentIndex() );
1250 if ( !mLayer || !item )
1253 mValueGroupBox->show();
1254 fillFieldValues( item->text(), -1, true );
1257 void QgsExpressionBuilderWidget::txtPython_textChanged()
1259 lblAutoSave->setText( tr(
"Saving…" ) );
1269 if ( tabWidget->currentIndex() != 1 )
1272 QListWidgetItem *item = cmbFileNames->currentItem();
1276 QString file = item->text();
1278 lblAutoSave->setText( QStringLiteral(
"Saved" ) );
1279 QGraphicsOpacityEffect *effect =
new QGraphicsOpacityEffect();
1280 lblAutoSave->setGraphicsEffect( effect );
1281 QPropertyAnimation *anim =
new QPropertyAnimation( effect,
"opacity" );
1282 anim->setDuration( 2000 );
1283 anim->setStartValue( 1.0 );
1284 anim->setEndValue( 0.0 );
1285 anim->setEasingCurve( QEasingCurve::OutQuad );
1286 anim->start( QAbstractAnimation::DeleteWhenStopped );
1293 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1303 QModelIndex idx = mProxyModel->mapToSource( expressionTree->currentIndex() );
1311 ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) )
1314 if ( QMessageBox::Yes == QMessageBox::question(
this, tr(
"Remove Stored Expression" ),
1315 tr(
"Do you really want to remove stored expressions '%1'?" ).arg( item->text() ),
1316 QMessageBox::Yes | QMessageBox::No ) )
1325 QList<QgsExpressionItem *> result;
1326 const QList<QStandardItem *> found { mModel->findItems( label, Qt::MatchFlag::MatchRecursive ) };
1327 for (
const auto &item : qgis::as_const( found ) )
1329 result.push_back( dynamic_cast<QgsExpressionItem *>( item ) );
1334 void QgsExpressionBuilderWidget::indicatorClicked(
int line,
int index, Qt::KeyboardModifiers state )
1336 if ( state & Qt::ControlModifier )
1338 int position = txtExpressionString->positionFromLineIndex( line, index );
1339 long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID, static_cast<long int>( position ) );
1341 QString help = getFunctionHelp( func );
1342 txtHelpText->setText( help );
1346 void QgsExpressionBuilderWidget::setExpressionState(
bool state )
1348 mExpressionValid = state;
1351 QString QgsExpressionBuilderWidget::helpStylesheet()
const 1357 style +=
" .functionname {color: #0a6099; font-weight: bold;} " 1358 " .argument {font-family: monospace; color: #bf0c0c; font-style: italic; } " 1359 " td.argument { padding-right: 10px; }";
1364 QString QgsExpressionBuilderWidget::loadFunctionHelp(
QgsExpressionItem *expressionItem )
1366 if ( !expressionItem )
1369 QString helpContents = expressionItem->
getHelpText();
1372 if ( helpContents.isEmpty() )
1374 QString name = expressionItem->data( Qt::UserRole ).toString();
1382 return "<head><style>" + helpStylesheet() +
"</style></head><body>" + helpContents +
"</body>";
1387 setFilterCaseSensitivity( Qt::CaseInsensitive );
1392 QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
1395 int count = sourceModel()->rowCount( index );
1396 bool matchchild =
false;
1397 for (
int i = 0; i < count; ++i )
1399 if ( filterAcceptsRow( i, index ) )
1413 if ( QSortFilterProxyModel::filterAcceptsRow( source_row, source_parent ) )
1420 for (
const QString &tag : tags )
1422 if ( tag.contains( filterRegExp() ) )
1433 if ( leftSort != rightSort )
1434 return leftSort < rightSort;
1436 QString leftString = sourceModel()->data( left, Qt::DisplayRole ).toString();
1437 QString rightString = sourceModel()->data( right, Qt::DisplayRole ).toString();
1440 if ( leftString.startsWith(
'$' ) )
1441 leftString = leftString.mid( 1 );
1442 if ( rightString.startsWith(
'$' ) )
1443 rightString = rightString.mid( 1 );
1445 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.
QStringList childGroups() const
Returns a list of all key top-level groups that contain keys that can be read using the QSettings obj...
void endGroup()
Resets the group to what it was before the corresponding beginGroup() call.
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.
A context for field formatter containing information like the project.
QVariant evaluate()
Evaluate the feature and return the result.
An expression node for CASE WHEN clauses.
void remove(const QString &key, QgsSettings::Section section=QgsSettings::NoSection)
Removes the setting key and any sub-settings of key in a section.
QString evalErrorString() const
Returns evaluation error.
static QString reportStyleSheet(QgsApplication::StyleSheetType styleSheetType=QgsApplication::StyleSheetType::Qt)
Returns a css style sheet for reports, the styleSheetType argument determines what type of stylesheet...
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
static QStringList tags(const QString &name)
Returns a string list of search tags for a specified function.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
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.
static const int SEARCH_TAGS_ROLE
Search tags role.
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.
void beginGroup(const QString &prefix, QgsSettings::Section section=QgsSettings::NoSection)
Appends prefix to the current group.
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.
A generic dialog for editing expression text, label and help text.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool nextFeature(QgsFeature &f)
void setProject(QgsProject *project)
Sets the project used in field formatter.
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 ...
QString displayNameWithAlias() const
Returns the name to use when displaying this field and adds the alias in parenthesis if it is defined...
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.