QGIS API Documentation  2.5.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties 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 );
37  mCombo->setModel( mFieldProxyModel );
38 
39  mButton = new QToolButton( this );
40  mButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
41  mButton->setIcon( QgsApplication::getThemeIcon( "/mIconExpression.svg" ) );
42 
43  layout->addWidget( mCombo );
44  layout->addWidget( mButton );
45 
46  // give focus to the combo
47  // hence if the widget is used as a delegate
48  // it will allow pressing on the expression dialog button
49  setFocusProxy( mCombo );
50 
51  connect( mCombo->lineEdit(), SIGNAL( textEdited( QString ) ), this, SLOT( expressionEdited( QString ) ) );
52  connect( mCombo->lineEdit(), SIGNAL( editingFinished() ), this, SLOT( expressionEditingFinished() ) );
53  connect( mCombo, SIGNAL( activated( int ) ), this, SLOT( currentFieldChanged() ) );
54  connect( mButton, SIGNAL( clicked() ), this, SLOT( editExpression() ) );
55  // NW TODO - Fix in 2.6
56 // connect( mCombo->lineEdit(), SIGNAL( returnPressed() ), this, SIGNAL( returnPressed() ) );
57 }
58 
60 {
61  mExpressionDialogTitle = title;
62 }
63 
64 void QgsFieldExpressionWidget::setFilters( QgsFieldProxyModel::Filters filters )
65 {
66  mFieldProxyModel->setFilters( filters );
67 }
68 
70 {
71  QHBoxLayout* layout = dynamic_cast<QHBoxLayout*>( this->layout() );
72  if ( isLeft )
73  {
74  QLayoutItem* item = layout->takeAt( 1 );
75  layout->insertWidget( 0, item->widget() );
76  }
77  else
78  layout->addWidget( mCombo );
79 }
80 
82 {
83  mDa = QSharedPointer<const QgsDistanceArea>( new QgsDistanceArea( da ) );
84 }
85 
87 {
88  return mCombo->currentText();
89 }
90 
91 bool QgsFieldExpressionWidget::isValidExpression( QString *expressionError ) const
92 {
93  QString temp;
94  QgsVectorLayer* vl = layer();
95  return QgsExpression::isValid( currentText(), vl ? vl->pendingFields() : QgsFields(), expressionError ? *expressionError : temp );
96 }
97 
99 {
101 }
102 
103 QString QgsFieldExpressionWidget::currentField( bool *isExpression , bool *isValid ) const
104 {
105  QString text = currentText();
106  if ( isValid )
107  {
108  *isValid = isValidExpression();
109  }
110  if ( isExpression )
111  {
112  *isExpression = this->isExpression();
113  }
114  return text;
115 }
116 
118 {
120 }
121 
123 {
124  QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( layer );
125  if ( vl )
126  {
127  setLayer( vl );
128  }
129 }
130 
132 {
134 }
135 
136 void QgsFieldExpressionWidget::setField( const QString &fieldName )
137 {
138  if ( fieldName.isEmpty() )
139  return;
140 
141  QModelIndex idx = mFieldProxyModel->sourceFieldModel()->indexFromName( fieldName );
142  if ( !idx.isValid() )
143  {
144  // try to remove quotes and white spaces
145  QString simpleFieldName = fieldName.trimmed();
146  if ( simpleFieldName.startsWith( '"' ) && simpleFieldName.endsWith( '"' ) )
147  {
148  simpleFieldName.remove( 0, 1 ).chop( 1 );
149  idx = mFieldProxyModel->sourceFieldModel()->indexFromName( simpleFieldName );
150  }
151 
152  if ( !idx.isValid() )
153  {
154  // new expression
156  idx = mFieldProxyModel->sourceFieldModel()->indexFromName( fieldName );
157  }
158  }
159  QModelIndex proxyIndex = mFieldProxyModel->mapFromSource( idx );
160  mCombo->setCurrentIndex( proxyIndex.row() );
162 }
163 
165 {
166  QString currentExpression = currentText();
167  QgsVectorLayer* vl = layer();
168 
169  QgsExpressionBuilderDialog dlg( vl, currentExpression );
170  if ( !mDa.isNull() )
171  {
172  dlg.setGeomCalculator( *mDa );
173  }
174  dlg.setWindowTitle( mExpressionDialogTitle );
175 
176  if ( dlg.exec() )
177  {
178  QString newExpression = dlg.expressionText();
179  setField( newExpression );
180  }
181 }
182 
183 void QgsFieldExpressionWidget::expressionEdited( const QString expression )
184 {
185  updateLineEditStyle( expression );
186  emit fieldChanged( expression, isValidExpression() );
187 }
188 
190 {
191  QgsDebugMsg( "Editing finsihed" );
192  const QString expression = mCombo->lineEdit()->text();
194  QModelIndex idx = mFieldProxyModel->sourceFieldModel()->indexFromName( expression );
195  QModelIndex proxyIndex = mFieldProxyModel->mapFromSource( idx );
196  mCombo->setCurrentIndex( proxyIndex.row() );
198 }
199 
201 {
202  if ( event->type() == QEvent::EnabledChange )
203  {
205  }
206 }
207 
209 {
211 
212  bool isExpression, isValid;
213  QString fieldName = currentField( &isExpression, &isValid );
214 
215  // display tooltip if widget is shorter than expression
216  QFontMetrics metrics( mCombo->lineEdit()->font() );
217  if ( metrics.width( fieldName ) > mCombo->lineEdit()->width() )
218  {
219  mCombo->setToolTip( fieldName );
220  }
221  else
222  {
223  mCombo->setToolTip( "" );
224  }
225 
226  emit fieldChanged( fieldName );
227  emit fieldChanged( fieldName, isValid );
228 }
229 
230 void QgsFieldExpressionWidget::updateLineEditStyle( const QString expression )
231 {
232  QPalette palette;
233  if ( !isEnabled() )
234  {
235  palette.setColor( QPalette::Text, Qt::gray );
236  }
237  else
238  {
239  bool isExpression, isValid;
240  if ( !expression.isEmpty() )
241  {
242  isExpression = true;
243  isValid = isExpressionValid( expression );
244  }
245  else
246  {
247  currentField( &isExpression, &isValid );
248  }
249  QFont font = mCombo->lineEdit()->font();
250  font.setItalic( isExpression );
251  mCombo->lineEdit()->setFont( font );
252 
253  if ( isExpression && !isValid )
254  {
255  palette.setColor( QPalette::Text, Qt::red );
256  }
257  else
258  {
259  palette.setColor( QPalette::Text, Qt::black );
260  }
261  }
262  mCombo->lineEdit()->setPalette( palette );
263 }
264 
265 bool QgsFieldExpressionWidget::isExpressionValid( const QString expressionStr )
266 {
267  QgsVectorLayer* vl = layer();
268 
269  QgsExpression expression( expressionStr );
270  expression.prepare( vl ? vl->pendingFields() : QgsFields() );
271  return !expression.hasParserError();
272 }
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:89
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:96
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:36
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:163
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
QgsFieldProxyModel * mFieldProxyModel
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
QSharedPointer< const QgsDistanceArea > mDa
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)