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::clicked,
this, &QgsExpressionBuilderWidget::btnNewFile_pressed );
81 connect( btnRemoveFile, &QPushButton::clicked,
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, &QToolButton::clicked,
this, &QgsExpressionBuilderWidget::importUserExpressions_pressed );
91 connect( btnExportExpressions, &QToolButton::clicked,
this, &QgsExpressionBuilderWidget::exportUserExpressions_pressed );
92 connect( btnClearEditor, &QToolButton::clicked, 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::clicked,
this, &QgsExpressionBuilderWidget::operatorButtonClicked );
132 txtSearchEdit->setShowSearchIcon(
true );
133 txtSearchEdit->setPlaceholderText( tr(
"Search…" ) );
135 mValuesModel = std::make_unique<QStandardItemModel>();
136 mProxyValues = std::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 );
167 btnRemoveFile->setEnabled( cmbFileNames->count() > 0 );
174 txtExpressionString->setWrapMode( QsciScintilla::WrapWord );
175 lblAutoSave->clear();
182 #if defined(QSCINTILLA_VERSION) && QSCINTILLA_VERSION >= 0x20a00
189 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::red ), -1 );
190 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::red ), -1 );
191 txtExpressionString->setIndicatorOutlineColor( QColor( Qt::red ), -1 );
194 txtExpressionString->indicatorDefine( QgsCodeEditor::HiddenIndicator, FUNCTION_MARKER_ID );
195 txtExpressionString->setIndicatorForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
196 txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
197 txtExpressionString->setIndicatorHoverStyle( QgsCodeEditor::DotsIndicator, FUNCTION_MARKER_ID );
199 connect( txtExpressionString, &QgsCodeEditorExpression::indicatorClicked,
this, &QgsExpressionBuilderWidget::indicatorClicked );
200 txtExpressionString->setAutoCompletionCaseSensitivity(
false );
201 txtExpressionString->setAutoCompletionSource( QsciScintilla::AcsAPIs );
202 txtExpressionString->setCallTipsVisible( 0 );
205 mFunctionBuilderHelp->setLineNumbersVisible(
false );
206 mFunctionBuilderHelp->setFoldingVisible(
false );
207 mFunctionBuilderHelp->setEdgeMode( QsciScintilla::EdgeNone );
208 mFunctionBuilderHelp->setEdgeColumn( 0 );
209 mFunctionBuilderHelp->setReadOnly(
true );
210 mFunctionBuilderHelp->setText( tr(
"\"\"\"Define a new function using the @qgsfunction decorator.\n\
212 The function accepts the following parameters\n\
214 : param [any]: Define any parameters you want to pass to your function before\n\
215 the following arguments.\n\
216 : param feature: The current feature\n\
217 : param parent: The QgsExpression object\n\
218 : param context: If there is an argument called ``context`` found at the last\n\
219 position, this variable will contain a ``QgsExpressionContext``\n\
220 object, that gives access to various additional information like\n\
221 expression variables. E.g. ``context.variable( 'layer_id' )``\n\
222 : returns: The result of the expression.\n\
226 The @qgsfunction decorator accepts the following arguments:\n\
229 : param args: Defines the number of arguments. With ``args = 'auto'`` the number of\n\
230 arguments will automatically be extracted from the signature.\n\
231 With ``args = -1``, any number of arguments are accepted.\n\
232 : param group: The name of the group under which this expression function will\n\
234 : param handlesnull: Set this to True if your function has custom handling for NULL values.\n\
235 If False, the result will always be NULL as soon as any parameter is NULL.\n\
236 Defaults to False.\n\
237 : param usesgeometry : Set this to True if your function requires access to\n\
238 feature.geometry(). Defaults to False.\n\
239 : param referenced_columns: An array of attribute names that are required to run\n\
240 this function. Defaults to [QgsFeatureRequest.ALL_ATTRIBUTES].\n\
248 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/splitter" ), splitter->saveState() );
249 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/editorsplitter" ), editorSplit->saveState() );
250 settings.
setValue( QStringLiteral(
"Windows/QgsExpressionBuilderWidget/functionsplitter" ), functionsplit->saveState() );
251 delete mExpressionTreeMenuProvider;
259 mExpressionTreeView->loadRecent( recentCollection );
262 mExpressionTreeView->loadUserExpressions();
267 init( context, recentCollection, flags );
273 init( context, recentCollection, flags );
274 mExpressionTreeView->loadFieldNames( fields );
281 mExpressionTreeView->setLayer( mLayer );
282 mExpressionPreviewWidget->setLayer( mLayer );
289 expressionContextUpdated();
290 txtExpressionString->setFields( mLayer->
fields() );
294 void QgsExpressionBuilderWidget::expressionContextUpdated()
296 txtExpressionString->setExpressionContext( mExpressionContext );
297 mExpressionTreeView->setExpressionContext( mExpressionContext );
298 mExpressionPreviewWidget->setExpressionContext( mExpressionContext );
306 void QgsExpressionBuilderWidget::expressionTreeItemChanged(
QgsExpressionItem *item )
308 txtSearchEditValues->clear();
316 mValuesModel->clear();
319 cbxValuesInUse->setChecked(
false );
321 mValueGroupBox->setVisible( isField );
323 mShowHelpButton->setText( isField ? tr(
"Show Values" ) : tr(
"Show Help" ) );
326 QString help = loadFunctionHelp( item );
327 txtHelpText->setText( help );
329 bool isUserExpression = item->parent() && item->parent()->text() == mUserExpressionsGroupName;
331 btnRemoveExpression->setEnabled( isUserExpression );
332 btnEditExpression->setEnabled( isUserExpression );
335 void QgsExpressionBuilderWidget::btnRun_pressed()
337 if ( !cmbFileNames->currentItem() )
340 QString file = cmbFileNames->currentItem()->text();
342 runPythonCode( txtPython->text() );
345 void QgsExpressionBuilderWidget::runPythonCode(
const QString &code )
349 QString pythontext = code;
352 mExpressionTreeView->refresh();
372 QDir myDir( mFunctionsPath );
373 if ( !myDir.exists() )
375 myDir.mkpath( mFunctionsPath );
378 if ( !fileName.endsWith( QLatin1String(
".py" ) ) )
380 fileName.append(
".py" );
383 fileName = mFunctionsPath + QDir::separator() + fileName;
384 QFile myFile( fileName );
385 if ( myFile.open( QIODevice::WriteOnly | QFile::Truncate ) )
387 QTextStream myFileStream( &myFile );
388 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
389 myFileStream << txtPython->text() << endl;
391 myFileStream << txtPython->text() << Qt::endl;
399 mFunctionsPath = path;
401 dir.setNameFilters( QStringList() << QStringLiteral(
"*.py" ) );
402 QStringList files = dir.entryList( QDir::Files );
403 cmbFileNames->clear();
404 const auto constFiles = files;
405 for (
const QString &name : constFiles )
407 QFileInfo info( mFunctionsPath + QDir::separator() + name );
408 if ( info.baseName() == QLatin1String(
"__init__" ) )
continue;
409 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"console/iconTabEditorConsole.svg" ) ), info.baseName() );
410 cmbFileNames->addItem( item );
412 if ( !cmbFileNames->currentItem() )
414 cmbFileNames->setCurrentRow( 0 );
417 if ( cmbFileNames->count() == 0 )
421 txtPython->setText( QStringLiteral(
"'''\n#Sample custom function file\n"
422 "#(uncomment to use and customize or Add button to create a new file) \n%1 \n '''" ).arg( txtPython->text() ) );
429 QList<QListWidgetItem *> items = cmbFileNames->findItems( fileName, Qt::MatchExactly );
430 if ( !items.isEmpty() )
433 QListWidgetItem *item =
new QListWidgetItem(
QgsApplication::getThemeIcon( QStringLiteral(
"console/iconTabEditorConsole.svg" ) ), fileName );
434 cmbFileNames->insertItem( 0, item );
435 cmbFileNames->setCurrentRow( 0 );
439 txtPython->setText( templatetxt );
443 void QgsExpressionBuilderWidget::btnNewFile_pressed()
446 QString text = QInputDialog::getText(
this, tr(
"New File" ),
447 tr(
"New file name:" ), QLineEdit::Normal,
449 if ( ok && !text.isEmpty() )
455 void QgsExpressionBuilderWidget::btnRemoveFile_pressed()
457 if ( QMessageBox::question(
this, tr(
"Remove File" ),
458 tr(
"Are you sure you want to remove current functions file?" ),
459 QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::No )
462 int currentRow = cmbFileNames->currentRow();
463 QString fileName = cmbFileNames->currentItem()->text();
464 if ( QFile::remove( mFunctionsPath + QDir::separator() + fileName.append(
".py" ) ) )
467 QListWidgetItem *itemToRemove =
whileBlocking( cmbFileNames )->takeItem( currentRow );
471 if ( cmbFileNames->count() > 0 )
473 cmbFileNames->setCurrentRow( currentRow > 0 ? currentRow - 1 : 0 );
474 loadCodeFromFile( mFunctionsPath + QDir::separator() + cmbFileNames->currentItem()->text() );
478 btnRemoveFile->setEnabled(
false );
484 QMessageBox::warning(
this, tr(
"Remove file" ), tr(
"Failed to remove function file '%1'." ).arg( fileName ) );
488 void QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged( QListWidgetItem *item, QListWidgetItem *lastitem )
492 QString filename = lastitem->text();
495 QString path = mFunctionsPath + QDir::separator() + item->text();
501 if ( !path.endsWith( QLatin1String(
".py" ) ) )
502 path.append(
".py" );
504 txtPython->loadScript( path );
509 txtPython->setText( code );
512 void QgsExpressionBuilderWidget::insertExpressionText(
const QString &text )
515 txtExpressionString->insertText( text );
516 txtExpressionString->setFocus();
521 Q_UNUSED( fieldValues )
525 void QgsExpressionBuilderWidget::fillFieldValues(
const QString &fieldName,
QgsVectorLayer *layer,
int countLimit,
bool forceUsedValues )
537 if ( fieldIndex < 0 )
544 if ( cbxValuesInUse->isVisible() && !cbxValuesInUse->isChecked() && !forceUsedValues )
548 values =
formatter->availableValues( setup.
config(), countLimit, fieldFormatterContext );
554 std::sort( values.begin(), values.end() );
556 mValuesModel->clear();
557 for (
const QVariant &value : std::as_const( values ) )
560 bool forceRepresentedValue =
false;
561 if ( value.isNull() )
562 strValue = QStringLiteral(
"NULL" );
563 else if ( value.type() == QVariant::Int || value.type() == QVariant::Double || value.type() == QVariant::LongLong )
564 strValue = value.toString();
565 else if ( value.type() == QVariant::StringList )
568 const QStringList strList = value.toStringList();
569 for ( QString
str : strList )
571 if ( !result.isEmpty() )
572 result.append( QStringLiteral(
", " ) );
574 result.append(
'\'' +
str.replace(
'\'', QLatin1String(
"''" ) ) +
'\'' );
576 strValue = QStringLiteral(
"array(%1)" ).arg( result );
577 forceRepresentedValue =
true;
579 else if ( value.type() == QVariant::List )
582 const QList list = value.toList();
583 for (
const QVariant &item : list )
585 if ( !result.isEmpty() )
586 result.append( QStringLiteral(
", " ) );
588 result.append( item.toString() );
590 strValue = QStringLiteral(
"array(%1)" ).arg( result );
591 forceRepresentedValue =
true;
594 strValue =
'\'' + value.toString().replace(
'\'', QLatin1String(
"''" ) ) +
'\'';
596 QString representedValue =
formatter->representValue(
layer, fieldIndex, setup.
config(), QVariant(), value );
597 if ( forceRepresentedValue || representedValue != value.toString() )
598 representedValue = representedValue + QStringLiteral(
" [" ) + strValue +
']';
600 QStandardItem *item =
new QStandardItem( representedValue );
601 item->setData( strValue );
602 mValuesModel->appendRow( item );
613 return QStringLiteral(
"<head><style>" ) + helpStylesheet() + QStringLiteral(
"</style></head><body>" ) + helpContents + QStringLiteral(
"</body>" );
621 return mExpressionValid;
626 mExpressionTreeView->saveToRecent(
expressionText(), collection );
631 mExpressionTreeView->loadRecent( collection );
636 return mExpressionTreeView;
642 mExpressionTreeView->loadUserExpressions();
647 mExpressionTreeView->saveToUserExpressions( label, expression, helpText );
652 mExpressionTreeView->removeFromUserExpressions( label );
658 mExpressionPreviewWidget->setGeomCalculator( da );
663 return txtExpressionString->text();
668 txtExpressionString->setText( expression );
673 return lblExpected->text();
678 lblExpected->setText( expected );
679 mExpectedOutputFrame->setVisible( !expected.isNull() );
684 mExpressionContext = context;
685 expressionContextUpdated();
688 void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
692 btnClearEditor->setEnabled( ! txtExpressionString->text().isEmpty() );
693 btnSaveExpression->setEnabled(
false );
695 mExpressionPreviewWidget->setExpressionText( text );
700 return mExpressionPreviewWidget->parserError();
705 mExpressionPreviewWidget->setVisible( isVisible );
710 return mExpressionPreviewWidget->evalError();
716 return mExpressionTreeView->model();
728 mExpressionTreeView->setProject(
project );
733 QWidget::showEvent( e );
734 txtExpressionString->setFocus();
737 void QgsExpressionBuilderWidget::createErrorMarkers(
const QList<QgsExpression::ParserError> &errors )
742 int errorFirstLine = error.firstLine - 1 ;
743 int errorFirstColumn = error.firstColumn - 1;
744 int errorLastColumn = error.lastColumn - 1;
745 int errorLastLine = error.lastLine - 1;
751 errorFirstLine = errorLastLine;
752 errorFirstColumn = errorLastColumn - 1;
754 txtExpressionString->fillIndicatorRange( errorFirstLine,
757 errorLastColumn, error.errorType );
761 void QgsExpressionBuilderWidget::createMarkers(
const QgsExpressionNode *inNode )
765 case QgsExpressionNode::NodeType::ntFunction:
768 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
769 txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->
fnIndex() );
772 int start_pos = txtExpressionString->positionFromLineIndex( inNode->
parserFirstLine - 1, start );
773 txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
776 const QList< QgsExpressionNode * > nodeList = node->
args()->
list();
784 case QgsExpressionNode::NodeType::ntLiteral:
788 case QgsExpressionNode::NodeType::ntUnaryOperator:
791 createMarkers( node->
operand() );
794 case QgsExpressionNode::NodeType::ntBetweenOperator:
801 case QgsExpressionNode::NodeType::ntBinaryOperator:
804 createMarkers( node->
opLeft() );
805 createMarkers( node->
opRight() );
808 case QgsExpressionNode::NodeType::ntColumnRef:
812 case QgsExpressionNode::NodeType::ntInOperator:
817 const QList< QgsExpressionNode * > nodeList = node->
list()->
list();
825 case QgsExpressionNode::NodeType::ntCondition:
828 const QList<QgsExpressionNodeCondition::WhenThen *> conditions = node->
conditions();
831 createMarkers( cond->whenExp() );
832 createMarkers( cond->thenExp() );
836 createMarkers( node->
elseExp() );
840 case QgsExpressionNode::NodeType::ntIndexOperator:
847 void QgsExpressionBuilderWidget::clearFunctionMarkers()
849 int lastLine = txtExpressionString->lines() - 1;
850 txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
853 void QgsExpressionBuilderWidget::clearErrors()
855 int lastLine = txtExpressionString->lines() - 1;
864 void QgsExpressionBuilderWidget::txtSearchEditValues_textChanged()
866 mProxyValues->setFilterCaseSensitivity( Qt::CaseInsensitive );
867 mProxyValues->setFilterWildcard( txtSearchEditValues->text() );
870 void QgsExpressionBuilderWidget::mValuesListView_doubleClicked(
const QModelIndex &index )
873 txtExpressionString->insertText(
' ' + index.data( Qt::UserRole + 1 ).toString() +
' ' );
874 txtExpressionString->setFocus();
877 void QgsExpressionBuilderWidget::operatorButtonClicked()
879 QPushButton *button = qobject_cast<QPushButton *>( sender() );
882 txtExpressionString->insertText(
' ' + button->text() +
' ' );
883 txtExpressionString->setFocus();
902 mValueGroupBox->show();
922 mValueGroupBox->show();
942 mValueGroupBox->show();
962 mValueGroupBox->show();
966 void QgsExpressionBuilderWidget::txtPython_textChanged()
968 lblAutoSave->setText( tr(
"Saving…" ) );
978 if ( tabWidget->currentIndex() != 1 )
981 QListWidgetItem *item = cmbFileNames->currentItem();
985 QString file = item->text();
987 lblAutoSave->setText( QStringLiteral(
"Saved" ) );
988 QGraphicsOpacityEffect *effect =
new QGraphicsOpacityEffect();
989 lblAutoSave->setGraphicsEffect( effect );
990 QPropertyAnimation *anim =
new QPropertyAnimation( effect,
"opacity" );
991 anim->setDuration( 2000 );
992 anim->setStartValue( 1.0 );
993 anim->setEndValue( 0.0 );
994 anim->setEasingCurve( QEasingCurve::OutQuad );
995 anim->start( QAbstractAnimation::DeleteWhenStopped );
1002 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1004 mExpressionTreeView->saveToUserExpressions( dlg.label().simplified(), dlg.expression(), dlg.helpText() );
1018 ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) )
1022 QString helpText = settings.
value( QStringLiteral(
"user/%1/helpText" ).arg( item->text() ),
"", QgsSettings::Section::Expressions ).toString();
1025 if ( dlg.exec() == QDialog::DialogCode::Accepted )
1028 if ( dlg.isLabelModified() )
1030 mExpressionTreeView->removeFromUserExpressions( item->text() );
1033 mExpressionTreeView->saveToUserExpressions( dlg.label().simplified(), dlg.expression(), dlg.helpText() );
1048 ( item->parent() && item->parent()->text() != mUserExpressionsGroupName ) )
1051 if ( QMessageBox::Yes == QMessageBox::question(
this, tr(
"Remove Stored Expression" ),
1052 tr(
"Do you really want to remove stored expressions '%1'?" ).arg( item->text() ),
1053 QMessageBox::Yes | QMessageBox::No ) )
1055 mExpressionTreeView->removeFromUserExpressions( item->text() );
1060 void QgsExpressionBuilderWidget::exportUserExpressions_pressed()
1063 QString lastSaveDir = settings.
value( QStringLiteral(
"lastExportExpressionsDir" ), QDir::homePath(),
QgsSettings::App ).toString();
1064 QString saveFileName = QFileDialog::getSaveFileName(
1066 tr(
"Export User Expressions" ),
1068 tr(
"User expressions" ) +
" (*.json)" );
1070 if ( saveFileName.isEmpty() )
1073 QFileInfo saveFileInfo( saveFileName );
1075 if ( saveFileInfo.suffix().isEmpty() )
1077 QString saveFileNameWithSuffix = saveFileName.append(
".json" );
1078 saveFileInfo = QFileInfo( saveFileNameWithSuffix );
1083 QJsonDocument exportJson = mExpressionTreeView->exportUserExpressions();
1084 QFile jsonFile( saveFileName );
1086 if ( !jsonFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
1087 QMessageBox::warning(
this, tr(
"Export user expressions" ), tr(
"Error while creating the expressions file." ) );
1089 if ( ! jsonFile.write( exportJson.toJson() ) )
1090 QMessageBox::warning(
this, tr(
"Export user expressions" ), tr(
"Error while creating the expressions file." ) );
1095 void QgsExpressionBuilderWidget::importUserExpressions_pressed()
1098 QString lastImportDir = settings.
value( QStringLiteral(
"lastImportExpressionsDir" ), QDir::homePath(),
QgsSettings::App ).toString();
1099 QString loadFileName = QFileDialog::getOpenFileName(
1101 tr(
"Import User Expressions" ),
1103 tr(
"User expressions" ) +
" (*.json)" );
1105 if ( loadFileName.isEmpty() )
1108 QFileInfo loadFileInfo( loadFileName );
1112 QFile jsonFile( loadFileName );
1114 if ( !jsonFile.open( QFile::ReadOnly ) )
1115 QMessageBox::warning(
this, tr(
"Import User Expressions" ), tr(
"Error while reading the expressions file." ) );
1117 QTextStream jsonStream( &jsonFile );
1118 QString jsonString = jsonFile.readAll();
1121 QJsonDocument importJson = QJsonDocument::fromJson( jsonString.toUtf8() );
1123 if ( importJson.isNull() )
1125 QMessageBox::warning(
this, tr(
"Import User Expressions" ), tr(
"Error while reading the expressions file." ) );
1129 mExpressionTreeView->loadExpressionsFromJson( importJson );
1135 return mExpressionTreeView->findExpressions( label );
1138 void QgsExpressionBuilderWidget::indicatorClicked(
int line,
int index, Qt::KeyboardModifiers state )
1140 if ( state & Qt::ControlModifier )
1142 int position = txtExpressionString->positionFromLineIndex( line, index );
1143 long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID,
static_cast<long int>( position ) );
1145 QString help = getFunctionHelp( func );
1146 txtHelpText->setText( help );
1150 void QgsExpressionBuilderWidget::onExpressionParsed(
bool state )
1154 mExpressionValid = state;
1157 createMarkers( mExpressionPreviewWidget->rootNode() );
1161 createErrorMarkers( mExpressionPreviewWidget->parserErrors() );
1165 QString QgsExpressionBuilderWidget::helpStylesheet()
const
1171 style +=
" .functionname {color: #0a6099; font-weight: bold;} "
1172 " .argument {font-family: monospace; color: #bf0c0c; font-style: italic; } "
1173 " td.argument { padding-right: 10px; }";
1178 QString QgsExpressionBuilderWidget::loadFunctionHelp(
QgsExpressionItem *expressionItem )
1180 if ( !expressionItem )
1183 QString helpContents = expressionItem->
getHelpText();
1186 if ( helpContents.isEmpty() )
1188 QString name = expressionItem->data( Qt::UserRole ).toString();
1196 return "<head><style>" + helpStylesheet() +
"</style></head><body>" + helpContents +
"</body>";
1203 QMenu *QgsExpressionBuilderWidget::ExpressionTreeMenuProvider::createContextMenu(
QgsExpressionItem *item )
1205 QMenu *menu =
nullptr;
1209 menu =
new QMenu( mExpressionBuilderWidget );