QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
qgslayertreefilterproxymodel.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayertreefilterproxymodel.cpp
3 
4  ---------------------
5  begin : 05.06.2020
6  copyright : (C) 2020 by Denis Rouzaud
7  email : [email protected]
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
18 
19 #include "qgslayertree.h"
20 #include "qgslayertreemodel.h"
21 #include "qgssymbol.h"
22 
24  : QSortFilterProxyModel( parent )
25 {
26  connect( QgsProject::instance(), &QgsProject::readProject, this, [ = ]
27  {
28  beginResetModel();
29  endResetModel();
30  } );
31 }
32 
33 void QgsLayerTreeFilterProxyModel::setCheckedLayers( const QList<QgsMapLayer *> layers )
34 {
35  // do not use invalidate() since it's not the filter which changes but the data
36  beginResetModel();
37  mCheckedLayers = layers;
38  endResetModel();
39 }
40 
41 int QgsLayerTreeFilterProxyModel::columnCount( const QModelIndex &parent ) const
42 {
43  Q_UNUSED( parent )
44  return 1;
45 }
46 
47 Qt::ItemFlags QgsLayerTreeFilterProxyModel::flags( const QModelIndex &idx ) const
48 {
49  if ( idx.column() == 0 )
50  {
51  return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
52  }
53  return Qt::NoItemFlags;
54 }
55 
56 QModelIndex QgsLayerTreeFilterProxyModel::index( int row, int column, const QModelIndex &parent ) const
57 {
58  QModelIndex newIndex = QSortFilterProxyModel::index( row, 0, parent );
59  if ( column == 0 )
60  return newIndex;
61 
62  return createIndex( row, column, newIndex.internalId() );
63 }
64 
65 QModelIndex QgsLayerTreeFilterProxyModel::parent( const QModelIndex &child ) const
66 {
67  return QSortFilterProxyModel::parent( createIndex( child.row(), 0, child.internalId() ) );
68 }
69 
70 QModelIndex QgsLayerTreeFilterProxyModel::sibling( int row, int column, const QModelIndex &idx ) const
71 {
72  const QModelIndex parent = idx.parent();
73  return index( row, column, parent );
74 }
75 
76 QgsMapLayer *QgsLayerTreeFilterProxyModel::mapLayer( const QModelIndex &idx ) const
77 {
78  QgsLayerTreeNode *node = nullptr;
79  if ( idx.column() == 0 )
80  {
81  node = mLayerTreeModel->index2node( mapToSource( idx ) );
82  }
83 
84  if ( !node || !QgsLayerTree::isLayer( node ) )
85  return nullptr;
86 
87  return QgsLayerTree::toLayer( node )->layer();
88 }
89 
90 void QgsLayerTreeFilterProxyModel::setFilterText( const QString &filterText )
91 {
92  if ( filterText == mFilterText )
93  return;
94 
95  mFilterText = filterText;
96  invalidateFilter();
97 }
98 
100 {
101  return mLayerTreeModel;
102 }
103 
105 {
106  mLayerTreeModel = layerTreeModel;
107  QSortFilterProxyModel::setSourceModel( layerTreeModel );
108 }
109 
110 void QgsLayerTreeFilterProxyModel::setFilters( const QgsMapLayerProxyModel::Filters &filters )
111 {
112  mFilters = filters;
113  invalidateFilter();
114 }
115 
116 bool QgsLayerTreeFilterProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
117 {
118  QgsLayerTreeNode *node = mLayerTreeModel->index2node( mLayerTreeModel->index( sourceRow, 0, sourceParent ) );
119  return nodeShown( node );
120 }
121 
123 {
124  return mCheckedLayers.contains( layer );
125 }
126 
128 {
129  if ( checked )
130  {
131  mCheckedLayers << layer;
132  }
133  else
134  {
135  mCheckedLayers.removeAll( layer );
136  }
137 }
138 
139 void QgsLayerTreeFilterProxyModel::setLayerCheckedPrivate( QgsMapLayer *layer, bool checked )
140 {
141  if ( checked && isLayerChecked( layer ) )
142  return;
143  if ( !checked && !isLayerChecked( layer ) )
144  return;
145 
146  QgsLayerTreeNode *node = mLayerTreeModel->rootGroup()->findLayer( layer );
147  const QModelIndex index = mapFromSource( mLayerTreeModel->node2index( node ) );
148 
149  setLayerChecked( layer, checked );
150 
151  emit dataChanged( index, index );
152 }
153 
154 bool QgsLayerTreeFilterProxyModel::layerShown( QgsMapLayer *layer ) const
155 {
156  Q_UNUSED( layer )
157  return true;
158 }
159 
160 bool QgsLayerTreeFilterProxyModel::nodeShown( QgsLayerTreeNode *node ) const
161 {
162  if ( !node )
163  return false;
164  if ( node->nodeType() == QgsLayerTreeNode::NodeGroup )
165  {
166  const auto constChildren = node->children();
167  for ( QgsLayerTreeNode *child : constChildren )
168  {
169  if ( nodeShown( child ) )
170  {
171  return true;
172  }
173  }
174  return false;
175  }
176  else
177  {
178  QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
179  if ( !layer )
180  return false;
181  if ( !mFilterText.isEmpty() && !layer->name().contains( mFilterText, Qt::CaseInsensitive ) )
182  return false;
183  if ( !QgsMapLayerProxyModel::layerMatchesFilters( layer, mFilters ) )
184  return false;
185 
186  return layerShown( layer );
187  }
188 }
189 
190 QVariant QgsLayerTreeFilterProxyModel::data( const QModelIndex &idx, int role ) const
191 {
192  if ( idx.column() == 0 )
193  {
194  if ( role == Qt::CheckStateRole )
195  {
196  QgsMapLayer *layer = mapLayer( idx );
197  if ( layer )
198  {
199  if ( isLayerChecked( layer ) )
200  {
201  return Qt::Checked;
202  }
203  else
204  {
205  return Qt::Unchecked;
206  }
207  }
208  else
209  {
210  // i.e. this is a group, analyze its children
211  bool hasChecked = false, hasUnchecked = false;
212  int n;
213  for ( n = 0; !hasChecked || !hasUnchecked; n++ )
214  {
215  const QVariant v = data( index( n, 0, idx ), role );
216  if ( !v.isValid() )
217  break;
218 
219  switch ( v.toInt() )
220  {
221  case Qt::PartiallyChecked:
222  // parent of partially checked child shared state
223  return Qt::PartiallyChecked;
224 
225  case Qt::Checked:
226  hasChecked = true;
227  break;
228 
229  case Qt::Unchecked:
230  hasUnchecked = true;
231  break;
232  }
233  }
234 
235  // unchecked leaf
236  if ( n == 0 )
237  return Qt::Unchecked;
238 
239  // both
240  if ( hasChecked && hasUnchecked )
241  return Qt::PartiallyChecked;
242 
243  if ( hasChecked )
244  return Qt::Checked;
245 
246  Q_ASSERT( hasUnchecked );
247  return Qt::Unchecked;
248  }
249  }
250  else
251  {
252  return mLayerTreeModel->data( mapToSource( idx ), role );
253  }
254  }
255  return QVariant();
256 }
257 
258 bool QgsLayerTreeFilterProxyModel::setData( const QModelIndex &index, const QVariant &value, int role )
259 {
260  if ( index.column() == 0 )
261  {
262  if ( role == Qt::CheckStateRole )
263  {
264  int i = 0;
265  for ( i = 0; ; i++ )
266  {
267  const QModelIndex child = QgsLayerTreeFilterProxyModel::index( i, 0, index );
268  if ( !child.isValid() )
269  break;
270 
271  setData( child, value, role );
272  }
273 
274  if ( i == 0 )
275  {
276  QgsMapLayer *layer = mapLayer( index );
277  if ( !layer )
278  {
279  return false;
280  }
281  if ( value.toInt() == Qt::Checked )
282  setLayerCheckedPrivate( layer, true );
283  else if ( value.toInt() == Qt::Unchecked )
284  setLayerCheckedPrivate( layer, false );
285  else
286  Q_ASSERT( false ); // expected checked or unchecked
287  }
288  emit dataChanged( index, index );
289  return true;
290  }
291 
292  return mLayerTreeModel->setData( mapToSource( index ), value, role );
293  }
294 
295  return false;
296 }
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
virtual void setLayerChecked(QgsMapLayer *layer, bool checked)
This will set if the layer is checked or not.
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
virtual bool setData(const QModelIndex &index, const QVariant &value, int role) override
virtual int columnCount(const QModelIndex &parent) const override
QgsMapLayer * mapLayer(const QModelIndex &idx) const
Returns the map layer at a given index.
void setFilters(const QgsMapLayerProxyModel::Filters &filters)
Defines the type layers (vector, raster, etc) shown in the tree If the list is empty,...
void setLayerTreeModel(QgsLayerTreeModel *layerTreeModel)
Sets the layer tree model.
virtual bool isLayerChecked(QgsMapLayer *layer) const
Returns if the layer is checked or not.
virtual void setFilterText(const QString &filterText=QString())
Sets the filter text to search for a layer in the tree.
void setCheckedLayers(const QList< QgsMapLayer * > layers)
Initialize the list of checked layers.
QModelIndex sibling(int row, int column, const QModelIndex &idx) const override
QModelIndex parent(const QModelIndex &child) const override
virtual QVariant data(const QModelIndex &index, int role) const override
QgsLayerTreeFilterProxyModel(QObject *parent=nullptr)
Constructor.
virtual Qt::ItemFlags flags(const QModelIndex &idx) const override
QgsLayerTreeModel * layerTreeModel() const
Rerturns the layer tree model.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
The QgsLayerTreeModel class is model implementation for Qt item views framework.
QModelIndex node2index(QgsLayerTreeNode *node) const
Returns index for a given node. If the node does not belong to the layer tree, the result is undefine...
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
QgsLayerTree * rootGroup() const
Returns pointer to the root node of the layer tree. Always a non nullptr value.
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Returns layer tree node for given index.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
This class is a base class for nodes in a layer tree.
@ NodeGroup
Container of other groups and layers.
NodeType nodeType() const
Find out about type of the node. It is usually shorter to use convenience functions from QgsLayerTree...
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:53
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
static bool layerMatchesFilters(const QgsMapLayer *layer, const Filters &filters)
Returns if the layer matches the given filters.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QString name
Definition: qgsmaplayer.h:76
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:467
void readProject(const QDomDocument &)
Emitted when a project is being read.