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