QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
qgslayertreeview.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayertreeview.cpp
3  --------------------------------------
4  Date : May 2014
5  Copyright : (C) 2014 by Martin Dobias
6  Email : wonder dot sk at gmail dot 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 "qgslayertreeview.h"
17 
18 #include "qgslayertree.h"
20 #include "qgslayertreemodel.h"
22 #include "qgslayertreeutils.h"
24 #include "qgsmaplayer.h"
25 #include "qgsmessagebar.h"
27 
28 #include "qgsgui.h"
29 
30 #include <QMenu>
31 #include <QContextMenuEvent>
32 #include <QHeaderView>
33 #include <QScrollBar>
34 
35 #ifdef ENABLE_MODELTEST
36 #include "modeltest.h"
37 #endif
38 
41 
42 
44  : QTreeView( parent )
45 
46 {
47  setHeaderHidden( true );
48 
49  setDragEnabled( true );
50  setAcceptDrops( true );
51  setDropIndicatorShown( true );
52  setEditTriggers( EditKeyPressed );
53  setExpandsOnDoubleClick( false ); // normally used for other actions
54 
55  // Ensure legend graphics are scrollable
56  header()->setStretchLastSection( false );
57  header()->setSectionResizeMode( QHeaderView::ResizeToContents );
58 
59  // If vertically scrolling by item, legend graphics can get clipped
60  setVerticalScrollMode( QAbstractItemView::ScrollPerPixel );
61 
62  setSelectionMode( ExtendedSelection );
63  setDefaultDropAction( Qt::MoveAction );
64 
65  // we need a custom item delegate in order to draw indicators
66  setItemDelegate( new QgsLayerTreeViewItemDelegate( this ) );
67  setStyle( new QgsLayerTreeViewProxyStyle( this ) );
68 
69 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
70  setLayerMarkWidth( static_cast< int >( QFontMetricsF( font() ).width( 'l' ) * Qgis::UI_SCALE_FACTOR ) );
71 #else
72  setLayerMarkWidth( static_cast< int >( QFontMetricsF( font() ).horizontalAdvance( 'l' ) * Qgis::UI_SCALE_FACTOR ) );
73 #endif
74 
75  connect( this, &QTreeView::collapsed, this, &QgsLayerTreeView::updateExpandedStateToNode );
76  connect( this, &QTreeView::expanded, this, &QgsLayerTreeView::updateExpandedStateToNode );
77 
78  connect( horizontalScrollBar(), &QScrollBar::valueChanged, this, &QgsLayerTreeView::onHorizontalScroll );
79 }
80 
82 {
83  delete mMenuProvider;
84 }
85 
86 void QgsLayerTreeView::setModel( QAbstractItemModel *model )
87 {
88  QgsLayerTreeModel *treeModel = qobject_cast<QgsLayerTreeModel *>( model );
89  if ( !treeModel )
90  return;
91 
92  if ( mMessageBar )
93  connect( treeModel, &QgsLayerTreeModel::messageEmitted,
94  [ = ]( const QString & message, Qgis::MessageLevel level = Qgis::Info, int duration = 5 )
95  {
96  Q_UNUSED( duration )
97  mMessageBar->pushMessage( message, level );
98  }
99  );
100 
101  mProxyModel = new QgsLayerTreeProxyModel( treeModel, this );
102 
103  connect( mProxyModel, &QAbstractItemModel::rowsInserted, this, &QgsLayerTreeView::modelRowsInserted );
104  connect( mProxyModel, &QAbstractItemModel::rowsRemoved, this, &QgsLayerTreeView::modelRowsRemoved );
105 
106 #ifdef ENABLE_MODELTEST
107  new ModelTest( mProxyModel, this );
108 #endif
109 
110  mProxyModel->setShowPrivateLayers( mShowPrivateLayers );
111  QTreeView::setModel( mProxyModel );
112 
114  connect( treeModel->rootGroup(), &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayerTreeView::onCustomPropertyChanged );
115 
116  connect( selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsLayerTreeView::onCurrentChanged );
117 
118  connect( treeModel, &QAbstractItemModel::modelReset, this, &QgsLayerTreeView::onModelReset );
119 
120  connect( treeModel, &QAbstractItemModel::dataChanged, this, &QgsLayerTreeView::onDataChanged );
121 
122  updateExpandedStateFromNode( treeModel->rootGroup() );
123 
124  //checkModel();
125 }
126 
128 {
129  return mProxyModel ? qobject_cast<QgsLayerTreeModel *>( mProxyModel->sourceModel() ) : nullptr;
130 }
131 
133 {
134  if ( !mDefaultActions )
136  return mDefaultActions;
137 }
138 
140 {
141  delete mMenuProvider;
143 }
144 
146 {
147  return layerForIndex( currentIndex() );
148 }
149 
151 {
152  if ( !layer )
153  {
154  setCurrentIndex( QModelIndex() );
155  return;
156  }
157 
158  QgsLayerTreeLayer *nodeLayer = layerTreeModel()->rootGroup()->findLayer( layer->id() );
159  if ( !nodeLayer )
160  return;
161 
162  setCurrentIndex( node2index( nodeLayer ) );
163 }
164 
165 void QgsLayerTreeView::setLayerVisible( QgsMapLayer *layer, bool visible )
166 {
167  if ( !layer )
168  return;
169  QgsLayerTreeLayer *nodeLayer = layerTreeModel()->rootGroup()->findLayer( layer->id() );
170  if ( !nodeLayer )
171  return;
172  nodeLayer->setItemVisibilityChecked( visible );
173 }
174 
175 void QgsLayerTreeView::contextMenuEvent( QContextMenuEvent *event )
176 {
177  if ( !mMenuProvider )
178  return;
179 
180  QModelIndex idx = indexAt( event->pos() );
181  if ( !idx.isValid() )
182  setCurrentIndex( QModelIndex() );
183 
184  QMenu *menu = mMenuProvider->createContextMenu();
185  if ( menu && menu->actions().count() != 0 )
186  menu->exec( mapToGlobal( event->pos() ) );
187  delete menu;
188 }
189 
190 
191 void QgsLayerTreeView::modelRowsInserted( const QModelIndex &index, int start, int end )
192 {
193  QgsLayerTreeNode *parentNode = index2node( index );
194  if ( !parentNode )
195  return;
196 
197  // Embedded widgets - replace placeholders in the model by actual widgets
198  if ( layerTreeModel()->testFlag( QgsLayerTreeModel::UseEmbeddedWidgets ) && QgsLayerTree::isLayer( parentNode ) )
199  {
200  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( parentNode );
201  if ( QgsMapLayer *layer = nodeLayer->layer() )
202  {
203  int widgetsCount = layer->customProperty( QStringLiteral( "embeddedWidgets/count" ), 0 ).toInt();
204  QList<QgsLayerTreeModelLegendNode *> legendNodes = layerTreeModel()->layerLegendNodes( nodeLayer, true );
205  for ( int i = 0; i < widgetsCount; ++i )
206  {
207  QString providerId = layer->customProperty( QStringLiteral( "embeddedWidgets/%1/id" ).arg( i ) ).toString();
208  if ( QgsLayerTreeEmbeddedWidgetProvider *provider = QgsGui::layerTreeEmbeddedWidgetRegistry()->provider( providerId ) )
209  {
210  QModelIndex index = legendNode2index( legendNodes[i] );
211  QWidget *wdgt = provider->createWidget( layer, i );
212  // Since column is resized to contents, limit the expanded width of embedded
213  // widgets, if they are not already limited, e.g. have the default MAX value.
214  // Else, embedded widget may grow very wide due to large legend graphics.
215  // NOTE: This approach DOES NOT work right. It causes horizontal scroll
216  // bar to disappear if the embedded widget is expanded and part
217  // of the last layer in the panel, even if much wider legend items
218  // are expanded above it. The correct width-limiting method should
219  // be setting fixed-width, hidpi-aware embedded widget items in a
220  // layout and appending an expanding QSpacerItem to end. This ensures
221  // full width is always created in the column by the embedded widget.
222  // See QgsLayerTreeOpacityWidget
223  //if ( wdgt->maximumWidth() == QWIDGETSIZE_MAX )
224  //{
225  // wdgt->setMaximumWidth( 250 );
226  //}
227 
228  setIndexWidget( index, wdgt );
229  }
230  }
231  }
232  }
233 
234 
235  if ( QgsLayerTree::isLayer( parentNode ) )
236  {
237  // if ShowLegendAsTree flag is enabled in model, we may need to expand some legend nodes
238  QStringList expandedNodeKeys = parentNode->customProperty( QStringLiteral( "expandedLegendNodes" ) ).toStringList();
239  if ( expandedNodeKeys.isEmpty() )
240  return;
241 
242  const auto constLayerLegendNodes = layerTreeModel()->layerLegendNodes( QgsLayerTree::toLayer( parentNode ), true );
243  for ( QgsLayerTreeModelLegendNode *legendNode : constLayerLegendNodes )
244  {
245  QString ruleKey = legendNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
246  if ( expandedNodeKeys.contains( ruleKey ) )
247  setExpanded( legendNode2index( legendNode ), true );
248  }
249  return;
250  }
251 
252  QList<QgsLayerTreeNode *> children = parentNode->children();
253  for ( int i = start; i <= end; ++i )
254  {
255  updateExpandedStateFromNode( children[i] );
256  }
257 
258  // make sure we still have correct current layer
260 }
261 
263 {
264  // make sure we still have correct current layer
266 }
267 
268 void QgsLayerTreeView::updateExpandedStateToNode( const QModelIndex &index )
269 {
270  if ( QgsLayerTreeNode *node = index2node( index ) )
271  {
272  node->setExpanded( isExpanded( index ) );
273  }
274  else if ( QgsLayerTreeModelLegendNode *node = index2legendNode( index ) )
275  {
276  QString ruleKey = node->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
277  QStringList lst = node->layerNode()->customProperty( QStringLiteral( "expandedLegendNodes" ) ).toStringList();
278  bool expanded = isExpanded( index );
279  bool isInList = lst.contains( ruleKey );
280  if ( expanded && !isInList )
281  {
282  lst.append( ruleKey );
283  node->layerNode()->setCustomProperty( QStringLiteral( "expandedLegendNodes" ), lst );
284  }
285  else if ( !expanded && isInList )
286  {
287  lst.removeAll( ruleKey );
288  node->layerNode()->setCustomProperty( QStringLiteral( "expandedLegendNodes" ), lst );
289  }
290  }
291 }
292 
294 {
295  QgsMapLayer *layerCurrent = layerForIndex( currentIndex() );
296  QString layerCurrentID = layerCurrent ? layerCurrent->id() : QString();
297  if ( mCurrentLayerID == layerCurrentID )
298  return;
299 
300  // update the current index in model (the item will be underlined)
301  QModelIndex proxyModelNodeLayerIndex;
302  if ( layerCurrent )
303  {
304  QgsLayerTreeLayer *nodeLayer = layerTreeModel()->rootGroup()->findLayer( layerCurrentID );
305  if ( nodeLayer )
306  proxyModelNodeLayerIndex = node2index( nodeLayer );
307  }
308 
309  if ( ! proxyModelNodeLayerIndex.isValid() )
310  {
311  mCurrentLayerID = QString();
312  layerTreeModel()->setCurrentIndex( QModelIndex() );
313  }
314  else
315  {
316  mCurrentLayerID = layerCurrentID;
317  layerTreeModel()->setCurrentIndex( mProxyModel->mapToSource( proxyModelNodeLayerIndex ) );
318  }
319 
320  //checkModel();
321 
322  emit currentLayerChanged( layerCurrent );
323 }
324 
326 {
327  QModelIndex idx = node2index( node );
328  if ( isExpanded( idx ) != expanded )
329  setExpanded( idx, expanded );
330 }
331 
332 void QgsLayerTreeView::onCustomPropertyChanged( QgsLayerTreeNode *node, const QString &key )
333 {
334  if ( key != QLatin1String( "expandedLegendNodes" ) || !QgsLayerTree::isLayer( node ) )
335  return;
336 
337  QSet<QString> expandedLegendNodes = qgis::listToSet( node->customProperty( QStringLiteral( "expandedLegendNodes" ) ).toStringList() );
338 
339  const QList<QgsLayerTreeModelLegendNode *> legendNodes = layerTreeModel()->layerLegendNodes( QgsLayerTree::toLayer( node ), true );
340  for ( QgsLayerTreeModelLegendNode *legendNode : legendNodes )
341  {
342  QString key = legendNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
343  if ( !key.isEmpty() )
344  setExpanded( legendNode2index( legendNode ), expandedLegendNodes.contains( key ) );
345  }
346 }
347 
349 {
351  //checkModel();
352 }
353 
355 {
356  QModelIndex idx = node2index( node );
357  setExpanded( idx, node->isExpanded() );
358 
359  const auto constChildren = node->children();
360  for ( QgsLayerTreeNode *child : constChildren )
362 }
363 
364 QgsMapLayer *QgsLayerTreeView::layerForIndex( const QModelIndex &index ) const
365 {
366  // Check if model has been set and index is valid
367  if ( layerTreeModel() && index.isValid() )
368  {
369  QgsLayerTreeNode *node = index2node( index );
370  if ( node )
371  {
372  if ( QgsLayerTree::isLayer( node ) )
373  return QgsLayerTree::toLayer( node )->layer();
374  }
375  else
376  {
377  // possibly a legend node
379  if ( legendNode )
380  return legendNode->layerNode()->layer();
381  }
382  }
383  return nullptr;
384 }
385 
387 {
388  return index2node( selectionModel()->currentIndex() );
389 }
390 
392 {
393  QgsLayerTreeNode *node = currentNode();
394  if ( QgsLayerTree::isGroup( node ) )
395  return QgsLayerTree::toGroup( node );
396  else if ( QgsLayerTree::isLayer( node ) )
397  {
398  QgsLayerTreeNode *parent = node->parent();
399  if ( QgsLayerTree::isGroup( parent ) )
400  return QgsLayerTree::toGroup( parent );
401  }
402 
403  if ( QgsLayerTreeModelLegendNode *legendNode = index2legendNode( selectionModel()->currentIndex() ) )
404  {
406  if ( QgsLayerTree::isGroup( parent->parent() ) )
407  return QgsLayerTree::toGroup( parent->parent() );
408  }
409 
410  return nullptr;
411 }
412 
414 {
415  return index2legendNode( selectionModel()->currentIndex() );
416 }
417 
418 QList<QgsLayerTreeNode *> QgsLayerTreeView::selectedNodes( bool skipInternal ) const
419 {
420  QModelIndexList mapped;
421  const QModelIndexList selected = selectionModel()->selectedIndexes();
422  mapped.reserve( selected.size() );
423  for ( const QModelIndex &index : selected )
424  mapped << mProxyModel->mapToSource( index );
425 
426  return layerTreeModel()->indexes2nodes( mapped, skipInternal );
427 }
428 
429 QList<QgsLayerTreeLayer *> QgsLayerTreeView::selectedLayerNodes() const
430 {
431  QList<QgsLayerTreeLayer *> layerNodes;
432  const auto constSelectedNodes = selectedNodes();
433  for ( QgsLayerTreeNode *node : constSelectedNodes )
434  {
435  if ( QgsLayerTree::isLayer( node ) )
436  layerNodes << QgsLayerTree::toLayer( node );
437  }
438  return layerNodes;
439 }
440 
441 QList<QgsMapLayer *> QgsLayerTreeView::selectedLayers() const
442 {
443  QList<QgsMapLayer *> list;
444  const auto constSelectedLayerNodes = selectedLayerNodes();
445  for ( QgsLayerTreeLayer *node : constSelectedLayerNodes )
446  {
447  if ( node->layer() )
448  list << node->layer();
449  }
450  return list;
451 }
452 
453 QList<QgsMapLayer *> QgsLayerTreeView::selectedLayersRecursive() const
454 {
455  QModelIndexList mapped;
456  const QModelIndexList selected = selectionModel()->selectedIndexes();
457  mapped.reserve( selected.size() );
458  for ( const QModelIndex &index : selected )
459  mapped << mProxyModel->mapToSource( index );
460 
461  const QList<QgsLayerTreeNode *> nodes = layerTreeModel()->indexes2nodes( mapped, false );
462  QSet<QgsMapLayer *> layersSet = QgsLayerTreeUtils::collectMapLayersRecursive( nodes );
463  return qgis::setToList( layersSet );
464 }
465 
467 {
468  if ( !mIndicators[node].contains( indicator ) )
469  {
470  mIndicators[node].append( indicator );
471  connect( indicator, &QgsLayerTreeViewIndicator::changed, this, [ = ]
472  {
473  update();
474  } );
475  update();
476  }
477 }
478 
480 {
481  mIndicators[node].removeOne( indicator );
482  update();
483 }
484 
485 QList<QgsLayerTreeViewIndicator *> QgsLayerTreeView::indicators( QgsLayerTreeNode *node ) const
486 {
487  return mIndicators.value( node );
488 }
489 
491 QStringList QgsLayerTreeView::viewOnlyCustomProperties()
492 {
493  return QStringList() << QStringLiteral( "expandedLegendNodes" );
494 }
496 
497 void QgsLayerTreeView::refreshLayerSymbology( const QString &layerId )
498 {
499  QgsLayerTreeLayer *nodeLayer = layerTreeModel()->rootGroup()->findLayer( layerId );
500  if ( nodeLayer )
501  layerTreeModel()->refreshLayerLegend( nodeLayer );
502 }
503 
504 
505 static void _expandAllLegendNodes( QgsLayerTreeLayer *nodeLayer, bool expanded, QgsLayerTreeModel *model )
506 {
507  // for layers we also need to find out with legend nodes contain some children and make them expanded/collapsed
508  // if we are collapsing, we just write out an empty list
509  QStringList lst;
510  if ( expanded )
511  {
512  const auto constLayerLegendNodes = model->layerLegendNodes( nodeLayer, true );
513  for ( QgsLayerTreeModelLegendNode *legendNode : constLayerLegendNodes )
514  {
515  QString parentKey = legendNode->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString();
516  if ( !parentKey.isEmpty() && !lst.contains( parentKey ) )
517  lst << parentKey;
518  }
519  }
520  nodeLayer->setCustomProperty( QStringLiteral( "expandedLegendNodes" ), lst );
521 }
522 
523 
524 static void _expandAllNodes( QgsLayerTreeGroup *parent, bool expanded, QgsLayerTreeModel *model )
525 {
526  const auto constChildren = parent->children();
527  for ( QgsLayerTreeNode *node : constChildren )
528  {
529  node->setExpanded( expanded );
530  if ( QgsLayerTree::isGroup( node ) )
531  _expandAllNodes( QgsLayerTree::toGroup( node ), expanded, model );
532  else if ( QgsLayerTree::isLayer( node ) )
533  _expandAllLegendNodes( QgsLayerTree::toLayer( node ), expanded, model );
534  }
535 }
536 
537 
539 {
540  // unfortunately expandAll() does not emit expanded() signals
541  _expandAllNodes( layerTreeModel()->rootGroup(), true, layerTreeModel() );
542  expandAll();
543 }
544 
546 {
547  // unfortunately collapseAll() does not emit collapsed() signals
548  _expandAllNodes( layerTreeModel()->rootGroup(), false, layerTreeModel() );
549  collapseAll();
550 }
551 
553 {
554  if ( mMessageBar == messageBar )
555  return;
556 
557  mMessageBar = messageBar;
558 
559  if ( mMessageBar )
561  [ = ]( const QString & message, Qgis::MessageLevel level = Qgis::Info, int duration = 5 )
562  {
563  Q_UNUSED( duration )
564  mMessageBar->pushMessage( message, level );
565  }
566  );
567 }
568 
570 {
571  mShowPrivateLayers = showPrivate;
572  mProxyModel->setShowPrivateLayers( showPrivate );
573 }
574 
576 {
577  return mShowPrivateLayers;
578 }
579 
580 void QgsLayerTreeView::mouseReleaseEvent( QMouseEvent *event )
581 {
582  // we need to keep last mouse position in order to know whether to emit an indicator's clicked() signal
583  // (the item delegate needs to know which indicator has been clicked)
584  mLastReleaseMousePos = event->pos();
585 
586  const QgsLayerTreeModel::Flags oldFlags = layerTreeModel()->flags();
587  if ( event->modifiers() & Qt::ControlModifier )
589  else
591  QTreeView::mouseReleaseEvent( event );
592  layerTreeModel()->setFlags( oldFlags );
593 }
594 
595 void QgsLayerTreeView::keyPressEvent( QKeyEvent *event )
596 {
597  if ( event->key() == Qt::Key_Space )
598  {
599  const auto constSelectedNodes = selectedNodes();
600 
601  if ( ! constSelectedNodes.isEmpty() )
602  {
603  bool isFirstNodeChecked = constSelectedNodes[0]->itemVisibilityChecked();
604  for ( QgsLayerTreeNode *node : constSelectedNodes )
605  {
606  node->setItemVisibilityChecked( ! isFirstNodeChecked );
607  }
608 
609  // if we call the original keyPress handler, the current item will be checked to the original state yet again
610  return;
611  }
612  }
613 
614  const QgsLayerTreeModel::Flags oldFlags = layerTreeModel()->flags();
615  if ( event->modifiers() & Qt::ControlModifier )
617  else
619  QTreeView::keyPressEvent( event );
620  layerTreeModel()->setFlags( oldFlags );
621 }
622 
623 void QgsLayerTreeView::dropEvent( QDropEvent *event )
624 {
625  if ( event->keyboardModifiers() & Qt::AltModifier )
626  {
627  event->accept();
628  }
629  QTreeView::dropEvent( event );
630 }
631 
632 void QgsLayerTreeView::resizeEvent( QResizeEvent *event )
633 {
634  // Since last column is resized to content (instead of stretched), the active
635  // selection rectangle ends at width of widest visible item in tree,
636  // regardless of which item is selected. This causes layer indicators to
637  // become 'inactive' (not clickable and no tool tip) unless their rectangle
638  // enters the view item's selection (active) rectangle.
639  // Always resetting the minimum section size relative to the viewport ensures
640  // the view item's selection rectangle extends to the right edge of the
641  // viewport, which allows indicators to become active again.
642  header()->setMinimumSectionSize( viewport()->width() );
643  QTreeView::resizeEvent( event );
644 }
645 
646 void QgsLayerTreeView::onHorizontalScroll( int value )
647 {
648  Q_UNUSED( value )
649  viewport()->update();
650 }
651 
652 void QgsLayerTreeView::onDataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles )
653 {
654  Q_UNUSED( topLeft )
655  Q_UNUSED( bottomRight )
656 
657  // If an item is resized asynchronously (e.g. wms legend)
658  // The items below will need to be shifted vertically.
659  // This doesn't happen automatically, unless the viewport update is triggered.
660 
661  if ( roles.contains( Qt::SizeHintRole ) )
662  viewport()->update();
663 
664  //checkModel();
665 }
666 
667 #if 0
668 // for model debugging
669 void QgsLayerTreeView::checkModel()
670 {
671  std::function<void( QgsLayerTreeNode *, int )> debug;
672  debug = [ & ]( QgsLayerTreeNode * node, int depth )
673  {
674  if ( depth == 1 )
675  qDebug() << "----------------------------------------------";
676 
677  qDebug() << depth << node->name() << node2index( node ) << layerTreeModel()->rowCount( node2sourceIndex( node ) ) << mProxyModel->rowCount( node2index( node ) );
678  Q_ASSERT( node == index2node( node2index( node ) ) );
679  Q_ASSERT( node == layerTreeModel()->index2node( node2sourceIndex( node ) ) );
680  Q_ASSERT( layerTreeModel()->rowCount( node2sourceIndex( node ) ) == mProxyModel->rowCount( node2index( node ) ) );
681 
682  for ( int i = 0; i < mProxyModel->rowCount( node2index( node ) ); i++ )
683  {
684  QgsLayerTreeNode *childNode { index2node( mProxyModel->index( i, 0, node2index( node ) ) ) };
685  if ( childNode )
686  debug( childNode, depth + 1 );
687  else
688  qDebug() << "Warning no child node!";
689  }
690  };
691  debug( layerTreeModel()->rootGroup(), 1 );
692 }
693 #endif
694 
696 {
697  return mProxyModel;
698 }
699 
700 QgsLayerTreeNode *QgsLayerTreeView::index2node( const QModelIndex &index ) const
701 {
702  return layerTreeModel()->index2node( mProxyModel->mapToSource( index ) );
703 }
704 
706 {
707  return mProxyModel->mapFromSource( node2sourceIndex( node ) );
708 }
709 
711 {
712  return layerTreeModel()->node2index( node );
713 }
714 
716 {
717  return layerTreeModel()->index2legendNode( mProxyModel->mapToSource( index ) );
718 }
719 
721 {
722  return mProxyModel->mapFromSource( legendNode2sourceIndex( legendNode ) );
723 }
724 
726 {
728 }
729 
731  : QSortFilterProxyModel( parent )
732  , mLayerTreeModel( treeModel )
733 {
734  setSourceModel( treeModel );
735 }
736 
737 void QgsLayerTreeProxyModel::setFilterText( const QString &filterText )
738 {
739  if ( filterText == mFilterText )
740  return;
741 
742  mFilterText = filterText;
743  invalidateFilter();
744 }
745 
746 bool QgsLayerTreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
747 {
748  QgsLayerTreeNode *node = mLayerTreeModel->index2node( mLayerTreeModel->index( sourceRow, 0, sourceParent ) );
749  return nodeShown( node );
750 }
751 
752 bool QgsLayerTreeProxyModel::nodeShown( QgsLayerTreeNode *node ) const
753 {
754  if ( !node )
755  return true;
756 
757  if ( node->nodeType() == QgsLayerTreeNode::NodeGroup )
758  {
759  return true;
760  }
761  else
762  {
763  QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
764  if ( !layer )
765  return true;
766  if ( !mFilterText.isEmpty() && !layer->name().contains( mFilterText, Qt::CaseInsensitive ) )
767  return false;
768  if ( ! mShowPrivateLayers && layer->flags().testFlag( QgsMapLayer::LayerFlag::Private ) )
769  {
770  return false;
771  }
772  return true;
773  }
774 }
775 
777 {
778  return mShowPrivateLayers;
779 }
780 
782 {
783  mShowPrivateLayers = showPrivate;
784  invalidateFilter();
785 }
MessageLevel
Level for messages This will be used both for message log and message bar in application.
Definition: qgis.h:89
@ Info
Definition: qgis.h:90
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:183
static QgsLayerTreeEmbeddedWidgetRegistry * layerTreeEmbeddedWidgetRegistry()
Returns the global layer tree embedded widget registry, used for registering widgets that may be embe...
Definition: qgsgui.cpp:106
Provider interface to be implemented in order to introduce new kinds of embedded widgets for use in l...
Layer tree group node serves as a container for layers and further groups.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
Layer tree node points to a map layer.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
virtual QVariant data(int role) const =0
Returns data associated with the item. Must be implemented in derived class.
QgsLayerTreeLayer * layerNode() const
Returns pointer to the parent layer node.
@ ParentRuleKeyRole
Rule key of the parent legend node - for legends with tree hierarchy (QString). Added in 2....
@ RuleKeyRole
Rule key of the node (QString)
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...
QList< QgsLayerTreeModelLegendNode * > layerLegendNodes(QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent=false)
Returns filtered list of active legend nodes attached to a particular layer node (by default it retur...
QModelIndex legendNode2index(QgsLayerTreeModelLegendNode *legendNode)
Returns index for a given legend node.
Qt::ItemFlags flags(const QModelIndex &index) const override
void setCurrentIndex(const QModelIndex &currentIndex)
Sets index of the current item. May be used by view. Item marked as current is underlined.
void setFlags(QgsLayerTreeModel::Flags f)
Sets OR-ed combination of model flags.
QgsLayerTree * rootGroup() const
Returns pointer to the root node of the layer tree. Always a non nullptr value.
void messageEmitted(const QString &message, Qgis::MessageLevel level=Qgis::Info, int duration=5)
Emits a message than can be displayed to the user in a GUI class.
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Returns layer tree node for given index.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
QList< QgsLayerTreeNode * > indexes2nodes(const QModelIndexList &list, bool skipInternal=false) const
Convert a list of indexes to a list of layer tree nodes.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
static QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index)
Returns legend node for given index.
void refreshLayerLegend(QgsLayerTreeLayer *nodeLayer)
Force a refresh of legend nodes of a layer node.
@ ActionHierarchical
Check/uncheck action has consequences on children (or parents for leaf node)
@ UseEmbeddedWidgets
Layer nodes may optionally include extra embedded widgets (if used in QgsLayerTreeView)....
This class is a base class for nodes in a layer tree.
@ NodeGroup
Container of other groups and layers.
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file.
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer. Properties are stored in a map and saved in project file.
void setExpanded(bool expanded)
Sets whether the node should be shown as expanded or collapsed in GUI.
virtual QString name() const =0
Returns name of the node.
NodeType nodeType() const
Find out about type of the node. It is usually shorter to use convenience functions from QgsLayerTree...
void customPropertyChanged(QgsLayerTreeNode *node, const QString &key)
Emitted when a custom property of a node within the tree has been changed or removed.
QgsLayerTreeNode * parent()
Gets pointer to the parent. If parent is nullptr, the node is a root node.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
bool isExpanded() const
Returns whether the node should be shown as expanded or collapsed in GUI.
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
void expandedChanged(QgsLayerTreeNode *node, bool expanded)
Emitted when the collapsed/expanded state of a node within the tree has been changed.
The QgsLayerTreeProxyModel class is a proxy model for QgsLayerTreeModel, supports private layers and ...
bool showPrivateLayers() const
Returns if private layers are shown.
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
void setFilterText(const QString &filterText=QString())
Sets filter to filterText.
void setShowPrivateLayers(bool showPrivate)
Determines if private layers are shown.
QgsLayerTreeProxyModel(QgsLayerTreeModel *treeModel, QObject *parent)
Constructs QgsLayerTreeProxyModel with source model treeModel and a parent.
static QSet< QgsMapLayer * > collectMapLayersRecursive(const QList< QgsLayerTreeNode * > &nodes)
Returns map layers from the given list of layer tree nodes.
The QgsLayerTreeViewDefaultActions class serves as a factory of actions that can be used together wit...
Indicator that can be used in a layer tree view to display icons next to items of the layer tree.
void changed()
Emitted when the indicator changes state (e.g.
Implementation of this interface can be implemented to allow QgsLayerTreeView instance to provide cus...
virtual QMenu * createContextMenu()=0
Returns a newly created menu instance (or nullptr on error)
void expandAllNodes()
Enhancement of QTreeView::expandAll() that also records expanded state in layer tree nodes.
QList< QgsLayerTreeViewIndicator * > indicators(QgsLayerTreeNode *node) const
Returns list of indicators associated with a particular layer tree node.
void currentLayerChanged(QgsMapLayer *layer)
Emitted when a current layer is changed.
void collapseAllNodes()
Enhancement of QTreeView::collapseAll() that also records expanded state in layer tree nodes.
QModelIndex legendNode2index(QgsLayerTreeModelLegendNode *legendNode)
Returns proxy model index for a given legend node.
QgsLayerTreeViewDefaultActions * defaultActions()
Gets access to the default actions that may be used with the tree view.
QString mCurrentLayerID
Keeps track of current layer ID (to check when to emit signal about change of current layer)
void setModel(QAbstractItemModel *model) override
Overridden setModel() from base class. Only QgsLayerTreeModel is an acceptable model.
QHash< QgsLayerTreeNode *, QList< QgsLayerTreeViewIndicator * > > mIndicators
Storage of indicators used with the tree view.
QList< QgsLayerTreeLayer * > selectedLayerNodes() const
Returns list of selected nodes filtered to just layer nodes.
void removeIndicator(QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator)
Removes a previously added indicator to a layer tree node.
void refreshLayerSymbology(const QString &layerId)
Force refresh of layer symbology. Normally not needed as the changes of layer's renderer are monitore...
void dropEvent(QDropEvent *event) override
QgsLayerTreeView(QWidget *parent=nullptr)
Constructor for QgsLayerTreeView.
QgsMapLayer * currentLayer() const
Returns the currently selected layer, or nullptr if no layers is selected.
QgsLayerTreeViewMenuProvider * menuProvider() const
Returns pointer to the context menu provider. May be nullptr.
void onExpandedChanged(QgsLayerTreeNode *node, bool expanded)
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Returns layer tree node for given proxy model tree index.
void resizeEvent(QResizeEvent *event) override
QList< QgsMapLayer * > selectedLayersRecursive() const
Gets list of selected layers, including those that are not directly selected, but their ancestor grou...
void mouseReleaseEvent(QMouseEvent *event) override
void updateExpandedStateFromNode(QgsLayerTreeNode *node)
QgsLayerTreeModelLegendNode * currentLegendNode() const
Gets current legend node.
QgsLayerTreeModel * layerTreeModel() const
Gets access to the model casted to QgsLayerTreeModel.
void setMenuProvider(QgsLayerTreeViewMenuProvider *menuProvider)
Sets provider for context menu. Takes ownership of the instance.
bool showPrivateLayers()
Returns the show private layers status.
void setLayerVisible(QgsMapLayer *layer, bool visible)
Convenience methods which sets the visible state of the specified map layer.
QModelIndex node2sourceIndex(QgsLayerTreeNode *node) const
Returns source model index for a given node.
QgsMapLayer * layerForIndex(const QModelIndex &index) const
QPoint mLastReleaseMousePos
Used by the item delegate for identification of which indicator has been clicked.
friend class QgsLayerTreeViewItemDelegate
void updateExpandedStateToNode(const QModelIndex &index)
QgsLayerTreeProxyModel * proxyModel() const
Returns the proxy model used by the view.
QgsLayerTreeViewMenuProvider * mMenuProvider
Context menu provider. Owned by the view.
QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index) const
Returns legend node for given proxy model tree index.
void contextMenuEvent(QContextMenuEvent *event) override
void keyPressEvent(QKeyEvent *event) override
void setMessageBar(QgsMessageBar *messageBar)
Set the message bar to display messages from the layer tree.
void addIndicator(QgsLayerTreeNode *node, QgsLayerTreeViewIndicator *indicator)
Adds an indicator to the given layer tree node.
void modelRowsInserted(const QModelIndex &index, int start, int end)
QList< QgsLayerTreeNode * > selectedNodes(bool skipInternal=false) const
Returns list of selected nodes.
void setLayerMarkWidth(int width)
Set width of contextual menu mark, at right of layer node items.
QModelIndex node2index(QgsLayerTreeNode *node) const
Returns proxy model index for a given node.
void setCurrentLayer(QgsMapLayer *layer)
Sets the currently selected layer.
void setShowPrivateLayers(bool showPrivate)
Set the show private layers to showPrivate.
QgsLayerTreeNode * currentNode() const
Gets current node. May be nullptr.
~QgsLayerTreeView() override
QgsLayerTreeGroup * currentGroupNode() const
Gets current group node. If a layer is current node, the function will return parent group....
QgsLayerTreeViewDefaultActions * mDefaultActions
helper class with default actions. Lazily initialized.
QList< QgsMapLayer * > selectedLayers() const
Gets list of selected layers.
QModelIndex legendNode2sourceIndex(QgsLayerTreeModelLegendNode *legendNode)
Returns index for a given legend node.
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 isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
Base class for all map layer types.
Definition: qgsmaplayer.h:85
QString name
Definition: qgsmaplayer.h:88
QgsMapLayer::LayerFlags flags() const
Returns the flags for this layer.
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:61
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::Info, int duration=-1)
A convenience method for pushing a message with the specified text to the bar.
QgsLayerTreeModelLegendNode * legendNode(const QString &rule, QgsLayerTreeModel &model)