QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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
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"
22#include "qgslogger.h"
23#include "qgsapplication.h"
24#include "qgsvectorlayer.h"
26
28 : QAbstractItemModel( parent )
29{
30}
31
32QModelIndex 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
77bool 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
100}
101
102void 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
181void 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
218void 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
241QModelIndex 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
251QModelIndex QgsFieldModel::parent( const QModelIndex &child ) const
252{
253 Q_UNUSED( child )
254 return QModelIndex();
255}
256
257int 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
267int QgsFieldModel::columnCount( const QModelIndex &parent ) const
268{
269 Q_UNUSED( parent )
270 return 1;
271}
272
273QVariant 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 static_cast< int >( QgsFieldModel::CustomRole::FieldName ):
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 static_cast< int >( QgsFieldModel::CustomRole::Expression ):
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 static_cast< int >( QgsFieldModel::CustomRole::FieldIndex ):
314 {
315 if ( isEmpty || exprIdx >= 0 )
316 {
317 return QVariant();
318 }
319 return index.row() - fieldOffset;
320 }
321
322 case static_cast< int >( QgsFieldModel::CustomRole::IsExpression ):
323 {
324 return exprIdx >= 0;
325 }
326
327 case static_cast< int >( QgsFieldModel::CustomRole::ExpressionValidity ):
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 static_cast< int >( QgsFieldModel::CustomRole::FieldType ):
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 static_cast< int >( QgsFieldModel::CustomRole::FieldOrigin ):
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 static_cast< int >( QgsFieldModel::CustomRole::IsEmpty ):
362 {
363 return isEmpty;
364 }
365
366 case static_cast< int >( QgsFieldModel::CustomRole::EditorWidgetType ):
367 {
368 if ( exprIdx < 0 && !isEmpty )
369 {
370 return mFields.at( index.row() - fieldOffset ).editorWidgetSetup().type();
371 }
372 return QVariant();
373 }
374
375 case static_cast< int >( QgsFieldModel::CustomRole::JoinedFieldIsEditable ):
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
393 case static_cast< int >( QgsFieldModel::CustomRole::FieldIsWidgetEditable ):
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
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
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.
@ FieldIsWidgetEditable
true if a is editable from the widget
@ FieldOrigin
Return the field origin (if a field, returns QVariant if expression)
@ Expression
Return field name or expression.
@ IsExpression
Return if index corresponds to an expression.
@ IsEmpty
Return if the index corresponds to the empty value.
@ ExpressionValidity
Return if expression is valid or not.
@ FieldName
Return field name if index corresponds to a field.
@ FieldIndex
Return field index if index corresponds to a field.
@ EditorWidgetType
Editor widget type.
@ FieldType
Return the field type (if a field, return QVariant if expression)
@ JoinedFieldIsEditable
true if a joined field is editable (returns QVariant if not a joined field)
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:41
QgsVectorLayer * layer
Definition: qgsfieldmodel.h:43
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:53
QString name
Definition: qgsfield.h:62
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:60
QString alias
Definition: qgsfield.h:63
QString comment
Definition: qgsfield.h:61
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:359
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.
QgsStringMap attributeAliases() const
Returns a map of field name to attribute alias.
QgsVectorLayerJoinBuffer * joinBuffer()
Returns the join buffer object.
void updatedFields()
Emitted whenever the fields available from this layer have been changed.
QgsEditFormConfig editFormConfig