PyQGIS Symbology – Changing Stroke Width of Polygon with Categorized Symbology

attributeerrorpyqgissymbology

I have a polygon layer with categorized symbology. I want to change the stroke width of all the categories without touching the categories or colours.

Here is the script I have after looking a similar questions

lyr = iface.activeLayer()
rndr = lyr.renderer()
sym = rndr.symbol()
syms.setStrokeWidth(3)
lyr.triggerRepaint()

I get the following error:

AttributeError: 'QgsCategorizedSymbolRenderer' object has no attribute
'symbol'

Best Answer

You can use the snippet below. I used an outline width of 0.5 in the example (3 is very wide).

lyr = iface.activeLayer()
renderer = lyr.renderer()
for i, cat in enumerate(renderer.categories()):
    props = cat.symbol().symbolLayer(0).properties()
    props['outline_width'] = 0.5 # Change to your desired outline width
    sym = QgsFillSymbol.createSimple(props)
    renderer.updateCategorySymbol(i, sym)
lyr.triggerRepaint()
# If you don't want the legend symbology in the TOC to reflect the changes,
# comment the line below
iface.layerTreeView().refreshLayerSymbology(lyr.id())

Result of the example above on a test layer:

enter image description here

Or... you can also try the following, using the method updateSymbols() which according to the docs should:

Update all the symbols but leave categories and colors.

lyr = iface.activeLayer()
renderer = lyr.renderer()
props = renderer.sourceSymbol().symbolLayer(0).properties()
props['outline_width'] = 0.2
sym = QgsFillSymbol.createSimple(props)
renderer.updateSymbols(sym)
lyr.triggerRepaint()
iface.layerTreeView().refreshLayerSymbology(lyr.id())

Both code snippets presented here worked fine for me in 3.20.

Edit 2: I played around with this a bit more for my own understanding. The two examples above work fine with a categorized renderer with one simple fill symbol layer for each category, but not if there are multiple symbol layers.

I came up with the script below which should change symbol outline but preserve any other fill symbol layers which are present.

lyr = iface.activeLayer()
renderer = lyr.renderer()
for i, cat in enumerate(renderer.categories()):
    old_sym = cat.symbol()
    props = old_sym.symbolLayer(0).properties()
    props['outline_width'] = 0.5
    new_sym = QgsFillSymbol.createSimple(props)
    if len(old_sym.symbolLayers()) > 1:
        for j in range(1, len(old_sym.symbolLayers())):
            new_sym.appendSymbolLayer(old_sym.symbolLayers()[j].clone())
    renderer.updateCategorySymbol(i, new_sym)
    del old_sym

lyr.triggerRepaint()
iface.layerTreeView().refreshLayerSymbology(lyr.id())

In the screencast below, you can see I have multiple symbol layers for some of the categories, which are preserved after running the script while the outline stroke width is changed.

enter image description here

Related Question