Index de l'article

Affichage personnalisé des attributs

Un autre exemple de sélection spécifique, avec 3 fonctions séparées, dont une pour afficher les attributs de la couche dans un dock personnalisé.

Imaginez que vous ayez un projet QGIS avec de grosses couches, beaucoup d'entités, beaucoup de champs... ce qui rend votre projet peu lisible.
Ces couches sont liées entre elles par un champ id. Il est alors possible de conditionner l'affichage des entités d'une couche sur un clic d'une entité d'une autre couche.
Le code suivant va zoomer sur l'entité cliquée, afficher certains de ses attributs, certains des attributs de la couche liée et masquer les autres entités de la couche liée.

from qgis.PyQt.QtWidgets import QDockWidget, QTextEdit
from qgis.PyQt.QtCore import Qt, QTimer
 
layerA = QgsProject.instance().mapLayersByName("Ma couche A")[0]
layerB = QgsProject.instance().mapLayersByName("Ma couche B")[0]
 
# Cacher B par défaut
layer_tree = QgsProject.instance().layerTreeRoot().findLayer(layerB.id())
if layer_tree is not None:
    layer_tree.setItemVisibilityChecked(False)

 

# Dock d'affichage
dock = QDockWidget("Attributs de la couche A", iface.mainWindow())
dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
 
text_edit = QTextEdit()
text_edit.setReadOnly(True)
dock.setWidget(text_edit)
iface.addDockWidget(Qt.RightDockWidgetArea, dock)
dock.hide()

 

# Fonction affichage des attributs
def displayAttributes(features, layer):
    if not features:
        dock.hide()
        return
 
    feat = features[0]
    attrs = feat.attributes()
    fields = layer.fields()
 
    data = {field.name(): attrs[i] for i, field in enumerate(fields)}
 
    groupsFields = {
    "ID": {"id_a": "ID A", "foreign_key_layer_b": "Clé B"}, \
    "Bla bla bla...": {"area": "Surface", "len": "Longueur"}, \
    "Bla bla bla...": {"blablafield": "Igo", "anotherblablafield": "Wesh"}}
 
    used_fields = set()
 
    msg = ""
    for titre, champs in groupsFields.items():
        msg += f"{titre}"
        for champ, label in champs.items():
            valeur = data.get(champ, "–")
            msg += f"- {label}: {valeur}"
            used_fields.add(champ)
        msg += "BR"
 
    autres_champs = [k for k in data.keys() if k not in used_fields]
    if autres_champs:
        msg += "Champs restants"
        for champ in autres_champs:
            msg += f"- {champ}: {data[champ]}"
 
    text_edit.setHtml(msg)
    dock.show()

 

# Fonction zoom
def zoom_after_delay():
    matching_features = list(layerB.getFeatures())
    if not matching_features:
        return
 
    extent = matching_features[0].geometry().boundingBox()
    for f in matching_features[1:]:
        extent.combineExtentWith(f.geometry().boundingBox())
 
    if extent.isEmpty():
        return
 
    extent_buffered = extent.buffered(extent.width() * 0.1)
    iface.mapCanvas().setExtent(extent_buffered)
    iface.mapCanvas().refresh()
    iface.mapCanvas().update()

 

# Fonction filtre
def custom_filter():
    selected_features = layerA.selectedFeatures()
 
    displayAttributes(selected_features, layerA)
 
    if not selected_features:
        layerB.setSubsetString("")
        if layer_tree is not None:
            layer_tree.setItemVisibilityChecked(False)
        return
 
    # Filtrer "Ma couche B" à partir de "Ma couche A"
    id_cads = [f['foreign_key_layer_b'] for f in selected_features]
 
    if len(id_cads) == 1:
        expr = f"id_b = '{id_cads[0]}'"
    else:
        id_cads_str = ", ".join(f"'{id}'" for id in id_cads)
        expr = f"id_b IN ({id_cads_str})"
 
    layerB.setSubsetString(expr)
    if layer_tree is not None:
        layer_tree.setItemVisibilityChecked(True)
 
    QTimer.singleShot(50, zoom_after_delay)

 

layerA.selectionChanged.connect(custom_filter)

 

Liens ou pièces jointes
Télécharger ce fichier (data_BDTOPO_V3_Dep05_adresse.zip)data_BDTOPO_V3_Dep05_adresse.zip[ ]3889 Ko
Télécharger ce fichier (data_IRIS_2019.zip)data_IRIS_2019.zip[ ]45905 Ko
Télécharger ce fichier (decathlon_france.zip)decathlon_france.zip[308 magasins Décathlon français depuis OSM le 27 décembre 2020]11 Ko
Télécharger ce fichier (glaciers.zip)glaciers.zip[ ]231 Ko
Télécharger ce fichier (iso_iris.zip)iso_iris.zip[Des zones isochrones à 15 minutes autour de 308 POIs.]12125 Ko
Télécharger ce fichier (Koln GML.zip)Koln gml.zip[ ]2818 Ko
Télécharger ce fichier (peaks.zip)peaks.zip[ ]14 Ko
Télécharger ce fichier (peaks_selection.zip)peaks_selection.zip[ ]1 Ko
Télécharger ce fichier (simple_countries.zip)simple_countries.zip[ ]1880 Ko
Accéder à cette adresse URL (https://hg-map.fr/extern/data/shapes/france/sol.zip)sol.zip[ ]0 Ko
Accéder à cette adresse URL (https://hg-map.fr/extern/data/shapes/france/TRONCON_HYDROGRAPHIQUE.zip)TRONCON_HYDROGRAPHIQUE.zip[ ]0 Ko
Accéder à cette adresse URL (https://hg-map.fr/extern/data/shapes/france/TRONCON_ROUTE.zip)TRONCON_ROUTE.zip[ ]0 Ko
Accéder à cette adresse URL (https://hg-map.fr/extern/data/shapes/france/TRONCON_VOIE_FERREE.zip)TRONCON_VOIE_FERREE.zip[ ]0 Ko
Télécharger ce fichier (World Stats.xlsx)World Stats[ ]27 Ko