QGIS API Documentation  2.7.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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 ( isLeft )
75  {
76  QLayoutItem* item = layout->takeAt( 1 );
77  layout->insertWidget( 0, item->widget() );
78  }
79  else
80  layout->addWidget( mCombo );
81 }
82 
84 {
85  mDa = QSharedPointer<const QgsDistanceArea>( new QgsDistanceArea( da ) );
86 }
87 
89 {
90  return mCombo->currentText();
91 }
92 
93 bool QgsFieldExpressionWidget::isValidExpression( QString *expressionError ) const
94 {
95  QString temp;
96  QgsVectorLayer* vl = layer();
97  return QgsExpression::isValid( currentText(), vl ? vl->pendingFields() : QgsFields(), expressionError ? *expressionError : temp );
98 }
99 
101 {
102  return !mFieldProxyModel->sourceFieldModel()->isField( currentText() );
103 }
104 
105 QString QgsFieldExpressionWidget::currentField( bool *isExpression, bool *isValid ) const
106 {
107  QString text = currentText();
108  if ( isValid )
109  {
110  *isValid = isValidExpression();
111  }
112  if ( isExpression )
113  {
114  *isExpression = this->isExpression();
115  }
116  return text;
117 }
118 
120 {
121  return mFieldProxyModel->sourceFieldModel()->layer();
122 }
123 
125 {
126  QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( layer );
127  if ( vl )
128  {
129  setLayer( vl );
130  }
131 }
132 
134 {
135  mFieldProxyModel->sourceFieldModel()->setLayer( layer );
136 }
137 
138 void QgsFieldExpressionWidget::setField( const QString &fieldName )
139 {
140  if ( fieldName.isEmpty() )
141  return;
142 
143  QModelIndex idx = mFieldProxyModel->sourceFieldModel()->indexFromName( fieldName );
144  if ( !idx.isValid() )
145  {
146  // try to remove quotes and white spaces
147  QString simpleFieldName = fieldName.trimmed();
148  if ( simpleFieldName.startsWith( '"' ) && simpleFieldName.endsWith( '"' ) )
149  {
150  simpleFieldName.remove( 0, 1 ).chop( 1 );
151  idx = mFieldProxyModel->sourceFieldModel()->indexFromName( simpleFieldName );
152  }
153 
154  if ( !idx.isValid() )
155  {
156  // new expression
157  mFieldProxyModel->sourceFieldModel()->setExpression( fieldName );
158  idx = mFieldProxyModel->sourceFieldModel()->indexFromName( fieldName );
159  }
160  }
161  QModelIndex proxyIndex = mFieldProxyModel->mapFromSource( idx );
162  mCombo->setCurrentIndex( proxyIndex.row() );
164 }
165 
167 {
168  QString currentExpression = currentText();
169  QgsVectorLayer* vl = layer();
170 
171  QgsExpressionBuilderDialog dlg( vl, currentExpression );
172  if ( !mDa.isNull() )
173  {
174  dlg.setGeomCalculator( *mDa );
175  }
176  dlg.setWindowTitle( mExpressionDialogTitle );
177 
178  if ( dlg.exec() )
179  {
180  QString newExpression = dlg.expressionText();
181  setField( newExpression );
182  }
183 }
184 
185 void QgsFieldExpressionWidget::expressionEdited( const QString expression )
186 {
187  updateLineEditStyle( expression );
188  emit fieldChanged( expression, isValidExpression() );
189 }
190 
192 {
193  QgsDebugMsg( "Editing finsihed" );
194  const QString expression = mCombo->lineEdit()->text();
195  mFieldProxyModel->sourceFieldModel()->setExpression( expression );
196  QModelIndex idx = mFieldProxyModel->sourceFieldModel()->indexFromName( expression );
197  QModelIndex proxyIndex = mFieldProxyModel->mapFromSource( idx );
198  mCombo->setCurrentIndex( proxyIndex.row() );
200 }
201 
203 {
204  if ( event->type() == QEvent::EnabledChange )
205  {
207  }
208 }
209 
211 {
213 
214  bool isExpression, isValid;
215  QString fieldName = currentField( &isExpression, &isValid );
216 
217  // display tooltip if widget is shorter than expression
218  QFontMetrics metrics( mCombo->lineEdit()->font() );
219  if ( metrics.width( fieldName ) > mCombo->lineEdit()->width() )
220  {
221  mCombo->setToolTip( fieldName );
222  }
223  else
224  {
225  mCombo->setToolTip( "" );
226  }
227 
228  emit fieldChanged( fieldName );
229  emit fieldChanged( fieldName, isValid );
230 }
231 
232 void QgsFieldExpressionWidget::updateLineEditStyle( const QString expression )
233 {
234  QPalette palette;
235  if ( !isEnabled() )
236  {
237  palette.setColor( QPalette::Text, Qt::gray );
238  }
239  else
240  {
241  bool isExpression, isValid;
242  if ( !expression.isEmpty() )
243  {
244  isExpression = true;
245  isValid = isExpressionValid( expression );
246  }
247  else
248  {
249  currentField( &isExpression, &isValid );
250  }
251  QFont font = mCombo->lineEdit()->font();
252  font.setItalic( isExpression );
253  mCombo->lineEdit()->setFont( font );
254 
255  if ( isExpression && !isValid )
256  {
257  palette.setColor( QPalette::Text, Qt::red );
258  }
259  else
260  {
261  palette.setColor( QPalette::Text, Qt::black );
262  }
263  }
264  mCombo->lineEdit()->setPalette( palette );
265 }
266 
267 bool QgsFieldExpressionWidget::isExpressionValid( const QString expressionStr )
268 {
269  QgsVectorLayer* vl = layer();
270 
271  QgsExpression expression( expressionStr );
272  expression.prepare( vl ? vl->pendingFields() : QgsFields() );
273  return !expression.hasParserError();
274 }
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:87
Base class for all map layer types.
Definition: qgsmaplayer.h:48
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
Definition: qgsexpression.h:94
QgsFieldModel * sourceFieldModel()
sourceFieldModel returns the QgsFieldModel used in this QSortFilterProxyModel
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:172
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)