
Qgis Impl Print Layouts
- 8 installs
- 29 repo stars
- Updated July 8, 2026
- openaec-foundation/qgis-claude-skill-package
Helps with ai & agent building tasks.
About
qgis-impl-print-layouts is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- qgis-impl-print-layouts
- AI & Agent Building
- AI-coding skill
Qgis Impl Print Layouts by the numbers
- 8 all-time installs (skills.sh)
- Ranked #12,335 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/openaec-foundation/qgis-claude-skill-package --skill qgis-impl-print-layoutsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 8 |
|---|---|
| repo stars | ★ 29 |
| Last updated | July 8, 2026 |
| Repository | openaec-foundation/qgis-claude-skill-package ↗ |
What it does
Helps with ai & agent building tasks.
Files
qgis-impl-print-layouts
Quick Reference
Layout Creation Workflow
| Step | Action | Key Class |
|---|---|---|
| 1. Create layout | QgsPrintLayout(project) + initializeDefaults() | QgsPrintLayout |
| 2. Register layout | layoutManager().addLayout(layout) | QgsLayoutManager |
| 3. Add map item | QgsLayoutItemMap(layout) + set extent | QgsLayoutItemMap |
| 4. Add decorations | Labels, legend, scale bar, north arrow | Various QgsLayoutItem* |
| 5. Configure export | Set DPI, page size | QgsLayoutExporter.*ExportSettings |
| 6. Export | exportToPdf(), exportToImage(), exportToSvg() | QgsLayoutExporter |
Layout Item Types
| Class | Purpose | Key Methods |
|---|---|---|
QgsLayoutItemMap | Map display | zoomToExtent(), setScale(), setCrs() |
QgsLayoutItemLabel | Text labels | setText(), adjustSizeToText() |
QgsLayoutItemLegend | Map legend | setLinkedMap(), setAutoUpdateModel() |
QgsLayoutItemScaleBar | Scale indicator | setLinkedMap(), setStyle(), applyDefaultSize() |
QgsLayoutItemPicture | Images/north arrows | setPicturePath() |
QgsLayoutItemAttributeTable | Data tables | setVectorLayer(), setMaximumNumberOfFeatures() |
QgsLayoutItemPolygon | Shape decorations | setSymbol() |
Export Result Codes
| Code | Constant | Meaning |
|---|---|---|
| 0 | Success | Export completed |
| 1 | Canceled | Export was canceled |
| 2 | MemoryError | Insufficient memory |
| 3 | FileError | Cannot write to file path |
| 4 | PrintError | Printing subsystem error |
| 5 | SvgLayerError | SVG layer export failed |
| 6 | IteratorError | Atlas iterator failed |
Critical Warnings
NEVER export a layout without setting the map item extent first -- the export produces a blank or incorrect map. ALWAYS call zoomToExtent() or setScale() on every QgsLayoutItemMap before exporting.
NEVER skip initializeDefaults() after creating a new QgsPrintLayout -- without it the layout has no pages and export fails silently.
NEVER forget to call layout.addLayoutItem(item) after creating a layout item -- items created without being added to the layout do not appear in exports.
ALWAYS register the layout with layoutManager().addLayout(layout) if you need it to persist in the project file.
ALWAYS check the export result code -- a return value other than QgsLayoutExporter.Success (0) indicates export failure.
NEVER call atlas.next() without calling atlas.beginRender() first -- the atlas iterator is not initialized until beginRender() is called.
---
Decision Tree: Which Export Method?
Need to export a map?
├── Single layout → use QgsLayoutExporter instance methods
│ ├── Need vector output? → exportToSvg()
│ ├── Need raster image? → exportToImage() (PNG, JPEG, TIFF)
│ └── Need print-ready document? → exportToPdf()
├── Multiple pages per feature? → Atlas generation
│ ├── Separate files per feature? → QgsLayoutExporter.exportToPdfs() (static)
│ └── Single multi-page PDF? → QgsLayoutExporter.exportToPdf() (static, with atlas)
└── Programmatic batch? → Loop with atlas.beginRender() / atlas.next() / atlas.endRender()---
Essential Patterns
Pattern 1: Complete Layout Creation
from qgis.core import (
QgsPrintLayout, QgsProject, QgsLayoutItemMap,
QgsLayoutItemLabel, QgsLayoutItemLegend,
QgsLayoutItemScaleBar, QgsLayoutSize,
QgsLayoutPoint, QgsUnitTypes
)
project = QgsProject.instance()
# Step 1: Create and initialize layout
layout = QgsPrintLayout(project)
layout.initializeDefaults() # REQUIRED: creates default A4 page
layout.setName("My Map Layout")
# Step 2: Register with project
manager = project.layoutManager()
manager.addLayout(layout)
# Step 3: Add map item -- ALWAYS set extent
map_item = QgsLayoutItemMap(layout)
map_item.attemptResize(QgsLayoutSize(200, 150, QgsUnitTypes.LayoutMillimeters))
map_item.attemptMove(QgsLayoutPoint(10, 10, QgsUnitTypes.LayoutMillimeters))
map_item.zoomToExtent(project.mapLayersByName("my_layer")[0].extent())
layout.addLayoutItem(map_item)
# Step 4: Add title label
title = QgsLayoutItemLabel(layout)
title.setText("Map Title")
title.adjustSizeToText()
title.attemptMove(QgsLayoutPoint(10, 5, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(title)
# Step 5: Add legend linked to map
legend = QgsLayoutItemLegend(layout)
legend.setLinkedMap(map_item)
legend.attemptMove(QgsLayoutPoint(220, 10, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(legend)
# Step 6: Add scale bar linked to map
scalebar = QgsLayoutItemScaleBar(layout)
scalebar.setLinkedMap(map_item)
scalebar.setStyle("Single Box")
scalebar.applyDefaultSize()
scalebar.attemptMove(QgsLayoutPoint(10, 170, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(scalebar)Pattern 2: Export to PDF
from qgis.core import QgsLayoutExporter
exporter = QgsLayoutExporter(layout)
pdf_settings = QgsLayoutExporter.PdfExportSettings()
pdf_settings.dpi = 300
result = exporter.exportToPdf("/path/to/output.pdf", pdf_settings)
if result != QgsLayoutExporter.Success:
raise RuntimeError(f"PDF export failed with code: {result}")Pattern 3: Export to Image (PNG/JPEG/TIFF)
exporter = QgsLayoutExporter(layout)
img_settings = QgsLayoutExporter.ImageExportSettings()
img_settings.dpi = 300
result = exporter.exportToImage("/path/to/output.png", img_settings)
if result != QgsLayoutExporter.Success:
raise RuntimeError(f"Image export failed with code: {result}")Pattern 4: Export to SVG
exporter = QgsLayoutExporter(layout)
svg_settings = QgsLayoutExporter.SvgExportSettings()
svg_settings.dpi = 300
result = exporter.exportToSvg("/path/to/output.svg", svg_settings)
if result != QgsLayoutExporter.Success:
raise RuntimeError(f"SVG export failed with code: {result}")Pattern 5: Atlas Generation
atlas = layout.atlas()
atlas.setCoverageLayer(coverage_layer)
atlas.setEnabled(True)
atlas.setFilenameExpression("'output_' || @atlas_featurenumber")
# Export each atlas page to separate PDFs
pdf_settings = QgsLayoutExporter.PdfExportSettings()
pdf_settings.dpi = 300
result = QgsLayoutExporter.exportToPdfs(
atlas, "/path/to/atlas_output/", pdf_settings
)
if result != QgsLayoutExporter.Success:
raise RuntimeError(f"Atlas export failed with code: {result}")Pattern 6: Atlas with Feature Filtering
atlas = layout.atlas()
atlas.setCoverageLayer(coverage_layer)
atlas.setEnabled(True)
atlas.setFilterFeatures(True)
atlas.setFilterExpression("\"type\" = 'residential'")
atlas.setFilenameExpression("\"name\" || '_map'")Pattern 7: Manual Atlas Iteration
atlas = layout.atlas()
atlas.setCoverageLayer(coverage_layer)
atlas.setEnabled(True)
exporter = QgsLayoutExporter(layout)
pdf_settings = QgsLayoutExporter.PdfExportSettings()
pdf_settings.dpi = 300
atlas.beginRender() # REQUIRED before iterating
while atlas.next():
feature_num = atlas.currentFeatureNumber()
page_name = atlas.nameForPage(feature_num)
output_path = f"/path/to/output_{page_name}.pdf"
exporter.exportToPdf(output_path, pdf_settings)
atlas.endRender() # ALWAYS call to clean up---
Common Operations
Page Setup
from qgis.core import QgsLayoutSize, QgsLayoutItemPage, QgsUnitTypes
# Change existing page size to A3 landscape
page = layout.pageCollection().page(0)
page.setPageSize(QgsLayoutSize(420, 297, QgsUnitTypes.LayoutMillimeters))
# Add a second page
new_page = QgsLayoutItemPage(layout)
new_page.setPageSize(QgsLayoutSize(297, 210, QgsUnitTypes.LayoutMillimeters))
layout.pageCollection().addPage(new_page)Map Item: Set Scale and CRS
from qgis.core import QgsCoordinateReferenceSystem
map_item.setScale(50000) # 1:50000
map_item.setCrs(QgsCoordinateReferenceSystem("EPSG:3857"))Map Item: Grid Overlay
from qgis.core import QgsLayoutItemMapGrid
grid = QgsLayoutItemMapGrid("Main Grid", map_item)
grid.setIntervalX(1000)
grid.setIntervalY(1000)
grid.setAnnotationEnabled(True)
grid.setFrameStyle(QgsLayoutItemMapGrid.Zebra)
map_item.grids().addGrid(grid)Add North Arrow (Picture Item)
from qgis.core import QgsLayoutItemPicture
picture = QgsLayoutItemPicture(layout)
picture.setPicturePath("/path/to/north_arrow.svg")
picture.attemptResize(QgsLayoutSize(20, 20, QgsUnitTypes.LayoutMillimeters))
picture.attemptMove(QgsLayoutPoint(250, 10, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(picture)Attribute Table in Layout
from qgis.core import QgsLayoutItemAttributeTable, QgsLayoutFrame
table = QgsLayoutItemAttributeTable.create(layout)
table.setVectorLayer(layer)
table.setMaximumNumberOfFeatures(20)
frame = QgsLayoutFrame(layout, table)
frame.attemptResize(QgsLayoutSize(200, 100, QgsUnitTypes.LayoutMillimeters))
frame.attemptMove(QgsLayoutPoint(10, 200, QgsUnitTypes.LayoutMillimeters))
table.addFrame(frame)
layout.addMultiFrame(table)Expression-Based Labels
label = QgsLayoutItemLabel(layout)
# Use QGIS expressions inside [% %] delimiters
label.setText("[% @project_title %] - Scale 1:[% @map_scale %]")
label.attemptMove(QgsLayoutPoint(10, 5, QgsUnitTypes.LayoutMillimeters))
label.adjustSizeToText()
layout.addLayoutItem(label)Layout Manager Operations
manager = QgsProject.instance().layoutManager()
# List all layouts
for l in manager.printLayouts():
print(l.name())
# Get layout by name
my_layout = manager.layoutByName("My Layout")
# Remove layout
manager.removeLayout(layout)Template Save/Load
from qgis.core import QgsReadWriteContext
from qgis.PyQt.QtXml import QDomDocument
# Save layout as .qpt template
doc = QDomDocument()
layout.writeXml(doc, QgsReadWriteContext())
with open("/path/to/template.qpt", "w") as f:
f.write(doc.toString())
# Load layout from .qpt template
with open("/path/to/template.qpt", "r") as f:
content = f.read()
doc = QDomDocument()
doc.setContent(content)
new_layout = QgsPrintLayout(project)
new_layout.readXml(doc.documentElement(), doc, QgsReadWriteContext())
new_layout.setName("From Template")
manager.addLayout(new_layout)Shape Decorations (Polygon/Polyline)
from qgis.core import QgsLayoutItemPolygon, QgsFillSymbol
from qgis.PyQt.QtCore import QPointF
from qgis.PyQt.QtGui import QPolygonF
polygon = QgsLayoutItemPolygon(
QPolygonF([QPointF(0, 0), QPointF(100, 0), QPointF(100, 50), QPointF(0, 50)]),
layout
)
props = {
'color': '0,0,0,0',
'style': 'no',
'outline_color': 'black',
'outline_width': '0.5'
}
symbol = QgsFillSymbol.createSimple(props)
polygon.setSymbol(symbol)
layout.addLayoutItem(polygon)---
Common Positioning Methods (All Layout Items)
| Method | Purpose |
|---|---|
attemptMove(QgsLayoutPoint) | Set position on page |
attemptResize(QgsLayoutSize) | Set item dimensions |
setFrameEnabled(bool) | Toggle border frame |
setBackgroundEnabled(bool) | Toggle background fill |
setLocked(bool) | Prevent accidental edits |
setReferencePoint(point) | Set anchor point for positioning |
---
Reference Links
- references/methods.md -- API signatures for QgsPrintLayout, QgsLayoutExporter, layout items
- references/examples.md -- Complete working examples for layout creation and export
- references/anti-patterns.md -- Common mistakes with layouts and exports
Official Sources
- https://qgis.org/pyqgis/master/core/QgsPrintLayout.html
- https://qgis.org/pyqgis/master/core/QgsLayoutExporter.html
- https://qgis.org/pyqgis/master/core/QgsLayoutItemMap.html
- https://docs.qgis.org/latest/en/docs/pyqgis_developer_cookbook/composer.html
Anti-Patterns (QGIS Print Layouts)
1. Exporting Without Setting Map Extent
# WRONG: Map item has no extent set -- produces blank or incorrect output
map_item = QgsLayoutItemMap(layout)
map_item.attemptResize(QgsLayoutSize(200, 150, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(map_item)
exporter.exportToPdf("/tmp/output.pdf", settings) # blank map!
# CORRECT: ALWAYS set the extent before export
map_item = QgsLayoutItemMap(layout)
map_item.attemptResize(QgsLayoutSize(200, 150, QgsUnitTypes.LayoutMillimeters))
map_item.zoomToExtent(layer.extent()) # set extent BEFORE export
layout.addLayoutItem(map_item)
exporter.exportToPdf("/tmp/output.pdf", settings)WHY: A QgsLayoutItemMap without an extent defaults to an empty or arbitrary view. The export succeeds (returns Success) but the map area is blank or shows the wrong area.
---
2. Skipping initializeDefaults()
# WRONG: Layout has no pages -- export produces nothing
layout = QgsPrintLayout(project)
layout.setName("No Pages")
# Missing: layout.initializeDefaults()
exporter = QgsLayoutExporter(layout)
result = exporter.exportToPdf("/tmp/output.pdf", settings) # empty or error
# CORRECT: ALWAYS call initializeDefaults() after creating a new layout
layout = QgsPrintLayout(project)
layout.initializeDefaults() # creates default A4 page
layout.setName("With Pages")WHY: QgsPrintLayout() creates an empty layout with zero pages. Without initializeDefaults(), there is no page to render, and exports fail silently or produce empty files.
---
3. Forgetting to Add Items to Layout
# WRONG: Item created but never added to layout
map_item = QgsLayoutItemMap(layout)
map_item.attemptResize(QgsLayoutSize(200, 150, QgsUnitTypes.LayoutMillimeters))
map_item.zoomToExtent(layer.extent())
# Missing: layout.addLayoutItem(map_item)
exporter.exportToPdf("/tmp/output.pdf", settings) # map not visible!
# CORRECT: ALWAYS call addLayoutItem() after configuring the item
map_item = QgsLayoutItemMap(layout)
map_item.attemptResize(QgsLayoutSize(200, 150, QgsUnitTypes.LayoutMillimeters))
map_item.zoomToExtent(layer.extent())
layout.addLayoutItem(map_item) # register with layoutWHY: Layout items exist as standalone objects until explicitly added. Without addLayoutItem(), the item is not part of the layout and does not appear in exports.
---
4. Atlas Iteration Without beginRender()
# WRONG: Calling next() without beginRender() -- iterator not initialized
atlas = layout.atlas()
atlas.setCoverageLayer(coverage_layer)
atlas.setEnabled(True)
while atlas.next(): # undefined behavior!
exporter.exportToPdf(f"/tmp/page_{atlas.currentFeatureNumber()}.pdf", settings)
# CORRECT: ALWAYS bracket iteration with beginRender() / endRender()
atlas.beginRender()
while atlas.next():
exporter.exportToPdf(f"/tmp/page_{atlas.currentFeatureNumber()}.pdf", settings)
atlas.endRender()WHY: The atlas iterator is not initialized until beginRender() is called. Calling next() on an uninitialized iterator produces undefined behavior or an infinite loop.
---
5. Not Checking Export Result
# WRONG: Ignoring the export result -- silent failure
exporter.exportToPdf("/tmp/output.pdf", settings)
print("Export done!") # might not have succeeded
# CORRECT: ALWAYS check the result code
result = exporter.exportToPdf("/tmp/output.pdf", settings)
if result != QgsLayoutExporter.Success:
raise RuntimeError(f"Export failed with code: {result}")WHY: Export can fail for many reasons (file permissions, memory, invalid path). The method returns an error code instead of raising an exception. Ignoring it masks failures.
---
6. Legend Not Linked to Map
# WRONG: Legend shows all project layers, not map-specific layers
legend = QgsLayoutItemLegend(layout)
layout.addLayoutItem(legend)
# Missing: legend.setLinkedMap(map_item)
# CORRECT: ALWAYS link the legend to the map item
legend = QgsLayoutItemLegend(layout)
legend.setLinkedMap(map_item)
layout.addLayoutItem(legend)WHY: An unlinked legend displays all layers in the project, not the layers visible in the map item. This creates misleading legends that do not match the map content.
---
7. Scale Bar Not Linked to Map
# WRONG: Scale bar has no reference map -- shows incorrect scale
scalebar = QgsLayoutItemScaleBar(layout)
scalebar.setStyle("Single Box")
scalebar.applyDefaultSize()
layout.addLayoutItem(scalebar)
# Missing: scalebar.setLinkedMap(map_item)
# CORRECT: ALWAYS link the scale bar to the map item
scalebar = QgsLayoutItemScaleBar(layout)
scalebar.setLinkedMap(map_item)
scalebar.setStyle("Single Box")
scalebar.applyDefaultSize()
layout.addLayoutItem(scalebar)WHY: A scale bar without a linked map cannot calculate the correct scale. It displays meaningless or zero values.
---
8. Attribute Table Without Frame
# WRONG: Table created but no frame added -- invisible in export
table = QgsLayoutItemAttributeTable.create(layout)
table.setVectorLayer(layer)
layout.addMultiFrame(table)
# Missing: QgsLayoutFrame creation and table.addFrame()
# CORRECT: ALWAYS create a frame and add it to the table
table = QgsLayoutItemAttributeTable.create(layout)
table.setVectorLayer(layer)
frame = QgsLayoutFrame(layout, table)
frame.attemptResize(QgsLayoutSize(200, 100, QgsUnitTypes.LayoutMillimeters))
frame.attemptMove(QgsLayoutPoint(10, 10, QgsUnitTypes.LayoutMillimeters))
table.addFrame(frame)
layout.addMultiFrame(table)WHY: QgsLayoutItemAttributeTable is a multi-frame object. Without at least one QgsLayoutFrame, the table has no visual representation and does not appear in the layout.
---
9. Using addLayout() Without Setting Name
# WRONG: Layout without a name causes issues with layoutByName()
layout = QgsPrintLayout(project)
layout.initializeDefaults()
project.layoutManager().addLayout(layout)
# Later: manager.layoutByName("") returns None or wrong result
# CORRECT: ALWAYS set a unique name before registering
layout = QgsPrintLayout(project)
layout.initializeDefaults()
layout.setName("Unique Layout Name")
project.layoutManager().addLayout(layout)WHY: The layout manager uses names for lookup. Layouts without names are inaccessible via layoutByName() and create confusion when multiple unnamed layouts exist.
---
10. Forgetting endRender() After Atlas Iteration
# WRONG: beginRender() without endRender() -- resources not cleaned up
atlas.beginRender()
while atlas.next():
exporter.exportToPdf(f"/tmp/page_{atlas.currentFeatureNumber()}.pdf", settings)
# Missing: atlas.endRender()
# CORRECT: ALWAYS call endRender() to release resources
atlas.beginRender()
while atlas.next():
exporter.exportToPdf(f"/tmp/page_{atlas.currentFeatureNumber()}.pdf", settings)
atlas.endRender()WHY: beginRender() acquires resources and locks the coverage layer for iteration. Without endRender(), those resources are not released, which can cause memory leaks and layer lock issues.
---
11. Writing to Non-Existent Output Directory
# WRONG: Parent directory does not exist -- FileError
result = exporter.exportToPdf("/tmp/nonexistent_dir/output.pdf", settings)
# result == QgsLayoutExporter.FileError
# CORRECT: ALWAYS ensure the output directory exists
import os
output_dir = "/tmp/map_exports/"
os.makedirs(output_dir, exist_ok=True)
result = exporter.exportToPdf(os.path.join(output_dir, "output.pdf"), settings)WHY: QgsLayoutExporter does not create parent directories. If the target directory does not exist, the export fails with FileError.
Working Code Examples (QGIS Print Layouts)
Example 1: Minimal Map Export to PDF
Creates a layout with a single map item and exports to PDF.
from qgis.core import (
QgsPrintLayout, QgsProject, QgsLayoutItemMap,
QgsLayoutExporter, QgsLayoutSize, QgsLayoutPoint,
QgsUnitTypes
)
project = QgsProject.instance()
layer = project.mapLayersByName("my_layer")[0]
# Create layout
layout = QgsPrintLayout(project)
layout.initializeDefaults()
layout.setName("Quick Export")
# Add map item with extent
map_item = QgsLayoutItemMap(layout)
map_item.attemptResize(QgsLayoutSize(277, 190, QgsUnitTypes.LayoutMillimeters))
map_item.attemptMove(QgsLayoutPoint(10, 10, QgsUnitTypes.LayoutMillimeters))
map_item.zoomToExtent(layer.extent())
layout.addLayoutItem(map_item)
# Export
exporter = QgsLayoutExporter(layout)
settings = QgsLayoutExporter.PdfExportSettings()
settings.dpi = 300
result = exporter.exportToPdf("/tmp/map_output.pdf", settings)
assert result == QgsLayoutExporter.Success, f"Export failed: {result}"---
Example 2: Full Map Composition (Title, Legend, Scale Bar, North Arrow)
from qgis.core import (
QgsPrintLayout, QgsProject, QgsLayoutItemMap,
QgsLayoutItemLabel, QgsLayoutItemLegend,
QgsLayoutItemScaleBar, QgsLayoutItemPicture,
QgsLayoutExporter, QgsLayoutSize, QgsLayoutPoint,
QgsUnitTypes
)
from qgis.PyQt.QtGui import QFont, QColor
project = QgsProject.instance()
layer = project.mapLayersByName("parcels")[0]
# Create layout
layout = QgsPrintLayout(project)
layout.initializeDefaults()
layout.setName("Full Composition")
project.layoutManager().addLayout(layout)
# Map item (main area)
map_item = QgsLayoutItemMap(layout)
map_item.attemptResize(QgsLayoutSize(180, 160, QgsUnitTypes.LayoutMillimeters))
map_item.attemptMove(QgsLayoutPoint(10, 25, QgsUnitTypes.LayoutMillimeters))
map_item.zoomToExtent(layer.extent())
layout.addLayoutItem(map_item)
# Title
title = QgsLayoutItemLabel(layout)
title.setText("Parcel Overview Map")
title.setFont(QFont("Arial", 18))
title.setFontColor(QColor(0, 0, 0))
title.adjustSizeToText()
title.attemptMove(QgsLayoutPoint(10, 5, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(title)
# Legend
legend = QgsLayoutItemLegend(layout)
legend.setLinkedMap(map_item)
legend.setTitle("Legend")
legend.attemptMove(QgsLayoutPoint(200, 25, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(legend)
# Scale bar
scalebar = QgsLayoutItemScaleBar(layout)
scalebar.setLinkedMap(map_item)
scalebar.setStyle("Double Box")
scalebar.applyDefaultSize()
scalebar.attemptMove(QgsLayoutPoint(10, 190, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(scalebar)
# North arrow
north_arrow = QgsLayoutItemPicture(layout)
north_arrow.setPicturePath("/path/to/north_arrow.svg")
north_arrow.attemptResize(QgsLayoutSize(15, 15, QgsUnitTypes.LayoutMillimeters))
north_arrow.attemptMove(QgsLayoutPoint(265, 25, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(north_arrow)
# Export to PDF
exporter = QgsLayoutExporter(layout)
settings = QgsLayoutExporter.PdfExportSettings()
settings.dpi = 300
result = exporter.exportToPdf("/tmp/full_composition.pdf", settings)
assert result == QgsLayoutExporter.Success---
Example 3: Atlas Export (Separate PDFs per Feature)
from qgis.core import (
QgsPrintLayout, QgsProject, QgsLayoutItemMap,
QgsLayoutItemLabel, QgsLayoutExporter, QgsLayoutSize,
QgsLayoutPoint, QgsUnitTypes
)
project = QgsProject.instance()
coverage = project.mapLayersByName("municipalities")[0]
# Create layout
layout = QgsPrintLayout(project)
layout.initializeDefaults()
layout.setName("Atlas Layout")
project.layoutManager().addLayout(layout)
# Map item -- atlas will control the extent
map_item = QgsLayoutItemMap(layout)
map_item.attemptResize(QgsLayoutSize(260, 180, QgsUnitTypes.LayoutMillimeters))
map_item.attemptMove(QgsLayoutPoint(10, 20, QgsUnitTypes.LayoutMillimeters))
map_item.zoomToExtent(coverage.extent())
layout.addLayoutItem(map_item)
# Dynamic title using atlas expression
title = QgsLayoutItemLabel(layout)
title.setText("[% @atlas_featurenumber %] - [% \"name\" %]")
title.adjustSizeToText()
title.attemptMove(QgsLayoutPoint(10, 5, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(title)
# Configure atlas
atlas = layout.atlas()
atlas.setCoverageLayer(coverage)
atlas.setEnabled(True)
atlas.setFilenameExpression("\"name\" || '_map'")
# Set map to follow atlas feature
map_item.setAtlasDriven(True)
map_item.setAtlasScalingMode(QgsLayoutItemMap.Auto)
map_item.setAtlasMargin(0.10) # 10% margin around feature
# Export all atlas pages to separate PDFs
pdf_settings = QgsLayoutExporter.PdfExportSettings()
pdf_settings.dpi = 300
result = QgsLayoutExporter.exportToPdfs(
atlas, "/tmp/atlas_output/", pdf_settings
)
assert result == QgsLayoutExporter.Success---
Example 4: Custom Page Size (A3 Landscape)
from qgis.core import (
QgsPrintLayout, QgsProject, QgsLayoutSize, QgsUnitTypes
)
project = QgsProject.instance()
layout = QgsPrintLayout(project)
layout.initializeDefaults()
layout.setName("A3 Landscape")
# Change default page to A3 landscape
page = layout.pageCollection().page(0)
page.setPageSize(QgsLayoutSize(420, 297, QgsUnitTypes.LayoutMillimeters))
project.layoutManager().addLayout(layout)---
Example 5: Layout with Attribute Table
from qgis.core import (
QgsPrintLayout, QgsProject, QgsLayoutItemMap,
QgsLayoutItemAttributeTable, QgsLayoutFrame,
QgsLayoutExporter, QgsLayoutSize, QgsLayoutPoint,
QgsUnitTypes
)
project = QgsProject.instance()
layer = project.mapLayersByName("sites")[0]
layout = QgsPrintLayout(project)
layout.initializeDefaults()
layout.setName("Map with Table")
project.layoutManager().addLayout(layout)
# Map item (top half)
map_item = QgsLayoutItemMap(layout)
map_item.attemptResize(QgsLayoutSize(277, 100, QgsUnitTypes.LayoutMillimeters))
map_item.attemptMove(QgsLayoutPoint(10, 10, QgsUnitTypes.LayoutMillimeters))
map_item.zoomToExtent(layer.extent())
layout.addLayoutItem(map_item)
# Attribute table (bottom half)
table = QgsLayoutItemAttributeTable.create(layout)
table.setVectorLayer(layer)
table.setMaximumNumberOfFeatures(15)
frame = QgsLayoutFrame(layout, table)
frame.attemptResize(QgsLayoutSize(277, 70, QgsUnitTypes.LayoutMillimeters))
frame.attemptMove(QgsLayoutPoint(10, 120, QgsUnitTypes.LayoutMillimeters))
table.addFrame(frame)
layout.addMultiFrame(table)
# Export
exporter = QgsLayoutExporter(layout)
settings = QgsLayoutExporter.PdfExportSettings()
settings.dpi = 300
result = exporter.exportToPdf("/tmp/map_with_table.pdf", settings)
assert result == QgsLayoutExporter.Success---
Example 6: Save and Load Layout Template
from qgis.core import QgsPrintLayout, QgsProject, QgsReadWriteContext
from qgis.PyQt.QtXml import QDomDocument
project = QgsProject.instance()
# --- Save existing layout as template ---
existing_layout = project.layoutManager().layoutByName("My Layout")
doc = QDomDocument()
existing_layout.writeXml(doc, QgsReadWriteContext())
with open("/tmp/my_template.qpt", "w") as f:
f.write(doc.toString())
# --- Load template into new layout ---
with open("/tmp/my_template.qpt", "r") as f:
content = f.read()
doc = QDomDocument()
doc.setContent(content)
new_layout = QgsPrintLayout(project)
new_layout.readXml(doc.documentElement(), doc, QgsReadWriteContext())
new_layout.setName("Loaded From Template")
project.layoutManager().addLayout(new_layout)---
Example 7: Multi-Page Layout
from qgis.core import (
QgsPrintLayout, QgsProject, QgsLayoutItemMap,
QgsLayoutItemLabel, QgsLayoutItemPage,
QgsLayoutSize, QgsLayoutPoint, QgsUnitTypes
)
project = QgsProject.instance()
layout = QgsPrintLayout(project)
layout.initializeDefaults()
layout.setName("Multi-Page Report")
# Page 1: Overview map
map_overview = QgsLayoutItemMap(layout)
map_overview.attemptResize(QgsLayoutSize(277, 190, QgsUnitTypes.LayoutMillimeters))
map_overview.attemptMove(QgsLayoutPoint(10, 10, QgsUnitTypes.LayoutMillimeters))
map_overview.zoomToExtent(project.mapLayersByName("region")[0].extent())
layout.addLayoutItem(map_overview)
# Add second page
page2 = QgsLayoutItemPage(layout)
page2.setPageSize(QgsLayoutSize(297, 210, QgsUnitTypes.LayoutMillimeters))
layout.pageCollection().addPage(page2)
# Page 2: Detail map (items on page 2 use y-offset = page1_height + gap)
page1_height = 210 # A4 portrait height in mm
detail_label = QgsLayoutItemLabel(layout)
detail_label.setText("Detail View")
detail_label.adjustSizeToText()
detail_label.attemptMove(QgsLayoutPoint(10, page1_height + 5, QgsUnitTypes.LayoutMillimeters))
layout.addLayoutItem(detail_label)
map_detail = QgsLayoutItemMap(layout)
map_detail.attemptResize(QgsLayoutSize(277, 180, QgsUnitTypes.LayoutMillimeters))
map_detail.attemptMove(QgsLayoutPoint(10, page1_height + 20, QgsUnitTypes.LayoutMillimeters))
map_detail.zoomToExtent(project.mapLayersByName("detail_area")[0].extent())
layout.addLayoutItem(map_detail)
project.layoutManager().addLayout(layout)---
Example 8: Export to Multiple Formats
from qgis.core import QgsLayoutExporter
exporter = QgsLayoutExporter(layout)
# PDF (vector, print-ready)
pdf_settings = QgsLayoutExporter.PdfExportSettings()
pdf_settings.dpi = 300
pdf_settings.forceVectorOutput = True
result = exporter.exportToPdf("/tmp/output.pdf", pdf_settings)
assert result == QgsLayoutExporter.Success
# PNG (raster, web use)
img_settings = QgsLayoutExporter.ImageExportSettings()
img_settings.dpi = 150
result = exporter.exportToImage("/tmp/output.png", img_settings)
assert result == QgsLayoutExporter.Success
# SVG (vector, editable)
svg_settings = QgsLayoutExporter.SvgExportSettings()
svg_settings.dpi = 300
svg_settings.forceVectorOutput = True
result = exporter.exportToSvg("/tmp/output.svg", svg_settings)
assert result == QgsLayoutExporter.Success---
Example 9: Map with Grid Overlay
from qgis.core import (
QgsPrintLayout, QgsProject, QgsLayoutItemMap,
QgsLayoutItemMapGrid, QgsLayoutSize, QgsLayoutPoint,
QgsUnitTypes, QgsCoordinateReferenceSystem
)
project = QgsProject.instance()
layout = QgsPrintLayout(project)
layout.initializeDefaults()
layout.setName("Grid Map")
map_item = QgsLayoutItemMap(layout)
map_item.attemptResize(QgsLayoutSize(250, 180, QgsUnitTypes.LayoutMillimeters))
map_item.attemptMove(QgsLayoutPoint(10, 10, QgsUnitTypes.LayoutMillimeters))
map_item.zoomToExtent(project.mapLayersByName("my_layer")[0].extent())
layout.addLayoutItem(map_item)
# Add coordinate grid
grid = QgsLayoutItemMapGrid("Coordinate Grid", map_item)
grid.setIntervalX(1000) # Grid line every 1000 map units
grid.setIntervalY(1000)
grid.setAnnotationEnabled(True)
grid.setFrameStyle(QgsLayoutItemMapGrid.Zebra)
grid.setCrs(QgsCoordinateReferenceSystem("EPSG:28992"))
map_item.grids().addGrid(grid)
project.layoutManager().addLayout(layout)API Signatures Reference (QGIS Print Layouts)
QgsPrintLayout
The main layout class for print compositions. Inherits from QgsLayout.
class QgsPrintLayout(QgsLayout):
def __init__(self, project: QgsProject) -> None
def initializeDefaults(self) -> None
# Creates a default A4 page. ALWAYS call after construction.
def setName(self, name: str) -> None
def name(self) -> str
def atlas(self) -> QgsLayoutAtlas
def addLayoutItem(self, item: QgsLayoutItem) -> None
def removeLayoutItem(self, item: QgsLayoutItem) -> None
def addMultiFrame(self, multiFrame: QgsLayoutMultiFrame) -> None
def removeMultiFrame(self, multiFrame: QgsLayoutMultiFrame) -> None
def pageCollection(self) -> QgsLayoutPageCollection
def writeXml(self, document: QDomDocument, context: QgsReadWriteContext) -> bool
def readXml(self, element: QDomElement, document: QDomDocument, context: QgsReadWriteContext) -> bool---
QgsLayoutManager
Manages all layouts within a project.
class QgsLayoutManager:
def addLayout(self, layout: QgsMasterLayoutInterface) -> bool
def removeLayout(self, layout: QgsMasterLayoutInterface) -> bool
def printLayouts(self) -> List[QgsPrintLayout]
def layoutByName(self, name: str) -> QgsMasterLayoutInterface
def layouts(self) -> List[QgsMasterLayoutInterface]
def clear(self) -> NoneUsage: Access via QgsProject.instance().layoutManager().
---
QgsLayoutItemMap
Displays a map within a layout. ALWAYS set the extent before export.
class QgsLayoutItemMap(QgsLayoutItem):
def __init__(self, layout: QgsLayout) -> None
def zoomToExtent(self, extent: QgsRectangle) -> None
# Sets the map extent to match the given rectangle.
def setScale(self, scale: float, forceUpdate: bool = True) -> None
# Sets the map scale (e.g., 50000 for 1:50000).
def scale(self) -> float
def setCrs(self, crs: QgsCoordinateReferenceSystem) -> None
def crs(self) -> QgsCoordinateReferenceSystem
def extent(self) -> QgsRectangle
def setExtent(self, extent: QgsRectangle) -> None
def setFollowVisibilityPreset(self, follow: bool) -> None
def setFollowVisibilityPresetName(self, name: str) -> None
def setLayers(self, layers: List[QgsMapLayer]) -> None
# Locks specific layers to display in this map item.
def layers(self) -> List[QgsMapLayer]
def setKeepLayerSet(self, enabled: bool) -> None
def grids(self) -> QgsLayoutItemMapGridStack
def overviews(self) -> QgsLayoutItemMapOverviewStack
def setMapRotation(self, rotation: float) -> None
def mapRotation(self) -> float---
QgsLayoutItemLabel
Text label item for titles, descriptions, and dynamic expressions.
class QgsLayoutItemLabel(QgsLayoutItem):
def __init__(self, layout: QgsLayout) -> None
def setText(self, text: str) -> None
# Supports QGIS expressions inside [% %] delimiters.
def text(self) -> str
def adjustSizeToText(self) -> None
def setFont(self, font: QFont) -> None
def setFontColor(self, color: QColor) -> None
def setHAlign(self, alignment: Qt.AlignmentFlag) -> None
def setVAlign(self, alignment: Qt.AlignmentFlag) -> None
def setMarginX(self, margin: float) -> None
def setMarginY(self, margin: float) -> None
def setMode(self, mode: QgsLayoutItemLabel.Mode) -> None
# Mode.ModeFont for plain text, Mode.ModeHtml for HTML rendering.---
QgsLayoutItemLegend
Legend item linked to a map item.
class QgsLayoutItemLegend(QgsLayoutItem):
def __init__(self, layout: QgsLayout) -> None
def setLinkedMap(self, map_item: QgsLayoutItemMap) -> None
def setAutoUpdateModel(self, auto: bool) -> None
# When True, legend updates automatically from linked map layers.
def setTitle(self, title: str) -> None
def setStyle(self, component: QgsLegendStyle.Style, style: QgsLegendStyle) -> None
def setColumnCount(self, count: int) -> None
def setSplitLayer(self, split: bool) -> None
def setEqualColumnWidth(self, equal: bool) -> None
def model(self) -> QgsLegendModel---
QgsLayoutItemScaleBar
Scale bar linked to a map item.
class QgsLayoutItemScaleBar(QgsLayoutItem):
def __init__(self, layout: QgsLayout) -> None
def setLinkedMap(self, map_item: QgsLayoutItemMap) -> None
def setStyle(self, name: str) -> None
# Styles: "Single Box", "Double Box", "Line Ticks Middle",
# "Line Ticks Down", "Line Ticks Up", "Numeric"
def applyDefaultSize(self) -> None
def setUnits(self, units: QgsUnitTypes.DistanceUnit) -> None
def setNumberOfSegments(self, segments: int) -> None
def setNumberOfSegmentsLeft(self, segments: int) -> None
def setUnitsPerSegment(self, units: float) -> None
def setMapUnitsPerScaleBarUnit(self, units: float) -> None
def setUnitLabel(self, label: str) -> None---
QgsLayoutItemPicture
Image item for logos, north arrows, and other graphics.
class QgsLayoutItemPicture(QgsLayoutItem):
def __init__(self, layout: QgsLayout) -> None
def setPicturePath(self, path: str) -> None
# Supports SVG, PNG, JPEG, and other image formats.
def setPictureRotation(self, rotation: float) -> None
def setResizeMode(self, mode: QgsLayoutItemPicture.ResizeMode) -> None
def setLinkedMap(self, map_item: QgsLayoutItemMap) -> None
# For north arrows: syncs rotation with map rotation.
def setSvgFillColor(self, color: QColor) -> None
def setSvgStrokeColor(self, color: QColor) -> None---
QgsLayoutItemAttributeTable
Displays feature attributes in a table format. Uses a multi-frame model.
class QgsLayoutItemAttributeTable(QgsLayoutMultiFrame):
@staticmethod
def create(layout: QgsLayout) -> QgsLayoutItemAttributeTable
def setVectorLayer(self, layer: QgsVectorLayer) -> None
def setMaximumNumberOfFeatures(self, max: int) -> None
def setFilterFeatures(self, filter: bool) -> None
def setFeatureFilter(self, expression: str) -> None
def setDisplayedFields(self, fields: List[str]) -> None
def addFrame(self, frame: QgsLayoutFrame) -> None
def setContentTextFormat(self, format: QgsTextFormat) -> None
def setHeaderTextFormat(self, format: QgsTextFormat) -> None---
QgsLayoutExporter
Handles export of layouts to PDF, SVG, and image formats.
class QgsLayoutExporter:
def __init__(self, layout: QgsLayout) -> None
# Instance methods (single layout export)
def exportToPdf(self, filePath: str, settings: PdfExportSettings) -> ExportResult
def exportToImage(self, filePath: str, settings: ImageExportSettings) -> ExportResult
def exportToSvg(self, filePath: str, settings: SvgExportSettings) -> ExportResult
# Static methods (atlas export)
@staticmethod
def exportToPdfs(atlas: QgsLayoutAtlas, baseFilePath: str, settings: PdfExportSettings) -> ExportResult
@staticmethod
def exportToPdf(atlas: QgsLayoutAtlas, filePath: str, settings: PdfExportSettings) -> ExportResult
# Exports all atlas pages to a single multi-page PDF.
# Export settings classes
class PdfExportSettings:
dpi: float # Default: 300
rasterizeWholeImage: bool
forceVectorOutput: bool
appendGeoreference: bool
textRenderFormat: QgsRenderContext.TextRenderFormat
simplifyGeometries: bool
class ImageExportSettings:
dpi: float # Default: 300
imageSizePixels: QSize # Override output size
cropToContents: bool
cropMargins: QgsMargins
generateWorldFile: bool
class SvgExportSettings:
dpi: float # Default: 300
forceVectorOutput: bool
exportAsLayers: bool
cropToContents: bool
# Result enum
Success = 0
Canceled = 1
MemoryError = 2
FileError = 3
PrintError = 4
SvgLayerError = 5
IteratorError = 6---
QgsLayoutAtlas
Atlas generation for iterating over coverage layer features.
class QgsLayoutAtlas:
def setCoverageLayer(self, layer: QgsVectorLayer) -> None
def coverageLayer(self) -> QgsVectorLayer
def setEnabled(self, enabled: bool) -> None
def enabled(self) -> bool
def setFilenameExpression(self, expression: str) -> bool
def setFilterFeatures(self, filter: bool) -> None
def setFilterExpression(self, expression: str) -> None
def setSortFeatures(self, sort: bool) -> None
def setSortExpression(self, expression: str) -> None
def setSortAscending(self, ascending: bool) -> None
def beginRender(self) -> bool
# MUST be called before iterating with next().
def next(self) -> bool
# Advances to the next atlas feature. Returns False when done.
def endRender(self) -> None
# ALWAYS call after iteration is complete.
def currentFeatureNumber(self) -> int
def nameForPage(self, pageNumber: int) -> str
def count(self) -> int
def seekTo(self, feature: int) -> bool
# Jump to a specific feature by index.---
QgsLayoutPageCollection
Manages pages within a layout.
class QgsLayoutPageCollection:
def page(self, index: int) -> QgsLayoutItemPage
def pageCount(self) -> int
def addPage(self, page: QgsLayoutItemPage) -> None
def deletePage(self, index: int) -> None
def pages(self) -> List[QgsLayoutItemPage]---
QgsLayoutItemPage
Represents a single page in the layout.
class QgsLayoutItemPage(QgsLayoutItem):
def __init__(self, layout: QgsLayout) -> None
def setPageSize(self, size: QgsLayoutSize) -> None
def pageSize(self) -> QgsLayoutSize---
Common Positioning Classes
class QgsLayoutSize:
def __init__(self, width: float, height: float, units: QgsUnitTypes.LayoutUnit = QgsUnitTypes.LayoutMillimeters) -> None
class QgsLayoutPoint:
def __init__(self, x: float, y: float, units: QgsUnitTypes.LayoutUnit = QgsUnitTypes.LayoutMillimeters) -> None---
QgsLayoutItemMapGrid
Grid overlay for map items.
class QgsLayoutItemMapGrid:
def __init__(self, name: str, map_item: QgsLayoutItemMap) -> None
def setIntervalX(self, interval: float) -> None
def setIntervalY(self, interval: float) -> None
def setAnnotationEnabled(self, enabled: bool) -> None
def setFrameStyle(self, style: QgsLayoutItemMapGrid.FrameStyle) -> None
# Styles: NoFrame, Zebra, InteriorTicks, ExteriorTicks, InteriorExteriorTicks, LineBorder
def setGridLineColor(self, color: QColor) -> None
def setGridLineWidth(self, width: float) -> None
def setCrs(self, crs: QgsCoordinateReferenceSystem) -> NoneUsage: Access via map_item.grids().addGrid(grid).