QGIS API Documentation  2.9.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsattributetablemodel.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  QgsAttributeTableModel.cpp
3  --------------------------------------
4  Date : Feb 2009
5  Copyright : (C) 2009 Vita Cizek
6  Email : weetya (at) gmail.com
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 "qgsapplication.h"
17 #include "qgsattributetablemodel.h"
19 
20 #include "qgsattributeaction.h"
22 #include "qgsexpression.h"
23 #include "qgsfield.h"
24 #include "qgslogger.h"
25 #include "qgsmapcanvas.h"
27 #include "qgsmaplayerregistry.h"
28 #include "qgsrendererv2.h"
29 #include "qgsvectorlayer.h"
30 
31 #include <QVariant>
32 
33 #include <limits>
34 
36  : QAbstractTableModel( parent )
37  , mLayerCache( layerCache )
38  , mFieldCount( 0 )
39  , mCachedField( -1 )
40 {
41  QgsDebugMsg( "entered." );
42 
43  if ( layerCache->layer()->geometryType() == QGis::NoGeometry )
44  {
45  mFeatureRequest.setFlags( QgsFeatureRequest::NoGeometry );
46  }
47 
49 
50  if ( !layer()->hasGeometryType() )
51  mFeatureRequest.setFlags( QgsFeatureRequest::NoGeometry );
52 
54 
55  connect( mLayerCache, SIGNAL( attributeValueChanged( QgsFeatureId, int, const QVariant& ) ), this, SLOT( attributeValueChanged( QgsFeatureId, int, const QVariant& ) ) );
56  connect( layer(), SIGNAL( featureDeleted( QgsFeatureId ) ), this, SLOT( featureDeleted( QgsFeatureId ) ) );
57  connect( layer(), SIGNAL( attributeDeleted( int ) ), this, SLOT( attributeDeleted( int ) ) );
58  connect( layer(), SIGNAL( updatedFields() ), this, SLOT( updatedFields() ) );
59  connect( layer(), SIGNAL( editCommandEnded() ), this, SLOT( editCommandEnded() ) );
60  connect( mLayerCache, SIGNAL( featureAdded( QgsFeatureId ) ), this, SLOT( featureAdded( QgsFeatureId ) ) );
61  connect( mLayerCache, SIGNAL( cachedLayerDeleted() ), this, SLOT( layerDeleted() ) );
62 }
63 
64 bool QgsAttributeTableModel::loadFeatureAtId( QgsFeatureId fid ) const
65 {
66  QgsDebugMsgLevel( QString( "loading feature %1" ).arg( fid ), 3 );
67 
68  if ( fid == std::numeric_limits<int>::min() )
69  {
70  return false;
71  }
72 
73  return mLayerCache->featureAtId( fid, mFeat );
74 }
75 
77 {
78  QgsDebugMsgLevel( QString( "(%2) fid: %1" ).arg( fid ).arg( mFeatureRequest.filterType() ), 4 );
79  mFieldCache.remove( fid );
80 
81  int row = idToRow( fid );
82 
83  if ( row != -1 )
84  {
85  beginRemoveRows( QModelIndex(), row, row );
86  removeRow( row );
87  endRemoveRows();
88  }
89 }
90 
91 bool QgsAttributeTableModel::removeRows( int row, int count, const QModelIndex &parent )
92 {
93  Q_UNUSED( parent );
94  QgsDebugMsgLevel( QString( "remove %2 rows at %1 (rows %3, ids %4)" ).arg( row ).arg( count ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ), 3 );
95 
96  // clean old references
97  for ( int i = row; i < row + count; i++ )
98  {
99  mIdRowMap.remove( mRowIdMap[ i ] );
100  mRowIdMap.remove( i );
101  }
102 
103  // update maps
104  int n = mRowIdMap.size() + count;
105  for ( int i = row + count; i < n; i++ )
106  {
107  QgsFeatureId id = mRowIdMap[i];
108  mIdRowMap[ id ] -= count;
109  mRowIdMap[ i-count ] = id;
110  mRowIdMap.remove( i );
111  }
112 
113 #ifdef QGISDEBUG
114  QgsDebugMsgLevel( QString( "after removal rows %1, ids %2" ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ), 4 );
115  QgsDebugMsgLevel( "id->row", 4 );
116  for ( QHash<QgsFeatureId, int>::iterator it = mIdRowMap.begin(); it != mIdRowMap.end(); ++it )
117  QgsDebugMsgLevel( QString( "%1->%2" ).arg( FID_TO_STRING( it.key() ) ).arg( *it ), 4 );
118 
119  QHash<QgsFeatureId, int>::iterator idit;
120 
121  QgsDebugMsgLevel( "row->id", 4 );
122  for ( QHash<int, QgsFeatureId>::iterator it = mRowIdMap.begin(); it != mRowIdMap.end(); ++it )
123  QgsDebugMsgLevel( QString( "%1->%2" ).arg( it.key() ).arg( FID_TO_STRING( *it ) ), 4 );
124 #endif
125 
126  Q_ASSERT( mRowIdMap.size() == mIdRowMap.size() );
127 
128  return true;
129 }
130 
132 {
133  QgsDebugMsgLevel( QString( "(%2) fid: %1" ).arg( fid ).arg( mFeatureRequest.filterType() ), 4 );
134  bool featOk = true;
135 
136  if ( mFeat.id() != fid )
137  featOk = loadFeatureAtId( fid );
138 
139  if ( featOk && mFeatureRequest.acceptFeature( mFeat ) )
140  {
141  mFieldCache[ fid ] = mFeat.attribute( mCachedField );
142 
143  int n = mRowIdMap.size();
144  beginInsertRows( QModelIndex(), n, n );
145 
146  mIdRowMap.insert( fid, n );
147  mRowIdMap.insert( n, fid );
148 
149  endInsertRows();
150 
151  reload( index( rowCount() - 1, 0 ), index( rowCount() - 1, columnCount() ) );
152  }
153 }
154 
155 void QgsAttributeTableModel::updatedFields()
156 {
157  QgsDebugMsg( "entered." );
158  loadAttributes();
159  emit modelChanged();
160 }
161 
162 void QgsAttributeTableModel::editCommandEnded()
163 {
164  reload( createIndex( mChangedCellBounds.top(), mChangedCellBounds.left() ),
165  createIndex( mChangedCellBounds.bottom(), mChangedCellBounds.right() ) );
166 
167  mChangedCellBounds = QRect();
168 }
169 
170 void QgsAttributeTableModel::attributeDeleted( int idx )
171 {
172  if ( idx == mCachedField )
173  {
174  prefetchColumnData( -1 );
175  }
176 }
177 
179 {
180  QgsDebugMsg( "entered." );
181 
182  beginRemoveRows( QModelIndex(), 0, rowCount() - 1 );
183  removeRows( 0, rowCount() );
184  endRemoveRows();
185 
186  mAttributeWidgetCaches.clear();
187  mAttributes.clear();
188  mWidgetFactories.clear();
189  mWidgetConfigs.clear();
190 }
191 
192 void QgsAttributeTableModel::attributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
193 {
194  QgsDebugMsgLevel( QString( "(%4) fid: %1, idx: %2, value: %3" ).arg( fid ).arg( idx ).arg( value.toString() ).arg( mFeatureRequest.filterType() ), 3 );
195 
196  if ( idx == mCachedField )
197  mFieldCache[ fid ] = value;
198 
199  // No filter request: skip all possibly heavy checks
200  if ( mFeatureRequest.filterType() == QgsFeatureRequest::FilterNone )
201  {
202  setData( index( idToRow( fid ), fieldCol( idx ) ), value, Qt::EditRole );
203  }
204  else
205  {
206  if ( loadFeatureAtId( fid ) )
207  {
208  if ( mFeatureRequest.acceptFeature( mFeat ) )
209  {
210  if ( !mIdRowMap.contains( fid ) )
211  {
212  // Feature changed in such a way, it will be shown now
213  featureAdded( fid );
214  }
215  else
216  {
217  // Update representation
218  setData( index( idToRow( fid ), fieldCol( idx ) ), value, Qt::EditRole );
219  }
220  }
221  else
222  {
223  if ( mIdRowMap.contains( fid ) )
224  {
225  // Feature changed such, that it is no longer shown
226  featureDeleted( fid );
227  }
228  // else: we don't care
229  }
230  }
231  }
232 }
233 
235 {
236  if ( !layer() )
237  {
238  return;
239  }
240 
241  bool ins = false, rm = false;
242 
243  QgsAttributeList attributes;
244  const QgsFields& fields = layer()->pendingFields();
245 
246  mWidgetFactories.clear();
247  mAttributeWidgetCaches.clear();
248  mWidgetConfigs.clear();
249 
250  for ( int idx = 0; idx < fields.count(); ++idx )
251  {
252  const QString widgetType = layer()->editorWidgetV2( idx );
253  QgsEditorWidgetFactory* widgetFactory = QgsEditorWidgetRegistry::instance()->factory( widgetType );
254  if ( widgetFactory && widgetType != "Hidden" )
255  {
256  mWidgetFactories.append( widgetFactory );
257  mWidgetConfigs.append( layer()->editorWidgetV2Config( idx ) );
258  mAttributeWidgetCaches.append( widgetFactory->createCache( layer(), idx, mWidgetConfigs.last() ) );
259 
260  attributes << idx;
261  }
262  }
263 
264  if ( mFieldCount < attributes.size() )
265  {
266  ins = true;
267  beginInsertColumns( QModelIndex(), mFieldCount, attributes.size() - 1 );
268  }
269  else if ( attributes.size() < mFieldCount )
270  {
271  rm = true;
272  beginRemoveColumns( QModelIndex(), attributes.size(), mFieldCount - 1 );
273  }
274 
275  mFieldCount = attributes.size();
276  mAttributes = attributes;
277 
278  if ( ins )
279  {
280  endInsertColumns();
281  }
282  else if ( rm )
283  {
284  endRemoveColumns();
285  }
286 }
287 
289 {
290  QgsDebugMsg( "entered." );
291 
292  if ( rowCount() != 0 )
293  {
294  beginRemoveRows( QModelIndex(), 0, rowCount() - 1 );
295  removeRows( 0, rowCount() );
296  endRemoveRows();
297  }
298 
299  QgsFeatureIterator features = mLayerCache->getFeatures( mFeatureRequest );
300 
301  int i = 0;
302 
303  QTime t;
304  t.start();
305 
306  QgsFeature feat;
307  while ( features.nextFeature( feat ) )
308  {
309  ++i;
310 
311  if ( t.elapsed() > 1000 )
312  {
313  bool cancel = false;
314  emit progress( i, cancel );
315  if ( cancel )
316  break;
317 
318  t.restart();
319  }
320  mFeat = feat;
321  featureAdded( feat.id() );
322  }
323 
324  emit finished();
325 
326  mFieldCount = mAttributes.size();
327 }
328 
330 {
331  if ( a == b )
332  return;
333 
334  int rowA = idToRow( a );
335  int rowB = idToRow( b );
336 
337  //emit layoutAboutToBeChanged();
338 
339  mRowIdMap.remove( rowA );
340  mRowIdMap.remove( rowB );
341  mRowIdMap.insert( rowA, b );
342  mRowIdMap.insert( rowB, a );
343 
344  mIdRowMap.remove( a );
345  mIdRowMap.remove( b );
346  mIdRowMap.insert( a, rowB );
347  mIdRowMap.insert( b, rowA );
348 
349  //emit layoutChanged();
350 }
351 
353 {
354  if ( !mIdRowMap.contains( id ) )
355  {
356  QgsDebugMsg( QString( "idToRow: id %1 not in the map" ).arg( id ) );
357  return -1;
358  }
359 
360  return mIdRowMap[id];
361 }
362 
364 {
365  return index( idToRow( id ), 0 );
366 }
367 
369 {
370  QModelIndexList indexes;
371 
372  int row = idToRow( id );
373  for ( int column = 0; column < columnCount(); ++column )
374  {
375  indexes.append( index( row, column ) );
376  }
377 
378  return indexes;
379 }
380 
382 {
383  if ( !mRowIdMap.contains( row ) )
384  {
385  QgsDebugMsg( QString( "rowToId: row %1 not in the map" ).arg( row ) );
386  // return negative infinite (to avoid collision with newly added features)
388  }
389 
390  return mRowIdMap[row];
391 }
392 
394 {
395  return mAttributes[ col ];
396 }
397 
399 {
400  return mAttributes.indexOf( idx );
401 }
402 
403 int QgsAttributeTableModel::rowCount( const QModelIndex &parent ) const
404 {
405  Q_UNUSED( parent );
406  return mRowIdMap.size();
407 }
408 
409 int QgsAttributeTableModel::columnCount( const QModelIndex &parent ) const
410 {
411  Q_UNUSED( parent );
412  return qMax( 1, mFieldCount ); // if there are zero columns all model indices will be considered invalid
413 }
414 
415 QVariant QgsAttributeTableModel::headerData( int section, Qt::Orientation orientation, int role ) const
416 {
417  if ( !layer() )
418  return QVariant();
419 
420  if ( role == Qt::DisplayRole )
421  {
422  if ( orientation == Qt::Vertical ) //row
423  {
424  return QVariant( section );
425  }
426  else if ( section >= 0 && section < mFieldCount )
427  {
428  QString attributeName = layer()->attributeAlias( mAttributes[section] );
429  if ( attributeName.isEmpty() )
430  {
431  QgsField field = layer()->pendingFields()[ mAttributes[section] ];
432  attributeName = field.name();
433  }
434  return QVariant( attributeName );
435  }
436  else
437  {
438  return tr( "feature id" );
439  }
440  }
441  else
442  {
443  return QVariant();
444  }
445 }
446 
447 QVariant QgsAttributeTableModel::data( const QModelIndex &index, int role ) const
448 {
449  if ( !index.isValid() ||
450  ( role != Qt::TextAlignmentRole
451  && role != Qt::DisplayRole
452  && role != Qt::EditRole
453  && role != SortRole
454  && role != FeatureIdRole
455  && role != FieldIndexRole
456  )
457  )
458  return QVariant();
459 
460  QgsFeatureId rowId = rowToId( index.row() );
461 
462  if ( role == FeatureIdRole )
463  return rowId;
464 
465  if ( index.column() >= mFieldCount )
466  return role == Qt::DisplayRole ? rowId : QVariant();
467 
468  int fieldId = mAttributes[ index.column()];
469 
470  if ( role == FieldIndexRole )
471  return fieldId;
472 
473  const QgsField& field = layer()->pendingFields()[ fieldId ];
474 
475  QVariant::Type fldType = field.type();
476  bool fldNumeric = ( fldType == QVariant::Int || fldType == QVariant::Double || fldType == QVariant::LongLong );
477 
478  if ( role == Qt::TextAlignmentRole )
479  {
480  if ( fldNumeric )
481  return QVariant( Qt::AlignRight );
482  else
483  return QVariant( Qt::AlignLeft );
484  }
485 
486  QVariant val;
487 
488  // if we don't have the row in current cache, load it from layer first
489  if ( mCachedField == fieldId )
490  {
491  val = mFieldCache[ rowId ];
492  }
493  else
494  {
495  if ( mFeat.id() != rowId || !mFeat.isValid() )
496  {
497  if ( !loadFeatureAtId( rowId ) )
498  return QVariant( "ERROR" );
499 
500  if ( mFeat.id() != rowId )
501  return QVariant( "ERROR" );
502  }
503 
504  val = mFeat.attribute( fieldId );
505  }
506 
507  if ( role == Qt::DisplayRole )
508  {
509  return mWidgetFactories[ index.column()]->representValue( layer(), fieldId, mWidgetConfigs[ index.column()], mAttributeWidgetCaches[ index.column()], val );
510  }
511 
512  return val;
513 }
514 
515 bool QgsAttributeTableModel::setData( const QModelIndex &index, const QVariant &value, int role )
516 {
517  Q_UNUSED( value )
518 
519  if ( !index.isValid() || index.column() >= mFieldCount || role != Qt::EditRole || !layer()->isEditable() )
520  return false;
521 
522  if ( !layer()->isModified() )
523  return false;
524 
525  if ( mChangedCellBounds.isNull() )
526  {
527  mChangedCellBounds = QRect( index.column(), index.row(), 1, 1 );
528  }
529  else
530  {
531  if ( index.column() < mChangedCellBounds.left() )
532  {
533  mChangedCellBounds.setLeft( index.column() );
534  }
535  if ( index.row() < mChangedCellBounds.top() )
536  {
537  mChangedCellBounds.setTop( index.row() );
538  }
539  if ( index.column() > mChangedCellBounds.right() )
540  {
541  mChangedCellBounds.setRight( index.column() );
542  }
543  if ( index.row() > mChangedCellBounds.bottom() )
544  {
545  mChangedCellBounds.setBottom( index.row() );
546  }
547  }
548 
549  return true;
550 }
551 
552 Qt::ItemFlags QgsAttributeTableModel::flags( const QModelIndex &index ) const
553 {
554  if ( !index.isValid() )
555  return Qt::ItemIsEnabled;
556 
557  if ( index.column() >= mFieldCount )
558  return Qt::NoItemFlags;
559 
560  Qt::ItemFlags flags = QAbstractItemModel::flags( index );
561 
562  if ( layer()->isEditable() &&
563  layer()->fieldEditable( mAttributes[ index.column()] ) )
564  flags |= Qt::ItemIsEditable;
565 
566  return flags;
567 }
568 
569 void QgsAttributeTableModel::reload( const QModelIndex &index1, const QModelIndex &index2 )
570 {
572  emit dataChanged( index1, index2 );
573 }
574 
576 {
577  beginResetModel();
578  endResetModel();
579 }
580 
581 void QgsAttributeTableModel::executeAction( int action, const QModelIndex &idx ) const
582 {
583  QgsFeature f = feature( idx );
584  layer()->actions()->doAction( action, f, fieldIdx( idx.column() ) );
585 }
586 
587 void QgsAttributeTableModel::executeMapLayerAction( QgsMapLayerAction* action, const QModelIndex &idx ) const
588 {
589  QgsFeature f = feature( idx );
590  action->triggerForFeature( layer(), &f );
591 }
592 
593 QgsFeature QgsAttributeTableModel::feature( const QModelIndex &idx ) const
594 {
595  QgsFeature f;
596  f.initAttributes( mAttributes.size() );
597  f.setFeatureId( rowToId( idx.row() ) );
598  for ( int i = 0; i < mAttributes.size(); i++ )
599  {
600  f.setAttribute( mAttributes[i], data( index( idx.row(), i ), Qt::EditRole ) );
601  }
602 
603  return f;
604 }
605 
607 {
608  mFieldCache.clear();
609 
610  if ( column == -1 )
611  {
612  mCachedField = -1;
613  }
614  else
615  {
616  if ( column >= mAttributes.count() )
617  return;
618  int fieldId = mAttributes[ column ];
619  const QgsFields& fields = layer()->pendingFields();
620  QStringList fldNames;
621  fldNames << fields[ fieldId ].name();
622 
623  QgsFeatureRequest r( mFeatureRequest );
625 
626  QgsFeature f;
627  while ( it.nextFeature( f ) )
628  {
629  mFieldCache.insert( f.id(), f.attribute( fieldId ) );
630  }
631 
632  mCachedField = fieldId;
633  }
634 }
635 
637 {
638  mFeatureRequest = request;
639  if ( layer() && !layer()->hasGeometryType() )
640  mFeatureRequest.setFlags( mFeatureRequest.flags() | QgsFeatureRequest::NoGeometry );
641 }
642 
644 {
645  return mFeatureRequest;
646 }