QGIS API Documentation  2.11.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
qgsfieldexpressionwidget.cpp
Go to the documentation of this file.
1 
2 /***************************************************************************
3  qgsfieldexpressionwidget.cpp
4  --------------------------------------
5  Date : 01.04.2014
6  Copyright : (C) 2014 Denis Rouzaud
7  Email : denis.rouzaud@gmail.com
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16 
17 #include <QHBoxLayout>
18 
19 #include "qgsapplication.h"
22 #include "qgsfieldproxymodel.h"
23 #include "qgsdistancearea.h"
24 
26  : QWidget( parent )
27  , mExpressionDialogTitle( tr( "Expression dialog" ) )
28  , mDa( 0 )
29  , mExpressionContextCallback( 0 )
30  , mExpressionContextCallbackContext( 0 )
31 {
32  QHBoxLayout* layout = new QHBoxLayout( this );
33  layout->setContentsMargins( 0, 0, 0, 0 );
34  mCombo = new QComboBox( this );
35  mCombo->setEditable( true );
36  mCombo->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Minimum );
37  int width = mCombo->minimumSizeHint().width();
38  mCombo->setMinimumWidth( width );
39  mFieldProxyModel = new QgsFieldProxyModel( mCombo );
40  mFieldProxyModel->sourceFieldModel()->setAllowExpression( true );
41  mCombo->setModel( mFieldProxyModel );
42 
43  mButton = new QToolButton( this );
44  mButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
45  mButton->setIcon( QgsApplication::getThemeIcon( "/mIconExpression.svg" ) );
46 
47  layout->addWidget( mCombo );
48  layout->addWidget( mButton );
49 
50  // give focus to the combo
51  // hence if the widget is used as a delegate
52  // it will allow pressing on the expression dialog button
53  setFocusProxy( mCombo );
54 
55  connect( mCombo->lineEdit(), SIGNAL( textEdited( QString ) ), this, SLOT( expressionEdited( QString ) ) );
56  connect( mCombo->lineEdit(), SIGNAL( editingFinished() ), this, SLOT( expressionEditingFinished() ) );
57  connect( mCombo, SIGNAL( activated( int ) ), this, SLOT( currentFieldChanged() ) );
58  connect( mButton, SIGNAL( clicked() ), this, SLOT( editExpression() ) );
59  // NW TODO - Fix in 2.6
60 // connect( mCombo->lineEdit(), SIGNAL( returnPressed() ), this, SIGNAL( returnPressed() ) );
61 
62  mExpressionContext.reset( new QgsExpressionContext() );
63  mExpressionContext->appendScope( QgsExpressionContextUtils::globalScope() );
64  mExpressionContext->appendScope( QgsExpressionContextUtils::projectScope() );
65 }
66 
68 {
69  mExpressionDialogTitle = title;
70 }
71 
72 void QgsFieldExpressionWidget::setFilters( QgsFieldProxyModel::Filters filters )
73 {
74  mFieldProxyModel->setFilters( filters );
75 }
76 
78 {
79  QHBoxLayout* layout = dynamic_cast<QHBoxLayout*>( this->layout() );
80  if ( !layout )
81  return;
82 
83  if ( isLeft )
84  {
85  QLayoutItem* item = layout->takeAt( 1 );
86  layout->insertWidget( 0, item->widget() );
87  }
88  else
89  layout->addWidget( mCombo );
90 }
91 
93 {
95 }
96 
98 {
99  return mCombo->currentText();
100 }
101 
103 {
104  QString temp;
105  return QgsExpression::isValid( currentText(), mExpressionContext.data(), expressionError ? *expressionError : temp );
106 }
107 
109 {
110  return !mFieldProxyModel->sourceFieldModel()->isField( currentText() );
111 }
112 
113 QString QgsFieldExpressionWidget::currentField( bool *isExpression, bool *isValid ) const
114 {
115  QString text = currentText();
116  if ( isValid )
117  {
118  *isValid = isValidExpression();
119  }
120  if ( isExpression )
121  {
122  *isExpression = this->isExpression();
123  }
124  return text;
125 }
126 
128 {
129  return mFieldProxyModel->sourceFieldModel()->layer();
130 }
131 
133 {
134  mExpressionContextCallback = fnGetExpressionContext;
135  mExpressionContextCallbackContext = context;
136 }
137 
139 {
140  QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( layer );
141  if ( vl )
142  {
143  setLayer( vl );
144  }
145 }
146 
148 {
149  mExpressionContext.reset( new QgsExpressionContext() );
150  mExpressionContext->appendScope( QgsExpressionContextUtils::globalScope() );
151  mExpressionContext->appendScope( QgsExpressionContextUtils::projectScope() );
152  if ( layer )
153  mExpressionContext->appendScope( QgsExpressionContextUtils::layerScope( layer ) );
154 
155  mFieldProxyModel->sourceFieldModel()->setLayer( layer );
156 }
157 
159 {
160  if ( fieldName.isEmpty() )
161  return;
162 
163  QModelIndex idx = mFieldProxyModel->sourceFieldModel()->indexFromName( fieldName );
164  if ( !idx.isValid() )
165  {
166  // try to remove quotes and white spaces
167  QString simpleFieldName = fieldName.trimmed();
168  if ( simpleFieldName.startsWith( '"' ) && simpleFieldName.endsWith( '"' ) )
169  {
170  simpleFieldName.remove( 0, 1 ).chop( 1 );
171  idx = mFieldProxyModel->sourceFieldModel()->indexFromName( simpleFieldName );
172  }
173 
174  if ( !idx.isValid() )
175  {
176  // new expression
177  mFieldProxyModel->sourceFieldModel()->setExpression( fieldName );
178  idx = mFieldProxyModel->sourceFieldModel()->indexFromName( fieldName );
179  }
180  }
181  QModelIndex proxyIndex = mFieldProxyModel->mapFromSource( idx );
182  mCombo->setCurrentIndex( proxyIndex.row() );
184 }
185 
187 {
188  QString currentExpression = currentText();
189  QgsVectorLayer* vl = layer();
190 
191  QgsExpressionContext context = mExpressionContextCallback ? mExpressionContextCallback( mExpressionContextCallbackContext ) : *mExpressionContext;
192 
193  QgsExpressionBuilderDialog dlg( vl, currentExpression, this, "generic", context );
194  if ( !mDa.isNull() )
195  {
196  dlg.setGeomCalculator( *mDa );
197  }
198  dlg.setWindowTitle( mExpressionDialogTitle );
199 
200  if ( dlg.exec() )
201  {
202  QString newExpression = dlg.expressionText();
203  setField( newExpression );
204  }
205 }
206 
208 {
209  updateLineEditStyle( expression );
210  emit fieldChanged( expression, isValidExpression() );
211 }
212 
214 {
215  QgsDebugMsg( "Editing finished" );
216  const QString expression = mCombo->lineEdit()->text();
217  mFieldProxyModel->sourceFieldModel()->setExpression( expression );
218  QModelIndex idx = mFieldProxyModel->sourceFieldModel()->indexFromName( expression );
219  QModelIndex proxyIndex = mFieldProxyModel->mapFromSource( idx );
220  mCombo->setCurrentIndex( proxyIndex.row() );
222 }
223 
225 {
226  if ( event->type() == QEvent::EnabledChange )
227  {
229  }
230 }
231 
233 {
235 
236  bool isExpression, isValid;
237  QString fieldName = currentField( &isExpression, &isValid );
238 
239  // display tooltip if widget is shorter than expression
240  QFontMetrics metrics( mCombo->lineEdit()->font() );
241  if ( metrics.width( fieldName ) > mCombo->lineEdit()->width() )
242  {
243  mCombo->setToolTip( fieldName );
244  }
245  else
246  {
247  mCombo->setToolTip( "" );
248  }
249 
250  emit fieldChanged( fieldName );
251  emit fieldChanged( fieldName, isValid );
252 }
253 
255 {
257  if ( !isEnabled() )
258  {
259  palette.setColor( QPalette::Text, Qt::gray );
260  }
261  else
262  {
263  bool isExpression, isValid;
264  if ( !expression.isEmpty() )
265  {
266  isExpression = true;
267  isValid = isExpressionValid( expression );
268  }
269  else
270  {
271  currentField( &isExpression, &isValid );
272  }
273  QFont font = mCombo->lineEdit()->font();
274  font.setItalic( isExpression );
275  mCombo->lineEdit()->setFont( font );
276 
277  if ( isExpression && !isValid )
278  {
279  palette.setColor( QPalette::Text, Qt::red );
280  }
281  else
282  {
283  palette.setColor( QPalette::Text, Qt::black );
284  }
285  }
286  mCombo->lineEdit()->setPalette( palette );
287 }
288 
290 {
291  QgsExpression expression( expressionStr );
292  expression.prepare( mExpressionContext.data() );
293  return !expression.hasParserError();
294 }
QLayout * layout() const
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:88
Base class for all map layer types.
Definition: qgsmaplayer.h:49
Type type() const
void setContentsMargins(int left, int top, int right, int bottom)
const QPalette & palette() const
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
Definition: qgsexpression.h:95
QgsFieldModel * sourceFieldModel()
sourceFieldModel returns the QgsFieldModel used in this QSortFilterProxyModel
int width() const
void setColor(ColorGroup group, ColorRole role, const QColor &color)
void changeEvent(QEvent *event) override
Q_DECL_DEPRECATED bool prepare(const QgsFields &fields)
Get the expression ready for evaluation - find out column indexes.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
bool isValidExpression(QString *expressionError=0) const
Return true if the current expression is valid.
static QIcon getThemeIcon(const QString &theName)
Helper to get a theme icon.
virtual QWidget * widget()
void setMinimumWidth(int minw)
bool isExpressionValid(const QString expressionStr)
void registerGetExpressionContextCallback(ExpressionContextCallback fnGetExpressionContext, const void *context)
Register callback function for retrieving the expression context for the expression.
void setExpression(const QString &expression)
setExpression sets a single expression to be added after the fields at the end of the model ...
int exec()
QString currentText() const
Return the current text that is set in the expression area.
void editExpression()
open the expression dialog to edit the current or add a new expression
void setLayer(QgsVectorLayer *layer)
set the layer used to display the fields and expression
QString & remove(int position, int n)
void setEditable(bool editable)
void chop(int n)
virtual QLayoutItem * takeAt(int index)
static Q_DECL_DEPRECATED bool isValid(const QString &text, const QgsFields &fields, QString &errorMessage)
void setIcon(const QIcon &icon)
QgsVectorLayer * layer()
returns the currently used layer
Definition: qgsfieldmodel.h:68
bool isField(const QString &expression)
void reset(T *other)
int width() const
bool isValid() const
bool isEnabled() const
void addWidget(QWidget *widget, int stretch, QFlags< Qt::AlignmentFlag > alignment)
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
The QgsFieldProxyModel class provides an easy to use model to display the list of fields of a layer...
virtual QSize minimumSizeHint() const
QgsFieldProxyModel * setFilters(Filters filters)
setFilters set flags that affect how fields are filtered
void setExpressionDialogTitle(QString title)
define the title used in the expression dialog
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setFocusProxy(QWidget *w)
bool isEmpty() const
QString trimmed() const
void setLayer(QgsVectorLayer *layer)
set the layer of whch fields are displayed
int row() const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
void setGeomCalculator(const QgsDistanceArea &da)
Sets geometry calculator used in distance/area calculations.
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
void setAllowExpression(bool allowExpression)
returns the currently used layer
void setGeomCalculator(const QgsDistanceArea &da)
set the geometry calculator used in the expression dialog
void expressionEdited(const QString expression)
when expression is edited by the user in the line edit, it will be checked for validity ...
void setSizePolicy(QSizePolicy)
T * data() const
QgsVectorLayer * layer() const
Returns the currently used layer.
void updateLineEditStyle(const QString expression=QString())
updateLineEditStyle will re-style (color/font) the line edit depending on content and status ...
void setItalic(bool enable)
General purpose distance and area calculator.
QLineEdit * lineEdit() const
void expressionEditingFinished()
when expression has been edited (finished) it will be added to the model
QString currentField(bool *isExpression=0, bool *isValid=0) const
currentField returns the currently selected field or expression if allowed
void setFilters(QgsFieldProxyModel::Filters filters)
setFilters allows fitering according to the type of field
void setModel(QAbstractItemModel *model)
virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const
void setWindowTitle(const QString &)
void setField(const QString &fieldName)
sets the current field or expression in the widget
void setCurrentIndex(int index)
void insertWidget(int index, QWidget *widget, int stretch, QFlags< Qt::AlignmentFlag > alignment)
void setToolTip(const QString &)
static QgsExpressionContextScope * projectScope()
Creates a new scope which contains variables and functions relating to the current QGIS project...
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool isNull() const
QModelIndex indexFromName(const QString &fieldName)
return the index corresponding to a given fieldName
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
Represents a vector layer which manages a vector based data sets.
A generic dialog for building expression strings.
QgsExpressionContext(* ExpressionContextCallback)(const void *context)
Callback function for retrieving the expression context for the expression.
QgsFieldExpressionWidget(QWidget *parent=0)
QgsFieldExpressionWidget creates a widget with a combo box to display the fields and expression and a...
void fieldChanged(QString fieldName)
the signal is emitted when the currently selected field changes
#define tr(sourceText)