Skip to content

Commit

Permalink
Merge pull request #3417 from nyalldawson/expression_line_edit
Browse files Browse the repository at this point in the history
Expression line edit widget
  • Loading branch information
nyalldawson committed Aug 21, 2016
2 parents 86feb6d + c468a04 commit b101510
Show file tree
Hide file tree
Showing 11 changed files with 556 additions and 21 deletions.
1 change: 1 addition & 0 deletions python/gui/gui.sip
Expand Up @@ -70,6 +70,7 @@
%Include qgsexpressionbuilderdialog.sip
%Include qgsexpressionbuilderwidget.sip
%Include qgsexpressionhighlighter.sip
%Include qgsexpressionlineedit.sip
%Include qgsexpressionselectiondialog.sip
%Include qgsextentgroupbox.sip
%Include qgsexternalresourcewidget.sip
Expand Down
90 changes: 90 additions & 0 deletions python/gui/qgsexpressionlineedit.sip
@@ -0,0 +1,90 @@
/** \ingroup gui
* @class QgsExpressionLineEdit
* @brief The QgsExpressionLineEdit widget includes a line edit for entering expressions
* together with a button to open the expression creation dialog.
*
* This widget is designed for use in contexts where no layer fields are available for
* use in an expression. In contexts where the expression is directly associated with
* a layer and fields can be used, then QgsFieldExpressionWidget is a more appropriate
* choice as it gives users direct access to select fields from a drop down list.
* @note added in QGIS 3.0
*/

class QgsExpressionLineEdit : QWidget
{
%TypeHeaderCode
#include "qgsexpressionlineedit.h"
%End

public:

/**
* Constructor for QgsExpressionLineEdit.
* @param parent parent widget
*/
explicit QgsExpressionLineEdit( QWidget *parent /TransferThis/ = nullptr );
/** Sets the title used in the expression builder dialog
* @param title dialog title
* @see expressionDialogTitle()
*/
void setExpressionDialogTitle( const QString& title );

/** Returns the title used for the expression dialog.
* @see setExpressionDialogTitle()
*/
QString expressionDialogTitle() const;

/** Sets whether the widget should show a multiline text editor.
* @param multiLine set to true to show multiline editor, or false
* to show single line editor (the default).
*/
void setMultiLine( bool multiLine );

/** Set the geometry calculator used in the expression dialog.
* @param distanceArea calculator
*/
void setGeomCalculator( const QgsDistanceArea &distanceArea );

/** Sets a layer associated with the widget. Required in order to get the fields and values
* from the layer.
* @param layer vector layer
*/
void setLayer( QgsVectorLayer* layer );

/** Returns the current expression shown in the widget.
* @see setExpression()
*/
QString expression() const;

/**
* Returns true if the current expression is valid.
* @param expressionError will be set to any generated error message if specified
*/
bool isValidExpression( QString *expressionError /Out/ = nullptr ) const;

/**
* Register an expression context generator class that will be used to retrieve
* an expression context for the widget.
* @param generator A QgsExpressionContextGenerator class that will be used to
* create an expression context when required.
*/
void registerExpressionContextGenerator( const QgsExpressionContextGenerator* generator );

signals:

/** Emitted when the expression is changed.
* @param expression new expression
*/
void expressionChanged( const QString& expression );

public slots:

/** Sets the current expression to show in the widget.
* @param expression expression string
* @see expression()
*/
void setExpression( const QString& expression );

protected:
void changeEvent( QEvent* event );
};
2 changes: 2 additions & 0 deletions src/gui/CMakeLists.txt
Expand Up @@ -208,6 +208,7 @@ SET(QGIS_GUI_SRCS
qgsexpressionbuilderdialog.cpp
qgsexpressionbuilderwidget.cpp
qgsexpressionhighlighter.cpp
qgsexpressionlineedit.cpp
qgsexpressionselectiondialog.cpp
qgsextentgroupbox.cpp
qgsexternalresourcewidget.cpp
Expand Down Expand Up @@ -365,6 +366,7 @@ SET(QGIS_GUI_MOC_HDRS
qgsexpressionbuilderdialog.h
qgsexpressionbuilderwidget.h
qgsexpressionhighlighter.h
qgsexpressionlineedit.h
qgsexpressionselectiondialog.h
qgsextentgroupbox.h
qgsexternalresourcewidget.h
Expand Down
1 change: 0 additions & 1 deletion src/gui/qgscodeeditor.cpp
Expand Up @@ -31,7 +31,6 @@ QgsCodeEditor::QgsCodeEditor( QWidget *parent, const QString& title, bool foldin
if ( !parent && mWidgetTitle.isEmpty() )
{
setWindowTitle( "Text Editor" );
setMinimumSize( 800, 300 );
}
else
{
Expand Down
221 changes: 221 additions & 0 deletions src/gui/qgsexpressionlineedit.cpp
@@ -0,0 +1,221 @@
/***************************************************************************
qgsexpressionlineedit.cpp
------------------------
Date : 18.08.2016
Copyright : (C) 2016 Nyall Dawson
Email : nyall dot dawson 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. *
* *
***************************************************************************/

#include "qgsexpressionlineedit.h"
#include "qgsfilterlineedit.h"
#include "qgsexpressioncontext.h"
#include "qgsapplication.h"
#include "qgsexpressionbuilderdialog.h"
#include "qgsexpressioncontextgenerator.h"
#include "qgscodeeditorsql.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QToolButton>


QgsExpressionLineEdit::QgsExpressionLineEdit( QWidget *parent )
: QWidget( parent )
, mLineEdit( nullptr )
, mCodeEditor( nullptr )
, mExpressionDialogTitle( tr( "Expression dialog" ) )
, mDa( nullptr )
, mExpressionContextGenerator( nullptr )
, mLayer( nullptr )
{
mButton = new QToolButton();
mButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
mButton->setIcon( QgsApplication::getThemeIcon( "/mIconExpression.svg" ) );
connect( mButton, SIGNAL( clicked() ), this, SLOT( editExpression() ) );

//sets up layout
setMultiLine( false );

mExpressionContext = QgsExpressionContext();
mExpressionContext << QgsExpressionContextUtils::globalScope()
<< QgsExpressionContextUtils::projectScope();
}

void QgsExpressionLineEdit::setExpressionDialogTitle( const QString& title )
{
mExpressionDialogTitle = title;
}

void QgsExpressionLineEdit::setMultiLine( bool multiLine )
{
QString exp = expression();

if ( multiLine && !mCodeEditor )
{
mCodeEditor = new QgsCodeEditorSQL();
mCodeEditor->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
delete mLineEdit;
mLineEdit = nullptr;

QHBoxLayout* newLayout = new QHBoxLayout();
newLayout->setContentsMargins( 0, 0, 0, 0 );
newLayout->addWidget( mCodeEditor );

QVBoxLayout* vLayout = new QVBoxLayout();
vLayout->addWidget( mButton );
vLayout->addStretch();
newLayout->addLayout( vLayout );

delete layout();
setLayout( newLayout );

setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );

setFocusProxy( mCodeEditor );
connect( mCodeEditor, SIGNAL( textChanged() ), this, SLOT( expressionEdited() ) );

setExpression( exp );
}
else if ( !multiLine && !mLineEdit )
{
delete mCodeEditor;
mCodeEditor = nullptr;
mLineEdit = new QgsFilterLineEdit();
mLineEdit->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum );

QHBoxLayout* newLayout = new QHBoxLayout();
newLayout->setContentsMargins( 0, 0, 0, 0 );
newLayout->addWidget( mLineEdit );
newLayout->addWidget( mButton );

delete layout();
setLayout( newLayout );

setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum );

setFocusProxy( mLineEdit );
connect( mLineEdit, SIGNAL( textChanged( QString ) ), this, SLOT( expressionEdited( QString ) ) );

setExpression( exp );
}
}

void QgsExpressionLineEdit::setGeomCalculator( const QgsDistanceArea &da )
{
mDa.reset( new QgsDistanceArea( da ) );
}

void QgsExpressionLineEdit::setLayer( QgsVectorLayer* layer )
{
mLayer = layer;
}

QString QgsExpressionLineEdit::expression() const
{
if ( mLineEdit )
return mLineEdit->text();
else if ( mCodeEditor )
return mCodeEditor->text();

return QString();
}

bool QgsExpressionLineEdit::isValidExpression( QString *expressionError ) const
{
QString temp;
return QgsExpression::isValid( expression(), &mExpressionContext, expressionError ? *expressionError : temp );
}

void QgsExpressionLineEdit::registerExpressionContextGenerator( const QgsExpressionContextGenerator* generator )
{
mExpressionContextGenerator = generator;
}

void QgsExpressionLineEdit::setExpression( const QString& newExpression )
{
if ( mLineEdit )
mLineEdit->setText( newExpression );
else if ( mCodeEditor )
mCodeEditor->setText( newExpression );
}

void QgsExpressionLineEdit::editExpression()
{
QString currentExpression = expression();

QgsExpressionContext context = mExpressionContextGenerator ? mExpressionContextGenerator->createExpressionContext() : mExpressionContext;

QgsExpressionBuilderDialog dlg( mLayer, currentExpression, this, "generic", context );
if ( !mDa.isNull() )
{
dlg.setGeomCalculator( *mDa );
}
dlg.setWindowTitle( mExpressionDialogTitle );

if ( dlg.exec() )
{
QString newExpression = dlg.expressionText();
setExpression( newExpression );
}
}

void QgsExpressionLineEdit::expressionEdited()
{
emit expressionChanged( expression() );
}

void QgsExpressionLineEdit::expressionEdited( const QString& expression )
{
updateLineEditStyle( expression );
emit expressionChanged( expression );
}

void QgsExpressionLineEdit::changeEvent( QEvent* event )
{
if ( event->type() == QEvent::EnabledChange )
{
updateLineEditStyle( expression() );
}
}

void QgsExpressionLineEdit::updateLineEditStyle( const QString& expression )
{
if ( !mLineEdit )
return;

QPalette palette;
if ( !isEnabled() )
{
palette.setColor( QPalette::Text, Qt::gray );
}
else
{
bool isValid = true;
if ( !expression.isEmpty() )
{
isValid = isExpressionValid( expression );
}
if ( !isValid )
{
palette.setColor( QPalette::Text, Qt::red );
}
else
{
palette.setColor( QPalette::Text, Qt::black );
}
}
mLineEdit->setPalette( palette );
}

bool QgsExpressionLineEdit::isExpressionValid( const QString& expressionStr )
{
QgsExpression expression( expressionStr );
expression.prepare( &mExpressionContext );
return !expression.hasParserError();
}

0 comments on commit b101510

Please sign in to comment.