Skip to content

Commit 8f4ca47

Browse files
committed
New QgsExpressionLineEdit widget
Consists of a filter line edit + button to open expression builder This widget is a bit like the existing QgsFieldExpressionWidget, but for cases where a combo box does not make sense. Eg, when no fields are available for the expression to use. It also has an optional multiline mode, which allows it to be used in place of the full-blown QgsExpressionBuilderWidget when space is a problem.
1 parent 86feb6d commit 8f4ca47

7 files changed

+545
-0
lines changed

python/gui/gui.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
%Include qgsexpressionbuilderdialog.sip
7171
%Include qgsexpressionbuilderwidget.sip
7272
%Include qgsexpressionhighlighter.sip
73+
%Include qgsexpressionlineedit.sip
7374
%Include qgsexpressionselectiondialog.sip
7475
%Include qgsextentgroupbox.sip
7576
%Include qgsexternalresourcewidget.sip

python/gui/qgsexpressionlineedit.sip

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/** \ingroup gui
2+
* @class QgsExpressionLineEdit
3+
* @brief The QgsExpressionLineEdit widget includes a line edit for entering expressions
4+
* together with a button to open the expression creation dialog.
5+
*
6+
* This widget is designed for use in contexts where no layer fields are available for
7+
* use in an expression. In contexts where the expression is directly associated with
8+
* a layer and fields can be used, then QgsFieldExpressionWidget is a more appropriate
9+
* choice as it gives users direct access to select fields from a drop down list.
10+
* @note added in QGIS 3.0
11+
*/
12+
13+
class QgsExpressionLineEdit : QWidget
14+
{
15+
%TypeHeaderCode
16+
#include "qgsexpressionlineedit.h"
17+
%End
18+
19+
public:
20+
21+
/**
22+
* Constructor for QgsExpressionLineEdit.
23+
* @param parent parent widget
24+
*/
25+
explicit QgsExpressionLineEdit( QWidget *parent /TransferThis/ = nullptr );
26+
/** Sets the title used in the expression builder dialog
27+
* @param title dialog title
28+
* @see expressionDialogTitle()
29+
*/
30+
void setExpressionDialogTitle( const QString& title );
31+
32+
/** Returns the title used for the expression dialog.
33+
* @see setExpressionDialogTitle()
34+
*/
35+
QString expressionDialogTitle() const;
36+
37+
/** Sets whether the widget should show a multiline text editor.
38+
* @param multiLine set to true to show multiline editor, or false
39+
* to show single line editor (the default).
40+
*/
41+
void setMultiLine( bool multiLine );
42+
43+
/** Set the geometry calculator used in the expression dialog.
44+
* @param distanceArea calculator
45+
*/
46+
void setGeomCalculator( const QgsDistanceArea &distanceArea );
47+
48+
/** Sets a layer associated with the widget. Required in order to get the fields and values
49+
* from the layer.
50+
* @param layer vector layer
51+
*/
52+
void setLayer( QgsVectorLayer* layer );
53+
54+
/** Returns the current expression shown in the widget.
55+
* @see setExpression()
56+
*/
57+
QString expression() const;
58+
59+
/**
60+
* Returns true if the current expression is valid.
61+
* @param expressionError will be set to any generated error message if specified
62+
*/
63+
bool isValidExpression( QString *expressionError /Out/ = nullptr ) const;
64+
65+
/**
66+
* Register an expression context generator class that will be used to retrieve
67+
* an expression context for the widget.
68+
* @param generator A QgsExpressionContextGenerator class that will be used to
69+
* create an expression context when required.
70+
*/
71+
void registerExpressionContextGenerator( const QgsExpressionContextGenerator* generator );
72+
73+
signals:
74+
75+
/** Emitted when the expression is changed.
76+
* @param expression new expression
77+
*/
78+
void expressionChanged( const QString& expression );
79+
80+
public slots:
81+
82+
/** Sets the current expression to show in the widget.
83+
* @param expression expression string
84+
* @see expression()
85+
*/
86+
void setExpression( const QString& expression );
87+
88+
protected:
89+
void changeEvent( QEvent* event );
90+
};

src/gui/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ SET(QGIS_GUI_SRCS
208208
qgsexpressionbuilderdialog.cpp
209209
qgsexpressionbuilderwidget.cpp
210210
qgsexpressionhighlighter.cpp
211+
qgsexpressionlineedit.cpp
211212
qgsexpressionselectiondialog.cpp
212213
qgsextentgroupbox.cpp
213214
qgsexternalresourcewidget.cpp
@@ -365,6 +366,7 @@ SET(QGIS_GUI_MOC_HDRS
365366
qgsexpressionbuilderdialog.h
366367
qgsexpressionbuilderwidget.h
367368
qgsexpressionhighlighter.h
369+
qgsexpressionlineedit.h
368370
qgsexpressionselectiondialog.h
369371
qgsextentgroupbox.h
370372
qgsexternalresourcewidget.h

src/gui/qgsexpressionlineedit.cpp

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
/***************************************************************************
2+
qgsexpressionlineedit.cpp
3+
------------------------
4+
Date : 18.08.2016
5+
Copyright : (C) 2016 Nyall Dawson
6+
Email : nyall dot dawson at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgsexpressionlineedit.h"
17+
#include "qgsfilterlineedit.h"
18+
#include "qgsexpressioncontext.h"
19+
#include "qgsapplication.h"
20+
#include "qgsexpressionbuilderdialog.h"
21+
#include "qgsexpressioncontextgenerator.h"
22+
#include "qgscodeeditorsql.h"
23+
#include <QHBoxLayout>
24+
#include <QVBoxLayout>
25+
#include <QToolButton>
26+
27+
28+
QgsExpressionLineEdit::QgsExpressionLineEdit( QWidget *parent )
29+
: QWidget( parent )
30+
, mLineEdit( nullptr )
31+
, mCodeEditor( nullptr )
32+
, mExpressionDialogTitle( tr( "Expression dialog" ) )
33+
, mDa( nullptr )
34+
, mExpressionContextGenerator( nullptr )
35+
, mLayer( nullptr )
36+
{
37+
mButton = new QToolButton();
38+
mButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
39+
mButton->setIcon( QgsApplication::getThemeIcon( "/mIconExpression.svg" ) );
40+
connect( mButton, SIGNAL( clicked() ), this, SLOT( editExpression() ) );
41+
42+
//sets up layout
43+
setMultiLine( false );
44+
45+
mExpressionContext = QgsExpressionContext();
46+
mExpressionContext << QgsExpressionContextUtils::globalScope()
47+
<< QgsExpressionContextUtils::projectScope();
48+
}
49+
50+
void QgsExpressionLineEdit::setExpressionDialogTitle( const QString& title )
51+
{
52+
mExpressionDialogTitle = title;
53+
}
54+
55+
void QgsExpressionLineEdit::setMultiLine( bool multiLine )
56+
{
57+
QString exp = expression();
58+
59+
if ( multiLine && !mCodeEditor )
60+
{
61+
mCodeEditor = new QgsCodeEditorSQL();
62+
mCodeEditor->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
63+
delete mLineEdit;
64+
mLineEdit = nullptr;
65+
66+
QHBoxLayout* newLayout = new QHBoxLayout();
67+
newLayout->setContentsMargins( 0, 0, 0, 0 );
68+
newLayout->addWidget( mCodeEditor );
69+
70+
QVBoxLayout* vLayout = new QVBoxLayout();
71+
vLayout->addWidget( mButton );
72+
vLayout->addStretch();
73+
newLayout->addLayout( vLayout );
74+
75+
delete layout();
76+
setLayout( newLayout );
77+
78+
setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
79+
80+
setFocusProxy( mCodeEditor );
81+
connect( mCodeEditor, SIGNAL( textChanged() ), this, SLOT( expressionEdited() ) );
82+
83+
setExpression( exp );
84+
}
85+
else if ( !multiLine && !mLineEdit )
86+
{
87+
delete mCodeEditor;
88+
mCodeEditor = nullptr;
89+
mLineEdit = new QgsFilterLineEdit();
90+
mLineEdit->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum );
91+
92+
QHBoxLayout* newLayout = new QHBoxLayout();
93+
newLayout->setContentsMargins( 0, 0, 0, 0 );
94+
newLayout->addWidget( mLineEdit );
95+
newLayout->addWidget( mButton );
96+
97+
delete layout();
98+
setLayout( newLayout );
99+
100+
setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum );
101+
102+
setFocusProxy( mLineEdit );
103+
connect( mLineEdit, SIGNAL( textChanged( QString ) ), this, SLOT( expressionEdited( QString ) ) );
104+
105+
setExpression( exp );
106+
}
107+
}
108+
109+
void QgsExpressionLineEdit::setGeomCalculator( const QgsDistanceArea &da )
110+
{
111+
mDa.reset( new QgsDistanceArea( da ) );
112+
}
113+
114+
void QgsExpressionLineEdit::setLayer( QgsVectorLayer* layer )
115+
{
116+
mLayer = layer;
117+
}
118+
119+
QString QgsExpressionLineEdit::expression() const
120+
{
121+
if ( mLineEdit )
122+
return mLineEdit->text();
123+
else if ( mCodeEditor )
124+
return mCodeEditor->text();
125+
126+
return QString();
127+
}
128+
129+
bool QgsExpressionLineEdit::isValidExpression( QString *expressionError ) const
130+
{
131+
QString temp;
132+
return QgsExpression::isValid( expression(), &mExpressionContext, expressionError ? *expressionError : temp );
133+
}
134+
135+
void QgsExpressionLineEdit::registerExpressionContextGenerator( const QgsExpressionContextGenerator* generator )
136+
{
137+
mExpressionContextGenerator = generator;
138+
}
139+
140+
void QgsExpressionLineEdit::setExpression( const QString& newExpression )
141+
{
142+
if ( mLineEdit )
143+
mLineEdit->setText( newExpression );
144+
else if ( mCodeEditor )
145+
mCodeEditor->setText( newExpression );
146+
}
147+
148+
void QgsExpressionLineEdit::editExpression()
149+
{
150+
QString currentExpression = expression();
151+
152+
QgsExpressionContext context = mExpressionContextGenerator ? mExpressionContextGenerator->createExpressionContext() : mExpressionContext;
153+
154+
QgsExpressionBuilderDialog dlg( mLayer, currentExpression, this, "generic", context );
155+
if ( !mDa.isNull() )
156+
{
157+
dlg.setGeomCalculator( *mDa );
158+
}
159+
dlg.setWindowTitle( mExpressionDialogTitle );
160+
161+
if ( dlg.exec() )
162+
{
163+
QString newExpression = dlg.expressionText();
164+
setExpression( newExpression );
165+
}
166+
}
167+
168+
void QgsExpressionLineEdit::expressionEdited()
169+
{
170+
emit expressionChanged( expression() );
171+
}
172+
173+
void QgsExpressionLineEdit::expressionEdited( const QString& expression )
174+
{
175+
updateLineEditStyle( expression );
176+
emit expressionChanged( expression );
177+
}
178+
179+
void QgsExpressionLineEdit::changeEvent( QEvent* event )
180+
{
181+
if ( event->type() == QEvent::EnabledChange )
182+
{
183+
updateLineEditStyle( expression() );
184+
}
185+
}
186+
187+
void QgsExpressionLineEdit::updateLineEditStyle( const QString& expression )
188+
{
189+
if ( !mLineEdit )
190+
return;
191+
192+
QPalette palette;
193+
if ( !isEnabled() )
194+
{
195+
palette.setColor( QPalette::Text, Qt::gray );
196+
}
197+
else
198+
{
199+
bool isValid = true;
200+
if ( !expression.isEmpty() )
201+
{
202+
isValid = isExpressionValid( expression );
203+
}
204+
if ( !isValid )
205+
{
206+
palette.setColor( QPalette::Text, Qt::red );
207+
}
208+
else
209+
{
210+
palette.setColor( QPalette::Text, Qt::black );
211+
}
212+
}
213+
mLineEdit->setPalette( palette );
214+
}
215+
216+
bool QgsExpressionLineEdit::isExpressionValid( const QString& expressionStr )
217+
{
218+
QgsExpression expression( expressionStr );
219+
expression.prepare( &mExpressionContext );
220+
return !expression.hasParserError();
221+
}

0 commit comments

Comments
 (0)