QGIS API Documentation  2.99.0-Master (c558d51)
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"
23 #include "qgsmaplayer.h"
24 
25 #include <QMenu>
26 #include <QContextMenuEvent>
27 
28 
30  : QTreeView( parent )
31  , mDefaultActions( nullptr )
32  , mMenuProvider( nullptr )
33 {
34  setHeaderHidden( true );
35 
36  setDragEnabled( true );
37  setAcceptDrops( true );
38  setDropIndicatorShown( true );
39  setEditTriggers( EditKeyPressed );
40  setExpandsOnDoubleClick( false ); // normally used for other actions
41 
42  setSelectionMode( ExtendedSelection );
43  setDefaultDropAction( Qt::MoveAction );
44 
45  connect( this, SIGNAL( collapsed( QModelIndex ) ), this, SLOT( updateExpandedStateToNode( QModelIndex ) ) );
46  connect( this, SIGNAL( expanded( QModelIndex ) ), this, SLOT( updateExpandedStateToNode( QModelIndex ) ) );
47 }
48 
50 {
51  delete mMenuProvider;
52 }
53 
54 void QgsLayerTreeView::setModel( QAbstractItemModel* model )
55 {
56  if ( !qobject_cast<QgsLayerTreeModel*>( model ) )
57  return;
58 
59  connect( model, SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( modelRowsInserted( QModelIndex, int, int ) ) );
60  connect( model, SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( modelRowsRemoved() ) );
61 
62  QTreeView::setModel( model );
63 
64  connect( layerTreeModel()->rootGroup(), SIGNAL( expandedChanged( QgsLayerTreeNode*, bool ) ), this, SLOT( onExpandedChanged( QgsLayerTreeNode*, bool ) ) );
65 
66  connect( selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ), this, SLOT( onCurrentChanged() ) );
67 
68  connect( layerTreeModel(), SIGNAL( modelReset() ), this, SLOT( onModelReset() ) );
69 
71 }
72 
74 {
75  return qobject_cast<QgsLayerTreeModel*>( model() );
76 }
77 
79 {
80  if ( !mDefaultActions )
82  return mDefaultActions;
83 }
84 
86 {
87  delete mMenuProvider;
89 }
90 
92 {
93  return layerForIndex( currentIndex() );
94 }
95 
97 {
98  if ( !layer )
99  {
100  setCurrentIndex( QModelIndex() );
101  return;
102  }
103 
104  QgsLayerTreeLayer* nodeLayer = layerTreeModel()->rootGroup()->findLayer( layer->id() );
105  if ( !nodeLayer )
106  return;
107 
108  setCurrentIndex( layerTreeModel()->node2index( nodeLayer ) );
109 }
110 
111 
112 void QgsLayerTreeView::contextMenuEvent( QContextMenuEvent *event )
113 {
114  if ( !mMenuProvider )
115  return;
116 
117  QModelIndex idx = indexAt( event->pos() );
118  if ( !idx.isValid() )
119  setCurrentIndex( QModelIndex() );
120 
121  QMenu* menu = mMenuProvider->createContextMenu();
122  if ( menu && menu->actions().count() != 0 )
123  menu->exec( mapToGlobal( event->pos() ) );
124  delete menu;
125 }
126 
127 
128 void QgsLayerTreeView::modelRowsInserted( const QModelIndex& index, int start, int end )
129 {
130  QgsLayerTreeNode* parentNode = layerTreeModel()->index2node( index );
131  if ( !parentNode )
132  return;
133 
134  // Embedded widgets - replace placeholders in the model by actual widgets
135  if ( layerTreeModel()->testFlag( QgsLayerTreeModel::UseEmbeddedWidgets ) && QgsLayerTree::isLayer( parentNode ) )
136  {
137  QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( parentNode );
138  if ( QgsMapLayer* layer = nodeLayer->layer() )
139  {
140  int widgetsCount = layer->customProperty( QStringLiteral( "embeddedWidgets/count" ), 0 ).toInt();
141  QList<QgsLayerTreeModelLegendNode*> legendNodes = layerTreeModel()->layerLegendNodes( nodeLayer, true );
142  for ( int i = 0; i < widgetsCount; ++i )
143  {
144  QString providerId = layer->customProperty( QStringLiteral( "embeddedWidgets/%1/id" ).arg( i ) ).toString();
145  if ( QgsLayerTreeEmbeddedWidgetProvider* provider = QgsLayerTreeEmbeddedWidgetRegistry::instance()->provider( providerId ) )
146  {
147  QModelIndex index = layerTreeModel()->legendNode2index( legendNodes[i] );
148  setIndexWidget( index, provider->createWidget( layer, i ) );
149  }
150  }
151  }
152  }
153 
154 
155  if ( QgsLayerTree::isLayer( parentNode ) )
156  {
157  // if ShowLegendAsTree flag is enabled in model, we may need to expand some legend nodes
158  QStringList expandedNodeKeys = parentNode->customProperty( QStringLiteral( "expandedLegendNodes" ) ).toStringList();
159  if ( expandedNodeKeys.isEmpty() )
160  return;
161 
162  Q_FOREACH ( QgsLayerTreeModelLegendNode* legendNode, layerTreeModel()->layerLegendNodes( QgsLayerTree::toLayer( parentNode ), true ) )
163  {
164  QString ruleKey = legendNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
165  if ( expandedNodeKeys.contains( ruleKey ) )
166  setExpanded( layerTreeModel()->legendNode2index( legendNode ), true );
167  }
168  return;
169  }
170 
171  QList<QgsLayerTreeNode*> children = parentNode->children();
172  for ( int i = start; i <= end; ++i )
173  {
174  updateExpandedStateFromNode( children[i] );
175  }
176 
177  // make sure we still have correct current layer
179 }
180 
182 {
183  // make sure we still have correct current layer
185 }
186 
188 {
189  if ( QgsLayerTreeNode* node = layerTreeModel()->index2node( index ) )
190  {
191  node->setExpanded( isExpanded( index ) );
192  }
193  else if ( QgsLayerTreeModelLegendNode* node = layerTreeModel()->index2legendNode( index ) )
194  {
195  QString ruleKey = node->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
196  QStringList lst = node->layerNode()->customProperty( QStringLiteral( "expandedLegendNodes" ) ).toStringList();
197  bool expanded = isExpanded( index );
198  bool isInList = lst.contains( ruleKey );
199  if ( expanded && !isInList )
200  {
201  lst.append( ruleKey );
202  node->layerNode()->setCustomProperty( QStringLiteral( "expandedLegendNodes" ), lst );
203  }
204  else if ( !expanded && isInList )
205  {
206  lst.removeAll( ruleKey );
207  node->layerNode()->setCustomProperty( QStringLiteral( "expandedLegendNodes" ), lst );
208  }
209  }
210 }
211 
213 {
214  QgsMapLayer* layerCurrent = layerForIndex( currentIndex() );
215  QString layerCurrentID = layerCurrent ? layerCurrent->id() : QString();
216  if ( mCurrentLayerID == layerCurrentID )
217  return;
218 
219  // update the current index in model (the item will be underlined)
220  QModelIndex nodeLayerIndex;
221  if ( layerCurrent )
222  {
223  QgsLayerTreeLayer* nodeLayer = layerTreeModel()->rootGroup()->findLayer( layerCurrentID );
224  if ( nodeLayer )
225  nodeLayerIndex = layerTreeModel()->node2index( nodeLayer );
226  }
227  layerTreeModel()->setCurrentIndex( nodeLayerIndex );
228 
229  mCurrentLayerID = layerCurrentID;
230  emit currentLayerChanged( layerCurrent );
231 }
232 
234 {
235  QModelIndex idx = layerTreeModel()->node2index( node );
236  if ( isExpanded( idx ) != expanded )
237  setExpanded( idx, expanded );
238 }
239 
241 {
243 }
244 
246 {
247  QModelIndex idx = layerTreeModel()->node2index( node );
248  setExpanded( idx, node->isExpanded() );
249 
250  Q_FOREACH ( QgsLayerTreeNode* child, node->children() )
252 }
253 
255 {
256  QgsLayerTreeNode* node = layerTreeModel()->index2node( index );
257  if ( node )
258  {
259  if ( QgsLayerTree::isLayer( node ) )
260  return QgsLayerTree::toLayer( node )->layer();
261  }
262  else
263  {
264  // possibly a legend node
266  if ( legendNode )
267  return legendNode->layerNode()->layer();
268  }
269 
270  return nullptr;
271 }
272 
274 {
275  return layerTreeModel()->index2node( selectionModel()->currentIndex() );
276 }
277 
279 {
280  QgsLayerTreeNode* node = currentNode();
281  if ( QgsLayerTree::isGroup( node ) )
282  return QgsLayerTree::toGroup( node );
283  else if ( QgsLayerTree::isLayer( node ) )
284  {
285  QgsLayerTreeNode* parent = node->parent();
286  if ( QgsLayerTree::isGroup( parent ) )
287  return QgsLayerTree::toGroup( parent );
288  }
289 
290  if ( QgsLayerTreeModelLegendNode* legendNode = layerTreeModel()->index2legendNode( selectionModel()->currentIndex() ) )
291  {
292  QgsLayerTreeLayer* parent = legendNode->layerNode();
293  if ( QgsLayerTree::isGroup( parent->parent() ) )
294  return QgsLayerTree::toGroup( parent->parent() );
295  }
296 
297  return nullptr;
298 }
299 
301 {
302  return layerTreeModel()->index2legendNode( selectionModel()->currentIndex() );
303 }
304 
305 QList<QgsLayerTreeNode*> QgsLayerTreeView::selectedNodes( bool skipInternal ) const
306 {
307  return layerTreeModel()->indexes2nodes( selectionModel()->selectedIndexes(), skipInternal );
308 }
309 
310 QList<QgsLayerTreeLayer*> QgsLayerTreeView::selectedLayerNodes() const
311 {
312  QList<QgsLayerTreeLayer*> layerNodes;
313  Q_FOREACH ( QgsLayerTreeNode* node, selectedNodes() )
314  {
315  if ( QgsLayerTree::isLayer( node ) )
316  layerNodes << QgsLayerTree::toLayer( node );
317  }
318  return layerNodes;
319 }
320 
321 QList<QgsMapLayer*> QgsLayerTreeView::selectedLayers() const
322 {
323  QList<QgsMapLayer*> list;
324  Q_FOREACH ( QgsLayerTreeLayer* node, selectedLayerNodes() )
325  {
326  if ( node->layer() )
327  list << node->layer();
328  }
329  return list;
330 }
331 
332 
333 void QgsLayerTreeView::refreshLayerSymbology( const QString& layerId )
334 {
335  QgsLayerTreeLayer* nodeLayer = layerTreeModel()->rootGroup()->findLayer( layerId );
336  if ( nodeLayer )
337  layerTreeModel()->refreshLayerLegend( nodeLayer );
338 }
339 
340 
341 static void _expandAllLegendNodes( QgsLayerTreeLayer* nodeLayer, bool expanded, QgsLayerTreeModel* model )
342 {
343  // for layers we also need to find out with legend nodes contain some children and make them expanded/collapsed
344  // if we are collapsing, we just write out an empty list
345  QStringList lst;
346  if ( expanded )
347  {
348  Q_FOREACH ( QgsLayerTreeModelLegendNode* legendNode, model->layerLegendNodes( nodeLayer, true ) )
349  {
350  QString parentKey = legendNode->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString();
351  if ( !parentKey.isEmpty() && !lst.contains( parentKey ) )
352  lst << parentKey;
353  }
354  }
355  nodeLayer->setCustomProperty( "expandedLegendNodes", lst );
356 }
357 
358 
359 static void _expandAllNodes( QgsLayerTreeGroup* parent, bool expanded, QgsLayerTreeModel* model )
360 {
361  Q_FOREACH ( QgsLayerTreeNode* node, parent->children() )
362  {
363  node->setExpanded( expanded );
364  if ( QgsLayerTree::isGroup( node ) )
365  _expandAllNodes( QgsLayerTree::toGroup( node ), expanded, model );
366  else if ( QgsLayerTree::isLayer( node ) )
367  _expandAllLegendNodes( QgsLayerTree::toLayer( node ), expanded, model );
368  }
369 }
370 
371 
373 {
374  // unfortunately expandAll() does not emit expanded() signals
375  _expandAllNodes( layerTreeModel()->rootGroup(), true, layerTreeModel() );
376  expandAll();
377 }
378 
380 {
381  // unfortunately collapseAll() does not emit collapsed() signals
382  _expandAllNodes( layerTreeModel()->rootGroup(), false, layerTreeModel() );
383  collapseAll();
384 }
Layer tree group node serves as a container for layers and further groups.
QgsLayerTreeModelLegendNode * currentLegendNode() const
Get current legend node.
QgsLayerTreeModel * layerTreeModel() const
Get access to the model casted to QgsLayerTreeModel.
static unsigned index
Base class for all map layer types.
Definition: qgsmaplayer.h:49
Implementation of this interface can be implemented to allow QgsLayerTreeView instance to provide cus...
QgsMapLayer * currentLayer() const
Get currently selected layer. May be null.
void setCurrentIndex(const QModelIndex &currentIndex)
Set index of the current item. May be used by view. Item marked as current is underlined.
QgsLayerTreeViewMenuProvider * mMenuProvider
Context menu provider. Owned by the view.
QList< QgsLayerTreeNode * > indexes2nodes(const QModelIndexList &list, bool skipInternal=false) const
Convert a list of indexes to a list of layer tree nodes.
QgsLayerTreeGroup * rootGroup() const
Return pointer to the root node of the layer tree. Always a non-null pointer.
static void _expandAllLegendNodes(QgsLayerTreeLayer *nodeLayer, bool expanded, QgsLayerTreeModel *model)
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 refreshLayerSymbology(const QString &layerId)
Force refresh of layer symbology. Normally not needed as the changes of layer&#39;s renderer are monitore...
QgsMapLayer * layer() const
void contextMenuEvent(QContextMenuEvent *event) override
QgsLayerTreeViewMenuProvider * menuProvider() const
Return pointer to the context menu provider. May be null.
QgsLayerTreeNode * currentNode() const
Get current node. May be null.
QList< QgsMapLayer * > selectedLayers() const
Get list of selected layers.
void modelRowsInserted(const QModelIndex &index, int start, int end)
QgsLayerTreeViewDefaultActions * defaultActions()
Get access to the default actions that may be used with the tree view.
QList< QgsLayerTreeNode * > selectedNodes(bool skipInternal=false) const
Return list of selected nodes.
virtual QMenu * createContextMenu()=0
Return a newly created menu instance (or null pointer on error)
QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group. No type checking is done - use isGroup() to find out whether this operation is ...
Definition: qgslayertree.h:46
QString mCurrentLayerID
Keeps track of current layer ID (to check when to emit signal about change of current layer) ...
The QgsLayerTreeViewDefaultActions class serves as a factory of actions that can be used together wit...
The QgsLayerTreeModel class is model implementation for Qt item views framework.
Rule key of the parent legend node - for legends with tree hierarchy (QString). Added in 2...
void updateExpandedStateToNode(const QModelIndex &index)
QModelIndex legendNode2index(QgsLayerTreeModelLegendNode *legendNode)
Return index for a given legend node.
Provider interface to be implemented in order to introduce new kinds of embedded widgets for use in l...
QgsLayerTreeGroup * currentGroupNode() const
Get current group node. If a layer is current node, the function will return parent group...
static QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index)
Return legend node for given index.
QgsLayerTreeNode * parent()
Get pointer to the parent. If parent is a null pointer, the node is a root node.
QgsLayerTreeLayer * layerNode() const
Return pointer to the parent layer node.
This class is a base class for nodes in a layer tree.
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsMapLayerRegistry.
void collapseAllNodes()
Enhancement of QTreeView::collapseAll() that also records expanded state in layer tree nodes...
bool isExpanded() const
Return whether the node should be shown as expanded or collapsed in GUI.
QList< QgsLayerTreeNode * > children()
Get list of children of the node. Children are owned by the parent.
bool isLayer(QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:40
void currentLayerChanged(QgsMapLayer *layer)
Emitted when a current layer is changed.
void refreshLayerLegend(QgsLayerTreeLayer *nodeLayer)
Force a refresh of legend nodes of a layer node.
QgsLayerTreeViewDefaultActions * mDefaultActions
helper class with default actions. Lazily initialized.
QList< QgsLayerTreeLayer * > selectedLayerNodes() const
Return list of selected nodes filtered to just layer nodes.
void setExpanded(bool expanded)
Set whether the node should be shown as expanded or collapsed in GUI.
QModelIndex node2index(QgsLayerTreeNode *node) const
Return index for a given node. If the node does not belong to the layer tree, the result is undefined...
void updateExpandedStateFromNode(QgsLayerTreeNode *node)
void onExpandedChanged(QgsLayerTreeNode *node, bool expanded)
QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer. No type checking is done - use isLayer() to find out whether this operation is ...
Definition: qgslayertree.h:52
Layer nodes may optionally include extra embedded widgets (if used in QgsLayerTreeView). Added in 2.16.
virtual QVariant data(int role) const =0
Return data associated with the item. Must be implemented in derived class.
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
static QgsLayerTreeEmbeddedWidgetRegistry * instance()
Means of accessing canonical single instance.
virtual void setModel(QAbstractItemModel *model) override
Overridden setModel() from base class. Only QgsLayerTreeModel is an acceptable model.
QgsLayerTreeLayer * findLayer(const QString &layerId) const
Find layer node representing the map layer specified by its ID. Searches recursively the whole sub-tr...
QList< QgsLayerTreeModelLegendNode * > layerLegendNodes(QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent=false)
Return filtered list of active legend nodes attached to a particular layer node (by default it return...
void setCurrentLayer(QgsMapLayer *layer)
Set currently selected layer. Null pointer will deselect any layer.
QgsLayerTreeView(QWidget *parent=nullptr)
void setMenuProvider(QgsLayerTreeViewMenuProvider *menuProvider)
Set provider for context menu. Takes ownership of the instance.
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Return layer tree node for given index.
void expandAllNodes()
Enhancement of QTreeView::expandAll() that also records expanded state in layer tree nodes...
bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:34
QgsMapLayer * layerForIndex(const QModelIndex &index) const
void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for the node. Properties are stored in a map and saved in project file...
Layer tree node points to a map layer.
static void _expandAllNodes(QgsLayerTreeGroup *parent, bool expanded, QgsLayerTreeModel *model)