QGIS API Documentation  2.15.0-Master (af20121)
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 {
109 }
110 
112 {
113  QString temp;
114  return QgsExpression::isValid( currentText(), mExpressionContext.data(), expressionError ? *expressionError : temp );
115 }
116 
118 {
119  return !mFieldProxyModel->sourceFieldModel()->isField( currentText() );
120 }
121 
123 {
124  QString text = currentText();
125  bool valueIsExpression = this->isExpression();
126  if ( isValid )
127  {
128  // valid if not an expression (ie, set to a field), or set to an expression and expression is valid
129  *isValid = !valueIsExpression || isValidExpression();
130  }
131  if ( isExpression )
132  {
133  *isExpression = valueIsExpression;
134  }
135  return text;
136 }
137 
139 {
140  return mFieldProxyModel->sourceFieldModel()->layer();
141 }
142 
144 {
145  mExpressionContextCallback = fnGetExpressionContext;
146  mExpressionContextCallbackContext = context;
147 }
148 
150 {
151  QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( layer );
152  if ( vl )
153  {
154  setLayer( vl );
155  }
156 }
157 
159 {
160  if ( mFieldProxyModel->sourceFieldModel()->layer() )
161  disconnect( mFieldProxyModel->sourceFieldModel()->layer(), SIGNAL( updatedFields() ), this, SLOT( reloadLayer() ) );
162 
163  mExpressionContext.reset( new QgsExpressionContext() );
164  mExpressionContext->appendScope( QgsExpressionContextUtils::globalScope() );
165  mExpressionContext->appendScope( QgsExpressionContextUtils::projectScope() );
166  if ( layer )
167  mExpressionContext->appendScope( QgsExpressionContextUtils::layerScope( layer ) );
168 
169  mFieldProxyModel->sourceFieldModel()->setLayer( layer );
170 
171  if ( mFieldProxyModel->sourceFieldModel()->layer() )
172  connect( mFieldProxyModel->sourceFieldModel()->layer(), SIGNAL( updatedFields() ), SLOT( reloadLayer() ), Qt::UniqueConnection );
173 }
174 
176 {
177  if ( fieldName.isEmpty() )
178  return;
179 
180  QModelIndex idx = mFieldProxyModel->sourceFieldModel()->indexFromName( fieldName );
181  if ( !idx.isValid() )
182  {
183  // try to remove quotes and white spaces
184  QString simpleFieldName = fieldName.trimmed();
185  if ( simpleFieldName.startsWith( '"' ) && simpleFieldName.endsWith( '"' ) )
186  {
187  simpleFieldName.remove( 0, 1 ).chop( 1 );
188  idx = mFieldProxyModel->sourceFieldModel()->indexFromName( simpleFieldName );
189  }
190 
191  if ( !idx.isValid() )
192  {
193  // new expression
194  mFieldProxyModel->sourceFieldModel()->setExpression( fieldName );
195  idx = mFieldProxyModel->sourceFieldModel()->indexFromName( fieldName );
196  }
197  }
198  QModelIndex proxyIndex = mFieldProxyModel->mapFromSource( idx );
199  mCombo->setCurrentIndex( proxyIndex.row() );
201 }
202 
204 {
205  QString currentExpression = currentText();
206  QgsVectorLayer* vl = layer();
207 
208  QgsExpressionContext context = mExpressionContextCallback ? mExpressionContextCallback( mExpressionContextCallbackContext ) : *mExpressionContext;
209 
210  QgsExpressionBuilderDialog dlg( vl, currentExpression, this, "generic", context );
211  if ( !mDa.isNull() )
212  {
213  dlg.setGeomCalculator( *mDa );
214  }
215  dlg.setWindowTitle( mExpressionDialogTitle );
216 
217  if ( dlg.exec() )
218  {
219  QString newExpression = dlg.expressionText();
220  setField( newExpression );
221  }
222 }
223 
225 {
226  updateLineEditStyle( expression );
227  emit fieldChanged( expression, isValidExpression() );
228 }
229 
231 {
232  QgsDebugMsg( "Editing finished" );
233  const QString expression = mCombo->lineEdit()->text();
234  mFieldProxyModel->sourceFieldModel()->setExpression( expression );
235  QModelIndex idx = mFieldProxyModel->sourceFieldModel()->indexFromName( expression );
236  QModelIndex proxyIndex = mFieldProxyModel->mapFromSource( idx );
237  mCombo->setCurrentIndex( proxyIndex.row() );
239 }
240 
242 {
243  if ( event->type() == QEvent::EnabledChange )
244  {
246  }
247 }
248 
249 void QgsFieldExpressionWidget::reloadLayer()
250 {
251  setLayer( mFieldProxyModel->sourceFieldModel()->layer() );
252 }
253 
254 void QgsFieldExpressionWidget::beforeResetModel()
255 {
256  // Backup expression
257  mBackupExpression = mCombo->currentText();
258 }
259 
260 void QgsFieldExpressionWidget::afterResetModel()
261 {
262  // Restore expression
263  mCombo->lineEdit()->setText( mBackupExpression );
264 }
265 
267 {
269 
270  bool isExpression, isValid;
271  QString fieldName = currentField( &isExpression, &isValid );
272 
273  // display tooltip if widget is shorter than expression
274  QFontMetrics metrics( mCombo->lineEdit()->font() );
275  if ( metrics.width( fieldName ) > mCombo->lineEdit()->width() )
276  {
277  mCombo->setToolTip( fieldName );
278  }
279  else
280  {
281  mCombo->setToolTip( "" );
282  }
283 
284  emit fieldChanged( fieldName );
285  emit fieldChanged( fieldName, isValid );
286 }
287 
289 {
291  if ( !isEnabled() )
292  {
293  palette.setColor( QPalette::Text, Qt::gray );
294  }
295  else
296  {
297  bool isExpression, isValid;
298  if ( !expression.isEmpty() )
299  {
300  isExpression = true;
301  isValid = isExpressionValid( expression );
302  }
303  else
304  {
305  currentField( &isExpression, &isValid );
306  }
307  QFont font = mCombo->lineEdit()->font();
308  font.setItalic( isExpression );
309  mCombo->lineEdit()->setFont( font );
310 
311  if ( isExpression && !isValid )
312  {
313  palette.setColor( QPalette::Text, Qt::red );
314  }
315  else
316  {
317  palette.setColor( QPalette::Text, Qt::black );
318  }
319  }
320  mCombo->lineEdit()->setPalette( palette );
321 }
322 
324 {
325  QgsExpression expression( expressionStr );
326  expression.prepare( mExpressionContext.data() );
327  return !expression.hasParserError();
328 }
QLayout * layout() const
Class for parsing and evaluation of expressions (formerly called "search strings").
QgsFieldProxyModel * setFilters(const QgsFieldProxyModel::Filters &filters)
Set flags that affect how fields are filtered in the model.
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()
Returns the QgsFieldModel used in this QSortFilterProxyModel.
int width() const
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
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)
QString asExpression() const
Returns the currently selected field or expression.
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)