I have a QGIS project and I want to run a Report with PyQGIS.

The report is based on two vectors: One is "municipalities_filtred" and the other is the "sections_filtred". The join for the Report is created by the common field "comune_min" that is derived from "municipalities_filtred" and has been created even in "sections_filtred" with intersection of this last one with "municipalities_filtred".

Here the image of a part of the pdf (multipage view, page 1 "SEZIONE DI SANT'ANTONINO, page 2 "PANORAMICA DI CADENZAZZO" and so on) created from the Report that is called "report.pdf" enter image description here page with title "PANORAMICA DI CADENAZZO" is created with a coverage layer based on "municipalities_filtred". page with title "SEZIONE DI CADENAZZO-ROBASACCO", "SEZIONE DI SANT'ANTONINO", "SEZIONE DI CADENAZZO" is created with coverage layer "sections_filtred"

The problem: I am able to create a PDF of the ENTIRE Report with the following code:

project_path = os.getcwd() + '/crdpp.qgz'

project_instance = QgsProject.instance()
layoutmanager = project_instance.layoutManager()
layout = layoutmanager.layoutByName("Report")
settings = QgsLayoutExporter.PdfExportSettings()
QgsLayoutExporter.exportToPdf(layout, './report.pdf', settings)

When the code runs a long PDF file called 'report.pdf' (corresponds to image above) is created based on a layout "Report" defined before outside pyQGIS. But I don't want to have a big PDF! I would like to export many PDFs for the report. One PDF for every feature of the common field "comune_min". One for Cadenazzo, one for Sant'Antonino and so on (see image above). Here the result of such approach and what should be the PDF for Cadenazzo. For Sant'Antonino should be another PDF. enter image description here The filenames would be "zp_Cadenazzo.pdf" and "zp_sant'Antonino"

Then, many PDFs that are subset of 'report.pdf' like that enter image description here

I have found that for the Atlas it can be done easily with this code based on https://gist.github.com/ThomasG77/28eedae0588548403aca2ba781ff443a#create-a-pdf-per-feature-of-coverage-layer:

for feature in municipalities_filtred.getFeatures():
    parameters = {'LAYOUT':'atlas',
    'COVERAGE_LAYER': municipalities_filtred,
    'FILTER_EXPRESSION':"comune_min ='{}'".format(feature['comune_min']),
    out = processing.run("native:atlaslayouttopdf", parameters)

By doing that there is one PDF every feature of the coverage layer "municipalities_filtred". So, I am happy because is what I would like to do with the Report. But with the Atlas you can only work with one coverage layer, for this reason you don't see "sections_filtred". With the atlas from the PDF of zp_Cadenazzo.pdf (see image above) I obtain only "PANORAMICA DI CADENAZZO", WITHOUT the pages with "SEZIONE DI CADENAZZO-ROBASACCO", "SEZIONE DI CADENAZZO". The strenght of the Report is that you can work with TWO coverage layers.

The difficulties is that for the Report QGIS hasn't a processing algorithm as "native:atlaslayouttopdf" and I don't know how to proceed.

1 Answer 1


The last statement comp.atlas().endRender() is outside the loop where comp variable is defined. It runs Ok when layoutmanager.printLayouts() returns any objects. If layoutmanager.printLayouts() returns nothing, you will get this error.

BTW I don't understand why you loop throw all layouts in the project. You already have the layout you need in previous statement layout = layoutmanager.layoutByName("Report").

I think you have to loop throw all municipalities and for each one set filter to sections layer (something like section_parent_id = municipality_id), refresh the map (sections.trigger_repaint()), then export layout to pdf.

  • if I tell you that my first vector is "sections_filtred_report", while the second is "municipalities_filtred". Could you show me your proposal based on my code? With your message I understand what to delete and that I have to change the for loop involving the two vector, but I don't understand how. The rule for the style is already "$id=@atlas_featureid" in the two vector, but is not written in python.
    – simone100
    Commented Jan 17, 2023 at 7:42
  • @bot from this answer people only have to consider the last paragraph because I have edited my original question and your two paraghraph of this answer doesn't make sense anymore. Please delete your first two paragraphs and write as: "I think you have to loop throw all municipalities_filtred and for each one set filter to sections_filtred layer (something like section_filtred_parent_id = municipality_filtred_id), refresh the map (sections_filtred.trigger_repaint()), then export layout to pdf."
    – simone100
    Commented Jan 18, 2023 at 7:40

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.