QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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"
26 
27 QgsFieldModel::QgsFieldModel( QObject *parent )
28  : QAbstractItemModel( parent )
29 {
30 }
31 
32 QModelIndex QgsFieldModel::indexFromName( const QString &fieldName )
33 {
34  QString fldName( fieldName ); // we may need a copy
35 
36  // only non-empty names should be used here, as by default all fields
37  // have no aliases set and calling key() fill return just first value
38  // from the aliases map
39  if ( mLayer && !fldName.isEmpty() )
40  {
41  // the name could be an alias
42  // it would be better to have "display name" directly in QgsFields
43  // rather than having to consult layer in various places in code!
44  const QString fieldNameWithAlias = mLayer->attributeAliases().key( fldName );
45  if ( !fieldNameWithAlias.isNull() )
46  fldName = fieldNameWithAlias;
47  }
48 
49  if ( mAllowEmpty && fieldName.isEmpty() )
50  return index( 0, 0 );
51 
52  int r = mFields.lookupField( fldName );
53  if ( r >= 0 )
54  {
55  if ( mAllowEmpty )
56  r++;
57 
58  QModelIndex idx = index( r, 0 );
59  if ( idx.isValid() )
60  {
61  return idx;
62  }
63  }
64 
65  if ( mAllowExpression )
66  {
67  const int exprIdx = mExpression.indexOf( fldName );
68  if ( exprIdx != -1 )
69  {
70  return index( ( mAllowEmpty ? 1 : 0 ) + mFields.count() + exprIdx, 0 );
71  }
72  }
73 
74  return QModelIndex();
75 }
76 
77 bool QgsFieldModel::isField( const QString &expression ) const
78 {
79  const int index = mFields.indexFromName( expression );
80  return index >= 0;
81 }
82 
84 {
85  if ( mLayer )
86  {
88  disconnect( mLayer, &QObject::destroyed, this, &QgsFieldModel::layerDeleted );
89  }
90 
91  mLayer = layer;
92 
93  if ( mLayer )
94  {
96  connect( mLayer, &QObject::destroyed, this, &QgsFieldModel::layerDeleted );
97  }
98 
99  updateModel();
100 }
101 
102 void QgsFieldModel::layerDeleted()
103 {
104  mLayer = nullptr;
105  updateModel();
106 }
107 
109 {
110  const int offset = mAllowEmpty ? 1 : 0;
111  if ( mLayer )
112  {
113  const QgsFields newFields = mLayer->fields();
114  if ( mFields.toList() != newFields.toList() )
115  {
116  // Try to handle two special cases: addition of a new field and removal of a field.
117  // It would be better to listen directly to attributeAdded/attributeDeleted
118  // so we would not have to check for addition/removal here.
119 
120  if ( mFields.count() == newFields.count() - 1 )
121  {
122  QgsFields tmpNewFields = newFields;
123  tmpNewFields.remove( tmpNewFields.count() - 1 );
124  if ( mFields.toList() == tmpNewFields.toList() )
125  {
126  // the only change is a new field at the end
127  beginInsertRows( QModelIndex(), mFields.count() + offset, mFields.count() + offset );
128  mFields = newFields;
129  endInsertRows();
130  return;
131  }
132  }
133 
134  if ( mFields.count() == newFields.count() + 1 )
135  {
136  QgsFields tmpOldFields = mFields;
137  tmpOldFields.remove( tmpOldFields.count() - 1 );
138  if ( tmpOldFields.toList() == newFields.toList() )
139  {
140  // the only change is a field removed at the end
141  beginRemoveRows( QModelIndex(), mFields.count() - 1 + offset, mFields.count() - 1 + offset );
142  mFields = newFields;
143  endRemoveRows();
144  return;
145  }
146 
147  for ( int i = 0; i < newFields.count(); ++i )
148  {
149  if ( mFields.at( i ) != newFields.at( i ) )
150  {
151  QgsFields tmpOldFields = mFields;
152  tmpOldFields.remove( i );
153  if ( tmpOldFields.toList() != newFields.toList() )
154  break; // the change is more complex - go with general case
155 
156  // the only change is a field removed at index i
157  beginRemoveRows( QModelIndex(), i + offset, i + offset );
158  mFields = newFields;
159  endRemoveRows();
160  return;
161  }
162  }
163  }
164 
165  // general case with reset - not good - resets selections
166  beginResetModel();
167  mFields = mLayer->fields();
168  endResetModel();
169  }
170  else
171  emit dataChanged( index( 0 + offset, 0 ), index( rowCount(), 0 ) );
172  }
173  else
174  {
175  beginResetModel();
176  mFields = QgsFields();
177  endResetModel();
178  }
179 }
180 
181 void QgsFieldModel::setAllowExpression( bool allowExpression )
182 {
184  return;
185 
187 
188  if ( !mAllowExpression )
189  {
190  const int start = mFields.count();
191  const int end = start + mExpression.count() - 1;
192  beginRemoveRows( QModelIndex(), start, end );
193  mExpression = QList<QString>();
194  endRemoveRows();
195  }
196 }
197 
199 {
200  if ( allowEmpty == mAllowEmpty )
201  return;
202 
203  if ( allowEmpty )
204  {
205  beginInsertRows( QModelIndex(), 0, 0 );
206  mAllowEmpty = true;
207  endInsertRows();
208  }
209  else
210  {
211  beginRemoveRows( QModelIndex(), 0, 0 );
212  mAllowEmpty = false;
213  endRemoveRows();
214  }
215 }
216 
217 
218 void QgsFieldModel::setExpression( const QString &expression )
219 {
220  if ( !mAllowExpression )
221  return;
222 
223  const QModelIndex idx = indexFromName( expression );
224  if ( idx.isValid() )
225  return;
226 
227  beginResetModel();
228  mExpression = QList<QString>();
229  if ( !expression.isEmpty() )
230  mExpression << expression;
231  endResetModel();
232 }
233 
235 {
236  beginResetModel();
237  mExpression = QList<QString>();
238  endResetModel();
239 }
240 
241 QModelIndex QgsFieldModel::index( int row, int column, const QModelIndex &parent ) const
242 {
243  if ( hasIndex( row, column, parent ) )
244  {
245  return createIndex( row, column, row );
246  }
247 
248  return QModelIndex();
249 }
250 
251 QModelIndex QgsFieldModel::parent( const QModelIndex &child ) const
252 {
253  Q_UNUSED( child )
254  return QModelIndex();
255 }
256 
257 int QgsFieldModel::rowCount( const QModelIndex &parent ) const
258 {
259  if ( parent.isValid() )
260  {
261  return 0;
262  }
263 
264  return ( mAllowEmpty ? 1 : 0 ) + ( mAllowExpression ? mFields.count() + mExpression.count() : mFields.count() );
265 }
266 
267 int QgsFieldModel::columnCount( const QModelIndex &parent ) const
268 {
269  Q_UNUSED( parent )
270  return 1;
271 }
272 
273 QVariant QgsFieldModel::data( const QModelIndex &index, int role ) const
274 {
275  if ( !index.isValid() )
276  return QVariant();
277 
278  int exprIdx = index.row() - mFields.count();
279  if ( mAllowEmpty )
280  exprIdx--;
281  const bool isEmpty = mAllowEmpty && index.row() == 0;
282  const int fieldOffset = mAllowEmpty ? 1 : 0;
283 
284  switch ( role )
285  {
286  case FieldNameRole:
287  {
288  if ( isEmpty || exprIdx >= 0 )
289  {
290  return QString();
291  }
292  const QgsField field = mFields.at( index.row() - fieldOffset );
293  return field.name();
294  }
295 
296  case ExpressionRole:
297  {
298  if ( exprIdx >= 0 )
299  {
300  return mExpression.at( exprIdx );
301  }
302  else if ( isEmpty )
303  {
304  return QVariant();
305  }
306  else
307  {
308  const QgsField field = mFields.at( index.row() - fieldOffset );
309  return field.name();
310  }
311  }
312 
313  case FieldIndexRole:
314  {
315  if ( isEmpty || exprIdx >= 0 )
316  {
317  return QVariant();
318  }
319  return index.row() - fieldOffset;
320  }
321 
322  case IsExpressionRole:
323  {
324  return exprIdx >= 0;
325  }
326 
328  {
329  if ( exprIdx >= 0 )
330  {
331  QgsExpression exp( mExpression.at( exprIdx ) );
332  QgsExpressionContext context;
333  if ( mLayer )
334  context.setFields( mLayer->fields() );
335 
336  exp.prepare( &context );
337  return !exp.hasParserError();
338  }
339  return true;
340  }
341 
342  case FieldTypeRole:
343  {
344  if ( exprIdx < 0 && !isEmpty )
345  {
346  const QgsField field = mFields.at( index.row() - fieldOffset );
347  return static_cast< int >( field.type() );
348  }
349  return QVariant();
350  }
351 
352  case FieldOriginRole:
353  {
354  if ( exprIdx < 0 && !isEmpty )
355  {
356  return static_cast< int >( mFields.fieldOrigin( index.row() - fieldOffset ) );
357  }
358  return QVariant();
359  }
360 
361  case IsEmptyRole:
362  {
363  return isEmpty;
364  }
365 
366  case EditorWidgetType:
367  {
368  if ( exprIdx < 0 && !isEmpty )
369  {
370  return mFields.at( index.row() - fieldOffset ).editorWidgetSetup().type();
371  }
372  return QVariant();
373  }
374 
376  {
377  if ( exprIdx < 0 && !isEmpty )
378  {
379  if ( mLayer && mFields.fieldOrigin( index.row() - fieldOffset ) == QgsFields::OriginJoin )
380  {
381  int srcFieldIndex;
382  const QgsVectorLayerJoinInfo *info = mLayer->joinBuffer()->joinForFieldIndex( index.row() - fieldOffset, mLayer->fields(), srcFieldIndex );
383 
384  if ( !info || !info->isEditable() )
385  return false;
386 
387  return true;
388  }
389  }
390  return QVariant();
391  }
392 
394  {
395  return !( mLayer->editFormConfig().readOnly( index.row() - fieldOffset ) );
396  }
397 
398 
399  case Qt::DisplayRole:
400  case Qt::EditRole:
401  case Qt::ToolTipRole:
402  {
403  if ( isEmpty )
404  {
405  return QVariant();
406  }
407  else if ( exprIdx >= 0 )
408  {
409  return mExpression.at( exprIdx );
410  }
411  else if ( role == Qt::EditRole )
412  {
413  return mFields.at( index.row() - fieldOffset ).name();
414  }
415  else if ( role == Qt::ToolTipRole )
416  {
417  return fieldToolTip( mFields.at( index.row() - fieldOffset ) );
418  }
419  else if ( mLayer )
420  {
421  return mLayer->attributeDisplayName( index.row() - fieldOffset );
422  }
423  else if ( mFields.size() > index.row() - fieldOffset )
424  {
425  return mFields.field( index.row() - fieldOffset ).displayName();
426  }
427  else
428  return QVariant();
429  }
430 
431  case Qt::ForegroundRole:
432  {
433  if ( !isEmpty && exprIdx >= 0 )
434  {
435  // if expression, test validity
436  QgsExpression exp( mExpression.at( exprIdx ) );
437  QgsExpressionContext context;
438  if ( mLayer )
439  context.setFields( mLayer->fields() );
440 
441  exp.prepare( &context );
442  if ( exp.hasParserError() )
443  {
444  return QBrush( QColor( Qt::red ) );
445  }
446  }
447  return QVariant();
448  }
449 
450  case Qt::FontRole:
451  {
452  if ( !isEmpty && exprIdx >= 0 )
453  {
454  // if the line is an expression, set it as italic
455  QFont font = QFont();
456  font.setItalic( true );
457  return font;
458  }
459  return QVariant();
460  }
461 
462  case Qt::DecorationRole:
463  {
464  if ( !isEmpty && exprIdx < 0 )
465  {
466  return mFields.iconForField( index.row() - fieldOffset );
467  }
468  return QIcon();
469  }
470 
471  default:
472  return QVariant();
473  }
474 }
475 
477 {
478  QString toolTip;
479  if ( !field.alias().isEmpty() )
480  {
481  toolTip = QStringLiteral( "<b>%1</b> (%2)" ).arg( field.alias(), field.name() );
482  }
483  else
484  {
485  toolTip = QStringLiteral( "<b>%1</b>" ).arg( field.name() );
486  }
487 
488  toolTip += QStringLiteral( "<br><font style='font-family:monospace; white-space: nowrap;'>%3</font>" ).arg( field.displayType( true ) );
489 
490  const QString comment = field.comment();
491 
492  if ( ! comment.isEmpty() )
493  {
494  toolTip += QStringLiteral( "<br><em>%1</em>" ).arg( comment );
495  }
496 
497  return toolTip;
498 }
499 
501 {
502  QString toolTip = QgsFieldModel::fieldToolTip( field );
503  const QgsFields fields = layer->fields();
504  const int fieldIdx = fields.indexOf( field.name() );
505 
506  if ( fieldIdx < 0 )
507  return QString();
508 
509  const QString expressionString = fields.fieldOrigin( fieldIdx ) == QgsFields::OriginExpression
510  ? layer->expressionField( fieldIdx )
511  : QString();
512 
513  if ( !expressionString.isEmpty() )
514  {
515  toolTip += QStringLiteral( "<br><font style='font-family:monospace;'>%3</font>" ).arg( expressionString );
516  }
517 
518  return toolTip;
519 }
520 
521 void QgsFieldModel::setFields( const QgsFields &fields )
522 {
523  setLayer( nullptr );
524  beginResetModel();
525  mFields = fields;
526  endResetModel();
527 }
528 
530 {
531  return mFields;
532 }
bool readOnly(int idx) const
This returns true if the field is manually set to read only or if the field does not support editing ...
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
@ FieldOriginRole
Return the field origin (if a field, returns QVariant if expression)
Definition: qgsfieldmodel.h:57
@ FieldIndexRole
Return field index if index corresponds to a field.
Definition: qgsfieldmodel.h:52
@ ExpressionRole
Return field name or expression.
Definition: qgsfieldmodel.h:53
@ JoinedFieldIsEditable
true if a joined field is editable (returns QVariant if not a joined field)
Definition: qgsfieldmodel.h:60
@ ExpressionValidityRole
Return if expression is valid or not.
Definition: qgsfieldmodel.h:55
@ IsExpressionRole
Return if index corresponds to an expression.
Definition: qgsfieldmodel.h:54
@ FieldTypeRole
Return the field type (if a field, return QVariant if expression)
Definition: qgsfieldmodel.h:56
@ EditorWidgetType
Editor widget type.
Definition: qgsfieldmodel.h:59
@ FieldNameRole
Return field name if index corresponds to a field.
Definition: qgsfieldmodel.h:51
@ IsEmptyRole
Return if the index corresponds to the empty value.
Definition: qgsfieldmodel.h:58
@ FieldIsWidgetEditable
true if a is editable from the widget
Definition: qgsfieldmodel.h:61
static QString fieldToolTip(const QgsField &field)
Returns a HTML formatted tooltip string for a field, containing details like the field name,...
int columnCount(const QModelIndex &parent) const override
QVariant data(const QModelIndex &index, int role) const override
QModelIndex parent(const QModelIndex &child) const override
void removeExpression()
Removes any custom expression from the model.
bool isField(const QString &expression) const
Returns true if a string represents a field reference, or false if it is an expression consisting of ...
void setAllowExpression(bool allowExpression)
Sets whether custom expressions are accepted and displayed in the model.
void setLayer(QgsVectorLayer *layer)
Set the layer from which fields are displayed.
void setExpression(const QString &expression)
Sets a single expression to be added after the fields at the end of the model.
QgsFields mFields
QgsFields fields() const
Returns the fields currently shown in the model.
void setFields(const QgsFields &fields)
Manually sets the fields to use for the model.
QgsFieldModel(QObject *parent=nullptr)
Constructor for QgsFieldModel - creates a model to display the fields of a given layer.
bool allowExpression
Definition: qgsfieldmodel.h:42
QgsVectorLayer * layer
Definition: qgsfieldmodel.h:44
static QString fieldToolTipExtended(const QgsField &field, const QgsVectorLayer *layer)
Returns a HTML formatted tooltip string for a field, containing details like the field name,...
QList< QString > mExpression
QModelIndex indexFromName(const QString &fieldName)
Returns the index corresponding to a given fieldName.
void setAllowEmptyFieldName(bool allowEmpty)
Sets whether an optional empty field ("not set") option is present in the model.
QgsVectorLayer * mLayer
int rowCount(const QModelIndex &parent=QModelIndex()) const override
virtual void updateModel()
Called when the model must be updated.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QString name
Definition: qgsfield.h:60
QString displayType(bool showConstraints=false) const
Returns the type to use when displaying this field, including the length and precision of the datatyp...
Definition: qgsfield.cpp:105
QString displayName() const
Returns the name to use when displaying this field.
Definition: qgsfield.cpp:88
QVariant::Type type
Definition: qgsfield.h:58
QString alias
Definition: qgsfield.h:61
QString comment
Definition: qgsfield.h:59
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:572
Container of fields for a vector layer.
Definition: qgsfields.h:45
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
Definition: qgsfields.cpp:212
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:202
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:207
void remove(int fieldIdx)
Removes the field with the given index.
Definition: qgsfields.cpp:101
@ OriginExpression
Field is calculated from an expression.
Definition: qgsfields.h:54
@ OriginJoin
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
Definition: qgsfields.h:52
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
Definition: qgsfields.cpp:189
QgsField field(int fieldIdx) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:168
int size() const
Returns number of items.
Definition: qgsfields.cpp:138
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:344
QIcon iconForField(int fieldIdx, bool considerOrigin=false) const
Returns an icon corresponding to a field index, based on the field's type and source.
Definition: qgsfields.cpp:275
const QgsVectorLayerJoinInfo * joinForFieldIndex(int index, const QgsFields &fields, int &sourceFieldIndex) const
Finds the vector join for a layer field index.
Defines left outer join from our vector layer to some other vector layer.
bool isEditable() const
Returns whether joined fields may be edited through the form of the target layer.
Represents a vector layer which manages a vector based data sets.
QString attributeDisplayName(int index) const
Convenience function that returns the attribute alias if defined or the field name else.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QString expressionField(int index) const
Returns the expression used for a given expression field.
QgsVectorLayerJoinBuffer * joinBuffer()
Returns the join buffer object.
QgsStringMap attributeAliases() const
Returns a map of field name to attribute alias.
void updatedFields()
Emitted whenever the fields available from this layer have been changed.
QgsEditFormConfig editFormConfig
const QgsField & field
Definition: qgsfield.h:463