Nominate our 2022 Qt Champions!

QWebEnginePage::printToPdf() - how to allow it to proceed

  • I need to know what code Qt requires to allow QWebEnginePage::printToPdf() to proceed to actually print the PDF?

    Through necessity rather than choice, I need to await completion of the printing to file before my code can continue, i.e. effectively I need a synchronous printToPdf().

    The outline of my code (PyQt5, QT 5.7) is:

    def synchronousPrintToPdf(self):
        # allow printing to proceed - what to put here??
        # wait for callbackPrintToPdf() to complete
    def callbackPrintToPdf(self, data: QByteArray):
        # save data to some output file
        # signify that printing has completed,
        # so that synchronousPrintToPdf() can exit

    The problem is not the behaviour of callbackPrintToPdf() --- if & when the printing proceeds it gets called and executes fine. Rather the issue is what QWebEnginePage::printToPdf() requires the calling code to do in order to allow it to actually proceed to do the printing.

    I have found that if the caller happens to put up a (modal) dialog after calling synchronousPrintToPdf() then at that point the printing proceeds and callbackPrintToPdf() gets executed.

    I need to know what the caller can do without a dialog (or any other interaction)? Stumbling around a bit, I have tried (in synchronousPrintToPdf()):

    self.printLoop = QEventLoop(self)

    However, this does not cause the printing to proceed. Nor does calling, say, QApplication.processEvents() or QApplication.sendPostedEvents() or QTimer() or QThread.yieldCurrentThread().

    I believe that after calling QWebEnginePage::printToPdf() the actual printing is done in a separate thread. What is it about putting up a dialog ( that allows this to execute so that I can use some part of it without actually waiting for interaction?


    I have discovered what it seems to be that prevents QWebEnginePage::printToPdf() from proceeding to actually print.

    It is not, as I had thought, the putting up of a new modal dialog. Rather, it appears that if you put a QWebEnginePage on a modal dialog (which is my situation) then printToPdf() will not proceed at all. You must exit the modal dialog before it will proceed. (I presume this is something to do with it running in a separate thread, and then the modal dialog loop not allowing that to run.)

    So, my code outline was:

    class HtmlPreviewDialog(QtWidgets.QDialog):
        def __init__():
            self.webView = QWebEngineView()
        def generatePdf():
            # set off QWebEngineView.printToPdf() to start saving
            # problem here: printToPdf() does not actually do the print
            # so synchronousPrintToPdf() just blocks with nothing happening
            # exit the dialog containing the QWebEngineView
    # Outside world calls:
    # Display dialog, wait till exit
    preview = HtmlPreviewDialog()
    # At this point the PDF is supposed to have been saved to file

    This does not work, because printToPdf() is called while still inside the modal dialog.

    It seems you have to change the architecture to:

    class HtmlPreviewDialog(QtWidgets.QDialog):
        def generatePdf():
            # exit the dialog containing the QWebEngineView
    # Outside world calls:
    # Display dialog, wait till exit
    preview = HtmlPreviewDialog()
    if preview.exec():
        # Now that the dialog has gone we can actually proceed to save, and wait till done
        # Note that we still have a reference to the dialog, even though it's closed
    # At this point the PDF has *actually* been saved to file

    It is not even enough to instead the call to self.synchronousPrintToPdf() to after the dialog's self.accept(). You must actually allow the dialog to close in response to the self.accept() before printToPdf() will proceed....

    I'm not madly keen that the activity of saving Pdf to file has to be moved to after the user has exited the dialog (e..g what happens if error saving?), but at least this actually works!

    Hope the above helps anyone struggling with same issue.

    I would still like to know what exactly printToPdf() needs to happen in the caller in order for it to proceed if it is invoked from a modal dialog, e.g. maybe there is something else I could have done to allow the call from there rather than having to re-architect so that it is not actually called from a dialog?

Log in to reply