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