I have created a widget for PyQGIS applications that is a (really) simplified version of the QGIS layer list widget. The main difference is that it has no group layers. The widget is also based on OpenOceanMap's legend written by Aaron Racicot but instead of working with QCheckBox controls it uses QTreeWidgetItem for representing every map layer.
This is a screenshot of what you may get:

What does the widget allow?
- Get an ordered list of map layers.
- Move up or down a layer affecting its z-order in the map.
- Make a layer visible or invisible in the map.
- Visualize/change the layer symbology (actually, you have to develop your own layer symbology dialog, I just give you a foo example).
- Change some layer properties.
- Load/Save QGIS layer style files.
- Remove layers.
- Integrate your map with other tools acting over loaded layers (e.g. Show/Hide all, Remove all).
What about the license?
It is GNU GPL v.2.
How to include it?
To include the widget on your PyQGIS application you should follow these steps:
1. Download this ZIP file and extract it to your application folder. It contains the legend.py file, which is actually the widget, a number of images (icons) to represent layer types and menu options, and finally, a dialog that is used to set layer properties.
You can get the icons from the QGIS project, I have not created them.
2. Import the Legend class.
In the main class of your application, which inherits from QMainWindow, import the Legend class from the legend.py file, this way:
from legend import Legend
3. Add the CreateLegendWidget method.
Include the CreateLegendWidget method in the main class of the application to add the dock widget into the GUI and associate the layer list widget to the canvas. The method is this:
def createLegendWidget( self ):
""" Create the map legend widget and associate to the canvas """
self.legend = Legend( self )
self.legend.setCanvas( self.canvas )
self.legend.setObjectName( "theMapLegend" )
self.LegendDock = QDockWidget( "Layers", self )
self.LegendDock.setObjectName( "legend" )
self.LegendDock.setAllowedAreas( Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea )
self.LegendDock.setWidget( self.legend )
self.LegendDock.setContentsMargins ( 9, 9, 9, 9 )
self.addDockWidget( Qt.LeftDockWidgetArea, self.LegendDock )
In the __init__ method of the application main class call the just added method:
self.createLegendWidget() # Create the legend widget
4. Compile/Import a resource file to include legend widget icons.
The layer list widget include some icons you would like to see. To accomplish this, compile the provided resources.qrc file with the following command:
$ pyrcc4 resources.qrc -o resources_rc.py
The legend.py file already contains a reference to the genereated file. Nevertheless, if you have your own resources file, you may add all the images in the imgs folder into a new prefix, for instance, "legend". If you want to choose another prefix, make sure to change the resource_prefix variable in the legend.py file as well.
resources_prefix = ":/legend/imgs/"
5. Let the legend to load and remove layers in order to avoid segmentation fault.
You must have a method to load layers into your map application, but now that you want to include a layer list widget you should let it control all these matters. The layer list widget has a SLOT to manage the layer set at the time you load, move or remove a layer. All you have to do is to call the appropriate methods (either addMapLayer or removeMapLayer) from QgsMapLayerRegistry instance, but do not use the setLayerSet canvas method yourself, the new widget will. So, comment or delete your setLayerSet calls.
QgsMapLayerRegistry.instance().addMapLayer( layer );
#self.canvas.setLayerSet( self.layers )
6. Load the properties dialog.
To allow the user set some layer properties you have to compile the dlgLayerProperties.ui file, which will provide a basic dialog.
$ pyuic4 dlgLayerProperties.ui -o dlgLayerProperties_ui.py

7. (Optional) Enable/Disable tools
The layer list widget emits a SIGNAL that allows you to enable/disable tools in your application, according to the active layer type, either raster or vector. The SIGNAL is called activeLayerChanged and if you want to use it, you are suppose to write something like this in your application main class:
self.connect( self.legend, SIGNAL( "activeLayerChanged" ),
self.enableTools )
... # Probably more code
def enableTools( self ):
if not self.legend.activeLayer():
# Disable some general tools
else:
# Enable some general tools
layerType = self.legend.activeLayer().layer().type()
if layerType == 0: # Vector Layer
# Enable vector tools and disable others
elif layerType == 1: # Raster Layer
# Enable raster tools and disable others
8. (Optional) Use a few Legend class methods to expose functionality to other tools or plugins:
- Iterate over the layers:
for layer in self.legend.layers:
...
- Show (or hide) all the layers in the map:
def showAllLayers( self ):
self.legend.setStatusForAllLayers( True )
- Get the active layer (for plugins):
return self.legend.activeLayer()
- Refresh the layer symbology (for plugins):
self.legend.refreshLayerSymbology( layer )
- Remove layers by Id (for plugins):
self.legend.removeLayers( layerIds )
That's it!
comments
http://gis-lab.info/qa/qgis-legend.html (gis-lab.info/qa/qgis-legend.html)
I'm developping a Python application and i'm using your devs. Great job!
I get some trouble with vectorLayerSymb ology method:it seems that the renderer is not convenient.
Line: renderer = layer.renderer()
AttributeError: 'NoneType' object has no attribute 'symbols'
So I have to use rendererV2, and rendererV2 doesn't support getPointSymbolA sImage method...
Do you have any idea to fix it?
Thanks
glad you find it useful.
The problem you have now is related to this legend's lack of support for symbology-ng (new generation) in QGIS.
You can replace the functions of this file [1] (in legend.py) to have single symbology-ng working, but not categorized nor graduated I guess.
Most of the functionality in this legend is adapted from [2] (in C++), so if you can deal with a bit of C++ code and adapt it to Python, please let me know, we could work enabling this legend to support symbology-ng. In that case I would set up a SVN repository for the code.
Regards,
Tuxman
---------
[1] http://downloads.tuxfamily.org/tuxgis/geoblogs/pyqgis_toc/functions_to_replace.py
[2] https://raw.github.com/qgis/Quantum-GIS/master/src/app/legend/qgslegendlayer.cpp
During last days I’ve found a solution looking into cpp code.
Now it works. But I think code is not realy clean (I’m a Python newby !) The next step for me : legend group !
The first question is : create a new class Legendgroup like LegendItem or as a subclass of legendItem ?
I look at C++ and try to "translate" into Python.
But I got a problem to give a type to QTreeWidgetItem s, I don't know the way to write such lines:
QgsLegendItem *litem = dynamic_cast( item );
QgsLegendGroup *group = dynamic_cast( item );
QgsLegendLayer *layer = dynamic_cast( item );
Regards
I recall I did something like that this way:
object.__class__ = AnotherClass
Please consider sharing your work.
Regards,
Tuxman
I'm back!
Work is going on, and I have much more question specialy if I compare legend.py to QGis legend behaviour:
In QGis when you check on or off a layer, only these one is redraw not all other.
That’s not the legend.py behaviour now.
What should be the best way to do this inside legend.py?
PS: I didn't forget your proposal to share my dev, but at this moment it is embeded inside my app, so hard to share, but…
Congratulations for your plugin. It is very useful for me.
But I have a doubt related with more than one layer added and their visualization in map canvas. I had one layer and it is every ok (I didn't use all the properties of the plugin). But when I add a new layer, this opens in my map canvas and the first one disappear. So in the legend I have two layers and only one in map canvas. I just want you to explain which funtion deals with this and if I have to do something else in my main code.
Hope that you helps me. Thank you.
Best regards.
thanks! You don't need this component anymore, because QGIS (thanks to Martin Dobias) now gives us a Layer Tree View, have a look at: http://bit.ly/1GJlVkN and tell me if there is something unclear to you.
Regards,
Tuxman
thank you so much for the answer. I try and I get it!! Now my layers are in correct order. But I miss other widgets that I would like to have in my application. For example, the possibility of zoom extent and to remove the layer (by clicking with the right button of the mouse in the layer, as actions). This actions belongs to legend.py but know I am not able to do that. Can you help please?
Tahnk you.
Beste regards,
Lia Duarte
please have a look at the documentation of the QgsLayerTreeVie w [1], specifically at the bullet point "Display of context menu."
You'll find there indications on how to implement your own context menu, as well as an example. Please give it a try; if I remember well, when I gave it a try it didn't work for me. If that's the case also for you, we could file a ticket reporting the issue.
If you have success, please tell me how you achieved it :)
Regards,
Tuxman
----
[1] http://www.lutraconsulting.co.uk/blog/2015/01/30/qgis-layer-tree-api-part-3/
It works!!! :) Thank you so much, it was very very useful this help.
Thank you for all the help.
Best regards,
Lia Duarte
BTW, we publish tips like this in our Twitter account: twitter.com/GeoTux2 Follow us there!
Regards,
Tuxman
RSS feed for comments to this post