QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
Loading...
Searching...
No Matches
qgslayertreemodel.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayertreemodel.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 <QMimeData>
17#include <QTextStream>
18
19#include "qgslayertreemodel.h"
20
21#include "qgsapplication.h"
22#include "qgslayertree.h"
24#include "qgsproject.h"
25#include "qgsmaphittest.h"
26#include "qgsmaplayer.h"
27#include "qgsmaplayerlegend.h"
28#include "qgsvectorlayer.h"
29#include "qgslayerdefinition.h"
30#include "qgsiconutils.h"
31#include "qgsmimedatautils.h"
33#include "qgsmaplayerstyle.h"
34#include "qgsrendercontext.h"
36
37#include <QPalette>
38
40 : QAbstractItemModel( parent )
41 , mRootNode( rootNode )
42 , mFlags( ShowLegend | AllowLegendChangeState | DeferredLegendInvalidation )
43{
45
46 mFontLayer.setBold( true );
47
49 mDeferLegendInvalidationTimer.setSingleShot( true );
50}
51
56
57QgsLayerTreeNode *QgsLayerTreeModel::index2node( const QModelIndex &index ) const
58{
59 if ( !index.isValid() )
60 return mRootNode;
61
62 QObject *obj = reinterpret_cast<QObject *>( index.internalPointer() );
63 return qobject_cast<QgsLayerTreeNode *>( obj );
64}
65
66
67int QgsLayerTreeModel::rowCount( const QModelIndex &parent ) const
68{
70 return legendNodeRowCount( nodeLegend );
71
73 if ( !n )
74 return 0;
75
76 if ( QgsLayerTree::isLayer( n ) )
77 {
78 if ( !testFlag( ShowLegend ) )
79 return 0;
80
82 }
83
84 return n->children().count();
85}
86
87int QgsLayerTreeModel::columnCount( const QModelIndex &parent ) const
88{
89 Q_UNUSED( parent )
90 return 1;
91}
92
93QModelIndex QgsLayerTreeModel::index( int row, int column, const QModelIndex &parent ) const
94{
95 if ( column < 0 || column >= columnCount( parent ) ||
96 row < 0 || row >= rowCount( parent ) )
97 return QModelIndex();
98
100 return legendNodeIndex( row, column, nodeLegend );
101
103 if ( !n )
104 return QModelIndex(); // have no children
105
107 {
108 return legendRootIndex( row, column, QgsLayerTree::toLayer( n ) );
109 }
110
111 return createIndex( row, column, static_cast<QObject *>( n->children().at( row ) ) );
112}
113
114
115QModelIndex QgsLayerTreeModel::parent( const QModelIndex &child ) const
116{
117 if ( !child.isValid() )
118 return QModelIndex();
119
120 if ( QgsLayerTreeNode *n = index2node( child ) )
121 {
122 return indexOfParentLayerTreeNode( n->parent() ); // must not be null
123 }
124 else if ( QgsLayerTreeModelLegendNode *legendNode = index2legendNode( child ) )
125 {
126 return legendParent( legendNode );
127 }
128 else
129 {
130 Q_ASSERT( false ); // no other node types!
131 return QModelIndex();
132 }
133
134}
135
136
138{
139 Q_ASSERT( parentNode );
140
141 QgsLayerTreeNode *grandParentNode = parentNode->parent();
142 if ( !grandParentNode )
143 return QModelIndex(); // root node -> invalid index
144
145 int row = grandParentNode->children().indexOf( parentNode );
146 Q_ASSERT( row >= 0 );
147
148 return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
149}
150
151
152QVariant QgsLayerTreeModel::data( const QModelIndex &index, int role ) const
153{
154 if ( !index.isValid() || index.column() > 1 )
155 return QVariant();
156
158 return legendNodeData( sym, role );
159
161 if ( role == Qt::DisplayRole || role == Qt::EditRole )
162 {
163 if ( QgsLayerTree::isGroup( node ) )
164 return QgsLayerTree::toGroup( node )->name();
165
166 if ( QgsLayerTree::isLayer( node ) )
167 {
168 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
169 QString name = nodeLayer->name();
170 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() );
171 if ( vlayer && nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toInt() && role == Qt::DisplayRole )
172 {
173 const bool estimatedCount = vlayer->dataProvider() ? QgsDataSourceUri( vlayer->dataProvider()->dataSourceUri() ).useEstimatedMetadata() : false;
174 const qlonglong count = vlayer->featureCount();
175
176 // if you modify this line, please update QgsSymbolLegendNode::updateLabel
177 name += QStringLiteral( " [%1%2]" ).arg(
178 estimatedCount ? QStringLiteral( "≈" ) : QString(),
179 count != -1 ? QLocale().toString( count ) : tr( "N/A" ) );
180 }
181 return name;
182 }
183 }
184 else if ( role == Qt::DecorationRole && index.column() == 0 )
185 {
186 if ( QgsLayerTree::isGroup( node ) )
187 return iconGroup();
188
189 if ( QgsLayerTree::isLayer( node ) )
190 {
191 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
192
193 QgsMapLayer *layer = nodeLayer->layer();
194 if ( !layer )
195 return QVariant();
196
197 // icons possibly overriding default icon
198 QIcon icon = QgsIconUtils::iconForLayer( layer );
199
200 // if there's just on legend entry that should be embedded in layer - do that!
201 if ( testFlag( ShowLegend ) && legendEmbeddedInParent( nodeLayer ) )
202 {
203 icon = legendIconEmbeddedInParent( nodeLayer );
204 }
205
206 if ( !icon.isNull() && layer->isEditable() && !( layer->properties() & Qgis::MapLayerProperty::UsersCannotToggleEditing ) && testFlag( UseTextFormatting ) )
207 {
208 const int iconSize = scaleIconSize( 16 );
209 QPixmap pixmap( icon.pixmap( iconSize, iconSize ) );
210
211 QPainter painter( &pixmap );
212 painter.drawPixmap( 0, 0, iconSize, iconSize, QgsApplication::getThemePixmap( layer->isModified() ? QStringLiteral( "/mIconEditableEdits.svg" ) : QStringLiteral( "/mActionToggleEditing.svg" ) ) );
213 painter.end();
214
215 icon = QIcon( pixmap );
216 }
217
218 return icon;
219 }
220 }
221 else if ( role == Qt::CheckStateRole )
222 {
224 return QVariant();
225
226 if ( QgsLayerTree::isLayer( node ) )
227 {
228 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
229
230 if ( nodeLayer->layer() && !nodeLayer->layer()->isSpatial() )
231 return QVariant(); // do not show checkbox for non-spatial tables
232
233 return nodeLayer->itemVisibilityChecked() ? Qt::Checked : Qt::Unchecked;
234 }
235 else if ( QgsLayerTree::isGroup( node ) )
236 {
237 QgsLayerTreeGroup *nodeGroup = QgsLayerTree::toGroup( node );
238 return nodeGroup->itemVisibilityChecked() ? Qt::Checked : Qt::Unchecked;
239 }
240 }
241 else if ( role == Qt::FontRole && testFlag( UseTextFormatting ) )
242 {
243 QFont f( QgsLayerTree::isLayer( node ) ? mFontLayer : ( QgsLayerTree::isGroup( node ) ? mFontGroup : QFont() ) );
244 if ( index == mCurrentIndex )
245 f.setUnderline( true );
246 if ( QgsLayerTree::isLayer( node ) )
247 {
248 const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
249 if ( ( !node->isVisible() && ( !layer || layer->isSpatial() ) ) || ( layer && !layer->isInScaleRange( mLegendMapViewScale ) ) )
250 {
251 f.setItalic( !f.italic() );
252 }
253 }
254 return f;
255 }
256 else if ( role == Qt::ForegroundRole && testFlag( UseTextFormatting ) )
257 {
258 QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
259 if ( QgsLayerTree::isLayer( node ) )
260 {
261 const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
262 if ( ( !node->isVisible() && ( !layer || layer->isSpatial() ) ) || ( layer && !layer->isInScaleRange( mLegendMapViewScale ) ) )
263 {
264 QColor fadedTextColor = brush.color();
265 fadedTextColor.setAlpha( 128 );
266 brush.setColor( fadedTextColor );
267 }
268 }
269 return brush;
270 }
271 else if ( role == Qt::ToolTipRole )
272 {
273 if ( QgsLayerTree::isLayer( node ) )
274 {
275 if ( QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer() )
276 {
277 QString title =
278 !layer->title().isEmpty() ? layer->title() :
279 !layer->shortName().isEmpty() ? layer->shortName() :
280 layer->name();
281
282 title = "<b>" + title.toHtmlEscaped() + "</b>";
283
284 if ( layer->isSpatial() && layer->crs().isValid() )
285 {
286 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
287 title += tr( " (%1 - %2)" ).arg( QgsWkbTypes::displayString( vl->wkbType() ), layer->crs().authid() ).toHtmlEscaped();
288 else
289 title += tr( " (%1)" ).arg( layer->crs().authid() ).toHtmlEscaped();
290 }
291
292 QStringList parts;
293 parts << title;
294
295 if ( !layer->abstract().isEmpty() )
296 {
297 parts << QString();
298 const QStringList abstractLines = layer->abstract().split( '\n' );
299 for ( const auto &l : abstractLines )
300 {
301 parts << l.toHtmlEscaped();
302 }
303 parts << QString();
304 }
305
306 QString source( layer->publicSource() );
307 if ( source.size() > 1024 )
308 {
309 source = source.left( 1023 ) + QString( QChar( 0x2026 ) );
310 }
311
312 parts << "<i>" + source.toHtmlEscaped() + "</i>";
313
314 QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
315 const bool showFeatureCount = nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toBool();
316 const bool estimatedCount = layer->dataProvider() ? QgsDataSourceUri( layer->dataProvider()->dataSourceUri() ).useEstimatedMetadata() : false;
317 if ( showFeatureCount && estimatedCount )
318 {
319 parts << tr( "<b>Feature count is estimated</b> : the feature count is determined by the database statistics" );
320 }
321
322 return parts.join( QLatin1String( "<br/>" ) );
323 }
324 }
325 }
326
327 return QVariant();
328}
329
330
331Qt::ItemFlags QgsLayerTreeModel::flags( const QModelIndex &index ) const
332{
333 if ( !index.isValid() )
334 {
335 Qt::ItemFlags rootFlags = Qt::ItemFlags();
336 if ( testFlag( AllowNodeReorder ) )
337 rootFlags |= Qt::ItemIsDropEnabled;
338 return rootFlags;
339 }
340
342 return legendNodeFlags( symn );
343
344 Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
345
346 if ( testFlag( AllowNodeRename ) )
347 f |= Qt::ItemIsEditable;
348
350 bool isEmbedded = node->customProperty( QStringLiteral( "embedded" ) ).toInt();
351
352 if ( testFlag( AllowNodeReorder ) )
353 {
354 // only root embedded nodes can be reordered
355 if ( !isEmbedded || ( isEmbedded && node->parent() && !node->parent()->customProperty( QStringLiteral( "embedded" ) ).toInt() ) )
356 f |= Qt::ItemIsDragEnabled;
357 }
358
360 f |= Qt::ItemIsUserCheckable;
361
362 if ( testFlag( AllowNodeReorder ) && QgsLayerTree::isGroup( node ) && !isEmbedded )
363 f |= Qt::ItemIsDropEnabled;
364
365 return f;
366}
367
368bool QgsLayerTreeModel::setData( const QModelIndex &index, const QVariant &value, int role )
369{
371 if ( sym )
372 {
373 if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
374 return false;
375 bool res = sym->setData( value, role );
376 if ( res )
377 emit dataChanged( index, index );
378 return res;
379 }
380
382 if ( !node )
383 return QAbstractItemModel::setData( index, value, role );
384
385 if ( role == Qt::CheckStateRole )
386 {
388 return false;
389
390 bool checked = static_cast< Qt::CheckState >( value.toInt() ) == Qt::Checked;
391 if ( checked && node->children().isEmpty() )
392 {
394 }
395 else if ( testFlag( ActionHierarchical ) )
396 {
397 if ( node->children().isEmpty() )
399 else
400 node->setItemVisibilityCheckedRecursive( checked );
401 }
402 else
403 {
404 node->setItemVisibilityChecked( checked );
405 }
406
408
409 return true;
410 }
411 else if ( role == Qt::EditRole )
412 {
413 if ( !testFlag( AllowNodeRename ) )
414 return false;
415
416 if ( QgsLayerTree::isLayer( node ) )
417 {
419 layer->setName( value.toString() );
420 emit dataChanged( index, index );
421 }
422 else if ( QgsLayerTree::isGroup( node ) )
423 {
424 QgsLayerTree::toGroup( node )->setName( value.toString() );
425 emit dataChanged( index, index );
426 }
427 }
428
429 return QAbstractItemModel::setData( index, value, role );
430}
431
433{
434 if ( !node || !node->parent() )
435 return QModelIndex(); // this is the only root item -> invalid index
436
437 QModelIndex parentIndex = node2index( node->parent() );
438
439 int row = node->parent()->children().indexOf( node );
440 Q_ASSERT( row >= 0 );
441 return index( row, 0, parentIndex );
442}
443
444
445static bool _isChildOfNode( QgsLayerTreeNode *child, QgsLayerTreeNode *node )
446{
447 if ( !child->parent() )
448 return false;
449
450 if ( child->parent() == node )
451 return true;
452
453 return _isChildOfNode( child->parent(), node );
454}
455
456static bool _isChildOfNodes( QgsLayerTreeNode *child, const QList<QgsLayerTreeNode *> &nodes )
457{
458 for ( QgsLayerTreeNode *n : nodes )
459 {
460 if ( _isChildOfNode( child, n ) )
461 return true;
462 }
463
464 return false;
465}
466
467
468QList<QgsLayerTreeNode *> QgsLayerTreeModel::indexes2nodes( const QModelIndexList &list, bool skipInternal ) const
469{
470 QList<QgsLayerTreeNode *> nodes;
471 const auto constList = list;
472 for ( const QModelIndex &index : constList )
473 {
475 if ( !node )
476 continue;
477
478 nodes << node;
479 }
480
481 if ( !skipInternal )
482 return nodes;
483
484 // remove any children of nodes if both parent node and children are selected
485 QList<QgsLayerTreeNode *> nodesFinal;
486 for ( QgsLayerTreeNode *node : std::as_const( nodes ) )
487 {
488 if ( !_isChildOfNodes( node, nodes ) )
489 nodesFinal << node;
490 }
491
492 return nodesFinal;
493}
494
499
501{
502 beginResetModel();
503
505
506 Q_ASSERT( mLegend.isEmpty() );
507
508 mRootNode = newRootGroup;
509
510 endResetModel();
511
513}
514
516{
517 // update title
518 QModelIndex idx = node2index( nodeLayer );
519 emit dataChanged( idx, idx );
520
521 // update children
522 int oldNodeCount = rowCount( idx );
523 if ( oldNodeCount > 0 )
524 {
525 beginRemoveRows( idx, 0, oldNodeCount - 1 );
526 removeLegendFromLayer( nodeLayer );
527 endRemoveRows();
528 }
529
530 addLegendToLayer( nodeLayer );
531 int newNodeCount = rowCount( idx );
532
533 // automatic collapse of legend nodes - useful if a layer has many legend nodes
534 if ( mAutoCollapseLegendNodesCount != -1 && oldNodeCount != newNodeCount && newNodeCount >= mAutoCollapseLegendNodesCount )
535 nodeLayer->setExpanded( false );
536}
537
539{
540 return mCurrentIndex;
541}
542
543void QgsLayerTreeModel::setCurrentIndex( const QModelIndex &currentIndex )
544{
546}
547
548
549void QgsLayerTreeModel::setLayerTreeNodeFont( int nodeType, const QFont &font )
550{
551 if ( nodeType == QgsLayerTreeNode::NodeGroup )
552 {
553 if ( mFontGroup != font )
554 {
555 mFontGroup = font;
557 }
558 }
559 else if ( nodeType == QgsLayerTreeNode::NodeLayer )
560 {
561 if ( mFontLayer != font )
562 {
563 mFontLayer = font;
565 }
566 }
567 else
568 {
569 QgsDebugMsgLevel( QStringLiteral( "invalid node type" ), 4 );
570 }
571}
572
573
574QFont QgsLayerTreeModel::layerTreeNodeFont( int nodeType ) const
575{
576 if ( nodeType == QgsLayerTreeNode::NodeGroup )
577 return mFontGroup;
578 else if ( nodeType == QgsLayerTreeNode::NodeLayer )
579 return mFontLayer;
580 else
581 {
582 QgsDebugMsgLevel( QStringLiteral( "invalid node type" ), 4 );
583 return QFont();
584 }
585}
586
588{
589 mLegendFilterByScale = scale;
590
591 // this could be later done in more efficient way
592 // by just updating active legend nodes, without refreshing original legend nodes
593 const auto layers = mRootNode->findLayers();
594 for ( QgsLayerTreeLayer *nodeLayer : layers )
595 refreshLayerLegend( nodeLayer );
596}
597
599{
601 setLegendFilter( settings, /* useExtent = */ true );
603}
604
605void QgsLayerTreeModel::setLegendFilter( const QgsMapSettings *settings, bool useExtent, const QgsGeometry &polygon, bool useExpressions )
606{
607 if ( settings && settings->hasValidSettings() )
608 {
609 std::unique_ptr< QgsLayerTreeFilterSettings > filterSettings = std::make_unique< QgsLayerTreeFilterSettings >( *settings );
610
611 if ( !useExtent ) // only expressions
612 {
614 }
615 else if ( polygon.type() == Qgis::GeometryType::Polygon )
616 {
618 }
619
620 if ( useExpressions )
621 {
623 }
624
626 }
627 else
628 {
629 setFilterSettings( nullptr );
630 }
631}
632
634{
635 return mFilterSettings ? &mFilterSettings->mapSettings() : nullptr;
636}
637
639{
640 if ( settings )
641 {
642 mFilterSettings = std::make_unique< QgsLayerTreeFilterSettings >( *settings );
643 mFilterSettings->mapSettings().setLayerStyleOverrides( mLayerStyleOverrides );
644
645 bool hitTestWasRunning = false;
646 if ( mHitTestTask )
647 {
648 // cancel outdated task -- this is owned by the task manager and will get automatically deleted accordingly
649 disconnect( mHitTestTask, &QgsTask::taskCompleted, this, &QgsLayerTreeModel::hitTestTaskCompleted );
650 mHitTestTask->cancel();
651 mHitTestTask = nullptr;
652 hitTestWasRunning = true;
653 }
654
655 std::unique_ptr< QgsMapHitTest > blockingHitTest;
658 else
659 blockingHitTest = std::make_unique< QgsMapHitTest >( *mFilterSettings );
660
661 if ( mHitTestTask )
662 {
663 connect( mHitTestTask, &QgsTask::taskCompleted, this, &QgsLayerTreeModel::hitTestTaskCompleted );
665
666 if ( !hitTestWasRunning )
667 emit hitTestStarted();
668 }
669 else
670 {
671 blockingHitTest->run();
672 mHitTestResults = blockingHitTest->results();
673 handleHitTestResults();
674 }
675 }
676 else
677 {
678 if ( !mFilterSettings )
679 return;
680
681 mFilterSettings.reset();
682 handleHitTestResults();
683 }
684}
685
690
691void QgsLayerTreeModel::handleHitTestResults()
692{
693 // temporarily disable autocollapse so that legend nodes stay visible
694 int bkAutoCollapse = autoCollapseLegendNodes();
696
697 // this could be later done in more efficient way
698 // by just updating active legend nodes, without refreshing original legend nodes
699 const auto layers = mRootNode->findLayers();
700 for ( QgsLayerTreeLayer *nodeLayer : layers )
701 refreshLayerLegend( nodeLayer );
702
703 setAutoCollapseLegendNodes( bkAutoCollapse );
704}
705
706void QgsLayerTreeModel::setLegendMapViewData( double mapUnitsPerPixel, int dpi, double scale )
707{
708 if ( mLegendMapViewDpi == dpi && qgsDoubleNear( mLegendMapViewMupp, mapUnitsPerPixel ) && qgsDoubleNear( mLegendMapViewScale, scale ) )
709 return;
710
711 double previousScale = mLegendMapViewScale;
712 mLegendMapViewScale = scale;
713 mLegendMapViewMupp = mapUnitsPerPixel;
714 mLegendMapViewDpi = dpi;
715
716 // now invalidate legend nodes!
718
719 if ( scale != previousScale )
720 refreshScaleBasedLayers( QModelIndex(), previousScale );
721}
722
723void QgsLayerTreeModel::legendMapViewData( double *mapUnitsPerPixel, int *dpi, double *scale ) const
724{
725 if ( mapUnitsPerPixel ) *mapUnitsPerPixel = mLegendMapViewMupp;
726 if ( dpi ) *dpi = mLegendMapViewDpi;
727 if ( scale ) *scale = mLegendMapViewScale;
728}
729
730QMap<QString, QString> QgsLayerTreeModel::layerStyleOverrides() const
731{
733}
734
735void QgsLayerTreeModel::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
736{
737 mLayerStyleOverrides = overrides;
738}
739
741{
742 if ( mTargetScreenProperties.contains( properties ) )
743 return;
744
745 mTargetScreenProperties.insert( properties );
746}
747
748QSet<QgsScreenProperties> QgsLayerTreeModel::targetScreenProperties() const
749{
751}
752
753int QgsLayerTreeModel::scaleIconSize( int standardSize )
754{
755 return QgsApplication::scaleIconSize( standardSize, true );
756}
757
759{
760 if ( mHitTestTask )
761 mHitTestTask->waitForFinished();
762}
763
765{
766 return static_cast< bool >( mHitTestTask );
767}
768
769void QgsLayerTreeModel::nodeWillAddChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
770{
771 beginInsertRows( node2index( node ), indexFrom, indexTo );
772}
773
774static QList<QgsLayerTreeLayer *> _layerNodesInSubtree( QgsLayerTreeNode *node, int indexFrom, int indexTo )
775{
776 QList<QgsLayerTreeNode *> children = node->children();
777 QList<QgsLayerTreeLayer *> newLayerNodes;
778 for ( int i = indexFrom; i <= indexTo; ++i )
779 {
780 QgsLayerTreeNode *child = children.at( i );
781 if ( QgsLayerTree::isLayer( child ) )
782 newLayerNodes << QgsLayerTree::toLayer( child );
783 else if ( QgsLayerTree::isGroup( child ) )
784 newLayerNodes << QgsLayerTree::toGroup( child )->findLayers();
785 }
786 return newLayerNodes;
787}
788
789void QgsLayerTreeModel::nodeAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
790{
791 Q_ASSERT( node );
792
793 endInsertRows();
794
795 const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo );
796 for ( QgsLayerTreeLayer *newLayerNode : subNodes )
797 connectToLayer( newLayerNode );
798}
799
800void QgsLayerTreeModel::nodeWillRemoveChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
801{
802 Q_ASSERT( node );
803
804 beginRemoveRows( node2index( node ), indexFrom, indexTo );
805
806 // disconnect from layers and remove their legend
807 const auto subNodes = _layerNodesInSubtree( node, indexFrom, indexTo );
808 for ( QgsLayerTreeLayer *nodeLayer : subNodes )
809 disconnectFromLayer( nodeLayer );
810}
811
813{
814 endRemoveRows();
815}
816
818{
819 Q_ASSERT( node );
820
821 const QModelIndex index = node2index( node );
822 emit dataChanged( index, index );
823}
824
825void QgsLayerTreeModel::nodeNameChanged( QgsLayerTreeNode *node, const QString &name )
826{
827 Q_UNUSED( name )
828 Q_ASSERT( node );
829
830 const QModelIndex index = node2index( node );
831 emit dataChanged( index, index );
832}
833
834
836{
837 if ( QgsLayerTree::isLayer( node ) && key == QLatin1String( "showFeatureCount" ) )
839}
840
841
843{
844 QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
845 if ( !nodeLayer )
846 return;
847
848 // deferred connection to the layer
849 connectToLayer( nodeLayer );
850}
851
853{
854 QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( sender() );
855 if ( !nodeLayer )
856 return;
857
858 disconnectFromLayer( nodeLayer );
859
860 // wait for the layer to appear again
862}
863
865{
866 if ( !mRootNode )
867 return;
868
869 if ( !testFlag( ShowLegend ) )
870 return;
871
872 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
873 if ( !layer )
874 return;
875
876 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
877 if ( !nodeLayer )
878 return;
879
880 refreshLayerLegend( nodeLayer );
881}
882
884{
885 if ( !mRootNode )
886 return;
887
888 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
889 if ( !layer )
890 return;
891
892 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
893 if ( !nodeLayer )
894 return;
895
896 const QModelIndex index = node2index( nodeLayer );
897 emit dataChanged( index, index );
898}
899
901{
902 QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
903 if ( !layer )
904 return;
905
906 QgsLayerTreeLayer *nodeLayer = mRootNode->findLayer( layer->id() );
907 if ( !nodeLayer )
908 return;
909
910 QModelIndex index = node2index( nodeLayer );
911 emit dataChanged( index, index );
912
913 if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ) ).toInt() )
914 refreshLayerLegend( nodeLayer );
915}
916
917
919{
920 QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( sender() );
921 if ( !legendNode )
922 return;
923
924 QModelIndex index = legendNode2index( legendNode );
925 if ( index.isValid() )
926 emit dataChanged( index, index );
927}
928
929void QgsLayerTreeModel::legendNodeSizeChanged()
930{
931 QgsLayerTreeModelLegendNode *legendNode = qobject_cast<QgsLayerTreeModelLegendNode *>( sender() );
932 if ( !legendNode )
933 return;
934
935 QModelIndex index = legendNode2index( legendNode );
936 if ( index.isValid() )
937 emit dataChanged( index, index, QVector<int> { Qt::SizeHintRole } );
938}
939
940void QgsLayerTreeModel::hitTestTaskCompleted()
941{
942 if ( mHitTestTask )
943 {
944 mHitTestResults = mHitTestTask->results();
945 handleHitTestResults();
946 emit hitTestCompleted();
947 }
948}
949
951{
952 if ( !nodeLayer->layer() )
953 {
954 // in order to connect to layer, we need to have it loaded.
955 // keep an eye on the layer ID: once loaded, we will use it
956 connect( nodeLayer, &QgsLayerTreeLayer::layerLoaded, this, &QgsLayerTreeModel::nodeLayerLoaded, Qt::UniqueConnection );
957 return;
958 }
959
960 // watch if the layer is getting removed
961 connect( nodeLayer, &QgsLayerTreeLayer::layerWillBeUnloaded, this, &QgsLayerTreeModel::nodeLayerWillBeUnloaded, Qt::UniqueConnection );
962
963 if ( testFlag( ShowLegend ) )
964 {
965 addLegendToLayer( nodeLayer );
966
967 // if we aren't loading a layer from a project, setup some nice default settings
968 if ( !mRootNode->customProperty( QStringLiteral( "loading" ) ).toBool() )
969 {
970 // automatic collapse of legend nodes - useful if a layer has many legend nodes
972 nodeLayer->setExpanded( false );
973
975 {
976 nodeLayer->setCustomProperty( QStringLiteral( "showFeatureCount" ), true );
977 }
978 }
979 }
980
981 QgsMapLayer *layer = nodeLayer->layer();
982 connect( layer, &QgsMapLayer::legendChanged, this, &QgsLayerTreeModel::layerLegendChanged, Qt::UniqueConnection );
983 connect( layer, &QgsMapLayer::flagsChanged, this, &QgsLayerTreeModel::layerFlagsChanged, Qt::UniqueConnection );
984
985 // using unique connection because there may be temporarily more nodes for a layer than just one
986 // which would create multiple connections, however disconnect() would disconnect all multiple connections
987 // even if we wanted to disconnect just one connection in each call.
988 connect( layer, &QgsMapLayer::editingStarted, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
989 connect( layer, &QgsMapLayer::editingStopped, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
990 connect( layer, &QgsMapLayer::layerModified, this, &QgsLayerTreeModel::layerNeedsUpdate, Qt::UniqueConnection );
991
992 emit dataChanged( node2index( nodeLayer ), node2index( nodeLayer ) );
993}
994
995// try to find out if the layer ID is present in the tree multiple times
996static int _numLayerCount( QgsLayerTreeGroup *group, const QString &layerId )
997{
998 int count = 0;
999 const auto constChildren = group->children();
1000 for ( QgsLayerTreeNode *child : constChildren )
1001 {
1002 if ( QgsLayerTree::isLayer( child ) )
1003 {
1004 if ( QgsLayerTree::toLayer( child )->layerId() == layerId )
1005 count++;
1006 }
1007 else if ( QgsLayerTree::isGroup( child ) )
1008 {
1009 count += _numLayerCount( QgsLayerTree::toGroup( child ), layerId );
1010 }
1011 }
1012 return count;
1013}
1014
1016{
1017 disconnect( nodeLayer, nullptr, this, nullptr ); // disconnect from delayed load of layer
1018
1019 if ( !nodeLayer->layer() )
1020 return; // we were never connected
1021
1022 if ( testFlag( ShowLegend ) )
1023 {
1024 removeLegendFromLayer( nodeLayer );
1025 }
1026
1027 if ( _numLayerCount( mRootNode, nodeLayer->layerId() ) == 1 )
1028 {
1029 // last instance of the layer in the tree: disconnect from all signals from layer!
1030 disconnect( nodeLayer->layer(), nullptr, this, nullptr );
1031 }
1032}
1033
1035{
1036 const auto constChildren = parentGroup->children();
1037 for ( QgsLayerTreeNode *node : constChildren )
1038 {
1039 if ( QgsLayerTree::isGroup( node ) )
1041 else if ( QgsLayerTree::isLayer( node ) )
1043 }
1044}
1045
1047{
1048 const auto constChildren = parentGroup->children();
1049 for ( QgsLayerTreeNode *node : constChildren )
1050 {
1051 if ( QgsLayerTree::isGroup( node ) )
1053 else if ( QgsLayerTree::isLayer( node ) )
1055 }
1056}
1057
1059{
1060 Q_ASSERT( mRootNode );
1061
1062 connect( mRootNode, &QgsLayerTreeNode::willAddChildren, this, &QgsLayerTreeModel::nodeWillAddChildren, Qt::ConnectionType::UniqueConnection );
1063 connect( mRootNode, &QgsLayerTreeNode::addedChildren, this, &QgsLayerTreeModel::nodeAddedChildren, Qt::ConnectionType::UniqueConnection );
1064 connect( mRootNode, &QgsLayerTreeNode::willRemoveChildren, this, &QgsLayerTreeModel::nodeWillRemoveChildren, Qt::ConnectionType::UniqueConnection );
1065 connect( mRootNode, &QgsLayerTreeNode::removedChildren, this, &QgsLayerTreeModel::nodeRemovedChildren, Qt::ConnectionType::UniqueConnection );
1066 connect( mRootNode, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeModel::nodeVisibilityChanged, Qt::ConnectionType::UniqueConnection );
1067 connect( mRootNode, &QgsLayerTreeNode::nameChanged, this, &QgsLayerTreeModel::nodeNameChanged, Qt::ConnectionType::UniqueConnection );
1068 connect( mRootNode, &QgsLayerTreeNode::customPropertyChanged, this, &QgsLayerTreeModel::nodeCustomPropertyChanged, Qt::ConnectionType::UniqueConnection );
1069
1071}
1072
1074{
1075 disconnect( mRootNode, nullptr, this, nullptr );
1076
1078}
1079
1081{
1082 QgsLayerTreeNode *node = index2node( idx );
1083 if ( !node )
1084 return;
1085
1086 int count = node->children().count();
1087 if ( count == 0 )
1088 return;
1089 emit dataChanged( index( 0, 0, idx ), index( count - 1, 0, idx ) );
1090 for ( int i = 0; i < count; ++i )
1091 recursivelyEmitDataChanged( index( i, 0, idx ) );
1092}
1093
1094void QgsLayerTreeModel::refreshScaleBasedLayers( const QModelIndex &idx, double previousScale )
1095{
1096 QgsLayerTreeNode *node = index2node( idx );
1097 if ( !node )
1098 return;
1099
1100 if ( node->nodeType() == QgsLayerTreeNode::NodeLayer )
1101 {
1102 const QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer();
1103 if ( layer && layer->hasScaleBasedVisibility() )
1104 {
1105 if ( layer->isInScaleRange( mLegendMapViewScale ) != layer->isInScaleRange( previousScale ) )
1106 emit dataChanged( idx, idx, QVector<int>() << Qt::FontRole << Qt::ForegroundRole );
1107 }
1108 }
1109 int count = node->children().count();
1110 for ( int i = 0; i < count; ++i )
1111 refreshScaleBasedLayers( index( i, 0, idx ), previousScale );
1112}
1113
1115{
1116 return Qt::CopyAction | Qt::MoveAction;
1117}
1118
1120{
1121 QStringList types;
1122 types << QStringLiteral( "application/qgis.layertreemodeldata" );
1123 return types;
1124}
1125
1126
1127QMimeData *QgsLayerTreeModel::mimeData( const QModelIndexList &indexes ) const
1128{
1129 // Sort the indexes. Depending on how the user selected the items, the indexes may be unsorted.
1130 QModelIndexList sortedIndexes = indexes;
1131 std::sort( sortedIndexes.begin(), sortedIndexes.end(), std::less<QModelIndex>() );
1132
1133 QList<QgsLayerTreeNode *> nodesFinal = indexes2nodes( sortedIndexes, true );
1134
1135 if ( nodesFinal.isEmpty() )
1136 return nullptr;
1137
1138 QMimeData *mimeData = new QMimeData();
1139
1140 QDomDocument layerTreeDoc;
1141 QDomElement rootLayerTreeElem = layerTreeDoc.createElement( QStringLiteral( "layer_tree_model_data" ) );
1142
1143 for ( QgsLayerTreeNode *node : std::as_const( nodesFinal ) )
1144 {
1145 node->writeXml( rootLayerTreeElem, QgsReadWriteContext() );
1146 }
1147 layerTreeDoc.appendChild( rootLayerTreeElem );
1148
1149 QString errorMessage;
1150 QgsReadWriteContext readWriteContext;
1151 QDomDocument layerDefinitionsDoc( QStringLiteral( "qgis-layer-definition" ) );
1152 QgsLayerDefinition::exportLayerDefinition( layerDefinitionsDoc, nodesFinal, errorMessage, QgsReadWriteContext() );
1153
1154 QString txt = layerDefinitionsDoc.toString();
1155
1156 mimeData->setData( QStringLiteral( "application/qgis.layertreemodeldata" ), layerTreeDoc.toString().toUtf8() );
1157 mimeData->setData( QStringLiteral( "application/qgis.application.pid" ), QString::number( QCoreApplication::applicationPid() ).toUtf8() );
1158 mimeData->setData( QStringLiteral( "application/qgis.layertree.source" ), QStringLiteral( ":0x%1" ).arg( reinterpret_cast<quintptr>( this ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) ).toUtf8() );
1159 mimeData->setData( QStringLiteral( "application/qgis.layertree.layerdefinitions" ), txt.toUtf8() );
1160 mimeData->setData( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ), QgsMimeDataUtils::layerTreeNodesToUriList( nodesFinal ) );
1161
1162 return mimeData;
1163}
1164
1165bool QgsLayerTreeModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
1166{
1167 if ( action == Qt::IgnoreAction )
1168 return true;
1169
1170 if ( !data->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
1171 return false;
1172
1173 if ( column >= columnCount( parent ) )
1174 return false;
1175
1176 // don't accept drops from some layer tree subclasses to non-matching subclasses
1177 const QString restrictTypes( data->data( QStringLiteral( "application/qgis.restrictlayertreemodelsubclass" ) ) );
1178 if ( !restrictTypes.isEmpty() && restrictTypes != QString( metaObject()->className() ) )
1179 return false;
1180
1181 QgsLayerTreeNode *nodeParent = index2node( parent );
1182 if ( !QgsLayerTree::isGroup( nodeParent ) )
1183 return false;
1184
1185 if ( parent.isValid() && row == -1 )
1186 row = 0; // if dropped directly onto group item, insert at first position
1187
1188 // if we are coming from another QGIS instance, we need to add the layers too
1189 bool ok = false;
1190 // the application pid is only provided from QGIS 3.14, so do not check to OK before defaulting to moving in the legend
1191 qint64 qgisPid = data->data( QStringLiteral( "application/qgis.application.pid" ) ).toInt( &ok );
1192
1193 if ( ok && qgisPid != QCoreApplication::applicationPid() )
1194 {
1195 QByteArray encodedLayerDefinitionData = data->data( QStringLiteral( "application/qgis.layertree.layerdefinitions" ) );
1196 QDomDocument layerDefinitionDoc;
1197 if ( !layerDefinitionDoc.setContent( QString::fromUtf8( encodedLayerDefinitionData ) ) )
1198 return false;
1199 QgsReadWriteContext context;
1200 QString errorMessage;
1201 QgsLayerDefinition::loadLayerDefinition( layerDefinitionDoc, QgsProject::instance(), QgsLayerTree::toGroup( nodeParent ), errorMessage, context );
1202 emit messageEmitted( tr( "New layers added from another QGIS instance" ) );
1203 }
1204 else
1205 {
1206 QByteArray encodedLayerTreeData = data->data( QStringLiteral( "application/qgis.layertreemodeldata" ) );
1207
1208 QDomDocument layerTreeDoc;
1209 if ( !layerTreeDoc.setContent( QString::fromUtf8( encodedLayerTreeData ) ) )
1210 return false;
1211
1212 QDomElement rootLayerTreeElem = layerTreeDoc.documentElement();
1213 if ( rootLayerTreeElem.tagName() != QLatin1String( "layer_tree_model_data" ) )
1214 return false;
1215
1216 QList<QgsLayerTreeNode *> nodes;
1217
1218 QDomElement elem = rootLayerTreeElem.firstChildElement();
1219 while ( !elem.isNull() )
1220 {
1222 if ( node )
1223 nodes << node;
1224
1225 elem = elem.nextSiblingElement();
1226 }
1227
1228 if ( nodes.isEmpty() )
1229 return false;
1230
1231 QgsLayerTree::toGroup( nodeParent )->insertChildNodes( row, nodes );
1232 }
1233 return true;
1234}
1235
1236bool QgsLayerTreeModel::removeRows( int row, int count, const QModelIndex &parent )
1237{
1238 QgsLayerTreeNode *parentNode = index2node( parent );
1239 if ( QgsLayerTree::isGroup( parentNode ) )
1240 {
1241 QgsLayerTree::toGroup( parentNode )->removeChildren( row, count );
1242 return true;
1243 }
1244 return false;
1245}
1246
1247void QgsLayerTreeModel::setFlags( QgsLayerTreeModel::Flags f )
1248{
1249 mFlags = f;
1250}
1251
1253{
1254 if ( on )
1255 mFlags |= f;
1256 else
1257 mFlags &= ~f;
1258}
1259
1260QgsLayerTreeModel::Flags QgsLayerTreeModel::flags() const
1261{
1262 return mFlags;
1263}
1264
1266{
1267 return mFlags.testFlag( f );
1268}
1269
1271{
1272 return QgsApplication::getThemeIcon( QStringLiteral( "/mActionFolder.svg" ) );
1273}
1274
1275QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::filterLegendNodes( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1276{
1277 QList<QgsLayerTreeModelLegendNode *> filtered;
1278
1279 if ( mLegendFilterByScale > 0 )
1280 {
1281 for ( QgsLayerTreeModelLegendNode *node : std::as_const( nodes ) )
1282 {
1283 if ( node->isScaleOK( mLegendFilterByScale ) )
1284 filtered << node;
1285 }
1286 }
1287 else if ( mFilterSettings )
1288 {
1289 if ( !nodes.isEmpty() && mFilterSettings->layers().contains( nodes.at( 0 )->layerNode()->layer() ) )
1290 {
1291 for ( QgsLayerTreeModelLegendNode *node : std::as_const( nodes ) )
1292 {
1294 {
1296 filtered << node;
1297 break;
1298
1306 {
1307 const QString ruleKey = node->data( QgsSymbolLegendNode::RuleKeyRole ).toString();
1308 const bool checked = ( mFilterSettings && !( mFilterSettings->flags() & Qgis::LayerTreeFilterFlag::SkipVisibilityCheck ) )
1309 || node->data( Qt::CheckStateRole ).toInt() == Qt::Checked;
1310
1311 if ( checked )
1312 {
1313 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( node->layerNode()->layer() ) )
1314 {
1315 auto it = mHitTestResults.constFind( vl->id() );
1316 if ( it != mHitTestResults.constEnd() && it->contains( ruleKey ) )
1317 {
1318 filtered << node;
1319 }
1320 }
1321 else
1322 {
1323 filtered << node;
1324 }
1325 }
1326 else // unknown node type or unchecked
1327 filtered << node;
1328 break;
1329 }
1330 }
1331 }
1332 }
1333 }
1334 else
1335 {
1336 return nodes;
1337 }
1338
1339 return filtered;
1340}
1341
1342
1343
1345// Legend nodes routines - start
1346
1348{
1349 const auto constMLegend = mLegend;
1350 for ( const LayerLegendData &data : constMLegend )
1351 {
1352 qDeleteAll( data.originalNodes );
1353 delete data.tree;
1354 }
1355 mLegend.clear();
1356
1357 if ( mHitTestTask )
1358 {
1359 // cancel outdated task -- this is owned by the task manager and will get automatically deleted accordingly
1360 disconnect( mHitTestTask, &QgsTask::taskCompleted, this, &QgsLayerTreeModel::hitTestTaskCompleted );
1361 mHitTestTask->cancel();
1362 mHitTestTask = nullptr;
1363 }
1364}
1365
1366
1368{
1369 if ( mLegend.contains( nodeLayer ) )
1370 {
1371 qDeleteAll( mLegend[nodeLayer].originalNodes );
1372 delete mLegend[nodeLayer].tree;
1373 mLegend.remove( nodeLayer );
1374 }
1375}
1376
1377
1379{
1380 if ( !nodeL || !nodeL->layer() )
1381 return;
1382
1383 QgsMapLayer *ml = nodeL->layer();
1384
1385 QgsMapLayerStyleOverride styleOverride( ml );
1386 if ( mLayerStyleOverrides.contains( ml->id() ) )
1387 styleOverride.setOverrideStyle( mLayerStyleOverrides.value( ml->id() ) );
1388
1389 QgsMapLayerLegend *layerLegend = ml->legend();
1390 if ( !layerLegend )
1391 return;
1392 QList<QgsLayerTreeModelLegendNode *> lstNew = layerLegend->createLayerTreeModelLegendNodes( nodeL );
1393
1394 // apply filtering defined in layer node's custom properties (reordering, filtering, custom labels)
1396
1398 {
1399 // generate placeholder legend nodes that will be replaced by widgets in QgsLayerTreeView
1400 int widgetsCount = ml->customProperty( QStringLiteral( "embeddedWidgets/count" ), 0 ).toInt();
1401 while ( widgetsCount > 0 )
1402 {
1403 lstNew.insert( 0, new EmbeddedWidgetLegendNode( nodeL ) );
1404 --widgetsCount;
1405 }
1406 }
1407
1408 QList<QgsLayerTreeModelLegendNode *> filteredLstNew = filterLegendNodes( lstNew );
1409
1410 const auto constLstNew = lstNew;
1411 for ( QgsLayerTreeModelLegendNode *n : constLstNew )
1412 {
1413 n->setParent( this );
1415 connect( n, &QgsLayerTreeModelLegendNode::sizeChanged, this, &QgsLayerTreeModel::legendNodeSizeChanged );
1416 }
1417
1418 // See if we have an embedded node - if we do, we will not use it among active nodes.
1419 // Legend node embedded in parent does not have to be the first one,
1420 // there can be also nodes generated for embedded widgets
1421 QgsLayerTreeModelLegendNode *embeddedNode = nullptr;
1422 const auto constFilteredLstNew = filteredLstNew;
1423 for ( QgsLayerTreeModelLegendNode *legendNode : constFilteredLstNew )
1424 {
1425 if ( legendNode->isEmbeddedInParent() )
1426 {
1427 embeddedNode = legendNode;
1428 filteredLstNew.removeOne( legendNode );
1429 break;
1430 }
1431 }
1432
1433 LayerLegendTree *legendTree = nullptr;
1434
1435 // maybe the legend nodes form a tree - try to create a tree structure from the list
1436 if ( testFlag( ShowLegendAsTree ) )
1437 legendTree = tryBuildLegendTree( filteredLstNew );
1438
1439 int count = legendTree ? legendTree->children[nullptr].count() : filteredLstNew.count();
1440
1441 if ( !filteredLstNew.isEmpty() )
1442 {
1443 // Make sure it's clear
1444 const QModelIndex nodeIndex { node2index( nodeL ) };
1445 if ( rowCount( nodeIndex ) > 0 )
1446 {
1447 beginRemoveRows( node2index( nodeL ), 0, rowCount( nodeIndex ) - 1 );
1448 mLegend[nodeL] = LayerLegendData();
1449 endRemoveRows();
1450 }
1451 beginInsertRows( node2index( nodeL ), 0, count - 1 );
1452 }
1453
1455 data.originalNodes = lstNew;
1456 data.activeNodes = filteredLstNew;
1457 data.embeddedNodeInParent = embeddedNode;
1458 data.tree = legendTree;
1459
1460 mLegend[nodeL] = data;
1461
1462 if ( !filteredLstNew.isEmpty() )
1463 {
1464 endInsertRows();
1465 }
1466
1467 // invalidate map based data even if the data is not map-based to make sure
1468 // the symbol sizes are computed at least once
1469 mInvalidatedNodes.insert( nodeL );
1471}
1472
1473
1474QgsLayerTreeModel::LayerLegendTree *QgsLayerTreeModel::tryBuildLegendTree( const QList<QgsLayerTreeModelLegendNode *> &nodes )
1475{
1476 // first check whether there are any legend nodes that are not top-level
1477 bool hasParentKeys = false;
1478 for ( QgsLayerTreeModelLegendNode *n : nodes )
1479 {
1480 if ( !n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString().isEmpty() )
1481 {
1482 hasParentKeys = true;
1483 break;
1484 }
1485 }
1486 if ( !hasParentKeys )
1487 return nullptr; // all legend nodes are top-level => stick with list representation
1488
1489 // make mapping from rules to nodes and do some sanity checks
1490 QHash<QString, QgsLayerTreeModelLegendNode *> rule2node;
1491 rule2node[QString()] = nullptr;
1492 for ( QgsLayerTreeModelLegendNode *n : nodes )
1493 {
1494 QString ruleKey = n->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
1495 if ( ruleKey.isEmpty() ) // in tree all nodes must have key
1496 return nullptr;
1497 if ( rule2node.contains( ruleKey ) ) // and they must be unique
1498 return nullptr;
1499 rule2node[ruleKey] = n;
1500 }
1501
1502 // create the tree structure
1503 LayerLegendTree *tree = new LayerLegendTree;
1504 for ( QgsLayerTreeModelLegendNode *n : nodes )
1505 {
1506 QString parentRuleKey = n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString();
1507 QgsLayerTreeModelLegendNode *parent = rule2node.value( parentRuleKey, nullptr );
1508 tree->parents[n] = parent;
1509 tree->children[parent] << n;
1510 }
1511 return tree;
1512}
1513
1515{
1516 double scale = 0.0;
1517 double mupp = 0.0;
1518 int dpi = 0;
1519 legendMapViewData( &mupp, &dpi, &scale );
1520 bool validData = !qgsDoubleNear( mupp, 0.0 ) && dpi != 0 && !qgsDoubleNear( scale, 0.0 );
1521
1522 // setup temporary render context
1523 std::unique_ptr<QgsRenderContext> context( new QgsRenderContext );
1524 context->setScaleFactor( dpi / 25.4 );
1525
1526 if ( !mTargetScreenProperties.isEmpty() )
1527 {
1528 mTargetScreenProperties.begin()->updateRenderContextForScreen( *context );
1529 }
1530
1531 context->setRendererScale( scale );
1532 context->setMapToPixel( QgsMapToPixel( mupp ) );
1534 return validData ? context.release() : nullptr;
1535}
1536
1537
1539{
1540 return qobject_cast<QgsLayerTreeModelLegendNode *>( reinterpret_cast<QObject *>( index.internalPointer() ) );
1541}
1542
1543
1545{
1546 const LayerLegendData &data = mLegend[legendNode->layerNode()];
1547 if ( data.tree )
1548 {
1549 if ( QgsLayerTreeModelLegendNode *parentLegendNode = data.tree->parents[legendNode] )
1550 {
1551 QModelIndex parentIndex = legendNode2index( parentLegendNode );
1552 int row = data.tree->children[parentLegendNode].indexOf( legendNode );
1553 return index( row, 0, parentIndex );
1554 }
1555 else
1556 {
1557 QModelIndex parentIndex = node2index( legendNode->layerNode() );
1558 int row = data.tree->children[nullptr].indexOf( legendNode );
1559 return index( row, 0, parentIndex );
1560 }
1561 }
1562
1563 QModelIndex parentIndex = node2index( legendNode->layerNode() );
1564 Q_ASSERT( parentIndex.isValid() );
1565 int row = data.activeNodes.indexOf( legendNode );
1566 if ( row < 0 ) // legend node may be filtered (exists within the list of original nodes, but not in active nodes)
1567 return QModelIndex();
1568
1569 return index( row, 0, parentIndex );
1570}
1571
1572
1574{
1575 const LayerLegendData &data = mLegend[node->layerNode()];
1576 if ( data.tree )
1577 return data.tree->children[node].count();
1578
1579 return 0; // they are leaves
1580}
1581
1582
1584{
1585 if ( !mLegend.contains( nL ) )
1586 return 0;
1587
1588 const LayerLegendData &data = mLegend[nL];
1589 if ( data.tree )
1590 return data.tree->children[nullptr].count();
1591
1592 int count = data.activeNodes.count();
1593 return count;
1594}
1595
1596
1597QModelIndex QgsLayerTreeModel::legendRootIndex( int row, int column, QgsLayerTreeLayer *nL ) const
1598{
1599 Q_ASSERT( mLegend.contains( nL ) );
1600 const LayerLegendData &data = mLegend[nL];
1601 if ( data.tree )
1602 return createIndex( row, column, static_cast<QObject *>( data.tree->children[nullptr].at( row ) ) );
1603
1604 return createIndex( row, column, static_cast<QObject *>( data.activeNodes.at( row ) ) );
1605}
1606
1607
1608QModelIndex QgsLayerTreeModel::legendNodeIndex( int row, int column, QgsLayerTreeModelLegendNode *node ) const
1609{
1610 const LayerLegendData &data = mLegend[node->layerNode()];
1611 if ( data.tree )
1612 return createIndex( row, column, static_cast<QObject *>( data.tree->children[node].at( row ) ) );
1613
1614 return QModelIndex(); // have no children
1615}
1616
1617
1619{
1620 QgsLayerTreeLayer *layerNode = legendNode->layerNode();
1621 const LayerLegendData &data = mLegend[layerNode];
1622 if ( data.tree )
1623 {
1624 if ( QgsLayerTreeModelLegendNode *parentNode = data.tree->parents[legendNode] )
1625 {
1626 QgsLayerTreeModelLegendNode *grandParentNode = data.tree->parents[parentNode]; // may be null (not a problem)
1627 int row = data.tree->children[grandParentNode].indexOf( parentNode );
1628 return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
1629 }
1630 else
1631 return indexOfParentLayerTreeNode( layerNode );
1632 }
1633
1634 return indexOfParentLayerTreeNode( layerNode );
1635}
1636
1637
1639{
1640 if ( role == Qt::CheckStateRole && !testFlag( AllowLegendChangeState ) )
1641 return QVariant();
1642 return node->data( role );
1643}
1644
1645
1647{
1648 Qt::ItemFlags f = node->flags();
1650 f &= ~Qt::ItemIsUserCheckable;
1651 return f;
1652}
1653
1654
1656{
1657 return static_cast< bool >( mLegend[nodeLayer].embeddedNodeInParent );
1658}
1659
1661{
1662 return mLegend[nodeLayer].embeddedNodeInParent;
1663}
1664
1665
1667{
1668 QgsLayerTreeModelLegendNode *legendNode = mLegend[nodeLayer].embeddedNodeInParent;
1669 if ( !legendNode )
1670 return QIcon();
1671 return QIcon( qvariant_cast<QPixmap>( legendNode->data( Qt::DecorationRole ) ) );
1672}
1673
1674
1675QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent )
1676{
1677 if ( !mLegend.contains( nodeLayer ) )
1678 return QList<QgsLayerTreeModelLegendNode *>();
1679
1680 const LayerLegendData &data = mLegend[nodeLayer];
1681 QList<QgsLayerTreeModelLegendNode *> lst( data.activeNodes );
1682 if ( !skipNodeEmbeddedInParent && data.embeddedNodeInParent )
1683 lst.prepend( data.embeddedNodeInParent );
1684 return lst;
1685}
1686
1687QList<QgsLayerTreeModelLegendNode *> QgsLayerTreeModel::layerOriginalLegendNodes( QgsLayerTreeLayer *nodeLayer )
1688{
1689 return mLegend.value( nodeLayer ).originalNodes;
1690}
1691
1692QgsLayerTreeModelLegendNode *QgsLayerTreeModel::findLegendNode( const QString &layerId, const QString &ruleKey ) const
1693{
1694 for ( auto it = mLegend.constBegin(); it != mLegend.constEnd(); ++it )
1695 {
1696 QgsLayerTreeLayer *layer = it.key();
1697 if ( layer->layerId() == layerId )
1698 {
1699 const auto activeNodes = mLegend.value( layer ).activeNodes;
1700 for ( QgsLayerTreeModelLegendNode *legendNode : activeNodes )
1701 {
1702 if ( legendNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString() == ruleKey )
1703 {
1704 //found it!
1705 return legendNode;
1706 }
1707 }
1708 }
1709 }
1710
1711 return nullptr;
1712}
1713
1721
1723{
1724 // we have varying icon sizes, and we want icon to be centered and
1725 // text to be left aligned, so we have to compute the max width of icons
1726 //
1727 // we do that for nodes which share a common parent
1728 //
1729 // we do that here because for symbols with size defined in map units
1730 // the symbol sizes changes depends on the zoom level
1731
1732 std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
1733
1734 for ( QgsLayerTreeLayer *layerNode : std::as_const( mInvalidatedNodes ) )
1735 {
1736 const LayerLegendData &data = mLegend.value( layerNode );
1737
1738 QList<QgsSymbolLegendNode *> symbolNodes;
1739 QMap<QString, int> widthMax;
1740 for ( QgsLayerTreeModelLegendNode *legendNode : std::as_const( data.originalNodes ) )
1741 {
1742 QgsSymbolLegendNode *n = qobject_cast<QgsSymbolLegendNode *>( legendNode );
1743 if ( n )
1744 {
1745 const QSize sz( n->minimumIconSize( context.get() ) );
1746 const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() );
1747 widthMax[parentKey] = std::max( sz.width(), widthMax.contains( parentKey ) ? widthMax[parentKey] : 0 );
1748 n->setIconSize( sz );
1749 symbolNodes.append( n );
1750 }
1751 }
1752 for ( QgsSymbolLegendNode *n : std::as_const( symbolNodes ) )
1753 {
1754 const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() );
1755 Q_ASSERT( widthMax[parentKey] > 0 );
1756 const int twiceMarginWidth = 2; // a one pixel margin avoids hugly rendering of icon
1757 n->setIconSize( QSize( widthMax[parentKey] + twiceMarginWidth, n->iconSize().rheight() + twiceMarginWidth ) );
1758 }
1759 for ( QgsLayerTreeModelLegendNode *legendNode : std::as_const( data.originalNodes ) )
1760 legendNode->invalidateMapBasedData();
1761 }
1762
1763 mInvalidatedNodes.clear();
1764}
1765
1766// Legend nodes routines - end
@ UsersCannotToggleEditing
Indicates that users are not allowed to toggle editing for this layer. Note that this does not imply ...
@ SkipVisibilityCheck
If set, the standard visibility check should be skipped.
@ Polygon
Polygons.
@ Vector
Vector layer.
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
static int scaleIconSize(int standardSize, bool applyDevicePixelRatio=false)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
static QPixmap getThemePixmap(const QString &name, const QColor &foreColor=QColor(), const QColor &backColor=QColor(), int size=16)
Helper to get a theme icon as a pixmap.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsTaskManager * taskManager()
Returns the application's task manager, used for managing application wide background task handling.
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
Class for storing the component parts of a RDBMS data source URI (e.g.
bool useEstimatedMetadata() const
Returns true if estimated metadata should be used for the connection.
A geometry is the spatial representation of a feature.
Qgis::GeometryType type
static QIcon iconForLayer(const QgsMapLayer *layer)
Returns the icon corresponding to a specified map layer.
static bool loadLayerDefinition(const QString &path, QgsProject *project, QgsLayerTreeGroup *rootGroup, QString &errorMessage)
Loads the QLR at path into QGIS. New layers are added to given project into layer tree specified by r...
static bool exportLayerDefinition(const QString &path, const QList< QgsLayerTreeNode * > &selectedTreeNodes, QString &errorMessage)
Exports the selected layer tree nodes to a QLR file.
Contains settings relating to filtering the contents of QgsLayerTreeModel and views.
void setFilterPolygon(const QgsGeometry &polygon)
Sets the optional filter polygon, used when testing for symbols to show in the legend.
void setLayerFilterExpressionsFromLayerTree(QgsLayerTree *tree)
Sets layer filter expressions using a layer tree.
void setFlags(Qgis::LayerTreeFilterFlags flags)
Sets the filter flags.
Layer tree group node serves as a container for layers and further groups.
void setName(const QString &n) override
Sets the group's name.
QString name() const override
Returns the group's name.
void insertChildNodes(int index, const QList< QgsLayerTreeNode * > &nodes)
Insert existing nodes at specified position.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
void removeChildren(int from, int count)
Remove child nodes from index "from".
Layer tree node points to a map layer.
QString layerId() const
Returns the ID for the map layer associated with this node.
void layerWillBeUnloaded()
Emitted when a previously available layer got unloaded (from layer registry).
void setName(const QString &n) override
Sets the layer's name.
QString name() const override
Returns the layer's name.
void layerLoaded()
Emitted when a previously unavailable layer got loaded.
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.
@ SimpleLegend
Simple label with icon legend node type.
@ RasterSymbolLegend
Raster symbol legend node type.
@ ImageLegend
Raster image legend node type.
@ DataDefinedSizeLegend
Marker symbol legend node type.
@ EmbeddedWidget
Embedded widget placeholder node type.
@ ColorRampLegend
Color ramp legend (since QGIS 3.18)
@ SymbolLegend
Vector symbol legend node type.
virtual void invalidateMapBasedData()
Notification from model that information from associated map view has changed.
@ ParentRuleKeyRole
Rule key of the parent legend node - for legends with tree hierarchy (QString). Added in 2....
@ RuleKeyRole
Rule key of the node (QString)
@ NodeTypeRole
Type of node. Added in 3.16.
void sizeChanged()
Emitted when the size of this node changes.
void dataChanged()
Emitted on internal data change so the layer tree model can forward the signal to views.
virtual Qt::ItemFlags flags() const
Returns item flags associated with the item. Default implementation returns Qt::ItemIsEnabled.
QgsLayerTreeLayer * layerNode() const
Returns pointer to the parent layer node.
virtual bool setData(const QVariant &value, int role)
Sets some data associated with the item. Default implementation does nothing and returns false.
void connectToLayer(QgsLayerTreeLayer *nodeLayer)
int columnCount(const QModelIndex &parent=QModelIndex()) const override
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...
Flags flags() const
Returns OR-ed combination of model flags.
double mLegendFilterByScale
scale denominator for filtering of legend nodes (<= 0 means no filtering)
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...
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
int autoCollapseLegendNodes() const
Returns at what number of legend nodes the layer node should be collapsed. -1 means no auto-collapse ...
void setFilterSettings(const QgsLayerTreeFilterSettings *settings=nullptr)
Sets the filter settings to use to filter legend nodes.
QVariant legendNodeData(QgsLayerTreeModelLegendNode *node, int role) const
void hitTestStarted()
Emitted when a hit test for visible legend items starts.
void setRootGroup(QgsLayerTree *newRootGroup)
Reset the model and use a new root group node.
Q_DECL_DEPRECATED void setLegendFilterByMap(const QgsMapSettings *settings)
Force only display of legend nodes which are valid for given map settings.
QModelIndex legendNode2index(QgsLayerTreeModelLegendNode *legendNode)
Returns index for a given legend node.
void setLegendFilterByScale(double scale)
Force only display of legend nodes which are valid for a given scale.
void hitTestCompleted()
Emitted when a hit test for visible legend items completes.
std::unique_ptr< QgsLayerTreeFilterSettings > mFilterSettings
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
void setLegendMapViewData(double mapUnitsPerPixel, int dpi, double scale)
Give the layer tree model hints about the currently associated map view so that legend nodes that use...
void nodeCustomPropertyChanged(QgsLayerTreeNode *node, const QString &key)
QgsLayerTreeModel(QgsLayerTree *rootNode, QObject *parent=nullptr)
Construct a new tree model with given layer tree (root node must not be nullptr).
void connectToLayers(QgsLayerTreeGroup *parentGroup)
QMap< QString, QSet< QString > > mHitTestResults
QModelIndex parent(const QModelIndex &child) const override
void setFlag(Flag f, bool on=true)
Enable or disable a model flag.
QModelIndex legendParent(QgsLayerTreeModelLegendNode *legendNode) const
void setLayerTreeNodeFont(int nodeType, const QFont &font)
Sets font for a particular type of layer tree node. nodeType should come from QgsLayerTreeNode::NodeT...
int legendNodeRowCount(QgsLayerTreeModelLegendNode *node) const
void nodeAddedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
void setAutoCollapseLegendNodes(int nodeCount)
Sets at what number of legend nodes the layer node should be collapsed. Setting -1 disables the auto-...
void legendMapViewData(double *mapUnitsPerPixel, int *dpi, double *scale) const
Gets hints about map view - to be used in legend nodes.
QModelIndex currentIndex() const
Gets index of the item marked as current. Item marked as current is underlined.
bool hitTestInProgress() const
Returns true if a hit test for visible legend items is currently in progress.
void recursivelyEmitDataChanged(const QModelIndex &index=QModelIndex())
emit dataChanged() for layer tree node items
bool legendEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
QHash< QgsLayerTreeLayer *, LayerLegendData > mLegend
Per layer data about layer's legend nodes.
void disconnectFromLayer(QgsLayerTreeLayer *nodeLayer)
void setCurrentIndex(const QModelIndex &currentIndex)
Sets index of the current item. May be used by view. Item marked as current is underlined.
QIcon legendIconEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
void layerFlagsChanged()
Emitted when layer flags have changed.
void nodeNameChanged(QgsLayerTreeNode *node, const QString &name)
Updates model when node's name has changed.
QPersistentModelIndex mCurrentIndex
Current index - will be underlined.
LayerLegendTree * tryBuildLegendTree(const QList< QgsLayerTreeModelLegendNode * > &nodes)
Qt::DropActions supportedDropActions() const override
QModelIndex legendNodeIndex(int row, int column, QgsLayerTreeModelLegendNode *node) const
QgsRenderContext * createTemporaryRenderContext() const
Returns a temporary render context.
void setFlags(QgsLayerTreeModel::Flags f)
Sets OR-ed combination of model flags.
QgsLayerTree * rootGroup() const
Returns pointer to the root node of the layer tree. Always a non nullptr value.
void nodeWillRemoveChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Returns layer tree node for given index.
QgsLayerTree * mRootNode
Pointer to the root node of the layer tree. Not owned by the model.
QSet< QgsScreenProperties > targetScreenProperties() const
Returns the target screen properties to use when generating icons.
QStringList mimeTypes() const override
QMimeData * mimeData(const QModelIndexList &indexes) const override
QSet< QgsScreenProperties > mTargetScreenProperties
int rowCount(const QModelIndex &parent=QModelIndex()) const override
QPointer< QgsMapHitTestTask > mHitTestTask
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
static int scaleIconSize(int standardSize)
Scales an layer tree model icon size to compensate for display pixel density, making the icon size hi...
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.
QgsLayerTreeModelLegendNode * legendNodeEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
Returns legend node that may be embedded in parent (i.e.
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
QList< QgsLayerTreeModelLegendNode * > filterLegendNodes(const QList< QgsLayerTreeModelLegendNode * > &nodes)
Filter nodes from QgsMapLayerLegend according to the current filtering rules.
const QgsLayerTreeFilterSettings * filterSettings() const
Returns the filter settings to use to filter legend nodes.
QList< QgsLayerTreeModelLegendNode * > layerOriginalLegendNodes(QgsLayerTreeLayer *nodeLayer)
Returns original (unfiltered) list of legend nodes attached to a particular layer node.
int mAutoCollapseLegendNodesCount
Minimal number of nodes when legend should be automatically collapsed. -1 = disabled.
QMap< QString, QString > layerStyleOverrides() const
Gets map of map layer style overrides (key: layer ID, value: style name) where a different style shou...
Qt::ItemFlags legendNodeFlags(QgsLayerTreeModelLegendNode *node) const
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Sets map of map layer style overrides (key: layer ID, value: style name) where a different style shou...
void nodeVisibilityChanged(QgsLayerTreeNode *node)
void refreshScaleBasedLayers(const QModelIndex &index=QModelIndex(), double previousScale=0.0)
Updates layer data for scale dependent layers, should be called when map scale changes.
void removeLegendFromLayer(QgsLayerTreeLayer *nodeLayer)
QModelIndex legendRootIndex(int row, int column, QgsLayerTreeLayer *nL) const
static QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index)
Returns legend node for given index.
QMap< QString, QString > mLayerStyleOverrides
Overrides of map layers' styles: key = layer ID, value = style XML.
void nodeWillAddChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
const QgsMapSettings * legendFilterMapSettings() const
Returns the current map settings used for the current legend filter (or nullptr if none is enabled)
QFont layerTreeNodeFont(int nodeType) const
Gets font for a particular type of layer tree node. nodeType should come from QgsLayerTreeNode::NodeT...
void refreshLayerLegend(QgsLayerTreeLayer *nodeLayer)
Force a refresh of legend nodes of a layer node.
void waitForHitTestBlocking()
When a current hit test for visible legend items is in progress, calling this method will block until...
void addLegendToLayer(QgsLayerTreeLayer *nodeL)
bool testFlag(Flag f) const
Check whether a flag is enabled.
void disconnectFromLayers(QgsLayerTreeGroup *parentGroup)
QModelIndex indexOfParentLayerTreeNode(QgsLayerTreeNode *parentNode) const
QSet< QgsLayerTreeLayer * > mInvalidatedNodes
Keep track of layer nodes for which the legend size needs to be recalculated.
@ ActionHierarchical
Check/uncheck action has consequences on children (or parents for leaf node)
@ AllowNodeChangeVisibility
Allow user to set node visibility with a checkbox.
@ ShowLegendAsTree
For legends that support it, will show them in a tree instead of a list (needs also ShowLegend)....
@ UseTextFormatting
Layer nodes will alter text appearance based on layer properties, such as scale based visibility.
@ AllowNodeReorder
Allow reordering with drag'n'drop.
@ ShowLegend
Add legend nodes for layer nodes.
@ DeferredLegendInvalidation
Defer legend model invalidation.
@ AllowNodeRename
Allow renaming of groups and layers.
@ AllowLegendChangeState
Allow check boxes for legend nodes (if supported by layer's legend)
@ UseEmbeddedWidgets
Layer nodes may optionally include extra embedded widgets (if used in QgsLayerTreeView)....
@ UseThreadedHitTest
Run legend hit tests in a background thread (since QGIS 3.30)
void addTargetScreenProperties(const QgsScreenProperties &properties)
Adds additional target screen properties to use when generating icons for Qt::DecorationRole data.
Flags mFlags
Sets of flags for the model.
Q_DECL_DEPRECATED void setLegendFilter(const QgsMapSettings *settings, bool useExtent=true, const QgsGeometry &polygon=QgsGeometry(), bool useExpressions=true)
Filter display of legend nodes for given map settings.
int legendRootRowCount(QgsLayerTreeLayer *nL) const
QgsLayerTreeModelLegendNode * findLegendNode(const QString &layerId, const QString &ruleKey) const
Searches through the layer tree to find a legend node with a matching layer ID and rule key.
This class is a base class for nodes in a layer tree.
@ NodeGroup
Container of other groups and layers.
@ NodeLayer
Leaf node pointing to a layer.
void removedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes has been removed from a node within the tree.
void nameChanged(QgsLayerTreeNode *node, QString name)
Emitted when the name of the node is changed.
bool isVisible() const
Returns whether a node is really visible (ie checked and all its ancestors checked as well)
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.
virtual void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context)=0
Write layer tree to XML.
static QgsLayerTreeNode * readXml(QDomElement &element, const QgsReadWriteContext &context)
Read layer tree from XML.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
void willRemoveChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes will be removed from a node within the tree.
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.
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.
void addedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes have been added to a node within the tree.
void willAddChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
Emitted when one or more nodes will be added to a node within the tree.
void visibilityChanged(QgsLayerTreeNode *node)
Emitted when check state of a node within the tree has been changed.
virtual void setItemVisibilityCheckedRecursive(bool checked)
Check or uncheck a node and all its children (taking into account exclusion rules)
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
bool itemVisibilityChecked() const
Returns whether a node is checked (independently of its ancestors or children)
void setItemVisibilityCheckedParentRecursive(bool checked)
Check or uncheck a node and all its parents.
Namespace with helper functions for layer tree operations.
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.
Executes a QgsMapHitTest in a background thread.
static void applyLayerNodeProperties(QgsLayerTreeLayer *nodeLayer, QList< QgsLayerTreeModelLegendNode * > &nodes)
update according to layer node's custom properties (order of items, user labels for items)
The QgsMapLayerLegend class is abstract interface for implementations of legends for one map layer.
virtual QList< QgsLayerTreeModelLegendNode * > createLayerTreeModelLegendNodes(QgsLayerTreeLayer *nodeLayer)=0
Returns list of legend nodes to be used for a particular layer tree layer node.
Restore overridden layer style on destruction.
void setOverrideStyle(const QString &style)
Temporarily apply a different style to the layer.
Base class for all map layer types.
Definition qgsmaplayer.h:74
virtual bool isSpatial() const
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
void legendChanged()
Signal emitted when legend of the layer has changed.
QgsMapLayerLegend * legend() const
Can be nullptr.
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
Q_INVOKABLE QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
void editingStarted()
Emitted when editing on this layer has started.
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
Qgis::LayerType type
Definition qgsmaplayer.h:81
virtual Qgis::MapLayerProperties properties() const
Returns the map layer properties of this layer.
virtual bool isEditable() const
Returns true if the layer can be edited.
QString title() const
Returns the title of the layer used by QGIS Server in GetCapabilities request.
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
virtual bool isModified() const
Returns true if the layer has been modified since last commit/save.
void flagsChanged()
Emitted when layer's flags have been modified.
void layerModified()
Emitted when modifications has been done on layer.
The QgsMapSettings class contains configuration for rendering of the map.
bool hasValidSettings() const
Check whether the map settings are valid and can be used for rendering.
Perform transforms between map coordinates and device coordinates.
static QByteArray layerTreeNodesToUriList(const QList< QgsLayerTreeNode * > &nodes)
Returns encoded URI list from a list of layer tree nodes.
static QgsProject * instance()
Returns the QgsProject singleton instance.
The class is used as a container of context for various read/write operations on other objects.
Contains information about the context of a rendering operation.
Stores properties relating to a screen.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
static const QgsSettingsEntryBool * settingsLayerTreeShowFeatureCountForNewLayers
Settings entry show feature counts for newly added layers by default.
Implementation of legend node interface for displaying preview of vector symbols and their labels and...
void setIconSize(QSize sz)
Set the icon size.
QSize minimumIconSize() const
Calculates the minimum icon size to prevent cropping.
QVariant data(int role) const override
Returns data associated with the item. Must be implemented in derived class.
long addTask(QgsTask *task, int priority=0)
Adds a task to the manager.
void taskCompleted()
Will be emitted by task to indicate its successful completion.
Represents a vector layer which manages a vector based data sets.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
static QString displayString(Qgis::WkbType type)
Returns a non-translated display string type for a WKB type, e.g., the geometry name used in WKT geom...
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:4916
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:4915
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:4332
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
Structure that stores all data associated with one map layer.
LayerLegendTree * tree
Optional pointer to a tree structure - see LayerLegendTree for details.
QList< QgsLayerTreeModelLegendNode * > originalNodes
Data structure for storage of legend nodes.
Structure that stores tree representation of map layer's legend.
QMap< QgsLayerTreeModelLegendNode *, QgsLayerTreeModelLegendNode * > parents
Pointer to parent for each active node. Top-level nodes have nullptr parent. Pointers are not owned.
QMap< QgsLayerTreeModelLegendNode *, QList< QgsLayerTreeModelLegendNode * > > children
List of children for each active node. Top-level nodes are under nullptr key. Pointers are not owned.