3

I'm trying to setup an application for my company, that generates a PDF file for specific user generated coordinates:

  1. The user enters an address into the frontend (Flask generated), which is translated via Here Maps Geocoder API to coordinates.
  2. The coordinates are sent to pyQGIS running on the company server.
  3. PyQGIS connects to PostgreSQL, performing some spatial joins. This way suitable information for the coordinates is collected in a PDF file being returned to the user.

The good thing: It works! Except for when loading QGIS Command Prompt says:

libpng warning: iCCP: known incorrect sRGB profile

QObject::connect: invalid null parameter

But it seems to be no big issue, it works anyway.

The bad thing: It only works when all the PyQGIS code is in one single script, looking like this (shortened, loading only the PDF cover):

def print_pdf(new_address_dict):
    x = new_address_dict['x']
    y = new_address_dict['y']
    address = new_address_dict['address']

    path = os.path.dirname(os.path.abspath(__file__))

    QgsApplication.setPrefixPath("C:/Program Files/QGIS 3.4", False)

    # Create a reference to the QgsApplication.  Setting the
    # second argument to False disables the GUI.
    qgs = QgsApplication([], False)

    # Load providers
    qgs.initQgis()
    print('QGIS initialisiert, Brudi')
    
    # Load project
    project = QgsProject.instance()
    composition = QgsPrintLayout(project)
    document = QDomDocument()
    project.read(path + '/pyqgis/dynamischer_test.qgz')
    
    # Load layout
    layout = project.layoutManager().layoutByName("dynamisches_layout")
    
    # Set Headline
    headline = layout.itemById('00 Titel')
    headline.setText(address)
    headline_shadow = layout.itemById('00 Schatten')
    headline_shadow.setText(address)

    # Set extent
    refmap = layout.itemById('00 HG')
    xmin = x - 250
    ymin = y - 250
    xmax = x + 250
    ymax = y + 250
    bb = QgsRectangle(xmin, ymin, xmax, ymax)
    refmap.setExtent(bb)
    
    # Set background
    hintergrund = project.mapLayersByName('gruen')[0]
    project.layerTreeRoot().findLayer(hintergrund.id()).setItemVisibilityChecked(True)
    print('Cover done')

    # Print pdf
    pdf_path = os.path.join(path + '/pyqgis/', 'test.pdf')
    exporter = QgsLayoutExporter(layout)
    image_settings = QgsLayoutExporter.PdfExportSettings()
    image_settings.dpi = 300
    result = exporter.exportToPdf(pdf_path, image_settings)
    
    # Exit QGIS
    qgs.exitQgis()
    print('Done!')

This isn't very clean and also QGIS is being loaded every time the user presses the 'Generate PDF'-button, which ist very unefficient. So I tried to move all the startup code, like creating a QgsApplication reference, loading the project etc, into an own method, which is called right at the start of the script. Also I wanted to have an own method for each of the pages in the PDF, like load_cover(), load_page1(), etc. I passed the project and layout variables into these new methods like this:

def load_cover(project, layout, address, x, y):
    headline = layout.itemById('00 Titel')
    headline.setText(address)
...

But when I things this way, there seems to be some issue with the context or threads or something. Python crashes at the very last line: result = exporter.exportToPdf(pdf_path, image_settings)

And Command Prompt says the following:

QPrinter: Must construct a QCoreApplication before a QPrinter

I'm new to python and PyQGIS and I'm stuck with this. Please help! I'm using Python 3.8 and QGIS 3.4.

1

Okay, I figured it out myself. In case somebody cares:

I moved it all to a class, changing

qgs = QgsApplication([], False)
qgs.initQgis()

to

self.qgs = QgsApplication([], False)
self.qgs.initQgis()

Seems to me like the problem was each function was moved to an own thread or something, where it couldn't find the qgs object anymore.

I do have some new issues now:

QObject::startTimer: Timers can only be used with threads started with QThread

QObject::startTimer: Timers can only be used with threads started with QThread

QPainter::begin: Paint device returned engine == 0, type: 3

QPainter::setRenderHint: Painter must be active to set rendering hints

QPainter::translate: Painter not active

QPainter::end: Painter not active, aborted

But it works anyway.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.