QGIS API Documentation  3.23.0-Master (4fd2f04bd0)
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  setLayerMarkWidth( static_cast< int >( QFontMetricsF( font() ).horizontalAdvance( 'l' ) * Qgis::UI_SCALE_FACTOR ) );
70 
71  connect( this, &QTreeView::collapsed, this, &QgsLayerTreeView::updateExpandedStateToNode );
72  connect( this, &QTreeView::expanded, this, &QgsLayerTreeView::updateExpandedStateToNode );
73 
74  connect( horizontalScrollBar(), &QScrollBar::valueChanged, this, &QgsLayerTreeView::onHorizontalScroll );
75 }
76 
78 {
79  delete mMenuProvider;
80 }
81 
82 void QgsLayerTreeView::setModel( QAbstractItemModel *model )
83 {
84  QgsLayerTreeModel *treeModel = qobject_cast<QgsLayerTreeModel *>( model );
85  if ( !treeModel )
86  return;
87 
88  if ( mMessageBar )
89  connect( treeModel, &QgsLayerTreeModel::messageEmitted,
90  [ = ]( const QString & message, Qgis::MessageLevel level = Qgis::MessageLevel::Info, int duration = 5 )
91  {
92  Q_UNUSED( duration )
93  mMessageBar->pushMessage( message, level );
94  }
95  );
96 
97  mProxyModel = new QgsLayerTreeProxyModel( treeModel, this );
98 
99  connect( mProxyModel, &QAbstractItemModel::rowsInserted, this, &QgsLayerTreeView::modelRowsInserted );
100  connect( mProxyModel, &QAbstractItemModel::rowsRemoved, this, &QgsLayerTreeView::modelRowsRemoved );
101 
102 #ifdef ENABLE_MODELTEST
103  new ModelTest( mProxyModel, this );
104 #endif
105 
106  mProxyModel->setShowPrivateLayers( mShowPrivateLayers );
107  QTreeView::setModel( mProxyModel );
108 
110  connect( treeModel->rootGroup(), &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayerTreeView::onCustomPropertyChanged );
111 
112  connect( selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsLayerTreeView::onCurrentChanged );
113 
114  connect( treeModel, &QAbstractItemModel::modelReset, this, &QgsLayerTreeView::onModelReset );
115 
116  connect( treeModel, &QAbstractItemModel::dataChanged, this, &QgsLayerTreeView::onDataChanged );
117 
118  updateExpandedStateFromNode( treeModel->rootGroup() );
119 
120  //checkModel();
121 }
122 
124 {
125  return mProxyModel ? qobject_cast<QgsLayerTreeModel *>( mProxyModel->sourceModel() ) : nullptr;
126 }
127 
129 {
130  if ( !mDefaultActions )
132  return mDefaultActions;
133 }
134 
136 {
137  delete mMenuProvider;
139 }
140 
142 {
143  return layerForIndex( currentIndex() );
144 }
145 
147 {
148  if ( !layer )
149  {
150  setCurrentIndex( QModelIndex() );
151  return;
152  }
153 
154  QgsLayerTreeLayer *nodeLayer = layerTreeModel()->rootGroup()->findLayer( layer->id() );
155  if ( !nodeLayer )
156  return;
157 
158  setCurrentIndex( node2index( nodeLayer ) );
159 }
160 
161 void QgsLayerTreeView::setLayerVisible( QgsMapLayer *layer, bool visible )
162 {
163  if ( !layer )
164  return;
165  QgsLayerTreeLayer *nodeLayer = layerTreeModel()->rootGroup()->findLayer( layer->id() );
166  if ( !nodeLayer )
167  return;
168  nodeLayer->setItemVisibilityChecked( visible );
169 }
170 
171 void QgsLayerTreeView::contextMenuEvent( QContextMenuEvent *event )
172 {
173  if ( !mMenuProvider )
174  return;
175 
176  const QModelIndex idx = indexAt( event->pos() );
177  if ( !idx.isValid() )
178  setCurrentIndex( QModelIndex() );
179 
180  QMenu *menu = mMenuProvider->createContextMenu();
181  if ( menu && menu->actions().count() != 0 )
182  menu->exec( mapToGlobal( event->pos() ) );
183  delete menu;
184 }
185 
186 
187 void QgsLayerTreeView::modelRowsInserted( const QModelIndex &index, int start, int end )
188 {
189  QgsLayerTreeNode *parentNode = index2node( index );
190  if ( !parentNode )
191  return;
192 
193  // Embedded widgets - replace placeholders in the model by actual widgets
194  if ( layerTreeModel()->testFlag( QgsLayerTreeModel::UseEmbeddedWidgets ) && QgsLayerTree::isLayer( parentNode ) )
195  {
196  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( parentNode );
197  if ( QgsMapLayer *layer = nodeLayer->layer() )
198  {
199  const int widgetsCount = layer->customProperty( QStringLiteral( "embeddedWidgets/count" ), 0 ).toInt();
200  QList<QgsLayerTreeModelLegendNode *> legendNodes = layerTreeModel()->layerLegendNodes( nodeLayer, true );
201  for ( int i = 0; i < widgetsCount; ++i )
202  {
203  const QString providerId = layer->customProperty( QStringLiteral( "embeddedWidgets/%1/id" ).arg( i ) ).toString();
204  if ( QgsLayerTreeEmbeddedWidgetProvider *provider = QgsGui::layerTreeEmbeddedWidgetRegistry()->provider( providerId ) )
205  {
206  const QModelIndex index = legendNode2index( legendNodes[i] );
207  QWidget *wdgt = provider->createWidget( layer, i );
208  // Since column is resized to contents, limit the expanded width of embedded
209  // widgets, if they are not already limited, e.g. have the default MAX value.
210  // Else, embedded widget may grow very wide due to large legend graphics.
211  // NOTE: This approach DOES NOT work right. It causes horizontal scroll
212  // bar to disappear if the embedded widget is expanded and part
213  // of the last layer in the panel, even if much wider legend items
214  // are expanded above it. The correct width-limiting method should
215  // be setting fixed-width, hidpi-aware embedded widget items in a
216  // layout and appending an expanding QSpacerItem to end. This ensures
217  // full width is always created in the column by the embedded widget.
218  // See QgsLayerTreeOpacityWidget
219  //if ( wdgt->maximumWidth() == QWIDGETSIZE_MAX )
220  //{
221  // wdgt->setMaximumWidth( 250 );
222  //}
223 
224  setIndexWidget( index, wdgt );
225  }
226  }
227  }
228  }
229 
230 
231  if ( QgsLayerTree::isLayer( parentNode ) )
232  {
233  // if ShowLegendAsTree flag is enabled in model, we may need to expand some legend nodes
234  const QStringList expandedNodeKeys = parentNode->customProperty( QStringLiteral( "expandedLegendNodes" ) ).toStringList();
235  if ( expandedNodeKeys.isEmpty() )
236  return;
237 
238  const auto constLayerLegendNodes = layerTreeModel()->layerLegendNodes( QgsLayerTree::toLayer( parentNode ), true );
239  for ( QgsLayerTreeModelLegendNode *legendNode : constLayerLegendNodes )
240  {
241  const QString ruleKey = legendNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
242  if ( expandedNodeKeys.contains( ruleKey ) )
243  setExpanded( legendNode2index( legendNode ), true );
244  }
245  return;
246  }
247 
248  QList<QgsLayerTreeNode *> children = parentNode->children();
249  for ( int i = start; i <= end; ++i )
250  {
251  updateExpandedStateFromNode( children[i] );
252  }
253 
254  // make sure we still have correct current layer
256 }
257 
259 {
260  // make sure we still have correct current layer
262 }
263 
264 void QgsLayerTreeView::updateExpandedStateToNode( const QModelIndex &index )
265 {
266  if ( QgsLayerTreeNode *node = index2node( index ) )
267  {
268  node->setExpanded( isExpanded( index ) );
269  }
270  else if ( QgsLayerTreeModelLegendNode *node = index2legendNode( index ) )
271  {
272  const QString ruleKey = node->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
273  QStringList lst = node->layerNode()->customProperty( QStringLiteral( "expandedLegendNodes" ) ).toStringList();
274  const bool expanded = isExpanded( index );
275  const bool isInList = lst.contains( ruleKey );
276  if ( expanded && !isInList )
277  {
278  lst.append( ruleKey );
279  node->layerNode()->setCustomProperty( QStringLiteral( "expandedLegendNodes" ), lst );
280  }
281  else if ( !expanded && isInList )
282  {
283  lst.removeAll( ruleKey );
284  node->layerNode()->setCustomProperty( QStringLiteral( "expandedLegendNodes" ), lst );
285  }
286  }
287 }
288 
290 {
291  QgsMapLayer *layerCurrent = layerForIndex( currentIndex() );
292  const QString layerCurrentID = layerCurrent ? layerCurrent->id() : QString();
293  if ( mCurrentLayerID == layerCurrentID )
294  return;
295 
296  // update the current index in model (the item will be underlined)
297  QModelIndex proxyModelNodeLayerIndex;
298  if ( layerCurrent )
299  {
300  QgsLayerTreeLayer *nodeLayer = layerTreeModel()->rootGroup()->findLayer( layerCurrentID );
301  if ( nodeLayer )
302  proxyModelNodeLayerIndex = node2index( nodeLayer );
303  }
304 
305  if ( ! proxyModelNodeLayerIndex.isValid() )
306  {
307  mCurrentLayerID = QString();
308  layerTreeModel()->setCurrentIndex( QModelIndex() );
309  }
310  else
311  {
312  mCurrentLayerID = layerCurrentID;
313  layerTreeModel()->setCurrentIndex( mProxyModel->mapToSource( proxyModelNodeLayerIndex ) );
314  }
315 
316  //checkModel();
317 
318  emit currentLayerChanged( layerCurrent );
319 }
320 
322 {
323  const QModelIndex idx = node2index( node );
324  if ( isExpanded( idx ) != expanded )
325  setExpanded( idx, expanded );
326 }
327 
328 void QgsLayerTreeView::onCustomPropertyChanged( QgsLayerTreeNode *node, const QString &key )
329 {
330  if ( key != QLatin1String( "expandedLegendNodes" ) || !QgsLayerTree::isLayer( node ) )
331  return;
332 
333  const QSet<QString> expandedLegendNodes = qgis::listToSet( node->customProperty( QStringLiteral( "expandedLegendNodes" ) ).toStringList() );
334 
335  const QList<QgsLayerTreeModelLegendNode *> legendNodes = layerTreeModel()->layerLegendNodes( QgsLayerTree::toLayer( node ), true );
336  for ( QgsLayerTreeModelLegendNode *legendNode : legendNodes )
337  {
338  const QString key = legendNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
339  if ( !key.isEmpty() )
340  setExpanded( legendNode2index( legendNode ), expandedLegendNodes.contains( key ) );
341  }
342 }
343 
345 {
347  //checkModel();
348 }
349 
351 {
352  const QModelIndex idx = node2index( node );
353  setExpanded( idx, node->isExpanded() );
354 
355  const auto constChildren = node->children();
356  for ( QgsLayerTreeNode *child : constChildren )
358 }
359 
360 QgsMapLayer *QgsLayerTreeView::layerForIndex( const QModelIndex &index ) const
361 {
362  // Check if model has been set and index is valid
363  if ( layerTreeModel() && index.isValid() )
364  {
365  QgsLayerTreeNode *node = index2node( index );
366  if ( node )
367  {
368  if ( QgsLayerTree::isLayer( node ) )
369  return QgsLayerTree::toLayer( node )->layer();
370  }
371  else
372  {
373  // possibly a legend node
375  if ( legendNode )
376  return legendNode->layerNode()->layer();
377  }
378  }
379  return nullptr;
380 }
381 
383 {
384  return index2node( selectionModel()->currentIndex() );
385 }
386 
388 {
389  QgsLayerTreeNode *node = currentNode();
390  if ( QgsLayerTree::isGroup( node ) )
391  return QgsLayerTree::toGroup( node );
392  else if ( QgsLayerTree::isLayer( node ) )
393  {
394  QgsLayerTreeNode *parent = node->parent();
395  if ( QgsLayerTree::isGroup( parent ) )
396  return QgsLayerTree::toGroup( parent );
397  }
398 
399  if ( QgsLayerTreeModelLegendNode *legendNode = index2legendNode( selectionModel()->currentIndex() ) )
400  {
402  if ( QgsLayerTree::isGroup( parent->parent() ) )
403  return QgsLayerTree::toGroup( parent->parent() );
404  }
405 
406  return nullptr;
407 }
408 
410 {
411  return index2legendNode( selectionModel()->currentIndex() );
412 }
413 
414 QList<QgsLayerTreeNode *> QgsLayerTreeView::selectedNodes( bool skipInternal ) const
415 {
416  QModelIndexList mapped;
417  const QModelIndexList selected = selectionModel()->selectedIndexes();
418  mapped.reserve( selected.size() );
419  for ( const QModelIndex &index : selected )
420  mapped << mProxyModel->mapToSource( index );
421 
422  return layerTreeModel()->indexes2nodes( mapped, skipInternal );
423 }
424 
425 QList<QgsLayerTreeLayer *> QgsLayerTreeView::selectedLayerNodes() const
426 {
427  QList<QgsLayerTreeLayer *> layerNodes;
428  const auto constSelectedNodes = selectedNodes();
429  for ( QgsLayerTreeNode *node : constSelectedNodes )
430  {
431  if ( QgsLayerTree::isLayer( node ) )
432  layerNodes << QgsLayerTree::toLayer( node );
433  }
434  return layerNodes;
435 }
436 
437 QList<QgsMapLayer *> QgsLayerTreeView::selectedLayers() const
438 {
439  QList<QgsMapLayer *> list;
440  const auto constSelectedLayerNodes = selectedLayerNodes();
441  for ( QgsLayerTreeLayer *node : constSelectedLayerNodes )
442  {
443  if ( node->layer() )
444  list << node->layer();
445  }
446  return list;
447 }
448 
449 QList<QgsMapLayer *> QgsLayerTreeView::selectedLayersRecursive() const
450 {
451  QModelIndexList mapped;
452  const QModelIndexList selected = selectionModel()->selectedIndexes();
453  mapped.reserve( selected.size() );
454  for ( const QModelIndex &index : selected )
455  mapped << mProxyModel->mapToSource( index );
456 
457  const QList<QgsLayerTreeNode *> nodes = layerTreeModel()->indexes2nodes( mapped, false );
458  const QSet<QgsMapLayer *> layersSet = QgsLayerTreeUtils::collectMapLayersRecursive( nodes );
459  return qgis::setToList( layersSet );
460 }
461 
463 {
464  if ( !mIndicators[node].contains( indicator ) )
465  {
466  mIndicators[node].append( indicator );
467  connect( indicator, &QgsLayerTreeViewIndicator::changed, this, [ = ]
468  {
469  update();
470  viewport()->repaint();
471  } );
472  update();
473  viewport()->repaint(); //update() does not automatically trigger a repaint()
474  }
475 }
476 
478 {
479  mIndicators[node].removeOne( indicator );
480  update();
481 }
482 
483 QList<QgsLayerTreeViewIndicator *> QgsLayerTreeView::indicators( QgsLayerTreeNode *node ) const
484 {
485  return mIndicators.value( node );
486 }
487 
489 QStringList QgsLayerTreeView::viewOnlyCustomProperties()
490 {
491  return QStringList() << QStringLiteral( "expandedLegendNodes" );
492 }
494 
495 void QgsLayerTreeView::refreshLayerSymbology( const QString &layerId )
496 {
497  QgsLayerTreeLayer *nodeLayer = layerTreeModel()->rootGroup()->findLayer( layerId );
498  if ( nodeLayer )
499  layerTreeModel()->refreshLayerLegend( nodeLayer );
500 }
501 
502 
503 static void _expandAllLegendNodes( QgsLayerTreeLayer *nodeLayer, bool expanded, QgsLayerTreeModel *model )
504 {
505  // for layers we also need to find out with legend nodes contain some children and make them expanded/collapsed
506  // if we are collapsing, we just write out an empty list
507  QStringList lst;
508  if ( expanded )
509  {
510  const auto constLayerLegendNodes = model->layerLegendNodes( nodeLayer, true );
511  for ( QgsLayerTreeModelLegendNode *legendNode : constLayerLegendNodes )
512  {
513  const QString parentKey = legendNode->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString();
514  if ( !parentKey.isEmpty() && !lst.contains( parentKey ) )
515  lst << parentKey;
516  }
517  }
518  nodeLayer->setCustomProperty( QStringLiteral( "expandedLegendNodes" ), lst );
519 }
520 
521 
522 static void _expandAllNodes( QgsLayerTreeGroup *parent, bool expanded, QgsLayerTreeModel *model )
523 {
524  const auto constChildren = parent->children();
525  for ( QgsLayerTreeNode *node : constChildren )
526  {
527  node->setExpanded( expanded );
528  if ( QgsLayerTree::isGroup( node ) )
529  _expandAllNodes( QgsLayerTree::toGroup( node ), expanded, model );
530  else if ( QgsLayerTree::isLayer( node ) )
531  _expandAllLegendNodes( QgsLayerTree::toLayer( node ), expanded, model );
532  }
533 }
534 
535 
537 {
538  // unfortunately expandAll() does not emit expanded() signals
539  _expandAllNodes( layerTreeModel()->rootGroup(), true, layerTreeModel() );
540  expandAll();
541 }
542 
544 {
545  // unfortunately collapseAll() does not emit collapsed() signals
546  _expandAllNodes( layerTreeModel()->rootGroup(), false, layerTreeModel() );
547  collapseAll();
548 }
549 
551 {
552  if ( mMessageBar == messageBar )
553  return;
554 
555  mMessageBar = messageBar;
556 
557  if ( mMessageBar )
559  [ = ]( const QString & message, Qgis::MessageLevel level = Qgis::MessageLevel::Info, int duration = 5 )
560  {
561  Q_UNUSED( duration )
562  mMessageBar->pushMessage( message, level );
563  }
564  );
565 }
566 
568 {
569  mShowPrivateLayers = showPrivate;
570  mProxyModel->setShowPrivateLayers( showPrivate );
571 }
572 
574 {
575  return mShowPrivateLayers;
576 }
577 
578 void QgsLayerTreeView::mouseReleaseEvent( QMouseEvent *event )
579 {
580  // we need to keep last mouse position in order to know whether to emit an indicator's clicked() signal
581  // (the item delegate needs to know which indicator has been clicked)
582  mLastReleaseMousePos = event->pos();
583 
584  const QgsLayerTreeModel::Flags oldFlags = layerTreeModel()->flags();
585  if ( event->modifiers() & Qt::ControlModifier )
587  else
589  QTreeView::mouseReleaseEvent( event );
590  layerTreeModel()->setFlags( oldFlags );
591 }
592 
593 void QgsLayerTreeView::keyPressEvent( QKeyEvent *event )
594 {
595  if ( event->key() == Qt::Key_Space )
596  {
597  const auto constSelectedNodes = selectedNodes();
598 
599  if ( ! constSelectedNodes.isEmpty() )
600  {
601  const bool isFirstNodeChecked = constSelectedNodes[0]->itemVisibilityChecked();
602  for ( QgsLayerTreeNode *node : constSelectedNodes )
603  {
604  node->setItemVisibilityChecked( ! isFirstNodeChecked );
605  }
606 
607  // if we call the original keyPress handler, the current item will be checked to the original state yet again
608  return;
609  }
610  }
611 
612  const QgsLayerTreeModel::Flags oldFlags = layerTreeModel()->flags();
613  if ( event->modifiers() & Qt::ControlModifier )
615  else
617  QTreeView::keyPressEvent( event );
618  layerTreeModel()->setFlags( oldFlags );
619 }
620 
621 void QgsLayerTreeView::dropEvent( QDropEvent *event )
622 {
623  if ( event->keyboardModifiers() & Qt::AltModifier )
624  {
625  event->accept();
626  }
627  QTreeView::dropEvent( event );
628 }
629 
630 void QgsLayerTreeView::resizeEvent( QResizeEvent *event )
631 {
632  // Since last column is resized to content (instead of stretched), the active
633  // selection rectangle ends at width of widest visible item in tree,
634  // regardless of which item is selected. This causes layer indicators to
635  // become 'inactive' (not clickable and no tool tip) unless their rectangle
636  // enters the view item's selection (active) rectangle.
637  // Always resetting the minimum section size relative to the viewport ensures
638  // the view item's selection rectangle extends to the right edge of the
639  // viewport, which allows indicators to become active again.
640  header()->setMinimumSectionSize( viewport()->width() );
641  QTreeView::resizeEvent( event );
642 }
643 
644 void QgsLayerTreeView::onHorizontalScroll( int value )
645 {
646  Q_UNUSED( value )
647  viewport()->update();
648 }
649 
650 void QgsLayerTreeView::onDataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles )
651 {
652  Q_UNUSED( topLeft )
653  Q_UNUSED( bottomRight )
654 
655  // If an item is resized asynchronously (e.g. wms legend)
656  // The items below will need to be shifted vertically.
657  // This doesn't happen automatically, unless the viewport update is triggered.
658 
659  if ( roles.contains( Qt::SizeHintRole ) )
660  viewport()->update();
661 
662  //checkModel();
663 }
664 
665 #if 0
666 // for model debugging
667 void QgsLayerTreeView::checkModel()
668 {
669  std::function<void( QgsLayerTreeNode *, int )> debug;
670  debug = [ & ]( QgsLayerTreeNode * node, int depth )
671  {
672  if ( depth == 1 )
673  qDebug() << "----------------------------------------------";
674 
675  qDebug() << depth << node->name() << node2index( node ) << layerTreeModel()->rowCount( node2sourceIndex( node ) ) << mProxyModel->rowCount( node2index( node ) );
676  Q_ASSERT( node == index2node( node2index( node ) ) );
677  Q_ASSERT( node == layerTreeModel()->index2node( node2sourceIndex( node ) ) );
678  Q_ASSERT( layerTreeModel()->rowCount( node2sourceIndex( node ) ) == mProxyModel->rowCount( node2index( node ) ) );
679 
680  for ( int i = 0; i < mProxyModel->rowCount( node2index( node ) ); i++ )
681  {
682  QgsLayerTreeNode *childNode { index2node( mProxyModel->index( i, 0, node2index( node ) ) ) };
683  if ( childNode )
684  debug( childNode, depth + 1 );
685  else
686  qDebug() << "Warning no child node!";
687  }
688  };
689  debug( layerTreeModel()->rootGroup(), 1 );
690 }
691 #endif
692 
694 {
695  return mProxyModel;
696 }
697 
698 QgsLayerTreeNode *QgsLayerTreeView::index2node( const QModelIndex &index ) const
699 {
700  return layerTreeModel()->index2node( mProxyModel->mapToSource( index ) );
701 }
702 
704 {
705  return mProxyModel->mapFromSource( node2sourceIndex( node ) );
706 }
707 
709 {
710  return layerTreeModel()->node2index( node );
711 }
712 
714 {
715  return QgsLayerTreeModel::index2legendNode( mProxyModel->mapToSource( index ) );
716 }
717 
719 {
720  return mProxyModel->mapFromSource( legendNode2sourceIndex( legendNode ) );
721 }
722 
724 {
726 }
727 
729  : QSortFilterProxyModel( parent )
730  , mLayerTreeModel( treeModel )
731 {
732  setSourceModel( treeModel );
733 }
734 
735 void QgsLayerTreeProxyModel::setFilterText( const QString &filterText )
736 {
737  if ( filterText == mFilterText )
738  return;
739 
740  mFilterText = filterText;
741  invalidateFilter();
742 }
743 
744 bool QgsLayerTreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
745 {
746  QgsLayerTreeNode *node = mLayerTreeModel->index2node( mLayerTreeModel->index( sourceRow, 0, sourceParent ) );
747  return nodeShown( node );
748 }
749 
750 bool QgsLayerTreeProxyModel::nodeShown( QgsLayerTreeNode *node ) const
751 {
752  if ( !node )
753  return true;
754 
755  if ( node->nodeType() == QgsLayerTreeNode::NodeGroup )
756  {
757  return true;
758  }
759  else
760  {
761  QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
762  if ( !layer )
763  return true;
764  if ( !mFilterText.isEmpty() && !layer->name().contains( mFilterText, Qt::CaseInsensitive ) )
765  return false;
766  if ( ! mShowPrivateLayers && layer->flags().testFlag( QgsMapLayer::LayerFlag::Private ) )
767  {
768  return false;
769  }
770  return true;
771  }
772 }
773 
775 {
776  return mShowPrivateLayers;
777 }
778 
780 {
781  mShowPrivateLayers = showPrivate;
782  invalidateFilter();
783 }
MessageLevel
Level for messages This will be used both for message log and message bar in application.
Definition: qgis.h:107
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:1348
static QgsLayerTreeEmbeddedWidgetRegistry * layerTreeEmbeddedWidgetRegistry()
Returns the global layer tree embedded widget registry, used for registering widgets that may be embe...
Definition: qgsgui.cpp:114
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.
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Returns layer tree node for given index.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void messageEmitted(const QString &message, Qgis::MessageLevel level=Qgis::MessageLevel::Info, int duration=5)
Emits a message than can be displayed to the user in a GUI class.
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:73
QString name
Definition: qgsmaplayer.h:76
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::MessageLevel::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)