QGIS API Documentation  2.9.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 : 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 {
30  QHBoxLayout* layout = new QHBoxLayout( this );
31  layout->setContentsMargins( 0, 0, 0, 0 );
32  mCombo = new QComboBox( this );
33  mCombo->setEditable( true );
34  mCombo->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Minimum );
35  int width = mCombo->minimumSizeHint().width();
36  mCombo->setMinimumWidth( width );
37  mFieldProxyModel = new QgsFieldProxyModel( mCombo );
38  mFieldProxyModel->sourceFieldModel()->setAllowExpression( true );
39  mCombo->setModel( mFieldProxyModel );
40 
41  mButton = new QToolButton( this );
42  mButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
43  mButton->setIcon( QgsApplication::getThemeIcon( "/mIconExpression.svg" ) );
44 
45  layout->addWidget( mCombo );
46  layout->addWidget( mButton );
47 
48  // give focus to the combo
49  // hence if the widget is used as a delegate
50  // it will allow pressing on the expression dialog button
51  setFocusProxy( mCombo );
52 
53  connect( mCombo->lineEdit(), SIGNAL( textEdited( QString ) ), this, SLOT( expressionEdited( QString ) ) );
54  connect( mCombo->lineEdit(), SIGNAL( editingFinished() ), this, SLOT( expressionEditingFinished() ) );
55  connect( mCombo, SIGNAL( activated( int ) ), this, SLOT( currentFieldChanged() ) );
56  connect( mButton, SIGNAL( clicked() ), this, SLOT( editExpression() ) );
57  // NW TODO - Fix in 2.6
58 // connect( mCombo->lineEdit(), SIGNAL( returnPressed() ), this, SIGNAL( returnPressed() ) );
59 }
60 
62 {
63  mExpressionDialogTitle = title;
64 }
65 
66 void QgsFieldExpressionWidget::setFilters( QgsFieldProxyModel::Filters filters )
67 {
68  mFieldProxyModel->setFilters( filters );
69 }
70 
72 {
73  QHBoxLayout* layout = dynamic_cast<QHBoxLayout*>( this->layout() );
74  if ( !layout )
75  return;
76 
77  if ( isLeft )
78  {
79  QLayoutItem* item = layout->takeAt( 1 );
80  layout->insertWidget( 0, item->widget() );
81  }
82  else
83  layout->addWidget( mCombo );
84 }
85 
87 {
88  mDa = QSharedPointer<const QgsDistanceArea>( new QgsDistanceArea( da ) );
89 }
90 
92 {
93  return mCombo->currentText();
94 }
95 
96 bool QgsFieldExpressionWidget::isValidExpression( QString *expressionError ) const
97 {
98  QString temp;
99  QgsVectorLayer* vl = layer();
100  return QgsExpression::isValid( currentText(), vl ? vl->pendingFields() : QgsFields(), expressionError ? *expressionError : temp );
101 }
102 
104 {
105  return !mFieldProxyModel->sourceFieldModel()->isField( currentText() );
106 }
107 
108 QString QgsFieldExpressionWidget::currentField( bool *isExpression, bool *isValid ) const
109 {
110  QString text = currentText();
111  if ( isValid )
112  {
113  *isValid = isValidExpression();
114  }
115  if ( isExpression )
116  {
117  *isExpression = this->isExpression();
118  }
119  return text;
120 }
121 
123 {
124  return mFieldProxyModel->sourceFieldModel()->layer();
125 }
126 
128 {
129  QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( layer );
130  if ( vl )
131  {
132  setLayer( vl );
133  }
134 }
135 
137 {
138  mFieldProxyModel->sourceFieldModel()->setLayer( layer );
139 }
140 
141 void QgsFieldExpressionWidget::setField( const QString &fieldName )
142 {
143  if ( fieldName.isEmpty() )
144  return;
145 
146  QModelIndex idx = mFieldProxyModel->sourceFieldModel()->indexFromName( fieldName );
147  if ( !idx.isValid() )
148  {
149  // try to remove quotes and white spaces
150  QString simpleFieldName = fieldName.trimmed();
151  if ( simpleFieldName.startsWith( '"' ) && simpleFieldName.endsWith( '"' ) )
152  {
153  simpleFieldName.remove( 0, 1 ).chop( 1 );
154  idx = mFieldProxyModel->sourceFieldModel()->indexFromName( simpleFieldName );
155  }
156 
157  if ( !idx.isValid() )
158  {
159  // new expression
160  mFieldProxyModel->sourceFieldModel()->setExpression( fieldName );
161  idx = mFieldProxyModel->sourceFieldModel()->indexFromName( fieldName );
162  }
163  }
164  QModelIndex proxyIndex = mFieldProxyModel->mapFromSource( idx );
165  mCombo->setCurrentIndex( proxyIndex.row() );
167 }
168 
170 {
171  QString currentExpression = currentText();
172  QgsVectorLayer* vl = layer();
173 
174  QgsExpressionBuilderDialog dlg( vl, currentExpression );
175  if ( !mDa.isNull() )
176  {
177  dlg.setGeomCalculator( *mDa );
178  }
179  dlg.setWindowTitle( mExpressionDialogTitle );
180 
181  if ( dlg.exec() )
182  {
183  QString newExpression = dlg.expressionText();
184  setField( newExpression );
185  }
186 }
187 
188 void QgsFieldExpressionWidget::expressionEdited( const QString expression )
189 {
190  updateLineEditStyle( expression );
191  emit fieldChanged( expression, isValidExpression() );
192 }
193 
195 {
196  QgsDebugMsg( "Editing finished" );
197  const QString expression = mCombo->lineEdit()->text();
198  mFieldProxyModel->sourceFieldModel()->setExpression( expression );
199  QModelIndex idx = mFieldProxyModel->sourceFieldModel()->indexFromName( expression );
200  QModelIndex proxyIndex = mFieldProxyModel->mapFromSource( idx );
201  mCombo->setCurrentIndex( proxyIndex.row() );
203 }
204 
206 {
207  if ( event->type() == QEvent::EnabledChange )
208  {
210  }
211 }
212 
214 {
216 
217  bool isExpression, isValid;
218  QString fieldName = currentField( &isExpression, &isValid );
219 
220  // display tooltip if widget is shorter than expression
221  QFontMetrics metrics( mCombo->lineEdit()->font() );
222  if ( metrics.width( fieldName ) > mCombo->lineEdit()->width() )
223  {
224  mCombo->setToolTip( fieldName );
225  }
226  else
227  {
228  mCombo->setToolTip( "" );
229  }
230 
231  emit fieldChanged( fieldName );
232  emit fieldChanged( fieldName, isValid );
233 }
234 
235 void QgsFieldExpressionWidget::updateLineEditStyle( const QString expression )
236 {
237  QPalette palette;
238  if ( !isEnabled() )
239  {
240  palette.setColor( QPalette::Text, Qt::gray );
241  }
242  else
243  {
244  bool isExpression, isValid;
245  if ( !expression.isEmpty() )
246  {
247  isExpression = true;
248  isValid = isExpressionValid( expression );
249  }
250  else
251  {
252  currentField( &isExpression, &isValid );
253  }
254  QFont font = mCombo->lineEdit()->font();
255  font.setItalic( isExpression );
256  mCombo->lineEdit()->setFont( font );
257 
258  if ( isExpression && !isValid )
259  {
260  palette.setColor( QPalette::Text, Qt::red );
261  }
262  else
263  {
264  palette.setColor( QPalette::Text, Qt::black );
265  }
266  }
267  mCombo->lineEdit()->setPalette( palette );
268 }
269 
270 bool QgsFieldExpressionWidget::isExpressionValid( const QString expressionStr )
271 {
272  QgsVectorLayer* vl = layer();
273 
274  QgsExpression expression( expressionStr );
275  expression.prepare( vl ? vl->pendingFields() : QgsFields() );
276  return !expression.hasParserError();
277 }
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:86
Base class for all map layer types.
Definition: qgsmaplayer.h:49
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
Definition: qgsexpression.h:93
QgsFieldModel * sourceFieldModel()
sourceFieldModel returns the QgsFieldModel used in this QSortFilterProxyModel
void changeEvent(QEvent *event) override
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.
bool isExpressionValid(const QString expressionStr)
void setExpression(const QString &expression)
setExpression sets a single expression to be added after the fields at the end of the model ...
Container of fields for a vector layer.
Definition: qgsfield.h:173
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
static bool isValid(const QString &text, const QgsFields &fields, QString &errorMessage)
QgsVectorLayer * layer()
returns the currently used layer
Definition: qgsfieldmodel.h:68
bool isField(const QString &expression)
The QgsFieldProxyModel class provides an easy to use model to display the list of fields of a layer...
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
void setLayer(QgsVectorLayer *layer)
set the layer of whch fields are displayed
void setGeomCalculator(const QgsDistanceArea &da)
Sets geometry calculator used in distance/area calculations.
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 ...
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 ...
General purpose distance and area calculator.
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 setField(const QString &fieldName)
sets the current field or expression in the widget
const QgsFields & pendingFields() const
returns field list in the to-be-committed state
QModelIndex indexFromName(const QString &fieldName)
return the index corresponding to a given fieldName
Represents a vector layer which manages a vector based data sets.
A generic dialog for building expression strings.
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)