4

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'

1 Answer 1

5

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

3
  • 1
    Thanks for your reply. This works for changing the line width. However, it also changes the polygon fill for all categories, which I would not like to happen. IS there ny way to stop it from doing that? Making the fill layer 100% transparent would also do the job
    – JUNGLE
    Commented Jan 20, 2022 at 22:04
  • 1
    I achieved what I was wanting by adding a line " props['color'] = '255,0,0,0' " after line 5 of your script. Thanks!
    – JUNGLE
    Commented Jan 20, 2022 at 22:28
  • @JUNGLE, I'm glad you got a working solution but I'm curious because I didn't encounter the behaviour you described when I tried it on a test layer with a simple fill symbol for each category. Do you have more than one symbol layer in your categories? If you want to explore the issue further, could you post some images of your layer appearance and the symbology settings? Otherwise, if you are happy with the solution you have, that's fine too ;-)
    – Ben W
    Commented Jan 20, 2022 at 23:19

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.