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