QGIS API Documentation  2.14.0-Essen
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  if ( isValid )
126  {
127  *isValid = isValidExpression();
128  }
129  if ( isExpression )
130  {
131  *isExpression = this->isExpression();
132  }
133  return text;
134 }
135 
137 {
138  return mFieldProxyModel->sourceFieldModel()->layer();
139 }
140 
142 {
143  mExpressionContextCallback = fnGetExpressionContext;
144  mExpressionContextCallbackContext = context;
145 }
146 
148 {
149  QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( layer );
150  if ( vl )
151  {
152  setLayer( vl );
153  }
154 }
155 
157 {
158  if ( mFieldProxyModel->sourceFieldModel()->layer() )
159  disconnect( mFieldProxyModel->sourceFieldModel()->layer(), SIGNAL( updatedFields() ), this, SLOT( reloadLayer() ) );
160 
161  mExpressionContext.reset( new QgsExpressionContext() );
162  mExpressionContext->appendScope( QgsExpressionContextUtils::globalScope() );
163  mExpressionContext->appendScope( QgsExpressionContextUtils::projectScope() );
164  if ( layer )
165  mExpressionContext->appendScope( QgsExpressionContextUtils::layerScope( layer ) );
166 
167  mFieldProxyModel->sourceFieldModel()->setLayer( layer );
168 
169  if ( mFieldProxyModel->sourceFieldModel()->layer() )
170  connect( mFieldProxyModel->sourceFieldModel()->layer(), SIGNAL( updatedFields() ), SLOT( reloadLayer() ), Qt::UniqueConnection );
171 }
172 
174 {
175  if ( fieldName.isEmpty() )
176  return;
177 
178  QModelIndex idx = mFieldProxyModel->sourceFieldModel()->indexFromName( fieldName );
179  if ( !idx.isValid() )
180  {
181  // try to remove quotes and white spaces
182  QString simpleFieldName = fieldName.trimmed();
183  if ( simpleFieldName.startsWith( '"' ) && simpleFieldName.endsWith( '"' ) )
184  {
185  simpleFieldName.remove( 0, 1 ).chop( 1 );
186  idx = mFieldProxyModel->sourceFieldModel()->indexFromName( simpleFieldName );
187  }
188 
189  if ( !idx.isValid() )
190  {
191  // new expression
192  mFieldProxyModel->sourceFieldModel()->setExpression( fieldName );
193  idx = mFieldProxyModel->sourceFieldModel()->indexFromName( fieldName );
194  }
195  }
196  QModelIndex proxyIndex = mFieldProxyModel->mapFromSource( idx );
197  mCombo->setCurrentIndex( proxyIndex.row() );
199 }
200 
202 {
203  QString currentExpression = currentText();
204  QgsVectorLayer* vl = layer();
205 
206  QgsExpressionContext context = mExpressionContextCallback ? mExpressionContextCallback( mExpressionContextCallbackContext ) : *mExpressionContext;
207 
208  QgsExpressionBuilderDialog dlg( vl, currentExpression, this, "generic", context );
209  if ( !mDa.isNull() )
210  {
211  dlg.setGeomCalculator( *mDa );
212  }
213  dlg.setWindowTitle( mExpressionDialogTitle );
214 
215  if ( dlg.exec() )
216  {
217  QString newExpression = dlg.expressionText();
218  setField( newExpression );
219  }
220 }
221 
223 {
224  updateLineEditStyle( expression );
225  emit fieldChanged( expression, isValidExpression() );
226 }
227 
229 {
230  QgsDebugMsg( "Editing finished" );
231  const QString expression = mCombo->lineEdit()->text();
232  mFieldProxyModel->sourceFieldModel()->setExpression( expression );
233  QModelIndex idx = mFieldProxyModel->sourceFieldModel()->indexFromName( expression );
234  QModelIndex proxyIndex = mFieldProxyModel->mapFromSource( idx );
235  mCombo->setCurrentIndex( proxyIndex.row() );
237 }
238 
240 {
241  if ( event->type() == QEvent::EnabledChange )
242  {
244  }
245 }
246 
247 void QgsFieldExpressionWidget::reloadLayer()
248 {
249  setLayer( mFieldProxyModel->sourceFieldModel()->layer() );
250 }
251 
252 void QgsFieldExpressionWidget::beforeResetModel()
253 {
254  // Backup expression
255  mBackupExpression = mCombo->currentText();
256 }
257 
258 void QgsFieldExpressionWidget::afterResetModel()
259 {
260  // Restore expression
261  mCombo->lineEdit()->setText( mBackupExpression );
262 }
263 
265 {
267 
268  bool isExpression, isValid;
269  QString fieldName = currentField( &isExpression, &isValid );
270 
271  // display tooltip if widget is shorter than expression
272  QFontMetrics metrics( mCombo->lineEdit()->font() );
273  if ( metrics.width( fieldName ) > mCombo->lineEdit()->width() )
274  {
275  mCombo->setToolTip( fieldName );
276  }
277  else
278  {
279  mCombo->setToolTip( "" );
280  }
281 
282  emit fieldChanged( fieldName );
283  emit fieldChanged( fieldName, isValid );
284 }
285 
287 {
289  if ( !isEnabled() )
290  {
291  palette.setColor( QPalette::Text, Qt::gray );
292  }
293  else
294  {
295  bool isExpression, isValid;
296  if ( !expression.isEmpty() )
297  {
298  isExpression = true;
299  isValid = isExpressionValid( expression );
300  }
301  else
302  {
303  currentField( &isExpression, &isValid );
304  }
305  QFont font = mCombo->lineEdit()->font();
306  font.setItalic( isExpression );
307  mCombo->lineEdit()->setFont( font );
308 
309  if ( isExpression && !isValid )
310  {
311  palette.setColor( QPalette::Text, Qt::red );
312  }
313  else
314  {
315  palette.setColor( QPalette::Text, Qt::black );
316  }
317  }
318  mCombo->lineEdit()->setPalette( palette );
319 }
320 
322 {
323  QgsExpression expression( expressionStr );
324  expression.prepare( mExpressionContext.data() );
325  return !expression.hasParserError();
326 }
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
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)