Skip to content

Commit

Permalink
[FEATURE][needs-docs] Hyperlink functions to show help in builder (#6796
Browse files Browse the repository at this point in the history
)
  • Loading branch information
NathanW2 committed Apr 21, 2018
1 parent 44c72a0 commit b22121a
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 0 deletions.
7 changes: 7 additions & 0 deletions python/core/expression/qgsexpressionnode.sip.in
Expand Up @@ -228,6 +228,13 @@ work like for example resolving a column name to an attribute index.
.. versionadded:: 2.12
%End

int parserFirstLine;

int parserFirstColumn;

int parserLastLine;

int parserLastColumn;

protected:

Expand Down
29 changes: 29 additions & 0 deletions python/core/expression/qgsexpressionnodeimpl.sip.in
Expand Up @@ -409,6 +409,21 @@ A combination of when and then. Simple as that.
QgsExpressionNodeCondition::WhenThen *clone() const /Factory/;
%Docstring
Get a deep copy of this WhenThen combination.
%End

QgsExpressionNode *whenExp() const;
%Docstring
The expression that makes the WHEN part of the condition.

:return: The expression node that makes the WHEN part of the condition check.
%End


QgsExpressionNode *thenExp() const;
%Docstring
The expression node that makes the THEN result part of the condition.

:return: The expression node that makes the THEN result part of the condition.
%End

private:
Expand All @@ -433,6 +448,20 @@ Create a new node with the given list of ``conditions`` and an optional ``elseEx
virtual QString dump() const;


WhenThenList conditions() const;
%Docstring
The list of WHEN THEN expression parts of the expression.

:return: The list of WHEN THEN expression parts of the expression.
%End

QgsExpressionNode *elseExp() const;
%Docstring
The ELSE expression used for the condition.

:return: The ELSE expression used for the condition.
%End

virtual QSet<QString> referencedColumns() const;

virtual QSet<QString> referencedVariables() const;
Expand Down
4 changes: 4 additions & 0 deletions src/core/expression/qgsexpressionnode.cpp
Expand Up @@ -52,4 +52,8 @@ void QgsExpressionNode::cloneTo( QgsExpressionNode *target ) const
{
target->mHasCachedValue = mHasCachedValue;
target->mCachedStaticValue = mCachedStaticValue;
target->parserLastColumn = parserLastColumn;
target->parserLastLine = parserLastLine;
target->parserFirstColumn = parserFirstColumn;
target->parserFirstLine = parserFirstLine;
}
27 changes: 27 additions & 0 deletions src/core/expression/qgsexpressionnode.h
Expand Up @@ -252,6 +252,33 @@ class CORE_EXPORT QgsExpressionNode SIP_ABSTRACT
*/
bool prepare( QgsExpression *parent, const QgsExpressionContext *context );

/**
* First line in the parser this node was found.
* \note This might not be complete for all nodes. Currently
* only \see QgsExpressionNode has this complete
*/
int parserFirstLine = 0;

/**
* First column in the parser this node was found.
* \note This might not be complete for all nodes. Currently
* only \see QgsExpressionNode has this complete
*/
int parserFirstColumn = 0;

/**
* Last line in the parser this node was found.
* \note This might not be complete for all nodes. Currently
* only \see QgsExpressionNode has this complete
*/
int parserLastLine = 0;

/**
* Last column in the parser this node was found.
* \note This might not be complete for all nodes. Currently
* only \see QgsExpressionNode has this complete
*/
int parserLastColumn = 0;

protected:

Expand Down
25 changes: 25 additions & 0 deletions src/core/expression/qgsexpressionnodeimpl.h
Expand Up @@ -398,6 +398,19 @@ class CORE_EXPORT QgsExpressionNodeCondition : public QgsExpressionNode
*/
QgsExpressionNodeCondition::WhenThen *clone() const SIP_FACTORY;

/**
* The expression that makes the WHEN part of the condition.
* \return The expression node that makes the WHEN part of the condition check.
*/
QgsExpressionNode *whenExp() const { return mWhenExp; }

/**
* The expression node that makes the THEN result part of the condition.
* \return The expression node that makes the THEN result part of the condition.
*/

QgsExpressionNode *thenExp() const { return mThenExp; }

private:
#ifdef SIP_RUN
WhenThen( const QgsExpressionNodeCondition::WhenThen &rh );
Expand Down Expand Up @@ -429,6 +442,18 @@ class CORE_EXPORT QgsExpressionNodeCondition : public QgsExpressionNode
bool prepareNode( QgsExpression *parent, const QgsExpressionContext *context ) override;
QString dump() const override;

/**
* The list of WHEN THEN expression parts of the expression.
* \return The list of WHEN THEN expression parts of the expression.
*/
WhenThenList conditions() const { return mConditions; }

/**
* The ELSE expression used for the condition.
* \return The ELSE expression used for the condition.
*/
QgsExpressionNode *elseExp() const { return mElseExp; }

QSet<QString> referencedColumns() const override;
QSet<QString> referencedVariables() const override;
bool needsGeometry() const override;
Expand Down
11 changes: 11 additions & 0 deletions src/core/qgsexpressionparser.yy
Expand Up @@ -17,6 +17,7 @@
#include <qglobal.h>
#include <QList>
#include <cstdlib>
#include "qgslogger.h"
#include "expression/qgsexpression.h"
#include "expression/qgsexpressionnode.h"
#include "expression/qgsexpressionnodeimpl.h"
Expand Down Expand Up @@ -68,6 +69,14 @@ struct expression_parser_context

#define BINOP(x, y, z) new QgsExpressionNodeBinaryOperator(x, y, z)

void addParserLocation(YYLTYPE* yyloc, QgsExpressionNode *node)
{
node->parserFirstLine = yyloc->first_line;
node->parserFirstColumn = yyloc->first_column;
node->parserLastLine = yyloc->last_line;
node->parserLastColumn = yyloc->last_column;
}

%}

// make the parser reentrant
Expand Down Expand Up @@ -221,6 +230,7 @@ expression:
YYERROR;
}
$$ = new QgsExpressionNodeFunction(fnIndex, $3);
addParserLocation(&@1, $$);
}

| FUNCTION '(' ')'
Expand All @@ -246,6 +256,7 @@ expression:
YYERROR;
}
$$ = new QgsExpressionNodeFunction(fnIndex, new QgsExpressionNode::NodeList());
addParserLocation(&@1, $$);
}

| expression IN '(' exp_list ')' { $$ = new QgsExpressionNodeInOperator($1, $4, false); }
Expand Down
114 changes: 114 additions & 0 deletions src/gui/qgsexpressionbuilderwidget.cpp
Expand Up @@ -17,6 +17,7 @@
#include "qgslogger.h"
#include "qgsexpression.h"
#include "qgsexpressionfunction.h"
#include "qgsexpressionnodeimpl.h"
#include "qgsmessageviewer.h"
#include "qgsapplication.h"
#include "qgspythonrunner.h"
Expand Down Expand Up @@ -118,6 +119,7 @@ QgsExpressionBuilderWidget::QgsExpressionBuilderWidget( QWidget *parent )
txtExpressionString->setWrapMode( QsciScintilla::WrapWord );
lblAutoSave->clear();


// Note: If you add a indicator here you should add it to clearErrors method if you need to clear it on text parse.
txtExpressionString->indicatorDefine( QgsCodeEditor::SquiggleIndicator, QgsExpression::ParserError::FunctionUnknown );
txtExpressionString->indicatorDefine( QgsCodeEditor::SquiggleIndicator, QgsExpression::ParserError::FunctionWrongArgs );
Expand All @@ -133,6 +135,14 @@ QgsExpressionBuilderWidget::QgsExpressionBuilderWidget( QWidget *parent )
txtExpressionString->setIndicatorForegroundColor( QColor( Qt::red ), -1 );
txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::red ), -1 );
txtExpressionString->setIndicatorOutlineColor( QColor( Qt::red ), -1 );

// Hidden function markers.
txtExpressionString->indicatorDefine( QgsCodeEditor::HiddenIndicator, FUNCTION_MARKER_ID );
txtExpressionString->setIndicatorForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
txtExpressionString->setIndicatorHoverForegroundColor( QColor( Qt::blue ), FUNCTION_MARKER_ID );
txtExpressionString->setIndicatorHoverStyle( QgsCodeEditor::DotsIndicator, FUNCTION_MARKER_ID );

connect( txtExpressionString, &QgsCodeEditorSQL::indicatorClicked, this, &QgsExpressionBuilderWidget::indicatorClicked );
}


Expand Down Expand Up @@ -384,6 +394,17 @@ void QgsExpressionBuilderWidget::fillFieldValues( const QString &fieldName, int
mFieldValues[fieldName] = strValues;
}

QString QgsExpressionBuilderWidget::getFunctionHelp( QgsExpressionFunction *function )
{
if ( !function )
return QString();

QString helpContents = QgsExpression::helpText( function->name() );

return "<head><style>" + helpStylesheet() + "</style></head><body>" + helpContents + "</body>";

}

void QgsExpressionBuilderWidget::registerItem( const QString &group,
const QString &label,
const QString &expressionText,
Expand Down Expand Up @@ -666,6 +687,7 @@ void QgsExpressionBuilderWidget::txtExpressionString_textChanged()
emit expressionParsed( true );
setParserError( false );
setEvalError( false );
createMarkers( exp.rootNode() );
}

}
Expand Down Expand Up @@ -767,6 +789,86 @@ void QgsExpressionBuilderWidget::showEvent( QShowEvent *e )
txtExpressionString->setFocus();
}

void QgsExpressionBuilderWidget::createMarkers( const QgsExpressionNode *inNode )
{
switch ( inNode->nodeType() )
{
case QgsExpressionNode::NodeType::ntFunction:
{
const QgsExpressionNodeFunction *node = static_cast<const QgsExpressionNodeFunction *>( inNode );
txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORCURRENT, FUNCTION_MARKER_ID );
txtExpressionString->SendScintilla( QsciScintilla::SCI_SETINDICATORVALUE, node->fnIndex() );
int start = inNode->parserFirstColumn - 1;
int end = inNode->parserLastColumn - 1;
int start_pos = txtExpressionString->positionFromLineIndex( inNode->parserFirstLine - 1, start );
txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORFILLRANGE, start_pos, end - start );
if ( node->args() )
{
const QList< QgsExpressionNode * > nodeList = node->args()->list();
for ( QgsExpressionNode *n : nodeList )
{
createMarkers( n );
}
}
break;
}
case QgsExpressionNode::NodeType::ntLiteral:
{
break;
}
case QgsExpressionNode::NodeType::ntUnaryOperator:
{
const QgsExpressionNodeUnaryOperator *node = static_cast<const QgsExpressionNodeUnaryOperator *>( inNode );
createMarkers( node->operand() );
break;
}
case QgsExpressionNode::NodeType::ntBinaryOperator:
{
const QgsExpressionNodeBinaryOperator *node = static_cast<const QgsExpressionNodeBinaryOperator *>( inNode );
createMarkers( node->opLeft() );
createMarkers( node->opRight() );
break;
}
case QgsExpressionNode::NodeType::ntColumnRef:
{
break;
}
case QgsExpressionNode::NodeType::ntInOperator:
{
const QgsExpressionNodeInOperator *node = static_cast<const QgsExpressionNodeInOperator *>( inNode );
if ( node->list() )
{
const QList< QgsExpressionNode * > nodeList = node->list()->list();
for ( QgsExpressionNode *n : nodeList )
{
createMarkers( n );
}
}
break;
}
case QgsExpressionNode::NodeType::ntCondition:
{
const QgsExpressionNodeCondition *node = static_cast<const QgsExpressionNodeCondition *>( inNode );
for ( QgsExpressionNodeCondition::WhenThen *cond : node->conditions() )
{
createMarkers( cond->whenExp() );
createMarkers( cond->thenExp() );
}
if ( node->elseExp() )
{
createMarkers( node->elseExp() );
}
break;
}
}
}

void QgsExpressionBuilderWidget::clearFunctionMarkers()
{
int lastLine = txtExpressionString->lines() - 1;
txtExpressionString->clearIndicatorRange( 0, 0, lastLine, txtExpressionString->text( lastLine ).length() - 1, FUNCTION_MARKER_ID );
}

void QgsExpressionBuilderWidget::clearErrors()
{
int lastLine = txtExpressionString->lines() - 1;
Expand Down Expand Up @@ -903,6 +1005,18 @@ void QgsExpressionBuilderWidget::autosave()
anim->start( QAbstractAnimation::DeleteWhenStopped );
}

void QgsExpressionBuilderWidget::indicatorClicked( int line, int index, Qt::KeyboardModifiers state )
{
if ( state & Qt::ControlModifier )
{
int position = txtExpressionString->positionFromLineIndex( line, index );
long fncIndex = txtExpressionString->SendScintilla( QsciScintilla::SCI_INDICATORVALUEAT, FUNCTION_MARKER_ID, ( long int )position );
QgsExpressionFunction *func = QgsExpression::Functions()[fncIndex];
QString help = getFunctionHelp( func );
txtHelpText->setText( help );
}
}

void QgsExpressionBuilderWidget::setExpressionState( bool state )
{
mExpressionValid = state;
Expand Down
5 changes: 5 additions & 0 deletions src/gui/qgsexpressionbuilderwidget.h
Expand Up @@ -307,6 +307,7 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp
void setAutoSave( bool enabled ) { mAutoSave = enabled; }

private slots:
void indicatorClicked( int line, int index, Qt::KeyboardModifiers state );
void showContextMenu( QPoint );
void setExpressionState( bool state );
void currentChanged( const QModelIndex &index, const QModelIndex & );
Expand Down Expand Up @@ -352,10 +353,14 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp
void showEvent( QShowEvent *e ) override;

private:
int FUNCTION_MARKER_ID = 25;
void createMarkers( const QgsExpressionNode *node );
void clearFunctionMarkers();
void clearErrors();
void runPythonCode( const QString &code );
void updateFunctionTree();
void fillFieldValues( const QString &fieldName, int countLimit );
QString getFunctionHelp( QgsExpressionFunction *function );
QString loadFunctionHelp( QgsExpressionItem *functionName );
QString helpStylesheet() const;

Expand Down

0 comments on commit b22121a

Please sign in to comment.