QGIS API Documentation  3.4.15-Madeira (e83d02e274)
qgsfieldmodel.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsfieldmodel.cpp
3  --------------------------------------
4  Date : 01.04.2014
5  Copyright : (C) 2014 Denis Rouzaud
6  Email : [email protected]
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15 
16 #include <QFont>
17 #include <QIcon>
18 
19 #include "qgsfieldmodel.h"
20 #include "qgsmaplayermodel.h"
21 #include "qgsmaplayerproxymodel.h"
22 #include "qgslogger.h"
23 #include "qgsapplication.h"
24 #include "qgsvectorlayer.h"
25 
26 QgsFieldModel::QgsFieldModel( QObject *parent )
27  : QAbstractItemModel( parent )
28 {
29 }
30 
31 QModelIndex QgsFieldModel::indexFromName( const QString &fieldName )
32 {
33  QString fldName( fieldName ); // we may need a copy
34 
35  if ( mLayer )
36  {
37  // the name could be an alias
38  // it would be better to have "display name" directly in QgsFields
39  // rather than having to consult layer in various places in code!
40  QString fieldNameWithAlias = mLayer->attributeAliases().key( fldName );
41  if ( !fieldNameWithAlias.isNull() )
42  fldName = fieldNameWithAlias;
43  }
44 
45  if ( mAllowEmpty && fieldName.isEmpty() )
46  return index( 0, 0 );
47 
48  int r = mFields.lookupField( fldName );
49  if ( r >= 0 )
50  {
51  if ( mAllowEmpty )
52  r++;
53 
54  QModelIndex idx = index( r, 0 );
55  if ( idx.isValid() )
56  {
57  return idx;
58  }
59  }
60 
61  if ( mAllowExpression )
62  {
63  int exprIdx = mExpression.indexOf( fldName );
64  if ( exprIdx != -1 )
65  {
66  return index( ( mAllowEmpty ? 1 : 0 ) + mFields.count() + exprIdx, 0 );
67  }
68  }
69 
70  return QModelIndex();
71 }
72 
73 bool QgsFieldModel::isField( const QString &expression ) const
74 {
75  int index = mFields.indexFromName( expression );
76  return index >= 0;
77 }
78 
80 {
81  if ( mLayer )
82  {
84  disconnect( mLayer, &QObject::destroyed, this, &QgsFieldModel::layerDeleted );
85  }
86 
87  mLayer = layer;
88 
89  if ( mLayer )
90  {
92  connect( mLayer, &QObject::destroyed, this, &QgsFieldModel::layerDeleted );
93  }
94 
95  updateModel();
96 }
97 
98 void QgsFieldModel::layerDeleted()
99 {
100  mLayer = nullptr;
101  updateModel();
102 }
103 
105 {
106  int offset = mAllowEmpty ? 1 : 0;
107  if ( mLayer )
108  {
109  QgsFields newFields = mLayer->fields();
110  if ( mFields.toList() != newFields.toList() )
111  {
112  // Try to handle two special cases: addition of a new field and removal of a field.
113  // It would be better to listen directly to attributeAdded/attributeDeleted
114  // so we would not have to check for addition/removal here.
115 
116  if ( mFields.count() == newFields.count() - 1 )
117  {
118  QgsFields tmpNewFields = newFields;
119  tmpNewFields.remove( tmpNewFields.count() - 1 );
120  if ( mFields.toList() == tmpNewFields.toList() )
121  {
122  // the only change is a new field at the end
123  beginInsertRows( QModelIndex(), mFields.count() + offset, mFields.count() + offset );
124  mFields = newFields;
125  endInsertRows();
126  return;
127  }
128  }
129 
130  if ( mFields.count() == newFields.count() + 1 )
131  {
132  QgsFields tmpOldFields = mFields;
133  tmpOldFields.remove( tmpOldFields.count() - 1 );
134  if ( tmpOldFields.toList() == newFields.toList() )
135  {
136  // the only change is a field removed at the end
137  beginRemoveRows( QModelIndex(), mFields.count() - 1 + offset, mFields.count() - 1 + offset );
138  mFields = newFields;
139  endRemoveRows();
140  return;
141  }
142 
143  for ( int i = 0; i < newFields.count(); ++i )
144  {
145  if ( mFields.at( i ) != newFields.at( i ) )
146  {
147  QgsFields tmpOldFields = mFields;
148  tmpOldFields.remove( i );
149  if ( tmpOldFields.toList() != newFields.toList() )
150  break; // the change is more complex - go with general case
151 
152  // the only change is a field removed at index i
153  beginRemoveRows( QModelIndex(), i + offset, i + offset );
154  mFields = newFields;
155  endRemoveRows();
156  return;
157  }
158  }
159  }
160 
161  // general case with reset - not good - resets selections
162  beginResetModel();
163  mFields = mLayer->fields();
164  endResetModel();
165  }
166  else
167  emit dataChanged( index( 0 + offset, 0 ), index( rowCount(), 0 ) );
168  }
169  else
170  {
171  beginResetModel();
172  mFields = QgsFields();
173  endResetModel();
174  }
175 }
176 
178 {
179  if ( allowExpression == mAllowExpression )
180  return;
181 
183 
184  if ( !mAllowExpression )
185  {
186  int start = mFields.count();
187  int end = start + mExpression.count() - 1;
188  beginRemoveRows( QModelIndex(), start, end );
189  mExpression = QList<QString>();
190  endRemoveRows();
191  }
192 }
193 
195 {
196  if ( allowEmpty == mAllowEmpty )
197  return;
198 
199  if ( allowEmpty )
200  {
201  beginInsertRows( QModelIndex(), 0, 0 );
202  mAllowEmpty = true;
203  endInsertRows();
204  }
205  else
206  {
207  beginRemoveRows( QModelIndex(), 0, 0 );
208  mAllowEmpty = false;
209  endRemoveRows();
210  }
211 }
212 
213 
214 void QgsFieldModel::setExpression( const QString &expression )
215 {
216  if ( !mAllowExpression )
217  return;
218 
219  QModelIndex idx = indexFromName( expression );
220  if ( idx.isValid() )
221  return;
222 
223  beginResetModel();
224  mExpression = QList<QString>();
225  if ( !expression.isEmpty() )
226  mExpression << expression;
227  endResetModel();
228 }
229 
231 {
232  beginResetModel();
233  mExpression = QList<QString>();
234  endResetModel();
235 }
236 
237 QModelIndex QgsFieldModel::index( int row, int column, const QModelIndex &parent ) const
238 {
239  if ( hasIndex( row, column, parent ) )
240  {
241  return createIndex( row, column, row );
242  }
243 
244  return QModelIndex();
245 }
246 
247 QModelIndex QgsFieldModel::parent( const QModelIndex &child ) const
248 {
249  Q_UNUSED( child );
250  return QModelIndex();
251 }
252 
253 int QgsFieldModel::rowCount( const QModelIndex &parent ) const
254 {
255  if ( parent.isValid() )
256  {
257  return 0;
258  }
259 
260  return ( mAllowEmpty ? 1 : 0 ) + ( mAllowExpression ? mFields.count() + mExpression.count() : mFields.count() );
261 }
262 
263 int QgsFieldModel::columnCount( const QModelIndex &parent ) const
264 {
265  Q_UNUSED( parent );
266  return 1;
267 }
268 
269 QVariant QgsFieldModel::data( const QModelIndex &index, int role ) const
270 {
271  if ( !index.isValid() )
272  return QVariant();
273 
274  int exprIdx = index.row() - mFields.count();
275  if ( mAllowEmpty )
276  exprIdx--;
277  bool isEmpty = mAllowEmpty && index.row() == 0;
278  int fieldOffset = mAllowEmpty ? 1 : 0;
279 
280  switch ( role )
281  {
282  case FieldNameRole:
283  {
284  if ( isEmpty || exprIdx >= 0 )
285  {
286  return "";
287  }
288  QgsField field = mFields.at( index.row() - fieldOffset );
289  return field.name();
290  }
291 
292  case ExpressionRole:
293  {
294  if ( exprIdx >= 0 )
295  {
296  return mExpression.at( exprIdx );
297  }
298  else if ( isEmpty )
299  {
300  return QVariant();
301  }
302  else
303  {
304  QgsField field = mFields.at( index.row() - fieldOffset );
305  return field.name();
306  }
307  }
308 
309  case FieldIndexRole:
310  {
311  if ( isEmpty || exprIdx >= 0 )
312  {
313  return QVariant();
314  }
315  return index.row() - fieldOffset;
316  }
317 
318  case IsExpressionRole:
319  {
320  return exprIdx >= 0;
321  }
322 
324  {
325  if ( exprIdx >= 0 )
326  {
327  QgsExpression exp( mExpression.at( exprIdx ) );
328  QgsExpressionContext context;
329  if ( mLayer )
330  context.setFields( mLayer->fields() );
331 
332  exp.prepare( &context );
333  return !exp.hasParserError();
334  }
335  return true;
336  }
337 
338  case FieldTypeRole:
339  {
340  if ( exprIdx < 0 && !isEmpty )
341  {
342  QgsField field = mFields.at( index.row() - fieldOffset );
343  return static_cast< int >( field.type() );
344  }
345  return QVariant();
346  }
347 
348  case FieldOriginRole:
349  {
350  if ( exprIdx < 0 && !isEmpty )
351  {
352  return static_cast< int >( mFields.fieldOrigin( index.row() - fieldOffset ) );
353  }
354  return QVariant();
355  }
356 
357  case IsEmptyRole:
358  {
359  return isEmpty;
360  }
361 
362  case Qt::DisplayRole:
363  case Qt::EditRole:
364  case Qt::ToolTipRole:
365  {
366  if ( isEmpty )
367  {
368  return QVariant();
369  }
370  else if ( exprIdx >= 0 )
371  {
372  return mExpression.at( exprIdx );
373  }
374  else if ( role == Qt::EditRole )
375  {
376  return mFields.at( index.row() - fieldOffset ).name();
377  }
378  else if ( role == Qt::ToolTipRole )
379  {
380  return fieldToolTip( mFields.at( index.row() - fieldOffset ) );
381  }
382  else if ( mLayer )
383  {
384  return mLayer->attributeDisplayName( index.row() - fieldOffset );
385  }
386  else
387  return QVariant();
388  }
389 
390  case Qt::ForegroundRole:
391  {
392  if ( !isEmpty && exprIdx >= 0 )
393  {
394  // if expression, test validity
395  QgsExpression exp( mExpression.at( exprIdx ) );
396  QgsExpressionContext context;
397  if ( mLayer )
398  context.setFields( mLayer->fields() );
399 
400  exp.prepare( &context );
401  if ( exp.hasParserError() )
402  {
403  return QBrush( QColor( Qt::red ) );
404  }
405  }
406  return QVariant();
407  }
408 
409  case Qt::FontRole:
410  {
411  if ( !isEmpty && exprIdx >= 0 )
412  {
413  // if the line is an expression, set it as italic
414  QFont font = QFont();
415  font.setItalic( true );
416  return font;
417  }
418  return QVariant();
419  }
420 
421  case Qt::DecorationRole:
422  {
423  if ( !isEmpty && exprIdx < 0 )
424  {
425  return mFields.iconForField( index.row() - fieldOffset );
426  }
427  return QIcon();
428  }
429 
430  default:
431  return QVariant();
432  }
433 }
434 
435 QString QgsFieldModel::fieldToolTip( const QgsField &field )
436 {
437  QString toolTip;
438  if ( !field.alias().isEmpty() )
439  {
440  toolTip = QStringLiteral( "<b>%1</b> (%2)" ).arg( field.alias(), field.name() );
441  }
442  else
443  {
444  toolTip = QStringLiteral( "<b>%1</b>" ).arg( field.name() );
445  }
446  QString typeString;
447  if ( field.length() > 0 )
448  {
449  if ( field.precision() > 0 )
450  {
451  typeString = QStringLiteral( "%1 (%2, %3)" ).arg( field.typeName() ).arg( field.length() ).arg( field.precision() );
452  }
453  else
454  {
455  typeString = QStringLiteral( "%1 (%2)" ).arg( field.typeName() ).arg( field.length() );
456  }
457  }
458  else
459  {
460  typeString = field.typeName();
461  }
462  toolTip += QStringLiteral( "<p>%1</p>" ).arg( typeString );
463  return toolTip;
464 }
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
Definition: qgsfields.cpp:212
Class for parsing and evaluation of expressions (formerly called "search strings").
bool isField(const QString &expression) const
Returns true if a string represents a field reference, or false if it is an expression consisting of ...
QIcon iconForField(int fieldIdx) const
Returns an icon corresponding to a field index, based on the field&#39;s type and source.
Definition: qgsfields.cpp:275
QString name
Definition: qgsfield.h:57
int precision
Definition: qgsfield.h:54
QString alias
Definition: qgsfield.h:58
static QString fieldToolTip(const QgsField &field)
Returns a HTML formatted tooltip string for a field, containing details like the field name...
void setExpression(const QString &expression)
Sets a single expression to be added after the fields at the end of the model.
Return field index if index corresponds to a field.
Definition: qgsfieldmodel.h:51
Container of fields for a vector layer.
Definition: qgsfields.h:42
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
QgsVectorLayer * layer()
Returns the layer associated with the model.
Return field name if index corresponds to a field.
Definition: qgsfieldmodel.h:50
Return the field origin (if a field, returns QVariant if expression)
Definition: qgsfieldmodel.h:56
int length
Definition: qgsfield.h:53
QVariant data(const QModelIndex &index, int role) const override
QModelIndex parent(const QModelIndex &child) const override
QgsFieldModel(QObject *parent=nullptr)
Constructor for QgsFieldModel - creates a model to display the fields of a given layer.
QString typeName() const
Gets the field type.
Definition: qgsfield.cpp:105
QgsFields fields() const FINAL
Returns the list of fields of this layer.
void setAllowEmptyFieldName(bool allowEmpty)
Sets whether an optional empty field ("not set") option is present in the model.
void removeExpression()
Removes any custom expression from the model.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setLayer(QgsVectorLayer *layer)
Set the layer from which fields are displayed.
Return if expression is valid or not.
Definition: qgsfieldmodel.h:54
void setAllowExpression(bool allowExpression)
Sets whether custom expressions are accepted and displayed in the model.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
Return the field type (if a field, return QVariant if expression)
Definition: qgsfieldmodel.h:55
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:48
int lookupField(const QString &fieldName) const
Looks up field&#39;s index from the field name.
Definition: qgsfields.cpp:320
void remove(int fieldIdx)
Removes a field with the given index.
Definition: qgsfields.cpp:101
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
QString attributeDisplayName(int index) const
Convenience function that returns the attribute alias if defined or the field name else...
Return field name or expression.
Definition: qgsfieldmodel.h:52
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:202
int columnCount(const QModelIndex &parent) const override
Return if index corresponds to an expression.
Definition: qgsfieldmodel.h:53
Return if the index corresponds to the empty value.
Definition: qgsfieldmodel.h:57
virtual void updateModel()
Called when the model must be updated.
FieldOrigin fieldOrigin(int fieldIdx) const
Gets field&#39;s origin (value from an enumeration)
Definition: qgsfields.cpp:189
QgsFields mFields
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
QList< QString > mExpression
QgsVectorLayer * mLayer
QModelIndex indexFromName(const QString &fieldName)
Returns the index corresponding to a given fieldName.
Represents a vector layer which manages a vector based data sets.
void updatedFields()
Is emitted, whenever the fields available from this layer have been changed.
QVariant::Type type
Definition: qgsfield.h:55
QgsStringMap attributeAliases() const
Returns a map of field name to attribute alias.
bool allowExpression()
Returns true if the model allows custom expressions to be created and displayed.
Definition: qgsfieldmodel.h:81