QGIS API Documentation 3.40.0-Bratislava (b56115d8743)
Loading...
Searching...
No Matches
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 "qgsvectorlayer.h"
22
24 : QAbstractItemModel( parent )
25{
26}
27
28QModelIndex QgsFieldModel::indexFromName( const QString &fieldName )
29{
30 QString fldName( fieldName ); // we may need a copy
31
32 // only non-empty names should be used here, as by default all fields
33 // have no aliases set and calling key() fill return just first value
34 // from the aliases map
35 if ( mLayer && !fldName.isEmpty() )
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 const 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 const 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
73bool QgsFieldModel::isField( const QString &expression ) const
74{
75 const 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
96}
97
98void QgsFieldModel::layerDeleted()
99{
100 mLayer = nullptr;
101 updateModel();
102}
103
105{
106 const int offset = mAllowEmpty ? 1 : 0;
107 if ( mLayer )
108 {
109 const 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
177void QgsFieldModel::setAllowExpression( bool allowExpression )
178{
180 return;
181
183
184 if ( !mAllowExpression )
185 {
186 const int start = mFields.count();
187 const 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
214void QgsFieldModel::setExpression( const QString &expression )
215{
216 if ( !mAllowExpression )
217 return;
218
219 const 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
237QModelIndex 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
247QModelIndex QgsFieldModel::parent( const QModelIndex &child ) const
248{
249 Q_UNUSED( child )
250 return QModelIndex();
251}
252
253int 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
263int QgsFieldModel::columnCount( const QModelIndex &parent ) const
264{
265 Q_UNUSED( parent )
266 return 1;
267}
268
269QVariant 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 const bool isEmpty = mAllowEmpty && index.row() == 0;
278 const int fieldOffset = mAllowEmpty ? 1 : 0;
279
280 switch ( role )
281 {
282 case static_cast< int >( QgsFieldModel::CustomRole::FieldName ):
283 {
284 if ( isEmpty || exprIdx >= 0 )
285 {
286 return QString();
287 }
288 const QgsField field = mFields.at( index.row() - fieldOffset );
289 return field.name();
290 }
291
292 case static_cast< int >( QgsFieldModel::CustomRole::Expression ):
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 const QgsField field = mFields.at( index.row() - fieldOffset );
305 return field.name();
306 }
307 }
308
309 case static_cast< int >( QgsFieldModel::CustomRole::FieldIndex ):
310 {
311 if ( isEmpty || exprIdx >= 0 )
312 {
313 return QVariant();
314 }
315 return index.row() - fieldOffset;
316 }
317
318 case static_cast< int >( QgsFieldModel::CustomRole::IsExpression ):
319 {
320 return exprIdx >= 0;
321 }
322
323 case static_cast< int >( QgsFieldModel::CustomRole::ExpressionValidity ):
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 static_cast< int >( QgsFieldModel::CustomRole::FieldType ):
339 {
340 if ( exprIdx < 0 && !isEmpty )
341 {
342 const QgsField field = mFields.at( index.row() - fieldOffset );
343 return static_cast< int >( field.type() );
344 }
345 return QVariant();
346 }
347
348 case static_cast< int >( QgsFieldModel::CustomRole::FieldOrigin ):
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 static_cast< int >( QgsFieldModel::CustomRole::IsEmpty ):
358 {
359 return isEmpty;
360 }
361
362 case static_cast< int >( QgsFieldModel::CustomRole::EditorWidgetType ):
363 {
364 if ( exprIdx < 0 && !isEmpty )
365 {
366 return mFields.at( index.row() - fieldOffset ).editorWidgetSetup().type();
367 }
368 return QVariant();
369 }
370
371 case static_cast< int >( QgsFieldModel::CustomRole::JoinedFieldIsEditable ):
372 {
373 if ( exprIdx < 0 && !isEmpty )
374 {
375 if ( mLayer && mFields.fieldOrigin( index.row() - fieldOffset ) == Qgis::FieldOrigin::Join )
376 {
377 int srcFieldIndex;
378 const QgsVectorLayerJoinInfo *info = mLayer->joinBuffer()->joinForFieldIndex( index.row() - fieldOffset, mLayer->fields(), srcFieldIndex );
379
380 if ( !info || !info->isEditable() )
381 return false;
382
383 return true;
384 }
385 }
386 return QVariant();
387 }
388
389 case static_cast< int >( QgsFieldModel::CustomRole::FieldIsWidgetEditable ):
390 {
391 return !( mLayer->editFormConfig().readOnly( index.row() - fieldOffset ) );
392 }
393
394
395 case Qt::DisplayRole:
396 case Qt::EditRole:
397 case Qt::ToolTipRole:
398 {
399 if ( isEmpty )
400 {
401 return QVariant();
402 }
403 else if ( exprIdx >= 0 )
404 {
405 return mExpression.at( exprIdx );
406 }
407 else if ( role == Qt::EditRole )
408 {
409 return mFields.at( index.row() - fieldOffset ).name();
410 }
411 else if ( role == Qt::ToolTipRole )
412 {
413 return fieldToolTip( mFields.at( index.row() - fieldOffset ) );
414 }
415 else if ( mLayer )
416 {
417 return mLayer->attributeDisplayName( index.row() - fieldOffset );
418 }
419 else if ( mFields.size() > index.row() - fieldOffset )
420 {
421 return mFields.field( index.row() - fieldOffset ).displayName();
422 }
423 else
424 return QVariant();
425 }
426
427 case Qt::ForegroundRole:
428 {
429 if ( !isEmpty && exprIdx >= 0 )
430 {
431 // if expression, test validity
432 QgsExpression exp( mExpression.at( exprIdx ) );
433 QgsExpressionContext context;
434 if ( mLayer )
435 context.setFields( mLayer->fields() );
436
437 exp.prepare( &context );
438 if ( exp.hasParserError() )
439 {
440 return QBrush( QColor( Qt::red ) );
441 }
442 }
443 return QVariant();
444 }
445
446 case Qt::FontRole:
447 {
448 if ( !isEmpty && exprIdx >= 0 )
449 {
450 // if the line is an expression, set it as italic
451 QFont font = QFont();
452 font.setItalic( true );
453 return font;
454 }
455 return QVariant();
456 }
457
458 case Qt::DecorationRole:
459 {
460 if ( !isEmpty && exprIdx < 0 )
461 {
462 return mFields.iconForField( index.row() - fieldOffset );
463 }
464 return QIcon();
465 }
466
467 default:
468 return QVariant();
469 }
470}
471
473{
474 QString toolTip;
475 if ( !field.alias().isEmpty() )
476 {
477 toolTip = QStringLiteral( "<b>%1</b> (%2)" ).arg( field.alias(), field.name() );
478 }
479 else
480 {
481 toolTip = QStringLiteral( "<b>%1</b>" ).arg( field.name() );
482 }
483
484 toolTip += QStringLiteral( "<br><font style='font-family:monospace; white-space: nowrap;'>%3</font>" ).arg( field.displayType( true ) );
485
486 const QString comment = field.comment();
487
488 if ( ! comment.isEmpty() )
489 {
490 toolTip += QStringLiteral( "<br><em>%1</em>" ).arg( comment );
491 }
492
493 return toolTip;
494}
495
497{
498 QString toolTip = QgsFieldModel::fieldToolTip( field );
499 const QgsFields fields = layer->fields();
500 const int fieldIdx = fields.indexOf( field.name() );
501
502 if ( fieldIdx < 0 )
503 return QString();
504
505 const QString expressionString = fields.fieldOrigin( fieldIdx ) == Qgis::FieldOrigin::Expression
506 ? layer->expressionField( fieldIdx )
507 : QString();
508
509 if ( !expressionString.isEmpty() )
510 {
511 toolTip += QStringLiteral( "<br><font style='font-family:monospace;'>%3</font>" ).arg( expressionString );
512 }
513
514 return toolTip;
515}
516
518{
519 setLayer( nullptr );
520 beginResetModel();
521 mFields = fields;
522 endResetModel();
523}
524
526{
527 return mFields;
528}
@ Expression
Field is calculated from an expression.
@ Join
Field originates from a joined layer.
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.
QgsVectorLayer * layer
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
QMetaType::Type type
Definition qgsfield.h:60
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:111
QString displayName() const
Returns the name to use when displaying this field.
Definition qgsfield.cpp:94
QString alias
Definition qgsfield.h:63
QString comment
Definition qgsfield.h:61
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:739
Container of fields for a vector layer.
Definition qgsfields.h:46
int count
Definition qgsfields.h:50
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
Q_INVOKABLE int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
Q_INVOKABLE int indexOf(const QString &fieldName) const
Gets the field index from the field name.
void remove(int fieldIdx)
Removes the field with the given index.
QgsField field(int fieldIdx) const
Returns the field at particular index (must be in range 0..N-1).
Qgis::FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
int size() const
Returns number of items.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
QIcon iconForField(int fieldIdx, bool considerOrigin=false) const
Returns an icon corresponding to a field index, based on the field's type and source.
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.
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