6

I have a QgsCategorizedSymbolRenderer and I'm trying to configure it so that symbol_a:

  1. appears at the top of the layers window legend, but also
  2. appears on top of all other QgsRendererCategory symbols (i.e. it should be rendered last).

In QGIS, I can do this by going to Symbology > Advanced > Symbol Levels, checking Enable symbol levels and configuring the Layer 0 for Label A to 1, leaving the others at 0.

Using PyQGIS, I can enable symbol levels, but can't find a way to change the specific symbol levels (i.e. the order they are rendered). Re-ordering the categories achieves this, but I'd prefer Label A to appear first in the layers window. Is this possible?

My existing code looks something like this:

categories = [
    QgsRendererCategory('value_a', symbol_a, 'Label A'),
    QgsRendererCategory('value_b', symbol_b, 'Label B'),
    QgsRendererCategory('value_c', symbol_c, 'Label C'),
]

renderer = QgsCategorizedSymbolRenderer('field_name', categories)
renderer.setUsingSymbolLevels(True)
2

1 Answer 1

2

This post give a good answer with a clause applied on the renderer :

myRenderer.setOrderBy(QgsFeatureRequest.OrderBy([QgsFeatureRequest.OrderByClause('Value', False)]))
myRenderer.setOrderByEnabled(True)

Edit: @simone100, to sort categories to specific levels, it's easier to work with symbol renderer categories. A full example could be (adapt layer and symbol if necessary):

# Define the layer
layer = iface.activeLayer()

# Field for classification
expression = 'CLASSE'

# Symbol
symbol = QgsFillSymbol.defaultSymbol(layer.geometryType())

# Categorized styles: declared order will be the same in the tree view
categories = [    
    QgsRendererCategory('S2', symbol, 'S2'),
    QgsRendererCategory('S3', symbol, 'S3'),
    QgsRendererCategory('S1', symbol, 'S1')
]

renderer = QgsCategorizedSymbolRenderer(expression, categories)
        
# Apply style to layer
layer.setRenderer(renderer)
        
# Refresh layer's symbology
iface.layerTreeView().refreshLayerSymbology(layer.id())
9
  • QgsFeatureRequest (expression: str, ascending: bool = True). You insert 'Value' and I assume that is the field name. Your answer don't tell how to insert the expression if you want to sort the attributes specifically for the levels. For example I have a field "Water" that has 3 attributes ('S1', 'S2', S3'). How can you insert in the expression by telling for S1 level 1, for S2 level 3, for S3 level 2? I assume you have to modify the part "OrderByClause('Value', False)" in something like "OrderByClause('CASE WHEN 'Water'='S1' THEN 1' and so on. But I don't know how to do.
    – simone100
    Jan 18, 2023 at 10:02
  • Documentation says that clause could be an field or an expression. Have you tried QGIS expressions like CASE or IF syntax ?
    – GeoGyro
    Jan 18, 2023 at 10:30
  • I did with the field 'CLASSE' but it does not work. expression = "CASE WHEN "CLASSE" is 'S1' THEN '3' END; CASE WHEN "CLASSE" is 'S2' THEN '2' END'; CASE WHEN "CLASSE" is 'S2' THEN '2' END' "
    – simone100
    Jan 18, 2023 at 10:43
  • renderer = QgsCategorizedSymbolRenderer("CLASSE", list) renderer.setUsingSymbolLevels(True) expression = QgsExpression("CASE WHEN 'CLASSE' is 'S1' THEN '3' END") renderer.setOrderBy(QgsFeatureRequest.OrderBy([QgsFeatureRequest.OrderByClause(expression, False)])) renderer.setOrderByEnabled(True) in this case renderer is enabled for level (so it works something!), there is no error but the layer level remains S1=0 and not S1=3 @GeoGyro
    – simone100
    Jan 18, 2023 at 10:56
  • 1
    Syntax matters, try: CASE WHEN "CLASSE" = 'S1' THEN '3' WHEN "CLASSE" = 'S2' THEN '2' WHEN "CLASSE" = 'S2' THEN '2' END
    – GeoGyro
    Jan 18, 2023 at 11:02

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.