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