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.

For more styling options check Advanced point layer styling.

Next


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

27 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.

    • Andrew Prentice said:

      Hi Strythe,
      your comment is old but I have managed what you appear to be asking. I believe the trick is in how you call the layer, the QgsProcessingUtils.mapLayerFromString line if that makes sense.

      (sink, styled_layer) = self.parameterAsSink(parameters, self.STYLED_OUTPUT, context,
                                                             extracted_layer.fields(), wkb_type_point, new_crs)
                      if sink is None:
                          raise QgsProcessingException(self.invalidSinkError(parameters, self.STYLED_OUTPUT))
                          
                      request = QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry)
                      
                      # simplify and the script works, surprise surprise
                      features = joined_tables_point.getFeatures()
      
                      for feature in features:
                          if feedback.isCanceled():
                              break
      
                          sink.addFeature(feature)
                      
                      layer_2_style = QgsProcessingUtils.mapLayerFromString(styled_layer, context)
                      
                      # these two lines take the symbol and colour at the relevant spot and applies it to the layer
                      symbol = QgsMarkerSymbol.createSimple({'name': layer_shapes[index], 'color': layer_colours[index]})
                      layer_2_style.renderer().setSymbol(symbol)
                      # make the change actual for the layer
                      layer_2_style.triggerRepaint()
      

      Good luck, Andrew

  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.

      • Antoine Wilbers said:

        is there somehow to find all the different objects, methods and classes associated with Renderer() and Symbol() ?

  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()

    • CitanulEht said:

      Hi Ilia, I know it’s been 5 years, but I’m running into the exact same issue, and it’s baffling me. After all this time, all of my Google searches keep leading back to this comment, on this post – did you ever figure it out? I’ve actually found a couple of different ways of doing this, but everything has the same problem; no amount of refresh/repaint commands I’ve come across, at any stage of adding the layers, work until I go into properties and re-save the function which has already been added by the script.

  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.

    • S.E. said:

      Hi, I’m curious about any update here, 2 years later maybe something has changed?
      I’d like to change a marker based on a rule, and use a .png file for this marker….
      I can set the rule, but then I’m apparently stuck with the wrong renderer (QgsRuleBasedRenderer instead of QgsSingleSymbolRenderer).
      This seems easy on the main QGIS interface, but with pyQGIS, I can’t find any doc!

  12. Please, show how to do this with a line layer. And maybe how to add a marker layer to a line layer. Thanks.

  13. Francisco Danubio Salas Rosette said:

    Good morning, it is possible to assign a more complex style, I need to create a style bar and when you click on each one it is drawn with its respective style

  14. Fahad said:

    Thank you very much for this outstanding tutorial! It was easy to understand and follow. I am new in PyQGIS and I want to ask if there is any way from the code line I can distinguish which one of them is objects or properties or functions/methods. Do they follow a certain convention/pattern?

  15. loop said:

    Hello there,

    I interpolate and crop the humidity data with PyQgis. There is no problem so far, but I get an error when I set a style file to the tif file I created with PyQgis. Can you help with this.

    processing.run(“native:setlayerstyle”, {‘INPUT’:’E:\GIS\pro\sourcefiles\idw_humidity.tif’,’STYLE’:’E:\GIS\styles\stylefile.qml’})

Leave a comment

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