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