QWebEnginePage::printToPdf() - how to allow it to proceed
JonB last edited by JonB
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
The outline of my code (PyQt5, QT 5.7) is:
def synchronousPrintToPdf(self): self.webView.page().printToPdf(self.callbackPrintToPdf) # 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) dialogafter calling
synchronousPrintToPdf()then at that point the printing proceeds and
I need to know what the caller can do without a dialog (or any other interaction)? Stumbling around a bit, I have tried (in
self.printLoop = QEventLoop(self) self.printLoop.exec()
However, this does not cause the printing to proceed. Nor does calling, say,
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
QWebEnginePageon 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() self.generatePdfButton.clicked.connect(self.generatePdf) 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 self.synchronousPrintToPdf() # exit the dialog containing the QWebEngineView self.accept() # Outside world calls: # Display dialog, wait till exit preview = HtmlPreviewDialog() preview.exec() # 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 self.accept() # 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 preview.synchronousPrintToPdf() # 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
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?