QGIS API Documentation  2.13.0-Master
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 : [email protected]
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( nullptr )
29  , mExpressionContextCallback( nullptr )
30  , mExpressionContextCallbackContext( nullptr )
31 {
32  QHBoxLayout* layout = new QHBoxLayout( this );
33  layout->setContentsMargins( 0, 0, 0, 0 );
34 
35  mCombo = new QComboBox( this );
36  mCombo->setEditable( true );
37  mCombo->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Minimum );
38  int width = mCombo->minimumSizeHint().width();
39  mCombo->setMinimumWidth( width );
40 
41  mFieldProxyModel = new QgsFieldProxyModel( mCombo );
42  mFieldProxyModel->sourceFieldModel()->setAllowExpression( true );
43  mCombo->setModel( mFieldProxyModel );
44 
45  mButton = new QToolButton( this );
46  mButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
47  mButton->setIcon( QgsApplication::getThemeIcon( "/mIconExpression.svg" ) );
48 
49  layout->addWidget( mCombo );
50  layout->addWidget( mButton );
51 
52  // give focus to the combo
53  // hence if the widget is used as a delegate
54  // it will allow pressing on the expression dialog button
55  setFocusProxy( mCombo );
56 
57  connect( mCombo->lineEdit(), SIGNAL( textEdited( QString ) ), this, SLOT( expressionEdited( QString ) ) );
58  connect( mCombo->lineEdit(), SIGNAL( editingFinished() ), this, SLOT( expressionEditingFinished() ) );
59  connect( mCombo, SIGNAL( activated( int ) ), this, SLOT( currentFieldChanged() ) );
60  connect( mButton, SIGNAL( clicked() ), this, SLOT( editExpression() ) );
61  connect( mFieldProxyModel, SIGNAL( modelAboutToBeReset() ), this, SLOT( beforeResetModel() ) );
62  connect( mFieldProxyModel, SIGNAL( modelReset() ), this, SLOT( afterResetModel() ) );
63  // NW TODO - Fix in 2.6
64 // connect( mCombo->lineEdit(), SIGNAL( returnPressed() ), this, SIGNAL( returnPressed() ) );
65 
66  mExpressionContext.reset( new QgsExpressionContext() );
67  mExpressionContext->appendScope( QgsExpressionContextUtils::globalScope() );
68  mExpressionContext->appendScope( QgsExpressionContextUtils::projectScope() );
69 }
70 
72 {
73  mExpressionDialogTitle = title;
74 }
75 
76 void QgsFieldExpressionWidget::setFilters( const QgsFieldProxyModel::Filters& filters )
77 {
78  mFieldProxyModel->setFilters( filters );
79 }
80 
82 {
83  QHBoxLayout* layout = dynamic_cast<QHBoxLayout*>( this->layout() );
84  if ( !layout )
85  return;
86 
87  if ( isLeft )
88  {
89  QLayoutItem* item = layout->takeAt( 1 );
90  layout->insertWidget( 0, item->widget() );
91  }
92  else
93  layout->addWidget( mCombo );
94 }
95 
97 {
99 }
100 
102 {
103  return mCombo->currentText();
104 }
105 
107 {
108  QString temp;
109  return QgsExpression::isValid( currentText(), mExpressionContext.data(), expressionError ? *expressionError : temp );
110 }
111 
113 {
114  return !mFieldProxyModel->sourceFieldModel()->isField( currentText() );
115 }
116 
118 {
119  QString text = currentText();
120  if ( isValid )
121  {
122  *isValid = isValidExpression();
123  }
124  if ( isExpression )
125  {
126  *isExpression = this->isExpression();
127  }
128  return text;
129 }
130 
132 {
133  return mFieldProxyModel->sourceFieldModel()->layer();
134 }
135 
137 {
138  mExpressionContextCallback = fnGetExpressionContext;
139  mExpressionContextCallbackContext = context;
140 }
141 
143 {
144  QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( layer );
145  if ( vl )
146  {
147  setLayer( vl );
148  }
149 }
150 
152 {
153  if ( mFieldProxyModel->sourceFieldModel()->layer() )
154  disconnect( mFieldProxyModel->sourceFieldModel()->layer(), SIGNAL( updatedFields() ), this, SLOT( reloadLayer() ) );
155 
156  mExpressionContext.reset( new QgsExpressionContext() );
157  mExpressionContext->appendScope( QgsExpressionContextUtils::globalScope() );
158  mExpressionContext->appendScope( QgsExpressionContextUtils::projectScope() );
159  if ( layer )
160  mExpressionContext->appendScope( QgsExpressionContextUtils::layerScope( layer ) );
161 
162  mFieldProxyModel->sourceFieldModel()->setLayer( layer );
163 
164  if ( mFieldProxyModel->sourceFieldModel()->layer() )
165  connect( mFieldProxyModel->sourceFieldModel()->layer(), SIGNAL( updatedFields() ), SLOT( reloadLayer() ), Qt::UniqueConnection );
166 }
167 
169 {
170  if ( fieldName.isEmpty() )
171  return;
172 
173  QModelIndex idx = mFieldProxyModel->sourceFieldModel()->indexFromName( fieldName );
174  if ( !idx.isValid() )
175  {
176  // try to remove quotes and white spaces
177  QString simpleFieldName = fieldName.trimmed();
178  if ( simpleFieldName.startsWith( '"' ) && simpleFieldName.endsWith( '"' ) )
179  {
180  simpleFieldName.remove( 0, 1 ).chop( 1 );
181  idx = mFieldProxyModel->sourceFieldModel()->indexFromName( simpleFieldName );
182  }
183 
184  if ( !idx.isValid() )
185  {
186  // new expression
187  mFieldProxyModel->sourceFieldModel()->setExpression( fieldName );
188  idx = mFieldProxyModel->sourceFieldModel()->indexFromName( fieldName );
189  }
190  }
191  QModelIndex proxyIndex = mFieldProxyModel->mapFromSource( idx );
192  mCombo->setCurrentIndex( proxyIndex.row() );
194 }
195 
197 {
198  QString currentExpression = currentText();
199  QgsVectorLayer* vl = layer();
200 
201  QgsExpressionContext context = mExpressionContextCallback ? mExpressionContextCallback( mExpressionContextCallbackContext ) : *mExpressionContext;
202 
203  QgsExpressionBuilderDialog dlg( vl, currentExpression, this, "generic", context );
204  if ( !mDa.isNull() )
205  {
206  dlg.setGeomCalculator( *mDa );
207  }
208  dlg.setWindowTitle( mExpressionDialogTitle );
209 
210  if ( dlg.exec() )
211  {
212  QString newExpression = dlg.expressionText();
213  setField( newExpression );
214  }
215 }
216 
218 {
219  updateLineEditStyle( expression );
220  emit fieldChanged( expression, isValidExpression() );
221 }
222 
224 {
225  QgsDebugMsg( "Editing finished" );
226  const QString expression = mCombo->lineEdit()->text();
227  mFieldProxyModel->sourceFieldModel()->setExpression( expression );
228  QModelIndex idx = mFieldProxyModel->sourceFieldModel()->indexFromName( expression );
229  QModelIndex proxyIndex = mFieldProxyModel->mapFromSource( idx );
230  mCombo->setCurrentIndex( proxyIndex.row() );
232 }
233 
235 {
236  if ( event->type() == QEvent::EnabledChange )
237  {
239  }
240 }
241 
242 void QgsFieldExpressionWidget::reloadLayer()
243 {
244  setLayer( mFieldProxyModel->sourceFieldModel()->layer() );
245 }
246 
247 void QgsFieldExpressionWidget::beforeResetModel()
248 {
249  // Backup expression
250  mBackupExpression = mCombo->currentText();
251 }
252 
253 void QgsFieldExpressionWidget::afterResetModel()
254 {
255  // Restore expression
256  mCombo->lineEdit()->setText( mBackupExpression );
257 }
258 
260 {
262 
263  bool isExpression, isValid;
264  QString fieldName = currentField( &isExpression, &isValid );
265 
266  // display tooltip if widget is shorter than expression
267  QFontMetrics metrics( mCombo->lineEdit()->font() );
268  if ( metrics.width( fieldName ) > mCombo->lineEdit()->width() )
269  {
270  mCombo->setToolTip( fieldName );
271  }
272  else
273  {
274  mCombo->setToolTip( "" );
275  }
276 
277  emit fieldChanged( fieldName );
278  emit fieldChanged( fieldName, isValid );
279 }
280 
282 {
284  if ( !isEnabled() )
285  {
286  palette.setColor( QPalette::Text, Qt::gray );
287  }
288  else
289  {
290  bool isExpression, isValid;
291  if ( !expression.isEmpty() )
292  {
293  isExpression = true;
294  isValid = isExpressionValid( expression );
295  }
296  else
297  {
298  currentField( &isExpression, &isValid );
299  }
300  QFont font = mCombo->lineEdit()->font();
301  font.setItalic( isExpression );
302  mCombo->lineEdit()->setFont( font );
303 
304  if ( isExpression && !isValid )
305  {
306  palette.setColor( QPalette::Text, Qt::red );
307  }
308  else
309  {
310  palette.setColor( QPalette::Text, Qt::black );
311  }
312  }
313  mCombo->lineEdit()->setPalette( palette );
314 }
315 
317 {
318  QgsExpression expression( expressionStr );
319  expression.prepare( mExpressionContext.data() );
320  return !expression.hasParserError();
321 }
QLayout * layout() const
Class for parsing and evaluation of expressions (formerly called "search strings").
QgsFieldProxyModel * setFilters(const QgsFieldProxyModel::Filters &filters)
setFilters set flags that affect how fields are filtered
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.
QgsFieldModel * sourceFieldModel()
sourceFieldModel returns the QgsFieldModel used in this QSortFilterProxyModel
int width() const
bool isValidExpression(QString *expressionError=nullptr) const
Return true if the current expression is valid.
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.
QString currentField(bool *isExpression=nullptr, bool *isValid=nullptr) const
currentField returns the currently selected field or expression if allowed
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
static QIcon getThemeIcon(const QString &theName)
Helper to get a theme icon.
virtual QWidget * widget()
void setMinimumWidth(int minw)
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 setFilters(const QgsFieldProxyModel::Filters &filters)
setFilters allows fitering according to the type of field
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)
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
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...
void expressionEdited(const QString &expression)
when expression is edited by the user in the line edit, it will be checked for validity ...
The QgsFieldProxyModel class provides an easy to use model to display the list of fields of a layer...
virtual QSize minimumSizeHint() const
bool isExpression() const
If the content is not just a simple field this method will return true.
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
bool isExpressionValid(const QString &expressionStr)
void setGeomCalculator(const QgsDistanceArea &da)
set the geometry calculator used in the expression dialog
void setSizePolicy(QSizePolicy)
T * data() const
QgsVectorLayer * layer() const
Returns the currently used layer.
void fieldChanged(const QString &fieldName)
the signal is emitted when the currently selected field changes
void setItalic(bool enable)
QgsFieldProxyModel::Filters filters() const
currently used filter on list of fields
General purpose distance and area calculator.
QLineEdit * lineEdit() const
void expressionEditingFinished()
when expression has been edited (finished) it will be added to the model
void setExpressionDialogTitle(const QString &title)
define the title used in the expression dialog
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 updateLineEditStyle(const QString &expression=QString())
updateLineEditStyle will re-style (color/font) the line edit depending on content and status ...
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.
virtual bool event(QEvent *event)
A generic dialog for building expression strings.
QgsFieldExpressionWidget(QWidget *parent=nullptr)
QgsFieldExpressionWidget creates a widget with a combo box to display the fields and expression and a...
QgsExpressionContext(* ExpressionContextCallback)(const void *context)
Callback function for retrieving the expression context for the expression.
#define tr(sourceText)