QGIS API Documentation  2.2.0-Valmiera
 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 "qgsfield.h"
21 #include "qgsvectorlayer.h"
22 #include "qgslogger.h"
23 #include "qgsattributeaction.h"
24 #include "qgsmapcanvas.h"
25 #include "qgsrendererv2.h"
26 #include "qgsmaplayerregistry.h"
27 #include "qgsexpression.h"
29 
30 #include <QtGui>
31 #include <QVariant>
32 
33 #include <limits>
34 
36  : QAbstractTableModel( parent )
37  , mLayerCache( layerCache )
38  , mCachedField( -1 )
39 {
40  QgsDebugMsg( "entered." );
41 
42  if ( layerCache->layer()->geometryType() == QGis::NoGeometry )
43  {
45  }
46 
48 
49  if ( !layer()->hasGeometryType() )
51 
53 
54  connect( mLayerCache, SIGNAL( attributeValueChanged( QgsFeatureId, int, const QVariant& ) ), this, SLOT( attributeValueChanged( QgsFeatureId, int, const QVariant& ) ) );
55  connect( layer(), SIGNAL( featureDeleted( QgsFeatureId ) ), this, SLOT( featureDeleted( QgsFeatureId ) ) );
56  connect( layer(), SIGNAL( attributeDeleted( int ) ), this, SLOT( attributeDeleted( int ) ) );
57  connect( layer(), SIGNAL( updatedFields() ), this, SLOT( updatedFields() ) );
58  connect( layer(), SIGNAL( editCommandEnded() ), this, SLOT( editCommandEnded() ) );
59  connect( mLayerCache, SIGNAL( featureAdded( QgsFeatureId ) ), this, SLOT( featureAdded( QgsFeatureId ) ) );
60  connect( mLayerCache, SIGNAL( cachedLayerDeleted() ), this, SLOT( layerDeleted() ) );
61 }
62 
64 {
65  qDeleteAll( mValueMaps );
66 }
67 
69 {
70  QgsDebugMsgLevel( QString( "loading feature %1" ).arg( fid ), 3 );
71 
72  if ( fid == std::numeric_limits<int>::min() )
73  {
74  return false;
75  }
76 
77  return mLayerCache->featureAtId( fid, mFeat );
78 }
79 
81 {
82  QgsDebugMsgLevel( QString( "(%2) fid: %1" ).arg( fid ).arg( mFeatureRequest.filterType() ), 4 );
83  mFieldCache.remove( fid );
84 
85  int row = idToRow( fid );
86 
87  if ( row != -1 )
88  {
89  beginRemoveRows( QModelIndex(), row, row );
90  removeRow( row );
91  endRemoveRows();
92  }
93 }
94 
95 bool QgsAttributeTableModel::removeRows( int row, int count, const QModelIndex &parent )
96 {
97  Q_UNUSED( parent );
98  QgsDebugMsgLevel( QString( "remove %2 rows at %1 (rows %3, ids %4)" ).arg( row ).arg( count ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ), 3 );
99 
100  // clean old references
101  for ( int i = row; i < row + count; i++ )
102  {
103  mIdRowMap.remove( mRowIdMap[ i ] );
104  mRowIdMap.remove( i );
105  }
106 
107  // update maps
108  int n = mRowIdMap.size() + count;
109  for ( int i = row + count; i < n; i++ )
110  {
111  QgsFeatureId id = mRowIdMap[i];
112  mIdRowMap[ id ] -= count;
113  mRowIdMap[ i-count ] = id;
114  mRowIdMap.remove( i );
115  }
116 
117 #ifdef QGISDEBUG
118  QgsDebugMsgLevel( QString( "after removal rows %1, ids %2" ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ), 4 );
119  QgsDebugMsgLevel( "id->row", 4 );
120  for ( QHash<QgsFeatureId, int>::iterator it = mIdRowMap.begin(); it != mIdRowMap.end(); ++it )
121  QgsDebugMsgLevel( QString( "%1->%2" ).arg( FID_TO_STRING( it.key() ) ).arg( *it ), 4 );
122 
123  QHash<QgsFeatureId, int>::iterator idit;
124 
125  QgsDebugMsgLevel( "row->id", 4 );
126  for ( QHash<int, QgsFeatureId>::iterator it = mRowIdMap.begin(); it != mRowIdMap.end(); ++it )
127  QgsDebugMsgLevel( QString( "%1->%2" ).arg( it.key() ).arg( FID_TO_STRING( *it ) ), 4 );
128 #endif
129 
130  Q_ASSERT( mRowIdMap.size() == mIdRowMap.size() );
131 
132  return true;
133 }
134 
136 {
137  QgsDebugMsgLevel( QString( "(%2) fid: %1" ).arg( fid ).arg( mFeatureRequest.filterType() ), 4 );
138  bool featOk = true;
139 
140  if ( mFeat.id() != fid )
141  featOk = loadFeatureAtId( fid );
142 
143  if ( featOk && mFeatureRequest.acceptFeature( mFeat ) )
144  {
146 
147  int n = mRowIdMap.size();
148  beginInsertRows( QModelIndex(), n, n );
149 
150  mIdRowMap.insert( fid, n );
151  mRowIdMap.insert( n, fid );
152 
153  endInsertRows();
154 
155  reload( index( rowCount() - 1, 0 ), index( rowCount() - 1, columnCount() ) );
156  }
157 }
158 
160 {
161  QgsDebugMsg( "entered." );
162  loadAttributes();
163  emit modelChanged();
164 }
165 
167 {
168  reload( createIndex( mChangedCellBounds.top(), mChangedCellBounds.left() ),
169  createIndex( mChangedCellBounds.bottom(), mChangedCellBounds.right() ) );
170 
171  mChangedCellBounds = QRect();
172 }
173 
175 {
176  if ( idx == mCachedField )
177  {
178  prefetchColumnData( -1 );
179  }
180 }
181 
183 {
184  QgsDebugMsg( "entered." );
185 
186  beginRemoveRows( QModelIndex(), 0, rowCount() - 1 );
187  removeRows( 0, rowCount() );
188  endRemoveRows();
189 
190  const QMap<QString, QVariant> *item;
191  foreach ( item, mValueMaps )
192  {
193  delete item;
194  }
195 
196  mValueMaps.clear();
197 }
198 
199 void QgsAttributeTableModel::attributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
200 {
201  QgsDebugMsgLevel( QString( "(%4) fid: %1, idx: %2, value: %3" ).arg( fid ).arg( idx ).arg( value.toString() ).arg( mFeatureRequest.filterType() ), 3 );
202  // No filter request: skip all possibly heavy checks
204  {
205  setData( index( idToRow( fid ), fieldCol( idx ) ), value, Qt::EditRole );
206  }
207  else
208  {
209  if ( loadFeatureAtId( fid ) )
210  {
212  {
213  if ( !mIdRowMap.contains( fid ) )
214  {
215  // Feature changed in such a way, it will be shown now
216  featureAdded( fid );
217  }
218  else
219  {
220  if ( idx == mCachedField )
221  mFieldCache[ fid ] = value;
222  // Update representation
223  setData( index( idToRow( fid ), fieldCol( idx ) ), value, Qt::EditRole );
224  }
225  }
226  else
227  {
228  if ( mIdRowMap.contains( fid ) )
229  {
230  // Feature changed such, that it is no longer shown
231  featureDeleted( fid );
232  }
233  // else: we don't care
234  }
235  }
236  }
237 }
238 
240 {
241  if ( !layer() )
242  {
243  return;
244  }
245 
246  bool ins = false, rm = false;
247 
248  QgsAttributeList attributes;
249  const QgsFields& fields = layer()->pendingFields();
250  for ( int idx = 0; idx < fields.count(); ++idx )
251  {
252  switch ( layer()->editType( idx ) )
253  {
255  continue;
256 
258  mValueMaps.insert( idx, new QMap< QString, QVariant >( layer()->valueMap( idx ) ) );
259  break;
260 
262  {
264 
266  if ( !layer )
267  continue;
268 
269  int ki = layer->fieldNameIndex( data.mKey );
270  int vi = layer->fieldNameIndex( data.mValue );
271 
272  QgsExpression *e = 0;
273  if ( !data.mFilterExpression.isEmpty() )
274  {
275  e = new QgsExpression( data.mFilterExpression );
276  if ( e->hasParserError() || !e->prepare( layer->pendingFields() ) )
277  continue;
278  }
279 
280  if ( ki >= 0 && vi >= 0 )
281  {
282  QSet<int> attributes;
283  attributes << ki << vi;
284 
286 
287  if ( e )
288  {
289  if ( e->needsGeometry() )
291 
292  foreach ( const QString &field, e->referencedColumns() )
293  {
294  int idx = layer->fieldNameIndex( field );
295  if ( idx < 0 )
296  continue;
297  attributes << idx;
298  }
299  }
300 
301  QMap< QString, QVariant > *map = new QMap< QString, QVariant >();
302 
303  QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFlags( flags ).setSubsetOfAttributes( attributes.toList() ) );
304  QgsFeature f;
305  while ( fit.nextFeature( f ) )
306  {
307  if ( e && !e->evaluate( &f ).toBool() )
308  continue;
309 
310  map->insert( f.attribute( vi ).toString(), f.attribute( ki ) );
311  }
312 
313  mValueMaps.insert( idx, map );
314  }
315  }
316  break;
317 
318  default:
319  break;
320  }
321 
322  attributes << idx;
323  }
324 
325  if ( mFieldCount < attributes.size() )
326  {
327  ins = true;
328  beginInsertColumns( QModelIndex(), mFieldCount, attributes.size() - 1 );
329  }
330  else if ( attributes.size() < mFieldCount )
331  {
332  rm = true;
333  beginRemoveColumns( QModelIndex(), attributes.size(), mFieldCount - 1 );
334  }
335 
336  mFieldCount = attributes.size();
337  mAttributes = attributes;
338 
339  if ( ins )
340  {
341  endInsertColumns();
342  }
343  else if ( rm )
344  {
345  endRemoveColumns();
346  }
347 }
348 
350 {
351  QgsDebugMsg( "entered." );
352 
353  beginRemoveRows( QModelIndex(), 0, rowCount() - 1 );
354  removeRows( 0, rowCount() );
355  endRemoveRows();
356 
358 
359  int i = 0;
360 
361  QTime t;
362  t.start();
363 
364  QgsFeature feat;
365  while ( features.nextFeature( feat ) )
366  {
367  ++i;
368 
369  if ( t.elapsed() > 1000 )
370  {
371  bool cancel = false;
372  emit( progress( i, cancel ) );
373  if ( cancel )
374  break;
375 
376  t.restart();
377  }
378  mFeat = feat;
379  featureAdded( feat.id() );
380  }
381 
382  emit finished();
383 
384  mFieldCount = mAttributes.size();
385 }
386 
388 {
389  if ( a == b )
390  return;
391 
392  int rowA = idToRow( a );
393  int rowB = idToRow( b );
394 
395  //emit layoutAboutToBeChanged();
396 
397  mRowIdMap.remove( rowA );
398  mRowIdMap.remove( rowB );
399  mRowIdMap.insert( rowA, b );
400  mRowIdMap.insert( rowB, a );
401 
402  mIdRowMap.remove( a );
403  mIdRowMap.remove( b );
404  mIdRowMap.insert( a, rowB );
405  mIdRowMap.insert( b, rowA );
406 
407  //emit layoutChanged();
408 }
409 
411 {
412  if ( !mIdRowMap.contains( id ) )
413  {
414  QgsDebugMsg( QString( "idToRow: id %1 not in the map" ).arg( id ) );
415  return -1;
416  }
417 
418  return mIdRowMap[id];
419 }
420 
422 {
423  return index( idToRow( id ), 0 );
424 }
425 
427 {
428  QModelIndexList indexes;
429 
430  int row = idToRow( id );
431  for ( int column = 0; column < columnCount(); ++column )
432  {
433  indexes.append( index( row, column ) );
434  }
435 
436  return indexes;
437 }
438 
440 {
441  if ( !mRowIdMap.contains( row ) )
442  {
443  QgsDebugMsg( QString( "rowToId: row %1 not in the map" ).arg( row ) );
444  // return negative infinite (to avoid collision with newly added features)
446  }
447 
448  return mRowIdMap[row];
449 }
450 
452 {
453  return mAttributes[ col ];
454 }
455 
457 {
458  return mAttributes.indexOf( idx );
459 }
460 
461 int QgsAttributeTableModel::rowCount( const QModelIndex &parent ) const
462 {
463  Q_UNUSED( parent );
464  return mRowIdMap.size();
465 }
466 
467 int QgsAttributeTableModel::columnCount( const QModelIndex &parent ) const
468 {
469  Q_UNUSED( parent );
470  return qMax( 1, mFieldCount ); // if there are zero columns all model indices will be considered invalid
471 }
472 
473 QVariant QgsAttributeTableModel::headerData( int section, Qt::Orientation orientation, int role ) const
474 {
475  if ( !layer() )
476  return QVariant();
477 
478  if ( role == Qt::DisplayRole )
479  {
480  if ( orientation == Qt::Vertical ) //row
481  {
482  return QVariant( section );
483  }
484  else if ( section >= 0 && section < mFieldCount )
485  {
486  QString attributeName = layer()->attributeAlias( mAttributes[section] );
487  if ( attributeName.isEmpty() )
488  {
489  QgsField field = layer()->pendingFields()[ mAttributes[section] ];
490  attributeName = field.name();
491  }
492  return QVariant( attributeName );
493  }
494  else
495  {
496  return tr( "feature id" );
497  }
498  }
499  else
500  {
501  return QVariant();
502  }
503 }
504 
505 QVariant QgsAttributeTableModel::data( const QModelIndex &index, int role ) const
506 {
507  if ( !index.isValid() ||
508  ( role != Qt::TextAlignmentRole
509  && role != Qt::DisplayRole
510  && role != Qt::EditRole
511  && role != SortRole
512  && role != FeatureIdRole
513  && role != FieldIndexRole
514  )
515  )
516  return QVariant();
517 
518  QgsFeatureId rowId = rowToId( index.row() );
519 
520  if ( role == FeatureIdRole )
521  return rowId;
522 
523  if ( index.column() >= mFieldCount )
524  return role == Qt::DisplayRole ? rowId : QVariant();
525 
526  int fieldId = mAttributes[ index.column()];
527 
528  if ( role == FieldIndexRole )
529  return fieldId;
530 
531  const QgsField& field = layer()->pendingFields()[ fieldId ];
532 
533  QVariant::Type fldType = field.type();
534  bool fldNumeric = ( fldType == QVariant::Int || fldType == QVariant::Double );
535 
536  if ( role == Qt::TextAlignmentRole )
537  {
538  if ( fldNumeric )
539  return QVariant( Qt::AlignRight );
540  else
541  return QVariant( Qt::AlignLeft );
542  }
543 
544  QVariant val;
545 
546  // if we don't have the row in current cache, load it from layer first
547  if ( mCachedField == fieldId )
548  {
549  val = mFieldCache[ rowId ];
550  }
551  else
552  {
553  if ( mFeat.id() != rowId || !mFeat.isValid() )
554  {
555  if ( !loadFeatureAtId( rowId ) )
556  return QVariant( "ERROR" );
557 
558  if ( mFeat.id() != rowId )
559  return QVariant( "ERROR" );
560  }
561 
562  val = mFeat.attribute( fieldId );
563  }
564 
565  // For sorting return unprocessed value
566  if ( SortRole == role )
567  {
568  return val;
569  }
570 
571  if ( val.isNull() )
572  {
573  // if the value is NULL, show that in table, but don't show "NULL" text in editor
574  if ( role == Qt::EditRole )
575  {
576  return QVariant( fldType );
577  }
578  else
579  {
580  QSettings settings;
581  return settings.value( "qgis/nullValue", "NULL" );
582  }
583  }
584 
585  if ( role == Qt::DisplayRole )
586  {
587  if ( mValueMaps.contains( fieldId ) )
588  {
589  return mValueMaps[ fieldId ]->key( val.toString(), QString( "(%1)" ).arg( val.toString() ) );
590  }
591 
592  if ( layer()->editType( fieldId ) == QgsVectorLayer::Calendar && val.canConvert( QVariant::Date ) )
593  {
594  return val.toDate().toString( layer()->dateFormat( fieldId ) );
595  }
596  }
597 
598 
599  return field.displayString( val );
600 }
601 
602 bool QgsAttributeTableModel::setData( const QModelIndex &index, const QVariant &value, int role )
603 {
604  Q_UNUSED( value )
605 
606  if ( !index.isValid() || index.column() >= mFieldCount || role != Qt::EditRole || !layer()->isEditable() )
607  return false;
608 
609  if ( !layer()->isModified() )
610  return false;
611 
612  if ( mChangedCellBounds.isNull() )
613  {
614  mChangedCellBounds = QRect( index.column(), index.row(), 0, 0 );
615  }
616  else
617  {
618  if ( index.column() < mChangedCellBounds.left() )
619  {
620  mChangedCellBounds.setLeft( index.column() );
621  }
622  if ( index.row() < mChangedCellBounds.top() )
623  {
624  mChangedCellBounds.setTop( index.row() );
625  }
626  if ( index.column() > mChangedCellBounds.right() )
627  {
628  mChangedCellBounds.setRight( index.column() );
629  }
630  if ( index.row() > mChangedCellBounds.bottom() )
631  {
632  mChangedCellBounds.setBottom( index.row() );
633  }
634  }
635 
636  return true;
637 }
638 
639 Qt::ItemFlags QgsAttributeTableModel::flags( const QModelIndex &index ) const
640 {
641  if ( !index.isValid() )
642  return Qt::ItemIsEnabled;
643 
644  if ( index.column() >= mFieldCount )
645  return Qt::NoItemFlags;
646 
647  Qt::ItemFlags flags = QAbstractItemModel::flags( index );
648 
649  if ( layer()->isEditable() &&
650  layer()->editType( mAttributes[ index.column()] ) != QgsVectorLayer::Immutable )
651  flags |= Qt::ItemIsEditable;
652 
653  return flags;
654 }
655 
656 void QgsAttributeTableModel::reload( const QModelIndex &index1, const QModelIndex &index2 )
657 {
659  emit dataChanged( index1, index2 );
660 }
661 
663 {
664  reset();
665 }
666 
667 void QgsAttributeTableModel::executeAction( int action, const QModelIndex &idx ) const
668 {
669  QgsFeature f = feature( idx );
670  layer()->actions()->doAction( action, f, fieldIdx( idx.column() ) );
671 }
672 
673 void QgsAttributeTableModel::executeMapLayerAction( QgsMapLayerAction* action, const QModelIndex &idx ) const
674 {
675  QgsFeature f = feature( idx );
676  action->triggerForFeature( layer(), &f );
677 }
678 
679 QgsFeature QgsAttributeTableModel::feature( const QModelIndex &idx ) const
680 {
681  QgsFeature f;
682  f.initAttributes( mAttributes.size() );
683  f.setFeatureId( rowToId( idx.row() ) );
684  for ( int i = 0; i < mAttributes.size(); i++ )
685  {
686  f.setAttribute( mAttributes[i], data( index( idx.row(), i ), Qt::EditRole ) );
687  }
688 
689  return f;
690 }
691 
693 {
694  mFieldCache.clear();
695 
696  if ( column == -1 )
697  {
698  mCachedField = -1;
699  }
700  else
701  {
702  if ( column >= mAttributes.count() )
703  return;
704  int fieldId = mAttributes[ column ];
705  const QgsFields& fields = layer()->pendingFields();
706  QStringList fldNames;
707  fldNames << fields[ fieldId ].name();
708 
709  QgsFeatureIterator it = mLayerCache->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( fldNames, fields ) );
710 
711  QgsFeature f;
712  while ( it.nextFeature( f ) )
713  {
714  mFieldCache.insert( f.id(), f.attribute( fieldId ) );
715  }
716 
717  mCachedField = fieldId;
718  }
719 }
720 
722 {
723  mFeatureRequest = request;
724  if ( layer() && !layer()->hasGeometryType() )
726 }