QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
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 "qgsdataitem.h"
19 #include "qgsmaplayermodel.h"
20 #include "qgsproject.h"
21 #include "qgsapplication.h"
22 #include "qgsvectorlayer.h"
23 
24 QgsMapLayerModel::QgsMapLayerModel( const QList<QgsMapLayer *> &layers, QObject *parent, QgsProject *project )
25  : QAbstractItemModel( parent )
26  , mProject( project ? project : QgsProject::instance() )
27 {
28  connect( mProject, static_cast < void ( QgsProject::* )( const QStringList & ) >( &QgsProject::layersWillBeRemoved ), this, &QgsMapLayerModel::removeLayers );
29  addLayers( layers );
30 }
31 
32 QgsMapLayerModel::QgsMapLayerModel( QObject *parent, QgsProject *project )
33  : QAbstractItemModel( parent )
34  , mProject( project ? project : QgsProject::instance() )
35 {
37  connect( mProject, static_cast < void ( QgsProject::* )( const QStringList & ) >( &QgsProject::layersWillBeRemoved ), this, &QgsMapLayerModel::removeLayers );
38  addLayers( mProject->mapLayers().values() );
39 }
40 
42 {
43  mItemCheckable = checkable;
44 }
45 
47 {
48  mCanReorder = allow;
49 }
50 
52 {
53  return mCanReorder;
54 }
55 
56 void QgsMapLayerModel::checkAll( Qt::CheckState checkState )
57 {
58  QMap<QString, Qt::CheckState>::iterator i = mLayersChecked.begin();
59  for ( ; i != mLayersChecked.end(); ++i )
60  {
61  *i = checkState;
62  }
63  emit dataChanged( index( 0, 0 ), index( rowCount() - 1, 0 ) );
64 }
65 
66 void QgsMapLayerModel::setAllowEmptyLayer( bool allowEmpty )
67 {
68  if ( allowEmpty == mAllowEmpty )
69  return;
70 
71  if ( allowEmpty )
72  {
73  beginInsertRows( QModelIndex(), 0, 0 );
74  mAllowEmpty = true;
75  endInsertRows();
76  }
77  else
78  {
79  beginRemoveRows( QModelIndex(), 0, 0 );
80  mAllowEmpty = false;
81  endRemoveRows();
82  }
83 }
84 
85 void QgsMapLayerModel::setShowCrs( bool showCrs )
86 {
87  if ( mShowCrs == showCrs )
88  return;
89 
90  mShowCrs = showCrs;
91  emit dataChanged( index( 0, 0 ), index( rowCount() - 1, 0 ), QVector<int>() << Qt::DisplayRole );
92 }
93 
94 QList<QgsMapLayer *> QgsMapLayerModel::layersChecked( Qt::CheckState checkState )
95 {
96  QList<QgsMapLayer *> layers;
97  const auto constMLayers = mLayers;
98  for ( QgsMapLayer *layer : constMLayers )
99  {
100  if ( mLayersChecked[layer->id()] == checkState )
101  {
102  layers.append( layer );
103  }
104  }
105  return layers;
106 }
107 
108 void QgsMapLayerModel::setLayersChecked( const QList<QgsMapLayer *> &layers )
109 {
110  QMap<QString, Qt::CheckState>::iterator i = mLayersChecked.begin();
111  for ( ; i != mLayersChecked.end(); ++i )
112  {
113  *i = Qt::Unchecked;
114  }
115  for ( const QgsMapLayer *layer : layers )
116  {
117  mLayersChecked[ layer->id() ] = Qt::Checked;
118  }
119  emit dataChanged( index( 0, 0 ), index( rowCount() - 1, 0 ), QVector<int>() << Qt::CheckStateRole );
120 }
121 
123 {
124  int r = mLayers.indexOf( layer );
125  if ( r >= 0 && mAllowEmpty )
126  r++;
127  return index( r, 0 );
128 }
129 
130 QgsMapLayer *QgsMapLayerModel::layerFromIndex( const QModelIndex &index ) const
131 {
132  return mProject->mapLayer( index.data( LayerIdRole ).toString() );
133 }
134 
135 void QgsMapLayerModel::setAdditionalItems( const QStringList &items )
136 {
137  if ( items == mAdditionalItems )
138  return;
139 
140  int offset = 0;
141  if ( mAllowEmpty )
142  offset++;
143 
144  offset += mLayers.count();
145 
146  //remove existing
147  if ( !mAdditionalItems.isEmpty() )
148  {
149  beginRemoveRows( QModelIndex(), offset, offset + mAdditionalItems.count() - 1 );
150  mAdditionalItems.clear();
151  endRemoveRows();
152  }
153 
154  //add new
155  beginInsertRows( QModelIndex(), offset, offset + items.count() - 1 );
156  mAdditionalItems = items;
157  endInsertRows();
158 }
159 
160 void QgsMapLayerModel::removeLayers( const QStringList &layerIds )
161 {
162  int offset = 0;
163  if ( mAllowEmpty )
164  offset++;
165 
166  for ( const QString &layerId : layerIds )
167  {
168  QModelIndex startIndex = index( 0, 0 );
169  QModelIndexList list = match( startIndex, LayerIdRole, layerId, 1 );
170  if ( !list.isEmpty() )
171  {
172  QModelIndex index = list[0];
173  beginRemoveRows( QModelIndex(), index.row(), index.row() );
174  mLayersChecked.remove( layerId );
175  mLayers.removeAt( index.row() - offset );
176  endRemoveRows();
177  }
178  }
179 }
180 
181 void QgsMapLayerModel::addLayers( const QList<QgsMapLayer *> &layers )
182 {
183  if ( !layers.empty( ) )
184  {
185  int offset = 0;
186  if ( mAllowEmpty )
187  offset++;
188 
189  beginInsertRows( QModelIndex(), mLayers.count() + offset, mLayers.count() + layers.count() - 1 + offset );
190  const auto constLayers = layers;
191  for ( QgsMapLayer *layer : constLayers )
192  {
193  mLayers.append( layer );
194  mLayersChecked.insert( layer->id(), Qt::Unchecked );
195  }
196  endInsertRows();
197  }
198 }
199 
200 QModelIndex QgsMapLayerModel::index( int row, int column, const QModelIndex &parent ) const
201 {
202  int offset = 0;
203  if ( mAllowEmpty )
204  offset++;
205 
206  if ( hasIndex( row, column, parent ) )
207  {
208  QgsMapLayer *layer = nullptr;
209  if ( row - offset >= 0 && row - offset < mLayers.count() )
210  layer = mLayers.at( row - offset );
211 
212  return createIndex( row, column, layer );
213  }
214 
215  return QModelIndex();
216 
217 }
218 
219 QModelIndex QgsMapLayerModel::parent( const QModelIndex &child ) const
220 {
221  Q_UNUSED( child )
222  return QModelIndex();
223 }
224 
225 
226 int QgsMapLayerModel::rowCount( const QModelIndex &parent ) const
227 {
228  if ( parent.isValid() )
229  return 0;
230 
231  return ( mAllowEmpty ? 1 : 0 ) + mLayers.length() + mAdditionalItems.count();
232 }
233 
234 int QgsMapLayerModel::columnCount( const QModelIndex &parent ) const
235 {
236  Q_UNUSED( parent )
237  return 1;
238 }
239 
240 
241 QVariant QgsMapLayerModel::data( const QModelIndex &index, int role ) const
242 {
243  if ( !index.isValid() )
244  return QVariant();
245 
246  bool isEmpty = index.row() == 0 && mAllowEmpty;
247  int additionalIndex = index.row() - ( mAllowEmpty ? 1 : 0 ) - mLayers.count();
248 
249  switch ( role )
250  {
251  case Qt::DisplayRole:
252  case Qt::EditRole:
253  {
254  if ( index.row() == 0 && mAllowEmpty )
255  return QVariant();
256 
257  if ( additionalIndex >= 0 )
258  return mAdditionalItems.at( additionalIndex );
259 
260  QgsMapLayer *layer = mLayers.value( index.row() - ( mAllowEmpty ? 1 : 0 ) );
261  if ( !layer )
262  return QVariant();
263 
264  if ( !mShowCrs || !layer->isSpatial() || role == Qt::EditRole )
265  {
266  return layer->name();
267  }
268  else
269  {
270  return tr( "%1 [%2]" ).arg( layer->name(), layer->crs().authid() );
271  }
272  }
273 
274  case LayerIdRole:
275  {
276  if ( isEmpty || additionalIndex >= 0 )
277  return QVariant();
278 
279  QgsMapLayer *layer = mLayers.value( index.row() - ( mAllowEmpty ? 1 : 0 ) );
280  return layer ? layer->id() : QVariant();
281  }
282 
283  case LayerRole:
284  {
285  if ( isEmpty || additionalIndex >= 0 )
286  return QVariant();
287 
288  return QVariant::fromValue<QgsMapLayer *>( mLayers.value( index.row() - ( mAllowEmpty ? 1 : 0 ) ) );
289  }
290 
291  case EmptyRole:
292  return isEmpty;
293 
294  case AdditionalRole:
295  return additionalIndex >= 0;
296 
297  case Qt::CheckStateRole:
298  {
299  if ( mItemCheckable )
300  {
301  if ( isEmpty || additionalIndex >= 0 )
302  return QVariant();
303 
304  QgsMapLayer *layer = mLayers.value( index.row() - ( mAllowEmpty ? 1 : 0 ) );
305  return layer ? mLayersChecked[layer->id()] : QVariant();
306  }
307 
308  return QVariant();
309  }
310 
311  case Qt::ToolTipRole:
312  {
313  QgsMapLayer *layer = mLayers.value( index.row() - ( mAllowEmpty ? 1 : 0 ) );
314  if ( layer )
315  {
316  QStringList parts;
317  QString title = layer->title().isEmpty() ? layer->shortName() : layer->title();
318  if ( title.isEmpty() )
319  title = layer->name();
320  title = "<b>" + title + "</b>";
321  if ( layer->isSpatial() && layer->crs().isValid() )
322  {
323  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
324  title = tr( "%1 (%2 - %3)" ).arg( title, QgsWkbTypes::displayString( vl->wkbType() ), layer->crs().authid() );
325  else
326  title = tr( "%1 (%2) " ).arg( title, layer->crs().authid() );
327  }
328  parts << title;
329 
330  if ( !layer->abstract().isEmpty() )
331  parts << "<br/>" + layer->abstract().replace( QLatin1String( "\n" ), QLatin1String( "<br/>" ) );
332  parts << "<i>" + layer->publicSource() + "</i>";
333  return parts.join( QLatin1String( "<br/>" ) );
334  }
335  return QVariant();
336  }
337 
338  case Qt::DecorationRole:
339  {
340  if ( isEmpty || additionalIndex >= 0 )
341  return QVariant();
342 
343  QgsMapLayer *layer = mLayers.value( index.row() - ( mAllowEmpty ? 1 : 0 ) );
344  if ( !layer )
345  return QVariant();
346 
347  return iconForLayer( layer );
348  }
349  }
350 
351  return QVariant();
352 }
353 
354 QHash<int, QByteArray> QgsMapLayerModel::roleNames() const
355 {
356  QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
357  roles[LayerIdRole] = "layerId";
358  roles[LayerRole] = "layer";
359 
360  return roles;
361 }
362 
363 Qt::ItemFlags QgsMapLayerModel::flags( const QModelIndex &index ) const
364 {
365  if ( !index.isValid() )
366  {
367  if ( mCanReorder )
368  return Qt::ItemIsDropEnabled;
369  else
370  return Qt::ItemFlags();
371  }
372 
373  bool isEmpty = index.row() == 0 && mAllowEmpty;
374  int additionalIndex = index.row() - ( mAllowEmpty ? 1 : 0 ) - mLayers.count();
375 
376  Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
377 
378  if ( mCanReorder && !isEmpty && additionalIndex < 0 )
379  {
380  flags |= Qt::ItemIsDragEnabled;
381  }
382 
383  if ( mItemCheckable && !isEmpty && additionalIndex < 0 )
384  {
385  flags |= Qt::ItemIsUserCheckable;
386  }
387  return flags;
388 }
389 
390 bool QgsMapLayerModel::insertRows( int row, int count, const QModelIndex &parent )
391 {
392  if ( parent.isValid() )
393  return false;
394 
395  int offset = 0;
396  if ( mAllowEmpty )
397  offset++;
398 
399  beginInsertRows( parent, row, row + count - 1 );
400  for ( int i = row; i < row + count; ++i )
401  mLayers.insert( i - offset, nullptr );
402  endInsertRows();
403 
404  return true;
405 }
406 
407 bool QgsMapLayerModel::removeRows( int row, int count, const QModelIndex &parent )
408 {
409  if ( parent.isValid() || row < 0 )
410  return false;
411 
412  int offset = 0;
413  if ( mAllowEmpty )
414  {
415  if ( row == 0 )
416  return false;
417 
418  offset++;
419  }
420 
421  if ( row - offset > mLayers.count() - 1 )
422  {
423  return false;
424  }
425 
426  beginRemoveRows( parent, row, row + count - 1 );
427  for ( int i = 0; i != count; ++i )
428  mLayers.removeAt( row - offset );
429  endRemoveRows();
430 
431  return true;
432 }
433 
434 QStringList QgsMapLayerModel::mimeTypes() const
435 {
436  QStringList types;
437  types << QStringLiteral( "application/qgis.layermodeldata" );
438  return types;
439 }
440 
441 bool QgsMapLayerModel::canDropMimeData( const QMimeData *data, Qt::DropAction action, int, int, const QModelIndex & ) const
442 {
443  if ( !mCanReorder || action != Qt::MoveAction || !data->hasFormat( QStringLiteral( "application/qgis.layermodeldata" ) ) )
444  return false;
445  return true;
446 }
447 
448 QMimeData *QgsMapLayerModel::mimeData( const QModelIndexList &indexes ) const
449 {
450  std::unique_ptr< QMimeData > mimeData = qgis::make_unique< QMimeData >();
451 
452  QByteArray encodedData;
453  QDataStream stream( &encodedData, QIODevice::WriteOnly );
454  QSet< QString > addedLayers;
455 
456  for ( const QModelIndex &i : indexes )
457  {
458  if ( i.isValid() )
459  {
460  const QString id = data( index( i.row(), 0, i.parent() ), LayerIdRole ).toString();
461  if ( !addedLayers.contains( id ) )
462  {
463  addedLayers.insert( id );
464  stream << id;
465  }
466  }
467  }
468  mimeData->setData( QStringLiteral( "application/qgis.layermodeldata" ), encodedData );
469  return mimeData.release();
470 }
471 
472 bool QgsMapLayerModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
473 {
474  if ( !canDropMimeData( data, action, row, column, parent ) || row < 0 )
475  return false;
476 
477  if ( action == Qt::IgnoreAction )
478  return true;
479  else if ( action != Qt::MoveAction )
480  return false;
481 
482  QByteArray encodedData = data->data( QStringLiteral( "application/qgis.layermodeldata" ) );
483  QDataStream stream( &encodedData, QIODevice::ReadOnly );
484  QStringList newItems;
485  int rows = 0;
486 
487  while ( !stream.atEnd() )
488  {
489  QString text;
490  stream >> text;
491  newItems << text;
492  ++rows;
493  }
494 
495  insertRows( row, rows, QModelIndex() );
496  for ( const QString &text : qgis::as_const( newItems ) )
497  {
498  QModelIndex idx = index( row, 0, QModelIndex() );
499  setData( idx, text, LayerIdRole );
500  row++;
501  }
502 
503  return true;
504 }
505 
507 {
508  return Qt::MoveAction;
509 }
510 
512 {
513  switch ( layer->type() )
514  {
516  {
517  return QgsLayerItem::iconRaster();
518  }
519 
521  {
522  return QgsLayerItem::iconMesh();
523  }
524 
526  {
528  }
529 
531  {
533  }
534 
536  {
537  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
538  if ( !vl )
539  {
540  return QIcon();
541  }
542  QgsWkbTypes::GeometryType geomType = vl->geometryType();
543  switch ( geomType )
544  {
546  {
547  return QgsLayerItem::iconPoint();
548  }
550  {
551  return QgsLayerItem::iconPolygon();
552  }
554  {
555  return QgsLayerItem::iconLine();
556  }
558  {
559  return QgsLayerItem::iconTable();
560  }
561  default:
562  {
563  return QIcon();
564  }
565  }
566  }
567 
568  default:
569  {
570  return QIcon();
571  }
572  }
573 }
574 
575 
576 bool QgsMapLayerModel::setData( const QModelIndex &index, const QVariant &value, int role )
577 {
578  if ( !index.isValid() )
579  return false;
580 
581  bool isEmpty = index.row() == 0 && mAllowEmpty;
582  int additionalIndex = index.row() - ( mAllowEmpty ? 1 : 0 ) - mLayers.count();
583 
584  switch ( role )
585  {
586  case Qt::CheckStateRole:
587  {
588  if ( !isEmpty && additionalIndex < 0 )
589  {
590  QgsMapLayer *layer = static_cast<QgsMapLayer *>( index.internalPointer() );
591  mLayersChecked[layer->id()] = ( Qt::CheckState )value.toInt();
592  emit dataChanged( index, index, QVector< int >() << Qt::CheckStateRole );
593  return true;
594  }
595  break;
596  }
597 
598  case LayerIdRole:
599  if ( !isEmpty && additionalIndex < 0 )
600  {
601  mLayers[index.row() - ( mAllowEmpty ? 1 : 0 )] = mProject->mapLayer( value.toString() );
602  emit dataChanged( index, index );
603  return true;
604  }
605  break;
606  }
607 
608  return false;
609 }
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString authid() const
Returns the authority identifier for the CRS.
static QIcon iconPoint()
Definition: qgsdataitem.cpp:76
static QIcon iconLine()
Definition: qgsdataitem.cpp:81
static QIcon iconTable()
Definition: qgsdataitem.cpp:91
static QIcon iconRaster()
Definition: qgsdataitem.cpp:96
static QIcon iconMesh()
Returns icon for mesh layer type.
static QIcon iconPolygon()
Definition: qgsdataitem.cpp:86
static QIcon iconPointCloud()
Returns icon for point cloud layer.
static QIcon iconVectorTile()
Returns icon for vector tile 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
void setAllowEmptyLayer(bool allowEmpty)
Sets whether an optional empty layer ("not set") option is present in the model.
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 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:85
QString name
Definition: qgsmaplayer.h:88
virtual bool isSpatial() const
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
QgsMapLayerType type
Definition: qgsmaplayer.h:92
QString publicSource() const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:91
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:317
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:301
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.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:141
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...
@ PointCloudLayer
Added in 3.18.
@ MeshLayer
Added in 3.2.
@ VectorTileLayer
Added in 3.14.