QGIS API Documentation  3.20.0-Odense (decaadbb31)
qgsmaplayermodel.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaplayermodel.cpp
3  --------------------------------------
4  Date : 01.04.2014
5  Copyright : (C) 2014 Denis Rouzaud
6  Email : [email protected]
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 <QIcon>
17 
18 #include "qgsmaplayermodel.h"
19 #include "qgsproject.h"
20 #include "qgsapplication.h"
21 #include "qgsvectorlayer.h"
22 #include "qgsiconutils.h"
23 #include <QMimeData>
24 
25 QgsMapLayerModel::QgsMapLayerModel( const QList<QgsMapLayer *> &layers, QObject *parent, QgsProject *project )
26  : QAbstractItemModel( parent )
27  , mProject( project ? project : QgsProject::instance() )
28 {
29  connect( mProject, static_cast < void ( QgsProject::* )( const QStringList & ) >( &QgsProject::layersWillBeRemoved ), this, &QgsMapLayerModel::removeLayers );
30  addLayers( layers );
31 }
32 
33 QgsMapLayerModel::QgsMapLayerModel( QObject *parent, QgsProject *project )
34  : QAbstractItemModel( parent )
35  , mProject( project ? project : QgsProject::instance() )
36 {
38  connect( mProject, static_cast < void ( QgsProject::* )( const QStringList & ) >( &QgsProject::layersWillBeRemoved ), this, &QgsMapLayerModel::removeLayers );
39  addLayers( mProject->mapLayers().values() );
40 }
41 
43 {
44  mItemCheckable = checkable;
45 }
46 
48 {
49  mCanReorder = allow;
50 }
51 
53 {
54  return mCanReorder;
55 }
56 
57 void QgsMapLayerModel::checkAll( Qt::CheckState checkState )
58 {
59  QMap<QString, Qt::CheckState>::iterator i = mLayersChecked.begin();
60  for ( ; i != mLayersChecked.end(); ++i )
61  {
62  *i = checkState;
63  }
64  emit dataChanged( index( 0, 0 ), index( rowCount() - 1, 0 ) );
65 }
66 
67 void QgsMapLayerModel::setAllowEmptyLayer( bool allowEmpty, const QString &text, const QIcon &icon )
68 {
69  mEmptyText = text;
70  mEmptyIcon = icon;
71  if ( allowEmpty == mAllowEmpty )
72  return;
73 
74  if ( allowEmpty )
75  {
76  beginInsertRows( QModelIndex(), 0, 0 );
77  mAllowEmpty = true;
78  endInsertRows();
79  }
80  else
81  {
82  beginRemoveRows( QModelIndex(), 0, 0 );
83  mAllowEmpty = false;
84  endRemoveRows();
85  }
86 }
87 
88 void QgsMapLayerModel::setShowCrs( bool showCrs )
89 {
90  if ( mShowCrs == showCrs )
91  return;
92 
93  mShowCrs = showCrs;
94  emit dataChanged( index( 0, 0 ), index( rowCount() - 1, 0 ), QVector<int>() << Qt::DisplayRole );
95 }
96 
97 QList<QgsMapLayer *> QgsMapLayerModel::layersChecked( Qt::CheckState checkState )
98 {
99  QList<QgsMapLayer *> layers;
100  const auto constMLayers = mLayers;
101  for ( QgsMapLayer *layer : constMLayers )
102  {
103  if ( mLayersChecked[layer->id()] == checkState )
104  {
105  layers.append( layer );
106  }
107  }
108  return layers;
109 }
110 
111 void QgsMapLayerModel::setLayersChecked( const QList<QgsMapLayer *> &layers )
112 {
113  QMap<QString, Qt::CheckState>::iterator i = mLayersChecked.begin();
114  for ( ; i != mLayersChecked.end(); ++i )
115  {
116  *i = Qt::Unchecked;
117  }
118  for ( const QgsMapLayer *layer : layers )
119  {
120  mLayersChecked[ layer->id() ] = Qt::Checked;
121  }
122  emit dataChanged( index( 0, 0 ), index( rowCount() - 1, 0 ), QVector<int>() << Qt::CheckStateRole );
123 }
124 
126 {
127  int r = mLayers.indexOf( layer );
128  if ( r >= 0 && mAllowEmpty )
129  r++;
130  return index( r, 0 );
131 }
132 
133 QgsMapLayer *QgsMapLayerModel::layerFromIndex( const QModelIndex &index ) const
134 {
135  return mProject->mapLayer( index.data( LayerIdRole ).toString() );
136 }
137 
138 void QgsMapLayerModel::setAdditionalItems( const QStringList &items )
139 {
140  if ( items == mAdditionalItems )
141  return;
142 
143  int offset = 0;
144  if ( mAllowEmpty )
145  offset++;
146 
147  offset += mLayers.count();
148 
149  //remove existing
150  if ( !mAdditionalItems.isEmpty() )
151  {
152  beginRemoveRows( QModelIndex(), offset, offset + mAdditionalItems.count() - 1 );
153  mAdditionalItems.clear();
154  endRemoveRows();
155  }
156 
157  //add new
158  beginInsertRows( QModelIndex(), offset, offset + items.count() - 1 );
159  mAdditionalItems = items;
160  endInsertRows();
161 }
162 
163 void QgsMapLayerModel::removeLayers( const QStringList &layerIds )
164 {
165  int offset = 0;
166  if ( mAllowEmpty )
167  offset++;
168 
169  for ( const QString &layerId : layerIds )
170  {
171  QModelIndex startIndex = index( 0, 0 );
172  QModelIndexList list = match( startIndex, LayerIdRole, layerId, 1 );
173  if ( !list.isEmpty() )
174  {
175  QModelIndex index = list[0];
176  beginRemoveRows( QModelIndex(), index.row(), index.row() );
177  mLayersChecked.remove( layerId );
178  mLayers.removeAt( index.row() - offset );
179  endRemoveRows();
180  }
181  }
182 }
183 
184 void QgsMapLayerModel::addLayers( const QList<QgsMapLayer *> &layers )
185 {
186  if ( !layers.empty( ) )
187  {
188  int offset = 0;
189  if ( mAllowEmpty )
190  offset++;
191 
192  beginInsertRows( QModelIndex(), mLayers.count() + offset, mLayers.count() + layers.count() - 1 + offset );
193  const auto constLayers = layers;
194  for ( QgsMapLayer *layer : constLayers )
195  {
196  mLayers.append( layer );
197  mLayersChecked.insert( layer->id(), Qt::Unchecked );
198  }
199  endInsertRows();
200  }
201 }
202 
203 QModelIndex QgsMapLayerModel::index( int row, int column, const QModelIndex &parent ) const
204 {
205  int offset = 0;
206  if ( mAllowEmpty )
207  offset++;
208 
209  if ( hasIndex( row, column, parent ) )
210  {
211  QgsMapLayer *layer = nullptr;
212  if ( row - offset >= 0 && row - offset < mLayers.count() )
213  layer = mLayers.at( row - offset );
214 
215  return createIndex( row, column, layer );
216  }
217 
218  return QModelIndex();
219 
220 }
221 
222 QModelIndex QgsMapLayerModel::parent( const QModelIndex &child ) const
223 {
224  Q_UNUSED( child )
225  return QModelIndex();
226 }
227 
228 
229 int QgsMapLayerModel::rowCount( const QModelIndex &parent ) const
230 {
231  if ( parent.isValid() )
232  return 0;
233 
234  return ( mAllowEmpty ? 1 : 0 ) + mLayers.length() + mAdditionalItems.count();
235 }
236 
237 int QgsMapLayerModel::columnCount( const QModelIndex &parent ) const
238 {
239  Q_UNUSED( parent )
240  return 1;
241 }
242 
243 
244 QVariant QgsMapLayerModel::data( const QModelIndex &index, int role ) const
245 {
246  if ( !index.isValid() )
247  return QVariant();
248 
249  bool isEmpty = index.row() == 0 && mAllowEmpty;
250  int additionalIndex = index.row() - ( mAllowEmpty ? 1 : 0 ) - mLayers.count();
251 
252  switch ( role )
253  {
254  case Qt::DisplayRole:
255  case Qt::EditRole:
256  {
257  if ( index.row() == 0 && mAllowEmpty )
258  return mEmptyText;
259 
260  if ( additionalIndex >= 0 )
261  return mAdditionalItems.at( additionalIndex );
262 
263  QgsMapLayer *layer = mLayers.value( index.row() - ( mAllowEmpty ? 1 : 0 ) );
264  if ( !layer )
265  return QVariant();
266 
267  if ( !mShowCrs || !layer->isSpatial() || role == Qt::EditRole )
268  {
269  return layer->name();
270  }
271  else
272  {
273  return tr( "%1 [%2]" ).arg( layer->name(), layer->crs().authid() );
274  }
275  }
276 
277  case LayerIdRole:
278  {
279  if ( isEmpty || additionalIndex >= 0 )
280  return QVariant();
281 
282  QgsMapLayer *layer = mLayers.value( index.row() - ( mAllowEmpty ? 1 : 0 ) );
283  return layer ? layer->id() : QVariant();
284  }
285 
286  case LayerRole:
287  {
288  if ( isEmpty || additionalIndex >= 0 )
289  return QVariant();
290 
291  return QVariant::fromValue<QgsMapLayer *>( mLayers.value( index.row() - ( mAllowEmpty ? 1 : 0 ) ) );
292  }
293 
294  case EmptyRole:
295  return isEmpty;
296 
297  case AdditionalRole:
298  return additionalIndex >= 0;
299 
300  case Qt::CheckStateRole:
301  {
302  if ( mItemCheckable )
303  {
304  if ( isEmpty || additionalIndex >= 0 )
305  return QVariant();
306 
307  QgsMapLayer *layer = mLayers.value( index.row() - ( mAllowEmpty ? 1 : 0 ) );
308  return layer ? mLayersChecked[layer->id()] : QVariant();
309  }
310 
311  return QVariant();
312  }
313 
314  case Qt::ToolTipRole:
315  {
316  QgsMapLayer *layer = mLayers.value( index.row() - ( mAllowEmpty ? 1 : 0 ) );
317  if ( layer )
318  {
319  QStringList parts;
320  QString title = layer->title().isEmpty() ? layer->shortName() : layer->title();
321  if ( title.isEmpty() )
322  title = layer->name();
323  title = "<b>" + title + "</b>";
324  if ( layer->isSpatial() && layer->crs().isValid() )
325  {
326  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
327  title = tr( "%1 (%2 - %3)" ).arg( title, QgsWkbTypes::displayString( vl->wkbType() ), layer->crs().authid() );
328  else
329  title = tr( "%1 (%2) " ).arg( title, layer->crs().authid() );
330  }
331  parts << title;
332 
333  if ( !layer->abstract().isEmpty() )
334  parts << "<br/>" + layer->abstract().replace( QLatin1String( "\n" ), QLatin1String( "<br/>" ) );
335  parts << "<i>" + layer->publicSource() + "</i>";
336  return parts.join( QLatin1String( "<br/>" ) );
337  }
338  return QVariant();
339  }
340 
341  case Qt::DecorationRole:
342  {
343  if ( isEmpty )
344  return mEmptyIcon.isNull() ? QVariant() : mEmptyIcon;
345 
346  if ( additionalIndex >= 0 )
347  return QVariant();
348 
349  QgsMapLayer *layer = mLayers.value( index.row() - ( mAllowEmpty ? 1 : 0 ) );
350  if ( !layer )
351  return QVariant();
352 
353  return iconForLayer( layer );
354  }
355  }
356 
357  return QVariant();
358 }
359 
360 QHash<int, QByteArray> QgsMapLayerModel::roleNames() const
361 {
362  QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
363  roles[LayerIdRole] = "layerId";
364  roles[LayerRole] = "layer";
365 
366  return roles;
367 }
368 
369 Qt::ItemFlags QgsMapLayerModel::flags( const QModelIndex &index ) const
370 {
371  if ( !index.isValid() )
372  {
373  if ( mCanReorder )
374  return Qt::ItemIsDropEnabled;
375  else
376  return Qt::ItemFlags();
377  }
378 
379  bool isEmpty = index.row() == 0 && mAllowEmpty;
380  int additionalIndex = index.row() - ( mAllowEmpty ? 1 : 0 ) - mLayers.count();
381 
382  Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
383 
384  if ( mCanReorder && !isEmpty && additionalIndex < 0 )
385  {
386  flags |= Qt::ItemIsDragEnabled;
387  }
388 
389  if ( mItemCheckable && !isEmpty && additionalIndex < 0 )
390  {
391  flags |= Qt::ItemIsUserCheckable;
392  }
393  return flags;
394 }
395 
396 bool QgsMapLayerModel::insertRows( int row, int count, const QModelIndex &parent )
397 {
398  if ( parent.isValid() )
399  return false;
400 
401  int offset = 0;
402  if ( mAllowEmpty )
403  offset++;
404 
405  beginInsertRows( parent, row, row + count - 1 );
406  for ( int i = row; i < row + count; ++i )
407  mLayers.insert( i - offset, nullptr );
408  endInsertRows();
409 
410  return true;
411 }
412 
413 bool QgsMapLayerModel::removeRows( int row, int count, const QModelIndex &parent )
414 {
415  if ( parent.isValid() || row < 0 )
416  return false;
417 
418  int offset = 0;
419  if ( mAllowEmpty )
420  {
421  if ( row == 0 )
422  return false;
423 
424  offset++;
425  }
426 
427  if ( row - offset > mLayers.count() - 1 )
428  {
429  return false;
430  }
431 
432  beginRemoveRows( parent, row, row + count - 1 );
433  for ( int i = 0; i != count; ++i )
434  mLayers.removeAt( row - offset );
435  endRemoveRows();
436 
437  return true;
438 }
439 
440 QStringList QgsMapLayerModel::mimeTypes() const
441 {
442  QStringList types;
443  types << QStringLiteral( "application/qgis.layermodeldata" );
444  return types;
445 }
446 
447 bool QgsMapLayerModel::canDropMimeData( const QMimeData *data, Qt::DropAction action, int, int, const QModelIndex & ) const
448 {
449  if ( !mCanReorder || action != Qt::MoveAction || !data->hasFormat( QStringLiteral( "application/qgis.layermodeldata" ) ) )
450  return false;
451  return true;
452 }
453 
454 QMimeData *QgsMapLayerModel::mimeData( const QModelIndexList &indexes ) const
455 {
456  std::unique_ptr< QMimeData > mimeData = std::make_unique< QMimeData >();
457 
458  QByteArray encodedData;
459  QDataStream stream( &encodedData, QIODevice::WriteOnly );
460  QSet< QString > addedLayers;
461 
462  for ( const QModelIndex &i : indexes )
463  {
464  if ( i.isValid() )
465  {
466  const QString id = data( index( i.row(), 0, i.parent() ), LayerIdRole ).toString();
467  if ( !addedLayers.contains( id ) )
468  {
469  addedLayers.insert( id );
470  stream << id;
471  }
472  }
473  }
474  mimeData->setData( QStringLiteral( "application/qgis.layermodeldata" ), encodedData );
475  return mimeData.release();
476 }
477 
478 bool QgsMapLayerModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
479 {
480  if ( !canDropMimeData( data, action, row, column, parent ) || row < 0 )
481  return false;
482 
483  if ( action == Qt::IgnoreAction )
484  return true;
485  else if ( action != Qt::MoveAction )
486  return false;
487 
488  QByteArray encodedData = data->data( QStringLiteral( "application/qgis.layermodeldata" ) );
489  QDataStream stream( &encodedData, QIODevice::ReadOnly );
490  QStringList newItems;
491  int rows = 0;
492 
493  while ( !stream.atEnd() )
494  {
495  QString text;
496  stream >> text;
497  newItems << text;
498  ++rows;
499  }
500 
501  insertRows( row, rows, QModelIndex() );
502  for ( const QString &text : std::as_const( newItems ) )
503  {
504  QModelIndex idx = index( row, 0, QModelIndex() );
505  setData( idx, text, LayerIdRole );
506  row++;
507  }
508 
509  return true;
510 }
511 
513 {
514  return Qt::MoveAction;
515 }
516 
518 {
519  return QgsIconUtils::iconForLayer( layer );
520 }
521 
522 bool QgsMapLayerModel::setData( const QModelIndex &index, const QVariant &value, int role )
523 {
524  if ( !index.isValid() )
525  return false;
526 
527  bool isEmpty = index.row() == 0 && mAllowEmpty;
528  int additionalIndex = index.row() - ( mAllowEmpty ? 1 : 0 ) - mLayers.count();
529 
530  switch ( role )
531  {
532  case Qt::CheckStateRole:
533  {
534  if ( !isEmpty && additionalIndex < 0 )
535  {
536  QgsMapLayer *layer = static_cast<QgsMapLayer *>( index.internalPointer() );
537  mLayersChecked[layer->id()] = ( Qt::CheckState )value.toInt();
538  emit dataChanged( index, index, QVector< int >() << Qt::CheckStateRole );
539  return true;
540  }
541  break;
542  }
543 
544  case LayerIdRole:
545  if ( !isEmpty && additionalIndex < 0 )
546  {
547  mLayers[index.row() - ( mAllowEmpty ? 1 : 0 )] = mProject->mapLayer( value.toString() );
548  emit dataChanged( index, index );
549  return true;
550  }
551  break;
552  }
553 
554  return false;
555 }
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString authid() const
Returns the authority identifier for the CRS.
static QIcon iconForLayer(const QgsMapLayer *layer)
Returns the icon corresponding to a specified map layer.
void setShowCrs(bool showCrs)
Sets whether the CRS of layers is also included in the model's display role.
void setItemsCanBeReordered(bool allow)
Sets whether items in the model can be reordered via drag and drop.
QHash< int, QByteArray > roleNames() const override
Returns strings for all roles supported by this model.
Qt::ItemFlags flags(const QModelIndex &index) const override
bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
QList< QgsMapLayer * > layersChecked(Qt::CheckState checkState=Qt::Checked)
layersChecked returns the list of layers which are checked (or unchecked)
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
bool insertRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
QModelIndex parent(const QModelIndex &child) const override
QgsProject * mProject
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
@ EmptyRole
True if index corresponds to the empty (not set) value.
@ LayerIdRole
Stores the map layer ID.
@ LayerRole
Stores pointer to the map layer itself.
@ AdditionalRole
True if index corresponds to an additional (non map layer) item.
QModelIndex indexFromLayer(QgsMapLayer *layer) const
indexFromLayer returns the model index for a given layer
void setAllowEmptyLayer(bool allowEmpty, const QString &text=QString(), const QIcon &icon=QIcon())
Sets whether an optional empty layer ("not set") option is present in the model.
void setAdditionalItems(const QStringList &items)
Sets a list of additional (non map layer) items to include at the end of the model.
int columnCount(const QModelIndex &parent=QModelIndex()) const override
void setItemsCheckable(bool checkable)
setItemsCheckable defines if layers should be selectable in the widget
static QIcon iconForLayer(QgsMapLayer *layer)
Returns the icon corresponding to a specified map layer.
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
QgsMapLayer * layerFromIndex(const QModelIndex &index) const
Returns the map layer corresponding to the specified index.
Qt::DropActions supportedDropActions() const override
void setLayersChecked(const QList< QgsMapLayer * > &layers)
Sets which layers are checked in the model.
void checkAll(Qt::CheckState checkState)
checkAll changes the checkstate for all the layers
void removeLayers(const QStringList &layerIds)
QStringList mimeTypes() const override
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
bool itemsCanBeReordered() const
Returns true if items in the model can be reordered via drag and drop.
QMap< QString, Qt::CheckState > mLayersChecked
QgsMapLayerModel(QObject *parent=nullptr, QgsProject *project=nullptr)
QgsMapLayerModel creates a model to display layers in widgets.
QList< QgsMapLayer * > mLayers
void addLayers(const QList< QgsMapLayer * > &layers)
QMimeData * mimeData(const QModelIndexList &indexes) const override
Base class for all map layer types.
Definition: qgsmaplayer.h:70
QString name
Definition: qgsmaplayer.h:73
virtual bool isSpatial() const
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
QString publicSource() const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:76
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
QString abstract() const
Returns the abstract of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:303
QString shortName() const
Returns the short name of the layer used by QGIS Server to identify the layer.
QString title() const
Returns the title of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:287
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:99
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the registry.
void layersAdded(const QList< QgsMapLayer * > &layers)
Emitted when one or more layers were added to the registry.
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
Represents a vector layer which manages a vector based data sets.
static QString displayString(Type type) SIP_HOLDGIL
Returns a non-translated display string type for a WKB type, e.g., the geometry name used in WKT geom...