So far, we have loaded vector layers, looked at their attributes, and filtered features based on these attributes. Now it’s time to look into how to change a vector layer’s style.

The following example assumes that you have loaded a point vector layer:

uri = "E:/Geodata/NaturalEarth/vector_v4/natural_earth_vector.gpkg_v4.1.0/packages/natural_earth_vector.gpkg|layername=ne_110m_populated_places_simple"
vlayer = iface.addVectorLayer(uri, "places", "ogr")

By default, QGIS displays points as randomly colored circles. Let’s see how we can alter the symbology!

First, let’s make the circles bigger:

vlayer.renderer().symbol().setSize(6)
vlayer.triggerRepaint()

Don’t forget to call triggerRepaint()! Otherwise you won’t see the changes taking effect.

Nice! Let’s take one step back and examine the code:

vlayer.renderer() gives us access to the vector layers renderer object. There are different types of renderers. As a QGIS user you know them as Single Symbol (QgsSingleSymbolRenderer), Categorized (QgsCategorizedSymbolRenderer), Graduated (QgsGraduatedSymbolRenderer), and so on.

By default, vector layers are assigned single symbol renderers. These renderers have a single symbol that we can access using the symbol() function.

Now that we have access to the symbol, we can change its size using the setSize() function, providing it a numeric value for the desired size.

Similarly, we can change the symbol’s color using setColor():

vlayer.renderer().symbol().setColor(QColor("blue"))
vlayer.triggerRepaint()

The setColor() function expects a QColor object as its parameter. This is why we create a new QColor object using QColor(“blue”). (There are many other ways to create a QColor object which you can find in the official Qt documentation.)

Finally, let’s get rid of those boring circles and have stars instead:

vlayer.renderer().symbol().symbolLayer(0).setShape(QgsSimpleMarkerSymbolLayerBase.Star)
vlayer.triggerRepaint()

Note how we are not calling symbol().setShape() but symbol().symbolLayer(0).setShape()! This is because each symbol can consist of multiple symbol layers but by default it has only one. We can access this first (and only) symbol layer using symbolLayer(0). The symbol layer is the right place to change the shape of the marker using setShape(). Available shapes (based on QgsSimpleMarkerSymbolLayerBase) include: Arrow, ArrowHead, ArrowHeadFilled, Circle, Cross, Cross2, CrossFill, DiagonalHalfSquare, Diamond, EquilateralTriangle, HalfSquare, Hexagon, LeftHalfTriangle, Line, Pentagon, QuarterCircle, QuarterSquare, RightHalfTriangle, SemiCircle, Square, Star, ThirdCircle, and Triangle.

Finally, you might have noticed that the symbol in the layer list (right next to the layer name) has not been updated. To fix this, we need to call the correct refresh function:

iface.layerTreeView().refreshLayerSymbology(vlayer.id())

The refreshLayerSymbology() function needs to know which layer’s symbology should be refreshed. That’s why we need to provide it with our vector layer’s id.

These are the basics of vector styling using a single symbol renderer with a symbol consisting of one symbol layer.

Next


PyQGIS 101 is a work in progress. I’d appreciate any feedback, particularly from beginners!

19 comments
  1. Thanks a lot for this, I tried playing with this too, what I tried to do was get my Openstreetmap edits with overpass, convert everything to points, and style my points with a heatmap. I found the function that would control it but didn’t manage to get it working. Would you be interested to do an example of this? Thanks again

    • Thank you for your feedback! I’ll look into the heatmap renderer and see what I can do.

      • Looks like this does the trick:
        vlayer.setRenderer(QgsHeatmapRenderer())

  2. Neil Falconer said:

    Thank you for these tutorial, greatly appreciated!! Very useful to get familiar with the various objects and their properties and functions/methods.

  3. Kurt said:

    thank you very much for this really nice intro into PyQGIS, hope there are coming more samples…

  4. Mohab said:

    so far this is the last tut, thank you for the simple intro to PyQGIS, you ‘ve reached a global appreciation as i’m far remote from you and very thankful for this nice intro…keep up!

  5. Kathinka Bradley said:

    Thank you very much for this easy to follow and well written tutorial! I learnt a great deal already.
    Sadly, I ran into an error I can’t figure out. I used the layers you suggested earlier and QGIS 2.18.3 and wrote the following:
    uri = “C:/someadress/” #here I used my adress for the layer
    vlayer = iface.addVectorLayer(uri, “places”, “ogr”)
    vlayer.renderer().symbol().symbolLayer(0).setShape(QgsSimpleMarkerSymbolLayerBase.Star)
    vlayer.triggerRepaint()
    Nothing changed though, the sizes, colours and shapes stayed the same. But the following error occured: “”AttributeError: ‘QgsVectorLayer’ object has no attribute ‘setShape”. I can’t figure out what I did wrong, pretty sure it was just a minor mistake, though.

    Could you please help me?

    • Dear Kathinka,
      This tutorial is written for QGIS 3. There are slight differences between the QGIS 2 and QGIS 3 Python APIs. For QGIS 2, you need:
      vlayer.rendererV2().symbol().symbolLayer(0).setShape(QgsSimpleMarkerSymbolLayerBase.Star)
      vlayer.triggerRepaint()

      If you want to follow this tutorial, please upgrade to QGIS 3.

      • Kathinka Bradley said:

        Okay, thank you very much!
        I just started working with QGIS a few months ago and wanted to get used to the old version first, a mistake I guess.

      • Either version is good but you’ll need to be careful which version the documentation is written for, particularly when it comes to the Python API.

  6. I changed the symbols of my layer to stars. I’ll tweet you my screenshot of this too.

    Your classes are concise. Very helpful.
    Thanks.

  7. Strythe said:

    First, thank you for this tutorial! It was easy to understand and follow. I just have a question. I’ve written a custom processing script using the template provided in QGIS 3 (link below). I want to know how to apply render styles within my existing script? The output that gets loaded is the sink, from what I understood with pyqgis so far. But I cannot really use the renderer methods to a sink object.

    https://github.com/qgis/QGIS/blob/master/python/plugins/processing/script/ScriptTemplate.py

    • Dear Strythe, thank you for your feedback! I’ll have to look into output styling again because the last information that I have is that there is no good solution for defining the output styling in a Processing script.

  8. Nathan Perry said:

    Thanks for these tutorials! I appreciate that you go over the basics of Python, since I’m not a beginner to QGIS at all, but I am to Python. Most tutorials seem to start with the very basics of QGIS, or else they assume familiarity with Python.

    This tutorial is where I start to get a bit confused as we string together several objects, classes, methods etc., and I’m not always sure how all the different parts work on each other via the dot operator. For example, the renderer() method returns one of several renderer objects (or an object in one of several classes?), but what does the symbol() method return? I guess it’s a symbol object, but; maybe there could be a bit of explanation of Python’s “sentence structure” when dealing with long lines like these?

    • Instead of vlayer.renderer().symbol().setSize(6), we could also write:

      vl_renderer = vlayer.renderer()
      vl_symbol = vl_renderer.symbol()
      vl_symbol.setSize(6)

      but this requires much more typing.

      I’ll have to think about how to explain the different renderer and symbol classes. If you’re interested: the background knowledge to understand this part is call “inheritance” in object oriented programming.

  9. Graeme said:

    Is it possible from an existing layer the settings out in a form that could then be applied to subsequent layers easily and tweaken in terms of different attributes to be used etc? For example, do all the hard work on an input layer representing an attribute of one hour a day, then load multiple (23) references to the same dataset and style, (and filter) quickly using Python to apply the same style, only based on the appropriate attribute for each new layer?

  10. Ilia said:

    Hi, I`v got a problem with labeling.
    I made the descriprion of my labeling and after script running nothing changed on my layer. But than I go to “Layer->Properites->Labels” I can see that “Rule-Based Labeling” is choosed and also I`ve got my labeling rule is active.

    I can display my labeling manualy by few simple steps:
    1. Open “Edit Rule” window.
    2. Press “OK” and go back to “Layer->Properites->Labels”
    3. Press “OK” and “Apply”.

    I can see my labels. Why it doesn`t work after sript running?
    Where is my mistake?

    So, my code looks like this (QGIS 3.4)

    settings = QgsPalLayerSettings()
    settings.placement = QgsPalLayerSettings.Horizontal
    settings.fieldName = ‘expression in string format’

    some text format description here…..

    settings.setFormat(textFormat)
    settings.enabled = True

    #create and append a new rule
    root = QgsRuleBasedLabeling.Rule(QgsPalLayerSettings())
    rule = QgsRuleBasedLabeling.Rule(settings)
    rule.setDescription(‘Morning Difference’)
    rule.setFilterExpression(‘filter exp in string format’)
    root.appendChild(rule)

    #Apply label configuration
    layer.setLabelsEnabled(True)
    rules = QgsRuleBasedLabeling(root)
    layer.setLabeling(rules)
    layer.triggerRepaint()

  11. Alex said:

    Hi. Very useful articles. Thanks.
    How to change QgsSimpleMarkerSymbolLayer to QgsRasterMarkerSymbolLayer? I want to set a png file as a marker.
    Can’t find it in docs and examples.

    • I don’t remember ever seeing a code snippet for that use case. I’d recommend contacting the developer mailing list.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: