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