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