QGIS API Documentation  2.12.0-Lyon
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( 0 )
29  , mExpressionContextCallback( 0 )
30  , mExpressionContextCallbackContext( 0 )
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  // NW TODO - Fix in 2.6
62 // connect( mCombo->lineEdit(), SIGNAL( returnPressed() ), this, SIGNAL( returnPressed() ) );
63 
64  mExpressionContext.reset( new QgsExpressionContext() );
65  mExpressionContext->appendScope( QgsExpressionContextUtils::globalScope() );
66  mExpressionContext->appendScope( QgsExpressionContextUtils::projectScope() );
67 }
68 
70 {
71  mExpressionDialogTitle = title;
72 }
73 
74 void QgsFieldExpressionWidget::setFilters( const QgsFieldProxyModel::Filters& filters )
75 {
76  mFieldProxyModel->setFilters( filters );
77 }
78 
80 {
81  QHBoxLayout* layout = dynamic_cast<QHBoxLayout*>( this->layout() );
82  if ( !layout )
83  return;
84 
85  if ( isLeft )
86  {
87  QLayoutItem* item = layout->takeAt( 1 );
88  layout->insertWidget( 0, item->widget() );
89  }
90  else
91  layout->addWidget( mCombo );
92 }
93 
95 {
97 }
98 
100 {
101  return mCombo->currentText();
102 }
103 
105 {
106  QString temp;
107  return QgsExpression::isValid( currentText(), mExpressionContext.data(), expressionError ? *expressionError : temp );
108 }
109 
111 {
112  return !mFieldProxyModel->sourceFieldModel()->isField( currentText() );
113 }
114 
115 QString QgsFieldExpressionWidget::currentField( bool *isExpression, bool *isValid ) const
116 {
117  QString text = currentText();
118  if ( isValid )
119  {
120  *isValid = isValidExpression();
121  }
122  if ( isExpression )
123  {
124  *isExpression = this->isExpression();
125  }
126  return text;
127 }
128 
130 {
131  return mFieldProxyModel->sourceFieldModel()->layer();
132 }
133 
135 {
136  mExpressionContextCallback = fnGetExpressionContext;
137  mExpressionContextCallbackContext = context;
138 }
139 
141 {
142  QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( layer );
143  if ( vl )
144  {
145  setLayer( vl );
146  }
147 }
148 
150 {
151  mExpressionContext.reset( new QgsExpressionContext() );
152  mExpressionContext->appendScope( QgsExpressionContextUtils::globalScope() );
153  mExpressionContext->appendScope( QgsExpressionContextUtils::projectScope() );
154  if ( layer )
155  mExpressionContext->appendScope( QgsExpressionContextUtils::layerScope( layer ) );
156 
157  mFieldProxyModel->sourceFieldModel()->setLayer( layer );
158 }
159 
161 {
162  if ( fieldName.isEmpty() )
163  return;
164 
165  QModelIndex idx = mFieldProxyModel->sourceFieldModel()->indexFromName( fieldName );
166  if ( !idx.isValid() )
167  {
168  // try to remove quotes and white spaces
169  QString simpleFieldName = fieldName.trimmed();
170  if ( simpleFieldName.startsWith( '"' ) && simpleFieldName.endsWith( '"' ) )
171  {
172  simpleFieldName.remove( 0, 1 ).chop( 1 );
173  idx = mFieldProxyModel->sourceFieldModel()->indexFromName( simpleFieldName );
174  }
175 
176  if ( !idx.isValid() )
177  {
178  // new expression
179  mFieldProxyModel->sourceFieldModel()->setExpression( fieldName );
180  idx = mFieldProxyModel->sourceFieldModel()->indexFromName( fieldName );
181  }
182  }
183  QModelIndex proxyIndex = mFieldProxyModel->mapFromSource( idx );
184  mCombo->setCurrentIndex( proxyIndex.row() );
186 }
187 
189 {
190  QString currentExpression = currentText();
191  QgsVectorLayer* vl = layer();
192 
193  QgsExpressionContext context = mExpressionContextCallback ? mExpressionContextCallback( mExpressionContextCallbackContext ) : *mExpressionContext;
194 
195  QgsExpressionBuilderDialog dlg( vl, currentExpression, this, "generic", context );
196  if ( !mDa.isNull() )
197  {
198  dlg.setGeomCalculator( *mDa );
199  }
200  dlg.setWindowTitle( mExpressionDialogTitle );
201 
202  if ( dlg.exec() )
203  {
204  QString newExpression = dlg.expressionText();
205  setField( newExpression );
206  }
207 }
208 
210 {
211  updateLineEditStyle( expression );
212  emit fieldChanged( expression, isValidExpression() );
213 }
214 
216 {
217  QgsDebugMsg( "Editing finished" );
218  const QString expression = mCombo->lineEdit()->text();
219  mFieldProxyModel->sourceFieldModel()->setExpression( expression );
220  QModelIndex idx = mFieldProxyModel->sourceFieldModel()->indexFromName( expression );
221  QModelIndex proxyIndex = mFieldProxyModel->mapFromSource( idx );
222  mCombo->setCurrentIndex( proxyIndex.row() );
224 }
225 
227 {
228  if ( event->type() == QEvent::EnabledChange )
229  {
231  }
232 }
233 
235 {
237 
238  bool isExpression, isValid;
239  QString fieldName = currentField( &isExpression, &isValid );
240 
241  // display tooltip if widget is shorter than expression
242  QFontMetrics metrics( mCombo->lineEdit()->font() );
243  if ( metrics.width( fieldName ) > mCombo->lineEdit()->width() )
244  {
245  mCombo->setToolTip( fieldName );
246  }
247  else
248  {
249  mCombo->setToolTip( "" );
250  }
251 
252  emit fieldChanged( fieldName );
253  emit fieldChanged( fieldName, isValid );
254 }
255 
257 {
259  if ( !isEnabled() )
260  {
261  palette.setColor( QPalette::Text, Qt::gray );
262  }
263  else
264  {
265  bool isExpression, isValid;
266  if ( !expression.isEmpty() )
267  {
268  isExpression = true;
269  isValid = isExpressionValid( expression );
270  }
271  else
272  {
273  currentField( &isExpression, &isValid );
274  }
275  QFont font = mCombo->lineEdit()->font();
276  font.setItalic( isExpression );
277  mCombo->lineEdit()->setFont( font );
278 
279  if ( isExpression && !isValid )
280  {
281  palette.setColor( QPalette::Text, Qt::red );
282  }
283  else
284  {
285  palette.setColor( QPalette::Text, Qt::black );
286  }
287  }
288  mCombo->lineEdit()->setPalette( palette );
289 }
290 
292 {
293  QgsExpression expression( expressionStr );
294  expression.prepare( mExpressionContext.data() );
295  return !expression.hasParserError();
296 }
QLayout * layout() const
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:92
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
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)
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)
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
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 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 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.
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)