18 #include <QTextStream>
20 #include <QInputDialog>
22 #include <QGraphicsOpacityEffect>
23 #include <QPropertyAnimation>
24 #include <QMessageBox>
25 #include <QVersionNumber>
27 #include <QJsonDocument>
28 #include <QJsonObject>
30 #include <QFileDialog>
61 if ( fieldIndex != -1 )
79 connect( btnRun, &QToolButton::pressed,
this, &QgsExpressionBuilderWidget::btnRun_pressed );
80 connect( btnNewFile, &QPushButton::pressed,
this, &QgsExpressionBuilderWidget::btnNewFile_pressed );
81 connect( btnRemoveFile, &QPushButton::pressed,
this, &QgsExpressionBuilderWidget::btnRemoveFile_pressed );
82 connect( cmbFileNames, &QListWidget::currentItemChanged,
this, &QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged );
83 connect( txtExpressionString, &QgsCodeEditorExpression::textChanged,
this, &QgsExpressionBuilderWidget::txtExpressionString_textChanged );
84 connect( txtPython, &QgsCodeEditorPython::textChanged,
this, &QgsExpressionBuilderWidget::txtPython_textChanged );
85 connect( txtSearchEditValues, &QgsFilterLineEdit::textChanged,
this, &QgsExpressionBuilderWidget::txtSearchEditValues_textChanged );
86 connect( mValuesListView, &QListView::doubleClicked,
this, &QgsExpressionBuilderWidget::mValuesListView_doubleClicked );
90 connect( btnImportExpressions, &QPushButton::pressed,
this, &QgsExpressionBuilderWidget::importUserExpressions_pressed );
91 connect( btnExportExpressions, &QPushButton::pressed,
this, &QgsExpressionBuilderWidget::exportUserExpressions_pressed );
92 connect( btnClearEditor, &QPushButton::pressed, txtExpressionString, &QgsCodeEditorExpression::clear );
105 mExpressionTreeMenuProvider =
new ExpressionTreeMenuProvider(
this );
106 mExpressionTreeView->setMenuProvider( mExpressionTreeMenuProvider );
108 txtHelpText->setOpenExternalLinks(
true );
109 mValueGroupBox->hide();
126 const auto pushButtons { mOperatorsGroupBox->findChildren<QPushButton *>() };
127 for ( QPushButton *button : pushButtons )
129 connect( button, &QAbstractButton::pressed,
this, &QgsExpressionBuilderWidget::operatorButtonClicked );
132 txtSearchEdit->setShowSearchIcon(
true );
133 txtSearchEdit->setPlaceholderText( tr(
"Search…" ) );
135 mValuesModel = qgis::make_unique<QStandardItemModel>();
136 mProxyValues = qgis::make_unique<QSortFilterProxyModel>();
137 mProxyValues->setSourceModel( mValuesModel.get() );
138 mValuesListView->setModel( mProxyValues.get() );
139 txtSearchEditValues->setShowSearchIcon(
true );
140 txtSearchEditValues->setPlaceholderText( tr(
"Search…" ) );
142 editorSplit->setSizes( QList<int>( {175, 300} ) );
144 functionsplit->setCollapsible( 0,
false );
145 connect( mShowHelpButton, &QPushButton::clicked,
this, [ = ]()
147 functionsplit->setSizes( QList<int>( {mOperationListGroup->width() - mHelpAndValuesWidget->minimumWidth(),
148 mHelpAndValuesWidget->minimumWidth()
150 mShowHelpButton->setEnabled(
false );
152 connect( functionsplit, &QSplitter::splitterMoved,
this, [ = ](
int,
int )
154 mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
158 splitter->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/splitter" ) ).toByteArray() );
159 editorSplit->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/editorsplitter" ) ).toByteArray() );
160 functionsplit->restoreState( settings.value( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/functionsplitter" ) ).toByteArray() );
161 mShowHelpButton->setEnabled( functionsplit->sizes().at( 1 ) == 0 );
163 txtExpressionString->setFoldingVisible(
false );
169 btnRemoveFile->setEnabled( cmbFileNames->count() > 0 );
176 txtExpressionString->setWrapMode( QsciScintilla::WrapWord );
177 lblAutoSave->clear();
184 #if defined(QSCINTILLA_VERSION) && QSCINTILLA_VERSION >= 0x20a00
191 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::red ), -1 );
192 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::red ), -1 );
193 txtExpressionString->setIndicatorOutlineColor( QColor( Qt::red ), -1 );
196 txtExpressionString->indicatorDefine( QgsCodeEditor::HiddenIndicator, FUNCTION_MARKER_ID );
197 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
198 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
199 txtExpressionString->setIndicatorHoverStyle( QgsCodeEditor::DotsIndicator, FUNCTION_MARKER_ID );
201 connect( txtExpressionString, &QgsCodeEditorExpression::indicatorClicked,
this, &QgsExpressionBuilderWidget::indicatorClicked );
202 txtExpressionString->setAutoCompletionCaseSensitivity(
false );
203 txtExpressionString->setAutoCompletionSource( QsciScintilla::AcsAPIs );
204 txtExpressionString->setCallTipsVisible( 0 );
207 mFunctionBuilderHelp->setMarginVisible(
false );
208 mFunctionBuilderHelp->setEdgeMode( QsciScintilla::EdgeNone );
209 mFunctionBuilderHelp->setEdgeColumn( 0 );
210 mFunctionBuilderHelp->setReadOnly(
true );
211 mFunctionBuilderHelp->setText( tr(
"\"\"\"Define a new function using the @qgsfunction decorator.\n\
213 The function accepts the following parameters\n\
215 : param [any]: Define any parameters you want to pass to your function before\n\
216 the following arguments.\n\
217 : param feature: The current feature\n\
218 : param parent: The QgsExpression object\n\
219 : param context: If there is an argument called ``context`` found at the last\n\
220 position, this variable will contain a ``QgsExpressionContext``\n\
221 object, that gives access to various additional information like\n\
222 expression variables. E.g. ``context.variable( 'layer_id' )``\n\
223 : returns: The result of the expression.\n\
227 The @qgsfunction decorator accepts the following arguments:\n\
230 : param args: Defines the number of arguments. With ``args = 'auto'`` the number of\n\
231 arguments will automatically be extracted from the signature.\n\
232 With ``args = -1``, any number of arguments are accepted.\n\
233 : param group: The name of the group under which this expression function will\n\
235 : param handlesnull: Set this to True if your function has custom handling for NULL values.\n\
236 If False, the result will always be NULL as soon as any parameter is NULL.\n\
237 Defaults to False.\n\
238 : param usesgeometry : Set this to True if your function requires access to\n\
239 feature.geometry(). Defaults to False.\n\
240 : param referenced_columns: An array of attribute names that are required to run\n\
241 this function. Defaults to [QgsFeatureRequest.ALL_ATTRIBUTES].\n\
249 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/splitter" ), splitter->saveState() );
250 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/editorsplitter" ), editorSplit->saveState() );
251 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/functionsplitter" ), functionsplit->saveState() );
252 delete mExpressionTreeMenuProvider;
260 mExpressionTreeView->loadRecent( recentCollection );
263 mExpressionTreeView->loadUserExpressions();
268 init( context, recentCollection, flags );
274 init( context, recentCollection, flags );
275 mExpressionTreeView->loadFieldNames( fields );
282 mExpressionTreeView->setLayer( mLayer );
283 mExpressionPreviewWidget->setLayer( mLayer );
291 txtExpressionString->setFields( mLayer->
fields() );
300 void QgsExpressionBuilderWidget::expressionTreeItemChanged(
QgsExpressionItem *item )
302 txtSearchEditValues->clear();
310 mValuesModel->clear();
313 cbxValuesInUse->setChecked(
false );
315 mValueGroupBox->setVisible( isField );
317 mShowHelpButton->setText( isField ? tr(
"Show Values" ) : tr(
"Show Help" ) );
320 QString help = loadFunctionHelp( item );
321 txtHelpText->setText( help );
323 bool isUserExpression = item->parent() && item->parent()->text() == mUserExpressionsGroupName;
325 btnRemoveExpression->setEnabled( isUserExpression );
326 btnEditExpression->setEnabled( isUserExpression );
329 void QgsExpressionBuilderWidget::btnRun_pressed()
331 if ( !cmbFileNames->currentItem() )
334 QString file = cmbFileNames->currentItem()->text();
336 runPythonCode( txtPython->text() );
339 void QgsExpressionBuilderWidget::runPythonCode(
const QString &code )
343 QString pythontext = code;
346 mExpressionTreeView->refresh();
351 QDir myDir( mFunctionsPath );
352 if ( !myDir.exists() )
354 myDir.mkpath( mFunctionsPath );
357 if ( !fileName.endsWith( QLatin1String(
".py" ) ) )
359 fileName.append(
".py" );
362 fileName = mFunctionsPath + QDir::separator() + fileName;
363 QFile myFile( fileName );
364 if ( myFile.open( QIODevice::WriteOnly | QFile::Truncate ) )
366 QTextStream myFileStream( &myFile );
367 myFileStream << txtPython->text() << endl;
374 mFunctionsPath = path;
376 dir.setNameFilters( QStringList() << QStringLiteral(
"*.py" ) );
377 QStringList files = dir.entryList( QDir::Files );
378 cmbFileNames->clear();
379 const auto constFiles = files;
380 for (
const QString &name : constFiles )
382 QFileInfo info( mFunctionsPath + QDir::separator() + name );
383 if ( info.baseName() == QLatin1String(
"__init__" ) )
continue;
384 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"console/iconTabEditorConsole.svg" ) ), info.baseName() );
385 cmbFileNames->addItem( item );
387 if ( !cmbFileNames->currentItem() )
389 cmbFileNames->setCurrentRow( 0 );
392 if ( cmbFileNames->count() == 0 )
396 txtPython->setText( QStringLiteral(
"'''\n#Sample custom function file\n "
397 "(uncomment to use and customize or Add button to create a new file) \n%1 \n '''" ).arg( txtPython->text() ) );
404 QList<QListWidgetItem *> items = cmbFileNames->findItems( fileName, Qt::MatchExactly );
405 if ( !items.isEmpty() )
408 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"console/iconTabEditorConsole.svg" ) ), fileName );
409 cmbFileNames->insertItem( 0, item );
410 cmbFileNames->setCurrentRow( 0 );
414 txtPython->setText( templatetxt );
418 void QgsExpressionBuilderWidget::btnNewFile_pressed()
421 QString text = QInputDialog::getText(
this, tr(
"New File" ),
422 tr(
"New file name:" ), QLineEdit::Normal,
424 if ( ok && !text.isEmpty() )
430 void QgsExpressionBuilderWidget::btnRemoveFile_pressed()
432 if ( QMessageBox::question(
this, tr(
"Remove File" ),
433 tr(
"Are you sure you want to remove current functions file?" ),
434 QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No )
437 int currentRow = cmbFileNames->currentRow();
438 QString fileName = cmbFileNames->currentItem()->text();
439 if ( QFile::remove( mFunctionsPath + QDir::separator() + fileName.append(
".py" ) ) )
442 QListWidgetItem *itemToRemove =
whileBlocking( cmbFileNames )->takeItem( currentRow );
446 if ( cmbFileNames->count() > 0 )
448 cmbFileNames->setCurrentRow( currentRow > 0 ? currentRow - 1 : 0 );
449 loadCodeFromFile( mFunctionsPath + QDir::separator() + cmbFileNames->currentItem()->text() );
453 btnRemoveFile->setEnabled(
false );
459 QMessageBox::warning(
this, tr(
"Remove file" ), tr(
"Failed to remove function file '%1'." ).arg( fileName ) );
463 void QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem )
467 QString filename = lastitem->text();
470 QString path = mFunctionsPath + QDir::separator() + item->text();
476 if ( !path.endsWith( QLatin1String(
".py" ) ) )
477 path.append(
".py" );
479 txtPython->loadScript( path );
484 txtPython->setText( code );
487 void QgsExpressionBuilderWidget::insertExpressionText(
const QString &text )
490 txtExpressionString->insertText( text );
491 txtExpressionString->setFocus();
496 Q_UNUSED( fieldValues )
500 void QgsExpressionBuilderWidget::fillFieldValues(
const QString &fieldName,
int countLimit,
bool forceUsedValues )
512 if ( fieldIndex < 0 )
519 if ( cbxValuesInUse->isVisible() && !cbxValuesInUse->isChecked() && !forceUsedValues )
523 values =
formatter->availableValues( setup.
config(), countLimit, fieldFormatterContext );
527 values = qgis::setToList( mLayer->
uniqueValues( fieldIndex, countLimit ) );
529 std::sort( values.begin(), values.end() );
531 mValuesModel->clear();
532 for (
const QVariant &value : qgis::as_const( values ) )
535 if ( value.isNull() )
536 strValue = QStringLiteral(
"NULL" );
537 else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong )
538 strValue = value.toString();
540 strValue =
'\'' + value.toString().replace(
'\'', QLatin1String(
"''" ) ) +
'\'';
542 QString representedValue =
formatter->representValue( mLayer, fieldIndex, setup.
config(), QVariant(), value );
543 if ( representedValue != value.toString() )
544 representedValue = representedValue + QStringLiteral(
" [" ) + strValue +
']';
546 QStandardItem *item =
new QStandardItem( representedValue );
547 item->setData( strValue );
548 mValuesModel->appendRow( item );
559 return QStringLiteral(
"<head><style>" ) + helpStylesheet() + QStringLiteral(
"</style></head><body>" ) + helpContents + QStringLiteral(
"</body>" );
567 return mExpressionValid;
572 mExpressionTreeView->saveToRecent(
expressionText(), collection );
577 mExpressionTreeView->loadRecent( collection );
582 return mExpressionTreeView;
588 mExpressionTreeView->loadUserExpressions();
593 mExpressionTreeView->saveToUserExpressions( label, expression, helpText );
598 mExpressionTreeView->removeFromUserExpressions( label );
604 mExpressionPreviewWidget->setGeomCalculator( da );
609 return txtExpressionString->text();
614 txtExpressionString->setText( expression );
619 return lblExpected->text();
624 lblExpected->setText( expected );
625 mExpectedOutputFrame->setVisible( !expected.isNull() );
630 mExpressionContext = context;
631 txtExpressionString->setExpressionContext( mExpressionContext );
632 mExpressionTreeView->setExpressionContext( context );
633 mExpressionPreviewWidget->setExpressionContext( context );
636 void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
640 btnClearEditor->setEnabled( ! txtExpressionString->text().isEmpty() );
641 btnSaveExpression->setEnabled(
false );
643 mExpressionPreviewWidget->setExpressionText( text );
648 return mExpressionPreviewWidget->parserError();
653 return mExpressionPreviewWidget->evalError();
659 return mExpressionTreeView->model();
671 mExpressionTreeView->setProject(
project );
676 QWidget::showEvent( e );
677 txtExpressionString->setFocus();
680 void QgsExpressionBuilderWidget::createErrorMarkers( QList<QgsExpression::ParserError> errors )
685 int errorFirstLine = error.firstLine - 1 ;
686 int errorFirstColumn = error.firstColumn - 1;
687 int errorLastColumn = error.lastColumn - 1;
688 int errorLastLine = error.lastLine - 1;
694 errorFirstLine = errorLastLine;
695 errorFirstColumn = errorLastColumn - 1;
697 txtExpressionString->fillIndicatorRange( errorFirstLine,
700 errorLastColumn, error.errorType );
704 void QgsExpressionBuilderWidget::createMarkers(
const QgsExpressionNode *inNode )
708 case QgsExpressionNode::NodeType::ntFunction:
711 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
712 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->
fnIndex() );
715 int start_pos = txtExpressionString->positionFromLineIndex( inNode->
parserFirstLine - 1, start );
716 txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
719 const QList< QgsExpressionNode * > nodeList = node->
args()->
list();
727 case QgsExpressionNode::NodeType::ntLiteral:
731 case QgsExpressionNode::NodeType::ntUnaryOperator:
734 createMarkers( node->
operand() );
737 case QgsExpressionNode::NodeType::ntBinaryOperator:
740 createMarkers( node->
opLeft() );
741 createMarkers( node->
opRight() );
744 case QgsExpressionNode::NodeType::ntColumnRef:
748 case QgsExpressionNode::NodeType::ntInOperator:
753 const QList< QgsExpressionNode * > nodeList = node->
list()->
list();
761 case QgsExpressionNode::NodeType::ntCondition:
766 createMarkers( cond->whenExp() );
767 createMarkers( cond->thenExp() );
771 createMarkers( node->
elseExp() );
775 case QgsExpressionNode::NodeType::ntIndexOperator:
782 void QgsExpressionBuilderWidget::clearFunctionMarkers()
784 int lastLine = txtExpressionString->lines() - 1;
785 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
788 void QgsExpressionBuilderWidget::clearErrors()
790 int lastLine = txtExpressionString->lines() - 1;
799 void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
801 mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
802 mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
805 void QgsExpressionBuilderWidget::mValuesListView_doubleClicked(
const QModelIndex &index )
808 txtExpressionString->insertText(
' ' + index.data( Qt::UserRole + 1 ).toString() +
' ' );
809 txtExpressionString->setFocus();
812 void QgsExpressionBuilderWidget::operatorButtonClicked()
814 QPushButton *button = qobject_cast<QPushButton *>( sender() );
817 txtExpressionString->insertText(
' ' + button->text() +
' ' );
818 txtExpressionString->setFocus();
826 if ( !mLayer || !item )
829 mValueGroupBox->show();
830 fillFieldValues( item->text(), 10 );
838 if ( !mLayer || !item )
841 mValueGroupBox->show();
842 fillFieldValues( item->text(), -1 );
850 if ( !mLayer || !item )
853 mValueGroupBox->show();
854 fillFieldValues( item->text(), 10,
true );
862 if ( !mLayer || !item )
865 mValueGroupBox->show();
866 fillFieldValues( item->text(), -1,
true );
869 void QgsExpressionBuilderWidget::txtPython_textChanged()
871 lblAutoSave->setText( tr(
"Saving…" ) );
881 if ( tabWidget->currentIndex() != 1 )
884 QListWidgetItem *item = cmbFileNames->currentItem();
888 QString file = item->text();
890 lblAutoSave->setText( QStringLiteral(
"Saved" ) );
891 QGraphicsOpacityEffect *effect =
new QGraphicsOpacityEffect();
892 lblAutoSave->setGraphicsEffect( effect );
893 QPropertyAnimation *anim =
new QPropertyAnimation( effect,
"opacity" );
894 anim->setDuration( 2000 );
895 anim->setStartValue( 1.0 );
896 anim->setEndValue( 0.0 );
897 anim->setEasingCurve( QEasingCurve::OutQuad );
898 anim->start( QAbstractAnimation::DeleteWhenStopped );
905 if ( dlg.exec() == QDialog::DialogCode::Accepted )
907 mExpressionTreeView->saveToUserExpressions( dlg.label(), dlg.expression(), dlg.helpText() );
921 ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) )
925 QString helpText = settings.
value( QStringLiteral(
"user/%1/helpText" ).arg( item->text() ),
"", QgsSettings::Section::Expressions ).toString();
928 if ( dlg.exec() == QDialog::DialogCode::Accepted )
931 if ( dlg.label() != item->text() )
933 mExpressionTreeView->removeFromUserExpressions( item->text() );
936 mExpressionTreeView->saveToUserExpressions( dlg.label(), dlg.expression(), dlg.helpText() );
951 ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) )
954 if ( QMessageBox::Yes == QMessageBox::question(
this, tr(
"Remove Stored Expression" ),
955 tr(
"Do you really want to remove stored expressions '%1'?" ).arg( item->text() ),
956 QMessageBox::Yes | QMessageBox::No ) )
958 mExpressionTreeView->removeFromUserExpressions( item->text() );
963 void QgsExpressionBuilderWidget::exportUserExpressions_pressed()
966 QString lastSaveDir = settings.
value( QStringLiteral(
"lastExportExpressionsDir" ), QDir::homePath(),
QgsSettings::App ).toString();
967 QString saveFileName = QFileDialog::getSaveFileName(
969 tr(
"Export User Expressions" ),
971 tr(
"User expressions" ) +
" (*.json)" );
973 if ( saveFileName.isEmpty() )
976 QFileInfo saveFileInfo( saveFileName );
978 if ( saveFileInfo.suffix().isEmpty() )
980 QString saveFileNameWithSuffix = saveFileName.append(
".json" );
981 saveFileInfo = QFileInfo( saveFileNameWithSuffix );
986 QJsonDocument exportJson = mExpressionTreeView->exportUserExpressions();
987 QFile jsonFile( saveFileName );
989 if ( !jsonFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
990 QMessageBox::warning(
this, tr(
"Export user expressions" ), tr(
"Error while creating the expressions file." ) );
992 if ( ! jsonFile.write( exportJson.toJson() ) )
993 QMessageBox::warning(
this, tr(
"Export user expressions" ), tr(
"Error while creating the expressions file." ) );
998 void QgsExpressionBuilderWidget::importUserExpressions_pressed()
1001 QString lastImportDir = settings.
value( QStringLiteral(
"lastImportExpressionsDir" ), QDir::homePath(),
QgsSettings::App ).toString();
1002 QString loadFileName = QFileDialog::getOpenFileName(
1004 tr(
"Import User Expressions" ),
1006 tr(
"User expressions" ) +
" (*.json)" );
1008 if ( loadFileName.isEmpty() )
1011 QFileInfo loadFileInfo( loadFileName );
1015 QFile jsonFile( loadFileName );
1017 if ( !jsonFile.open( QFile::ReadOnly ) )
1018 QMessageBox::warning(
this, tr(
"Import User Expressions" ), tr(
"Error while reading the expressions file." ) );
1020 QTextStream jsonStream( &jsonFile );
1021 QString jsonString = jsonFile.readAll();
1024 QJsonDocument importJson = QJsonDocument::fromJson( jsonString.toUtf8() );
1026 if ( importJson.isNull() )
1028 QMessageBox::warning(
this, tr(
"Import User Expressions" ), tr(
"Error while reading the expressions file." ) );
1032 mExpressionTreeView->loadExpressionsFromJson( importJson );
1038 return mExpressionTreeView->findExpressions( label );
1041 void QgsExpressionBuilderWidget::indicatorClicked(
int line,
int index, Qt::KeyboardModifiers state )
1043 if ( state & Qt::ControlModifier )
1045 int position = txtExpressionString->positionFromLineIndex( line, index );
1046 long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID,
static_cast<long int>( position ) );
1048 QString help = getFunctionHelp( func );
1049 txtHelpText->setText( help );
1053 void QgsExpressionBuilderWidget::onExpressionParsed(
bool state )
1057 mExpressionValid = state;
1060 createMarkers( mExpressionPreviewWidget->rootNode() );
1064 createErrorMarkers( mExpressionPreviewWidget->parserErrors() );
1068 QString QgsExpressionBuilderWidget::helpStylesheet()
const
1074 style +=
" .functionname {color: #0a6099; font-weight: bold;} "
1075 " .argument {font-family: monospace; color: #bf0c0c; font-style: italic; } "
1076 " td.argument { padding-right: 10px; }";
1081 QString QgsExpressionBuilderWidget::loadFunctionHelp(
QgsExpressionItem *expressionItem )
1083 if ( !expressionItem )
1086 QString helpContents = expressionItem->
getHelpText();
1089 if ( helpContents.isEmpty() )
1091 QString name = expressionItem->data( Qt::UserRole ).toString();
1099 return "<head><style>" + helpStylesheet() +
"</style></head><body>" + helpContents +
"</body>";
1106 QMenu *QgsExpressionBuilderWidget::ExpressionTreeMenuProvider::createContextMenu(
QgsExpressionItem *item )
1108 QMenu *menu =
nullptr;
1112 menu =
new QMenu( mExpressionBuilderWidget );
1118 menu->addAction( tr(
"Load First 10 Unique Used Values" ), mExpressionBuilderWidget, SLOT( loadSampleUsedValues() ) );