mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
Code completion for expression builder [FEATURE]
This commit is contained in:
parent
32ee71634f
commit
cebaa81996
@ -79,6 +79,9 @@
|
||||
%If ( HAVE_QSCI_SIP )
|
||||
%Include auto_generated/qgscodeeditorsql.sip
|
||||
%End
|
||||
%If ( HAVE_QSCI_SIP )
|
||||
%Include auto_generated/qgscodeeditorexpression.sip
|
||||
%End
|
||||
%Include auto_generated/qgscollapsiblegroupbox.sip
|
||||
%Include auto_generated/qgscolorbrewercolorrampdialog.sip
|
||||
%Include auto_generated/qgscolorbutton.sip
|
||||
|
@ -229,6 +229,7 @@ SET(QGIS_GUI_SRCS
|
||||
qgscodeeditorhtml.cpp
|
||||
qgscodeeditorpython.cpp
|
||||
qgscodeeditorsql.cpp
|
||||
qgscodeeditorexpression.cpp
|
||||
qgscollapsiblegroupbox.cpp
|
||||
qgscolorbrewercolorrampdialog.cpp
|
||||
qgscolorbutton.cpp
|
||||
@ -406,6 +407,7 @@ SET(QGIS_GUI_MOC_HDRS
|
||||
qgscodeeditorhtml.h
|
||||
qgscodeeditorpython.h
|
||||
qgscodeeditorsql.h
|
||||
qgscodeeditorexpression.h
|
||||
qgscollapsiblegroupbox.h
|
||||
qgscolorbrewercolorrampdialog.h
|
||||
qgscolorbutton.h
|
||||
|
163
src/gui/qgscodeeditorexpression.cpp
Normal file
163
src/gui/qgscodeeditorexpression.cpp
Normal file
@ -0,0 +1,163 @@
|
||||
/***************************************************************************
|
||||
qgscodeeditorexpressoin.cpp - An expression editor based on QScintilla
|
||||
--------------------------------------
|
||||
Date : 8.9.2018
|
||||
Copyright : (C) 2018 by Matthias Kuhn
|
||||
Email : matthias@opengis.ch
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgsapplication.h"
|
||||
#include "qgscodeeditorexpression.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QString>
|
||||
#include <QFont>
|
||||
#include <QLabel>
|
||||
|
||||
|
||||
QgsCodeEditorExpression::QgsCodeEditorExpression( QWidget *parent )
|
||||
: QgsCodeEditor( parent )
|
||||
{
|
||||
if ( !parent )
|
||||
{
|
||||
setTitle( tr( "Expression Editor" ) );
|
||||
}
|
||||
setMarginVisible( false );
|
||||
setFoldingVisible( true );
|
||||
setAutoCompletionCaseSensitivity( false );
|
||||
initializeLexer();
|
||||
}
|
||||
|
||||
void QgsCodeEditorExpression::setExpressionContext( const QgsExpressionContext &context )
|
||||
{
|
||||
mVariables.clear();
|
||||
|
||||
const QStringList variableNames = context.filteredVariableNames();
|
||||
for ( const QString &var : variableNames )
|
||||
{
|
||||
mVariables << '@' + var;
|
||||
}
|
||||
|
||||
mContextFunctions = context.functionNames();
|
||||
|
||||
mFunctions.clear();
|
||||
|
||||
const int count = QgsExpression::functionCount();
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
QgsExpressionFunction *func = QgsExpression::Functions()[i];
|
||||
if ( func->isDeprecated() ) // don't show deprecated functions
|
||||
continue;
|
||||
if ( func->isContextual() )
|
||||
{
|
||||
//don't show contextual functions by default - it's up the the QgsExpressionContext
|
||||
//object to provide them if supported
|
||||
continue;
|
||||
}
|
||||
|
||||
QString signature = func->name();
|
||||
if ( !signature.startsWith( '$' ) )
|
||||
{
|
||||
signature += '(';
|
||||
|
||||
QStringList paramNames;
|
||||
const auto ¶meters = func->parameters();
|
||||
for ( const auto ¶m : parameters )
|
||||
{
|
||||
paramNames << param.name();
|
||||
}
|
||||
|
||||
// No named parameters but there should be parameteres? Show an ellipsis at least
|
||||
if ( parameters.isEmpty() && func->params() )
|
||||
signature += QChar( 0x2026 );
|
||||
|
||||
signature += paramNames.join( ", " );
|
||||
|
||||
signature += ')';
|
||||
}
|
||||
mFunctions << signature;
|
||||
}
|
||||
|
||||
updateApis();
|
||||
}
|
||||
|
||||
void QgsCodeEditorExpression::setFields( const QgsFields &fields )
|
||||
{
|
||||
mFieldNames.clear();
|
||||
|
||||
for ( const QgsField &field : fields )
|
||||
{
|
||||
mFieldNames << field.name();
|
||||
}
|
||||
|
||||
updateApis();
|
||||
}
|
||||
|
||||
|
||||
void QgsCodeEditorExpression::initializeLexer()
|
||||
{
|
||||
QFont font = getMonospaceFont();
|
||||
#ifdef Q_OS_MAC
|
||||
// The font size gotten from getMonospaceFont() is too small on Mac
|
||||
font.setPointSize( QLabel().font().pointSize() );
|
||||
#endif
|
||||
mSqlLexer = new QgsCaseInsensitiveLexerExpression( this );
|
||||
mSqlLexer->setDefaultFont( font );
|
||||
mSqlLexer->setFont( font, -1 );
|
||||
font.setBold( true );
|
||||
mSqlLexer->setFont( font, QsciLexerSQL::Keyword );
|
||||
mSqlLexer->setColor( Qt::darkYellow, QsciLexerSQL::DoubleQuotedString ); // fields
|
||||
|
||||
setLexer( mSqlLexer );
|
||||
}
|
||||
|
||||
void QgsCodeEditorExpression::updateApis()
|
||||
{
|
||||
mApis = new QsciAPIs( mSqlLexer );
|
||||
|
||||
for ( const QString &var : qgis::as_const( mVariables ) )
|
||||
{
|
||||
mApis->add( var );
|
||||
}
|
||||
|
||||
for ( const QString &function : qgis::as_const( mContextFunctions ) )
|
||||
{
|
||||
mApis->add( function );
|
||||
}
|
||||
|
||||
for ( const QString &function : qgis::as_const( mFunctions ) )
|
||||
{
|
||||
mApis->add( function );
|
||||
}
|
||||
|
||||
for ( const QString &fieldName : qgis::as_const( mFieldNames ) )
|
||||
{
|
||||
mApis->add( fieldName );
|
||||
}
|
||||
|
||||
mApis->prepare();
|
||||
mSqlLexer->setAPIs( mApis );
|
||||
}
|
||||
|
||||
bool QgsCaseInsensitiveLexerExpression::caseSensitive() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#if 0
|
||||
const char *QgsCaseInsensitiveLexerExpression::wordCharacters() const
|
||||
{
|
||||
static QString wordChars;
|
||||
|
||||
wordChars = QsciLexerSQL::wordCharacters();
|
||||
wordChars += '@';
|
||||
return wordChars.toUtf8().constData();
|
||||
}
|
||||
|
||||
#endif
|
97
src/gui/qgscodeeditorexpression.h
Normal file
97
src/gui/qgscodeeditorexpression.h
Normal file
@ -0,0 +1,97 @@
|
||||
/***************************************************************************
|
||||
qgscodeeditorsql.h - A SQL editor based on QScintilla
|
||||
--------------------------------------
|
||||
Date : 06-Oct-2013
|
||||
Copyright : (C) 2013 by Salvatore Larosa
|
||||
Email : lrssvtml (at) gmail (dot) com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSCODEEDITOREXPRESSION_H
|
||||
#define QGSCODEEDITOREXPRESSION_H
|
||||
|
||||
#include "qgis_sip.h"
|
||||
#include "qgis_gui.h"
|
||||
#include "qgscodeeditor.h"
|
||||
#include "qgsexpressioncontext.h"
|
||||
|
||||
#include <Qsci/qscilexersql.h>
|
||||
|
||||
SIP_IF_MODULE( HAVE_QSCI_SIP )
|
||||
|
||||
/**
|
||||
* \ingroup gui
|
||||
*
|
||||
* A QGIS expression editor based on QScintilla2. Adds syntax highlighting and
|
||||
* code autocompletion.
|
||||
*
|
||||
* \since QGIS 3.4
|
||||
*/
|
||||
class GUI_EXPORT QgsCodeEditorExpression : public QgsCodeEditor
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
//! Constructor for QgsCodeEditorExpression
|
||||
QgsCodeEditorExpression( QWidget *parent SIP_TRANSFERTHIS = nullptr );
|
||||
|
||||
/**
|
||||
* Variables and functions from this expression context will be added to
|
||||
* the API.
|
||||
* Will also reload all globally registered functions.
|
||||
*/
|
||||
void setExpressionContext( const QgsExpressionContext &context );
|
||||
|
||||
/**
|
||||
* Field names will be added to the API.
|
||||
*/
|
||||
void setFields( const QgsFields &fields );
|
||||
|
||||
private:
|
||||
void initializeLexer();
|
||||
void updateApis();
|
||||
QsciAPIs *mApis;
|
||||
QsciLexerSQL *mSqlLexer;
|
||||
|
||||
QStringList mVariables;
|
||||
QStringList mContextFunctions;
|
||||
QStringList mFunctions;
|
||||
QStringList mFieldNames;
|
||||
};
|
||||
|
||||
#ifndef SIP_RUN
|
||||
///@cond PRIVATE
|
||||
|
||||
/**
|
||||
* Internal use.
|
||||
|
||||
setAutoCompletionCaseSensitivity( false ) is not sufficient when installing
|
||||
a lexer, since its caseSensitive() method is actually used, and defaults
|
||||
to true.
|
||||
\note not available in Python bindings
|
||||
\ingroup gui
|
||||
*/
|
||||
class QgsCaseInsensitiveLexerExpression : public QsciLexerSQL
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
//! constructor
|
||||
explicit QgsCaseInsensitiveLexerExpression( QObject *parent = nullptr ) : QsciLexerSQL( parent ) {}
|
||||
|
||||
bool caseSensitive() const override;
|
||||
|
||||
#if 0
|
||||
const char *wordCharacters() const override;
|
||||
#endif
|
||||
};
|
||||
///@endcond
|
||||
#endif
|
||||
|
||||
#endif
|
@ -49,7 +49,7 @@ QgsExpressionBuilderWidget::QgsExpressionBuilderWidget( QWidget *parent )
|
||||
connect( btnNewFile, &QToolButton::pressed, this, &QgsExpressionBuilderWidget::btnNewFile_pressed );
|
||||
connect( cmbFileNames, &QListWidget::currentItemChanged, this, &QgsExpressionBuilderWidget::cmbFileNames_currentItemChanged );
|
||||
connect( expressionTree, &QTreeView::doubleClicked, this, &QgsExpressionBuilderWidget::expressionTree_doubleClicked );
|
||||
connect( txtExpressionString, &QgsCodeEditorSQL::textChanged, this, &QgsExpressionBuilderWidget::txtExpressionString_textChanged );
|
||||
connect( txtExpressionString, &QgsCodeEditorExpression::textChanged, this, &QgsExpressionBuilderWidget::txtExpressionString_textChanged );
|
||||
connect( txtPython, &QgsCodeEditorPython::textChanged, this, &QgsExpressionBuilderWidget::txtPython_textChanged );
|
||||
connect( txtSearchEditValues, &QgsFilterLineEdit::textChanged, this, &QgsExpressionBuilderWidget::txtSearchEditValues_textChanged );
|
||||
connect( txtSearchEdit, &QgsFilterLineEdit::textChanged, this, &QgsExpressionBuilderWidget::txtSearchEdit_textChanged );
|
||||
@ -142,7 +142,10 @@ QgsExpressionBuilderWidget::QgsExpressionBuilderWidget( QWidget *parent )
|
||||
txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
|
||||
txtExpressionString->setIndicatorHoverStyle( QgsCodeEditor::DotsIndicator, FUNCTION_MARKER_ID );
|
||||
|
||||
connect( txtExpressionString, &QgsCodeEditorSQL::indicatorClicked, this, &QgsExpressionBuilderWidget::indicatorClicked );
|
||||
connect( txtExpressionString, &QgsCodeEditorExpression::indicatorClicked, this, &QgsExpressionBuilderWidget::indicatorClicked );
|
||||
txtExpressionString->setAutoCompletionCaseSensitivity( true );
|
||||
txtExpressionString->setAutoCompletionSource( QsciScintilla::AcsAPIs );
|
||||
txtExpressionString->setCallTipsVisible( 0 );
|
||||
|
||||
setExpectedOutputFormat( QString() );
|
||||
}
|
||||
@ -341,6 +344,8 @@ void QgsExpressionBuilderWidget::loadFieldNames( const QgsFields &fields )
|
||||
if ( fields.isEmpty() )
|
||||
return;
|
||||
|
||||
txtExpressionString->setFields( fields );
|
||||
|
||||
QStringList fieldNames;
|
||||
//Q_FOREACH ( const QgsField& field, fields )
|
||||
fieldNames.reserve( fields.count() );
|
||||
@ -696,6 +701,7 @@ void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
|
||||
|
||||
void QgsExpressionBuilderWidget::loadExpressionContext()
|
||||
{
|
||||
txtExpressionString->setExpressionContext( mExpressionContext );
|
||||
QStringList variableNames = mExpressionContext.filteredVariableNames();
|
||||
Q_FOREACH ( const QString &variable, variableNames )
|
||||
{
|
||||
|
@ -55,7 +55,7 @@ void QgsExpressionLineEdit::setMultiLine( bool multiLine )
|
||||
|
||||
if ( multiLine && !mCodeEditor )
|
||||
{
|
||||
mCodeEditor = new QgsCodeEditorSQL();
|
||||
mCodeEditor = new QgsCodeEditorExpression();
|
||||
mCodeEditor->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
|
||||
delete mLineEdit;
|
||||
mLineEdit = nullptr;
|
||||
|
@ -27,7 +27,7 @@ class QgsFilterLineEdit;
|
||||
class QToolButton;
|
||||
class QgsDistanceArea;
|
||||
class QgsExpressionContextGenerator;
|
||||
class QgsCodeEditorSQL;
|
||||
class QgsCodeEditorExpression;
|
||||
|
||||
/**
|
||||
* \ingroup gui
|
||||
@ -170,7 +170,7 @@ class GUI_EXPORT QgsExpressionLineEdit : public QWidget
|
||||
|
||||
private:
|
||||
QgsFilterLineEdit *mLineEdit = nullptr;
|
||||
QgsCodeEditorSQL *mCodeEditor = nullptr;
|
||||
QgsCodeEditorExpression *mCodeEditor = nullptr;
|
||||
QToolButton *mButton = nullptr;
|
||||
QString mExpressionDialogTitle;
|
||||
std::unique_ptr<QgsDistanceArea> mDa;
|
||||
|
@ -273,7 +273,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QgsCodeEditorSQL" name="txtExpressionString" native="true"/>
|
||||
<widget class="QgsCodeEditorExpression" name="txtExpressionString" native="true"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
@ -768,9 +768,9 @@ Saved scripts are auto loaded on QGIS startup.</string>
|
||||
<header>qgsfilterlineedit.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>QgsCodeEditorSQL</class>
|
||||
<class>QgsCodeEditorExpression</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>qgscodeeditorsql.h</header>
|
||||
<header>qgscodeeditorexpression.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
|
Loading…
x
Reference in New Issue
Block a user