6

My QGIS 2.8.2 standalone app provides several decorating layers (e.g. cartographic grid, background layers, reference points) which should be not changed by the user (neither editing nor hiding), as well as layers, the user can modify and query.

The app has a legend widget which shows all layers, since all layers are registered with map registry. The widget is populated with the layer tree as described in How to add a legend to a canvas in a standalone PyQGIS application?.

Instead of exposing all layers in the tree view, I want to restrict the tree view to the layer group, whose children represent the user mutable layers. Thus the user can change visibility and ordering of only these layers, and must keep the decoration as it is.

What I tried so far is to initiate QgsLayerTreeMapCanvasBridge with this group node (instead of layerTreeRoot), pass the group node to QgsLayerTreeModel(), or changed the root group of the model with QgsLayerTreeModel.setRootGroup(). In each case Python crashes, with an error message like this:

QObject::connect: Cannot connect (null)::willAddChildren( QgsLayerTreeNode*, int, int ) to QgsLayerTreeModel::nodeWillAddChildren( QgsLayerTreeNode*, int, int )

Is it possible to expose only some group oder layer nodes in the treeview, and how can I implement this?

6

In the meanwhile I found a solution, maybe a workaround for my app to show a legend with only parts of the layers drawn on the map canvas. For the interested audience I want to give a summary.

Adding maplayers to mapLayerRegistry, automatically builds a maplayerTree, when parameter addToLegend remains True. I set it to False, to build a layer tree with layer and group nodes in a separate step.

QgsMapLayerRegistry.instance().addMapLayer(layer, False)
...
self.layer_tree_root = QgsProject.instance().layerTreeRoot()
group = self.layer_tree_root.addGroup(group_name)
group.addLayer(layer)

This tree has all data and methods needed to control the visibility of all layers on mapcanvas. E.g. to control visibility programmatically

QgsProject.instance().layerTreeRoot().findLayer(layer.id()).setVisible(2)

without keeping track of a layerSet, which has to be updated and passed to mapcanvas each time the state of some layer has changed.

To control visibility and other stuff via a legend, we have to show the tree in a tree widget and synchonize it with the canvas through an QgsLayerTreeMapCanvasBridge object.

To control just a part of the tree through the treeWidget was what I couldn't get working. As a workaround I create explicitly a second tree with just the group node I want to expose:

# initialized in myCanvasClass.__init__()
self.maps_treeview = QTreeView()

# in the myCanvasClass.someMethod() where layers are added to mapLayer registry
self.model = QgsLayerTreeModel(QgsProject.instance().layerTreeRoot().findGroup('Map'))
self.model.setFlag(QgsLayerTreeModel.AllowNodeChangeVisibility)
self.model.setFlag(QgsLayerTreeModel.ShowLegend)
self.maps_treeview.setHeaderHidden(True)
self.maps_treeview.setModel(self.model)

And somewhere outside the class definition, within all UI related stuff:

self.canvas = myCanvasClass()
self.gridLayout_5.addWidget(self.canvas.maps_treeview, 0, 0, 1, 1)

It works!. I can control layers by code using methods of the (invisible) complete layer tree. And additionally the user can switch on and off only a subset of layers by checking boxes interactively.

Since this mixture may be interference-prone I am still interested in a more elegant and well founded solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.