Set the Map Canvas Background Colour in PyQGIS
The background is the colour behind every layer, and it is set in three different places depending on what you are doing: on the canvas widget for the on-screen view, on QgsMapSettings for a render job, and in the project for the setting that persists. Changing one does not change the others, which is why an export so often comes out white when the canvas is dark, or transparent when nobody asked for transparency.
This page is a focused recipe within Map Canvas Control and Image Export in PyQGIS. It covers setting the colour on the canvas, on a render job, producing a transparent PNG for compositing, and storing the choice so it survives a project reload.
Prerequisites
- QGIS 3.34 LTR (bundled Python 3.12) or newer.
- For the canvas sections, QGIS Desktop with
ifaceavailable. - For the export sections, no GUI is required — see Render a Layer to an Image Headless in PyQGIS.
Set it on the canvas
One call, and the canvas repaints itself.
from qgis.PyQt.QtGui import QColor
canvas = iface.mapCanvas()
canvas.setCanvasColor(QColor("#0f1a17"))
canvas.refresh()
print(canvas.canvasColor().name())
Breakdown: QColor accepts a hex string, an (r, g, b) triple, or a named colour such as "white". setCanvasColor() changes only the widget — nothing is written to the project, so the colour is gone the next time the project is opened. canvasColor().name() returns the hex form, which is the easiest way to read the current value back.
This is the right call for a plugin that offers a dark-map mode or flips the background temporarily while something is being previewed. It is the wrong call for anything that needs to persist.
Set it on a render job
A QgsMapSettings built from scratch has a transparent background. That is a sensible default for compositing and a surprising one when the image lands in a report.
from qgis.core import QgsMapSettings, QgsMapRendererParallelJob, QgsProject
from qgis.PyQt.QtCore import QSize
from qgis.PyQt.QtGui import QColor
layer = QgsProject.instance().mapLayersByName("parcels")[0]
settings = QgsMapSettings()
settings.setLayers([layer])
settings.setDestinationCrs(layer.crs())
settings.setOutputSize(QSize(1200, 900))
settings.setBackgroundColor(QColor("#ffffff"))
extent = layer.extent()
extent.scale(1.1)
settings.setExtent(extent)
job = QgsMapRendererParallelJob(settings)
job.start()
job.waitForFinished()
job.renderedImage().save("/data/exports/parcels.png", "png")
Breakdown: setBackgroundColor() on the settings object is what the render job actually uses; the canvas colour has no influence on it. Setting it explicitly even when you want white is worth the line, because it documents the intent and removes any dependence on defaults. Copy-constructing the settings from canvas.mapSettings() instead would inherit the canvas colour, which is the shortcut when you want the export to match the screen.
Produce a transparent PNG
For a map that will be composited into a layout, a web page or a report template, transparency is the point.
from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtGui import QColor
settings.setBackgroundColor(QColor(Qt.transparent))
job = QgsMapRendererParallelJob(settings)
job.start()
job.waitForFinished()
image = job.renderedImage()
print("has alpha:", image.hasAlphaChannel())
image.save("/data/exports/parcels_transparent.png", "png")
Breakdown: QColor(Qt.transparent) is fully transparent black, which is the correct value — a colour with zero alpha rather than a colour that merely looks like the page. hasAlphaChannel() confirms the rendered image can actually carry transparency; a format such as JPEG cannot, so saving a transparent render as .jpg silently composites it onto black. Always save transparent output as PNG.
Make it persist in the project
To have the colour survive a reload, write it into the project's settings — which is where QGIS itself stores the value from Project Properties.
from qgis.core import QgsProject
from qgis.PyQt.QtGui import QColor
colour = QColor("#0f1a17")
project = QgsProject.instance()
project.writeEntry("Gui", "/CanvasColorRedPart", colour.red())
project.writeEntry("Gui", "/CanvasColorGreenPart", colour.green())
project.writeEntry("Gui", "/CanvasColorBluePart", colour.blue())
iface.mapCanvas().setCanvasColor(colour)
iface.mapCanvas().refresh()
project.write()
Breakdown: QGIS stores the canvas colour as three separate integer entries under the Gui scope rather than as a single hex string, which is why three calls are needed. writeEntry() only stages the change in memory; project.write() is what saves the .qgz file. Setting the canvas colour as well means the change is visible immediately rather than only after the next reload — writing the entries alone updates the file but not the current view, which looks like the call did nothing.
Keep the layers readable against the background
Darkening the canvas is only half a dark map. Layer styling was designed against whatever background it was authored on, and moving it to the opposite end of the scale can leave dark linework invisible and light labels lost. A background change that is not accompanied by a styling review produces a map that looks striking in a screenshot and is unusable in practice.
The practical approach is to keep two saved styles per layer and swap them alongside the background, rather than trying to recolour everything in code:
from qgis.PyQt.QtGui import QColor
DARK = {"bg": "#0f1a17", "style": "/data/styles/parcels_dark.qml"}
LIGHT = {"bg": "#f6f3ea", "style": "/data/styles/parcels_light.qml"}
def apply_theme(canvas, layers, theme):
canvas.setCanvasColor(QColor(theme["bg"]))
for layer in layers:
layer.loadNamedStyle(theme["style"])
layer.triggerRepaint()
canvas.refresh()
Breakdown: loadNamedStyle() replaces the layer's whole renderer and labelling configuration from a .qml file, so a designer can build both variants in the GUI and the script simply picks one. Keeping the background and the style path together in a dictionary means the two can never drift apart — the failure this prevents is exactly a dark background applied without the matching styling. triggerRepaint() per layer plus one refresh() on the canvas is the minimum needed to make the swap visible.
QGIS version compatibility
The examples target QGIS 3.34 LTR (Python 3.12).
| QGIS version | Python | Notes |
|---|---|---|
| 3.28 LTR | 3.9 | Identical API and project entry keys. |
| 3.34 LTR | 3.12 | Baseline for this page. |
| 3.40 / 3.44 | 3.12 | Project background colour also exposed through QgsProject.backgroundColor(); the Gui entries remain valid. |
setCanvasColor(), setBackgroundColor() and the project entry keys are unchanged across 3.x.
Troubleshooting
- The export is transparent when you wanted white. A
QgsMapSettingsbuilt from scratch defaults to transparent. CallsetBackgroundColor()explicitly. - The export is white when the canvas is dark. The settings object does not inherit the canvas colour. Copy-construct from
canvas.mapSettings()or set the colour on both. - The transparent PNG has a black background. It was saved as JPEG, which has no alpha channel. Save as PNG.
- The colour is lost when the project reopens. Only the canvas widget was changed. Write the three
Guientries and callproject.write(). - Writing the project entries changed nothing on screen. They take effect on load. Set the canvas colour too for an immediate result.
- The background shows through the layers oddly. A layer has partial opacity, so the background is compositing through it. Check
layer.opacity().
Conclusion
The background colour lives in three independent places, and knowing which one you are setting removes the whole family of "the export does not match the screen" surprises. Set it on the canvas for the view, on QgsMapSettings for every export — including explicitly when you want white — and in the project's Gui entries when the choice should persist.
Frequently Asked Questions
Why is my exported image transparent when I did not ask for it?
A QgsMapSettings constructed directly defaults to a transparent background. Call setBackgroundColor(QColor("#ffffff")), or copy-construct the settings from canvas.mapSettings() to inherit the project's colour.
How do I make a transparent PNG for compositing?
Set settings.setBackgroundColor(QColor(Qt.transparent)) and save the rendered image as PNG. JPEG has no alpha channel, so a transparent render saved as JPEG is flattened onto black.
Why does the canvas colour reset when I reopen the project?setCanvasColor() only changes the widget. Persist the choice by writing the CanvasColorRedPart, GreenPart and BluePart entries under the Gui scope and calling project.write().
Does the canvas colour affect an export? Only if the render settings were copied from the canvas. A settings object built from scratch has its own background and ignores the canvas entirely.
Can I set a background image instead of a colour? Not on the canvas itself — the background is a single colour. The equivalent is a raster or a plain-coloured polygon layer placed at the bottom of the draw order, which then renders like any other layer and appears in exports without any special handling.
Does the background colour affect printed layouts? No. A print layout has its own page background, set on the layout rather than the canvas. Changing the canvas colour has no effect on a PDF exported from a layout, which catches people out when a dark screen map prints on white paper.
Can different projects have different canvas colours?
Yes — the colour is stored per project, so each .qgz carries its own and switching projects switches the background.