7

I'm building a standalone app with PyQGIS. I encountered some difficulties building a canvas that show the result layers after some operations.

The canvas works, but the associated legend does not allow hiding and showing the corresponding layers, despite the use of the appropriated flags.

Displayed canvas, with an unchecked layer still visible

Here is the code I wrote :

from qgis.core import *
from qgis.gui import *
from qgis.PyQt.QtCore import *
from qgis.PyQt.QtWidgets import *


def combine_extends(layers):
    """Combine extends of all layers"""
    global_extend = layers[0].extent()
    if len(layers) > 1:
        for layer in layers[1:]:
            global_extend.combineExtentWith(layer.extent())
    return global_extend


def set_canvas(window_title, crs_epsg, layers=None):
    """Set a canvas instance with basic configuration
    If layers are provided, render them on canvas"""
    # Basic configuration
    canvas = QgsMapCanvas()
    canvas.setWindowTitle(window_title)
    canvas.setCanvasColor(Qt.white)
    crs = QgsCoordinateReferenceSystem(crs_epsg)
    canvas.setDestinationCrs(crs)
    # Optionnal layers rendering
    if layers:
        render_layers(canvas, layers)

    canvas

    return canvas


def render_layers(canvas, layers):
    """Render layers on canvas and refresh it"""
    # Set layers to render, and define an adapted zoom level
    try:
        canvas.setExtent(combine_extends(layers))
        canvas.setLayers(layers)
        canvas.zoomToFullExtent()

    except:
        raise Exception("Error while rendering layers")


def add_legends(project, canvas, layers):
    # Some more tests

    layer_tree_root = project.layerTreeRoot()
    bridge = QgsLayerTreeMapCanvasBridge(layer_tree_root, canvas)

    # Cleanup persisting former layers_group in RAM
    if layer_tree_root.findGroup("Layers"):
        layer_tree_root.clear()

    layers_group = layer_tree_root.addGroup("Layers")
    for layer in layers:
        layers_group.addLayer(layer)

    model = QgsLayerTreeModel(layer_tree_root, layer_tree_root.findGroup("Layers"))
    model.setFlag(QgsLayerTreeModel.AllowNodeReorder)
    model.setFlag(QgsLayerTreeModel.AllowNodeRename)
    model.setFlag(QgsLayerTreeModel.AllowNodeChangeVisibility)
    model.setFlag(QgsLayerTreeModel.ShowLegend)

    tree_view = QTreeView(canvas)
    tree_view.setHeaderHidden(True)
    tree_view.setModel(model)

    bridge.setCanvasLayers()
    print(str(canvas))


def render(project, canvas, layers):
    render_layers(canvas, layers)
    add_legends(project, canvas, layers)

    # handle canvas refresh
    canvas.show()
    canvas.refresh()
    canvas.repaint()

I call render once all processings are done.

QGIS version: 3.22.4, Environment: Ubuntu WSL

1 Answer 1

10

QgsLayerTreeMapCanvasBridge must be in global scope. But you define bridge in add_legend method's scope. Therefore, variable bridge is gone after add_legend finished.

One of the solutions:

  • Add bridge = None above the line where you call the render method

    ...
    ...
    
    bridge = None
    
    render(project, canvas, layers)
    
  • Then add global bridge to top of the add_legend method:

    def add_legends(project, canvas, layers):
        global bridge
    
        # other lines
    

enter image description here

1
  • Thank you ! I took the liberty to lightly adapt your suggestion, essencially creating the bridge then passing it as arg to my render method. : python ## Creating the bridge between canvas and project to syncronize the UI layer_tree_root = project.layerTreeRoot() bridge = QgsLayerTreeMapCanvasBridge(layer_tree_root, canvas) # Processing # Rendering final layers print("Rendering maps...") render(canvas, bridge, layer_tree_root, reprojected_trimmed_layers) Aug 21, 2023 at 14:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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