QGIS API Documentation 3.41.0-Master (af5edcb665c)
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#include "moc_qgsfieldmodel.cpp"
23
25 : QAbstractItemModel( parent )
26{
27}
28
29QModelIndex QgsFieldModel::indexFromName( const QString &fieldName )
30{
31 QString fldName( fieldName ); // we may need a copy
32
33 // only non-empty names should be used here, as by default all fields
34 // have no aliases set and calling key() fill return just first value
35 // from the aliases map
36 if ( mLayer && !fldName.isEmpty() )
37 {
38 // the name could be an alias
39 // it would be better to have "display name" directly in QgsFields
40 // rather than having to consult layer in various places in code!
41 const QString fieldNameWithAlias = mLayer->attributeAliases().key( fldName );
42 if ( !fieldNameWithAlias.isNull() )
43 fldName = fieldNameWithAlias;
44 }
45
46 if ( mAllowEmpty && fieldName.isEmpty() )
47 return index( 0, 0 );
48
49 int r = mFields.lookupField( fldName );
50 if ( r >= 0 )
51 {
52 if ( mAllowEmpty )
53 r++;
54
55 QModelIndex idx = index( r, 0 );
56 if ( idx.isValid() )
57 {
58 return idx;
59 }
60 }
61
62 if ( mAllowExpression )
63 {
64 const int exprIdx = mExpression.indexOf( fldName );
65 if ( exprIdx != -1 )
66 {
67 return index( ( mAllowEmpty ? 1 : 0 ) + mFields.count() + exprIdx, 0 );
68 }
69 }
70
71 return QModelIndex();
72}
73
74bool QgsFieldModel::isField( const QString &expression ) const
75{
76 const int index = mFields.indexFromName( expression );
77 return index >= 0;
78}
79
81{
82 if ( mLayer )
83 {
85 disconnect( mLayer, &QObject::destroyed, this, &QgsFieldModel::layerDeleted );
86 }
87
88 mLayer = layer;
89
90 if ( mLayer )
91 {
93 connect( mLayer, &QObject::destroyed, this, &QgsFieldModel::layerDeleted );
94 }
95
97}
98
99void QgsFieldModel::layerDeleted()
100{
101 mLayer = nullptr;
102 updateModel();
103}
104
106{
107 const int offset = mAllowEmpty ? 1 : 0;
108 if ( mLayer )
109 {
110 const QgsFields newFields = mLayer->fields();
111 if ( mFields.toList() != newFields.toList() )
112 {
113 // Try to handle two special cases: addition of a new field and removal of a field.
114 // It would be better to listen directly to attributeAdded/attributeDeleted
115 // so we would not have to check for addition/removal here.
116
117 if ( mFields.count() == newFields.count() - 1 )
118 {
119 QgsFields tmpNewFields = newFields;
120 tmpNewFields.remove( tmpNewFields.count() - 1 );
121 if ( mFields.toList() == tmpNewFields.toList() )
122 {
123 // the only change is a new field at the end
124 beginInsertRows( QModelIndex(), mFields.count() + offset, mFields.count() + offset );
125 mFields = newFields;
126 endInsertRows();
127 return;
128 }
129 }
130
131 if ( mFields.count() == newFields.count() + 1 )
132 {
133 QgsFields tmpOldFields = mFields;
134 tmpOldFields.remove( tmpOldFields.count() - 1 );
135 if ( tmpOldFields.toList() == newFields.toList() )
136 {
137 // the only change is a field removed at the end
138 beginRemoveRows( QModelIndex(), mFields.count() - 1 + offset, mFields.count() - 1 + offset );
139 mFields = newFields;
140 endRemoveRows();
141 return;
142 }
143
144 for ( int i = 0; i < newFields.count(); ++i )
145 {
146 if ( mFields.at( i ) != newFields.at( i ) )
147 {
148 QgsFields tmpOldFields = mFields;
149 tmpOldFields.remove( i );
150 if ( tmpOldFields.toList() != newFields.toList() )
151 break; // the change is more complex - go with general case
152
153 // the only change is a field removed at index i
154 beginRemoveRows( QModelIndex(), i + offset, i + offset );
155 mFields = newFields;
156 endRemoveRows();
157 return;
158 }
159 }
160 }
161
162 // general case with reset - not good - resets selections
163 beginResetModel();
164 mFields = mLayer->fields();
165 endResetModel();
166 }
167 else
168 emit dataChanged( index( 0 + offset, 0 ), index( rowCount(), 0 ) );
169 }
170 else
171 {
172 beginResetModel();
173 mFields = QgsFields();
174 endResetModel();
175 }
176}
177
178void QgsFieldModel::setAllowExpression( bool allowExpression )
179{
181 return;
182
184
185 if ( !mAllowExpression )
186 {
187 const int start = mFields.count();
188 const int end = start + mExpression.count() - 1;
189 beginRemoveRows( QModelIndex(), start, end );
190 mExpression = QList<QString>();
191 endRemoveRows();
192 }
193}
194
196{
197 if ( allowEmpty == mAllowEmpty )
198 return;
199
200 if ( allowEmpty )
201 {
202 beginInsertRows( QModelIndex(), 0, 0 );
203 mAllowEmpty = true;
204 endInsertRows();
205 }
206 else
207 {
208 beginRemoveRows( QModelIndex(), 0, 0 );
209 mAllowEmpty = false;
210 endRemoveRows();
211 }
212}
213
214
215void QgsFieldModel::setExpression( const QString &expression )
216{
217 if ( !mAllowExpression )
218 return;
219
220 const QModelIndex idx = indexFromName( expression );
221 if ( idx.isValid() )
222 return;
223
224 beginResetModel();
225 mExpression = QList<QString>();
226 if ( !expression.isEmpty() )
227 mExpression << expression;
228 endResetModel();
229}
230
232{
233 beginResetModel();
234 mExpression = QList<QString>();
235 endResetModel();
236}
237
238QModelIndex QgsFieldModel::index( int row, int column, const QModelIndex &parent ) const
239{
240 if ( hasIndex( row, column, parent ) )
241 {
242 return createIndex( row, column, row );
243 }
244
245 return QModelIndex();
246}
247
248QModelIndex QgsFieldModel::parent( const QModelIndex &child ) const
249{
250 Q_UNUSED( child )
251 return QModelIndex();
252}
253
254int QgsFieldModel::rowCount( const QModelIndex &parent ) const
255{
256 if ( parent.isValid() )
257 {
258 return 0;
259 }
260
261 return ( mAllowEmpty ? 1 : 0 ) + ( mAllowExpression ? mFields.count() + mExpression.count() : mFields.count() );
262}
263
264int QgsFieldModel::columnCount( const QModelIndex &parent ) const
265{
266 Q_UNUSED( parent )
267 return 1;
268}
269
270QVariant QgsFieldModel::data( const QModelIndex &index, int role ) const
271{
272 if ( !index.isValid() )
273 return QVariant();
274
275 int exprIdx = index.row() - mFields.count();
276 if ( mAllowEmpty )
277 exprIdx--;
278 const bool isEmpty = mAllowEmpty && index.row() == 0;
279 const int fieldOffset = mAllowEmpty ? 1 : 0;
280
281 switch ( role )
282 {
283 case static_cast< int >( QgsFieldModel::CustomRole::FieldName ):
284 {
285 if ( isEmpty || exprIdx >= 0 )
286 {
287 return QString();
288 }
289 const QgsField field = mFields.at( index.row() - fieldOffset );
290 return field.name();
291 }
292
293 case static_cast< int >( QgsFieldModel::CustomRole::Expression ):
294 {
295 if ( exprIdx >= 0 )
296 {
297 return mExpression.at( exprIdx );
298 }
299 else if ( isEmpty )
300 {
301 return QVariant();
302 }
303 else
304 {
305 const QgsField field = mFields.at( index.row() - fieldOffset );
306 return field.name();
307 }
308 }
309
310 case static_cast< int >( QgsFieldModel::CustomRole::FieldIndex ):
311 {
312 if ( isEmpty || exprIdx >= 0 )
313 {
314 return QVariant();
315 }
316 return index.row() - fieldOffset;
317 }
318
319 case static_cast< int >( QgsFieldModel::CustomRole::IsExpression ):
320 {
321 return exprIdx >= 0;
322 }
323
324 case static_cast< int >( QgsFieldModel::CustomRole::ExpressionValidity ):
325 {
326 if ( exprIdx >= 0 )
327 {
328 QgsExpression exp( mExpression.at( exprIdx ) );
329 QgsExpressionContext context;
330 if ( mLayer )
331 context.setFields( mLayer->fields() );
332
333 exp.prepare( &context );
334 return !exp.hasParserError();
335 }
336 return true;
337 }
338
339 case static_cast< int >( QgsFieldModel::CustomRole::FieldType ):
340 {
341 if ( exprIdx < 0 && !isEmpty )
342 {
343 const QgsField field = mFields.at( index.row() - fieldOffset );
344 return static_cast< int >( field.type() );
345 }
346 return QVariant();
347 }
348
349 case static_cast< int >( QgsFieldModel::CustomRole::FieldOrigin ):
350 {
351 if ( exprIdx < 0 && !isEmpty )
352 {
353 return static_cast< int >( mFields.fieldOrigin( index.row() - fieldOffset ) );
354 }
355 return QVariant();
356 }
357
358 case static_cast< int >( QgsFieldModel::CustomRole::IsEmpty ):
359 {
360 return isEmpty;
361 }
362
363 case static_cast< int >( QgsFieldModel::CustomRole::EditorWidgetType ):
364 {
365 if ( exprIdx < 0 && !isEmpty )
366 {
367 return mFields.at( index.row() - fieldOffset ).editorWidgetSetup().type();
368 }
369 return QVariant();
370 }
371
372 case static_cast< int >( QgsFieldModel::CustomRole::JoinedFieldIsEditable ):
373 {
374 if ( exprIdx < 0 && !isEmpty )
375 {
376 if ( mLayer && mFields.fieldOrigin( index.row() - fieldOffset ) == Qgis::FieldOrigin::Join )
377 {
378 int srcFieldIndex;
379 const QgsVectorLayerJoinInfo *info = mLayer->joinBuffer()->joinForFieldIndex( index.row() - fieldOffset, mLayer->fields(), srcFieldIndex );
380
381 if ( !info || !info->isEditable() )
382 return false;
383
384 return true;
385 }
386 }
387 return QVariant();
388 }
389
390 case static_cast< int >( QgsFieldModel::CustomRole::FieldIsWidgetEditable ):
391 {
392 return !( mLayer->editFormConfig().readOnly( index.row() - fieldOffset ) );
393 }
394
395
396 case Qt::DisplayRole:
397 case Qt::EditRole:
398 case Qt::ToolTipRole:
399 {
400 if ( isEmpty )
401 {
402 return QVariant();
403 }
404 else if ( exprIdx >= 0 )
405 {
406 return mExpression.at( exprIdx );
407 }
408 else if ( role == Qt::EditRole )
409 {
410 return mFields.at( index.row() - fieldOffset ).name();
411 }
412 else if ( role == Qt::ToolTipRole )
413 {
414 return fieldToolTip( mFields.at( index.row() - fieldOffset ) );
415 }
416 else if ( mLayer )
417 {
418 return mLayer->attributeDisplayName( index.row() - fieldOffset );
419 }
420 else if ( mFields.size() > index.row() - fieldOffset )
421 {
422 return mFields.field( index.row() - fieldOffset ).displayName();
423 }
424 else
425 return QVariant();
426 }
427
428 case Qt::ForegroundRole:
429 {
430 if ( !isEmpty && exprIdx >= 0 )
431 {
432 // if expression, test validity
433 QgsExpression exp( mExpression.at( exprIdx ) );
434 QgsExpressionContext context;
435 if ( mLayer )
436 context.setFields( mLayer->fields() );
437
438 exp.prepare( &context );
439 if ( exp.hasParserError() )
440 {
441 return QBrush( QColor( Qt::red ) );
442 }
443 }
444 return QVariant();
445 }
446
447 case Qt::FontRole:
448 {
449 if ( !isEmpty && exprIdx >= 0 )
450 {
451 // if the line is an expression, set it as italic
452 QFont font = QFont();
453 font.setItalic( true );
454 return font;
455 }
456 return QVariant();
457 }
458
459 case Qt::DecorationRole:
460 {
461 if ( !isEmpty && exprIdx < 0 )
462 {
463 return mFields.iconForField( index.row() - fieldOffset );
464 }
465 return QIcon();
466 }
467
468 default:
469 return QVariant();
470 }
471}
472
474{
475 QString toolTip;
476 if ( !field.alias().isEmpty() )
477 {
478 toolTip = QStringLiteral( "<b>%1</b> (%2)" ).arg( field.alias(), field.name() );
479 }
480 else
481 {
482 toolTip = QStringLiteral( "<b>%1</b>" ).arg( field.name() );
483 }
484
485 toolTip += QStringLiteral( "<br><font style='font-family:monospace; white-space: nowrap;'>%3</font>" ).arg( field.displayType( true ) );
486
487 const QString comment = field.comment();
488
489 if ( ! comment.isEmpty() )
490 {
491 toolTip += QStringLiteral( "<br><em>%1</em>" ).arg( comment );
492 }
493
494 return toolTip;
495}
496
498{
499 QString toolTip = QgsFieldModel::fieldToolTip( field );
500 const QgsFields fields = layer->fields();
501 const int fieldIdx = fields.indexOf( field.name() );
502
503 if ( fieldIdx < 0 )
504 return QString();
505
506 const QString expressionString = fields.fieldOrigin( fieldIdx ) == Qgis::FieldOrigin::Expression
507 ? layer->expressionField( fieldIdx )
508 : QString();
509
510 if ( !expressionString.isEmpty() )
511 {
512 toolTip += QStringLiteral( "<br><font style='font-family:monospace;'>%3</font>" ).arg( expressionString );
513 }
514
515 return toolTip;
516}
517
519{
520 setLayer( nullptr );
521 beginResetModel();
522 mFields = fields;
523 endResetModel();
524}
525
527{
528 return mFields;
529}
@ 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:112
QString displayName() const
Returns the name to use when displaying this field.
Definition qgsfield.cpp:95
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:740
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