3

I'm trying to change a vector layer's style by changing the renderer (from QgsRuleBasedRenderer to QgsCategorizedSymbolRenderer), render an image, and undo the change afterwards:

layer = self.get_layer('branch')
old_renderer = layer.renderer()
unique_values = layer.uniqueValues(layer.fields().indexFromName('line_id'))
colors = ['#fa4b3c', '#d900ff', '#a72605', '#879695', '#1fbc00', '#000dff', '#ea75ff', '#fffb00', '#3a7e31', '#ffa560', '#49b9ff', '#00ffb3', ]
categories = []
for unique_value, color in zip(unique_values, colors):
    symbol = QgsSymbol.defaultSymbol(layer.geometryType())
    symbol.setColor(QColor(color))
    symbol.setWidth(1.25)
    category = QgsRendererCategory(unique_value, symbol, str(unique_value))
    categories.append(category)
layer.setRenderer(QgsCategorizedSymbolRenderer('line_id', categories))
layer.triggerRepaint()

def finished(path=path):
    img = render.renderedImage()
    img.save("/Users/myuser/render.png", "png")

render.finished.connect(finished)
render.start()
loop = QEventLoop()
render.finished.connect(loop.quit)
loop.exec_()

if old_renderer is not None:
    # layer.setRenderer(old_renderer)
    layer.triggerRepaint()

The above works, but does not undo the change in renderer at the end. If I uncomment the

    layer.setRenderer(old_renderer)

it crashes the whole app instantaneously.

How do I reapply the old renderer?

3
  • 1
    Does it work if you use old_renderer = layer.renderer().clone()? FYI if you launch QGIS in a terminal, you might get some information upon crashing. Commented Mar 3, 2023 at 7:56
  • old_renderer = layer.renderer().clone() should work, same reason as gis.stackexchange.com/a/452791/144074
    – Kalak
    Commented Mar 3, 2023 at 8:02
  • 1
    Great, it works with clone(), thanks! Answer it so I can accept the answer. Commented Mar 3, 2023 at 8:23

1 Answer 1

4

Some objects accessible in the QGIS Python API are tricky to handle as their actual lifespan might differ from what you'd expect as a Python user.

In Python, objects usually exist until there is no name or other reference "pointing" to them anymore. So you would expect to be able to always access whatever old_renderer is a name to and not have your program crash.

In the world of C++ objects made available via the QGIS Python API however, this is not always the case. Objects referenced by whatever magic happens in the background between Python and C++ in memory might get deleted when you don't expect it.

In your case, the renderer object which you assigned a name to with old_renderer = layer.renderer() does not exist anymore (probably because the call to layer.setRenderer() took care of "cleaning up" the previous renderer but I am not sure) and your program crashes with wrapped C/C++ object of type (...) has been deleted (...).

To avoid this kind of issue, you can create a full blown, independent copy of the object in question using .clone(), in this case: old_renderer = layer.renderer().clone()

PS: My explanation might not be fully correct but should be alright for a basic understanding of the issue.

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.