I've tested at least 20 different workarounds for this.
I want to print a widget.
My widget is an Invoice and it have inside a lot of other widgets like QLineEdits, QLabels with text, QLabels that act as images, QDropDown menus, etc. This is a Screenshot (all data inside like the N.I.F are temporary placeholders for a real business):
So it should be easy, right? It does not seem so.
- First I tried to sent it to Printers, and that alone was a very hard job dealing with lot of different printing specific options. After all my research I found QPrintDialog deals with most of the stuff
- After testing that for a while I noticed it grabs the widget quite fine, but it prints it to a very tiny section, so after much research I found a way to scale that, this is what I ended up with.
Code:
def print_as_pdf(widgettoprint):
# Printer
printer = QPrinter()
setupdialog = QPrintDialog(printer, widgettoprint)
if setupdialog.exec() == QPrintDialog.Accepted:
# Renderer
painter = QPainter(printer)
painter.begin(printer)
# Scale it
xscale = printer.pageRect().width() / widgettoprint.width()
yscale = printer.pageRect().height() / widgettoprint.height()
scale = min(xscale, yscale)
painter.translate(printer.paperRect().x() + printer.pageRect().width() / 2,
printer.paperRect().y() + printer.pageRect().height() / 2)
painter.scale(scale, scale)
painter.translate(-1 * widgettoprint.width() / 2, -1 * widgettoprint.height() / 2)
widgettoprint.render(painter)
painter.end()
return True
else:
return False
It does the trick, it prints an entire A4 page as intended, but the quality of the texts is quite bad. I found out it just takes an screenshot of it instead of saving the data as text or vector data.
So that wouldn't work for me, but I had to try. I tried to print as PDF with the same method (choosing PDF as a printer) and it just generates a PDF with an screenshot on it. I can't select or copy the text data.
- So I researched for alternatives. I found about Reportlab but couldn't make it work. I also tried PyFPDF, but especifically creating a PDF without choosing a printer would break the ability to actually print the invoice to a different printer.
- Okay, days of frustration spent trying to find alternatives without rebuilding from scratch my 7000 lines of code program. I found this blog about jinja2 and qt workaround and decided to give it a try. Re-creating the entire invoice as an HTML template would be a fair amount of work (as there is a lot more different invoices, not just this one). But a new problem arises, PyQt5 doesn't have QtWebKit or QWebView, and as far as I could get was to print a white empty PDF with this code and QtWebEngine:
Code:
import os
from jinja2 import Environment, FileSystemLoader
from PyQt5 import QtWebEngineWidgets
from PyQt5.QtPrintSupport import QPrinter, QPrintDialog
from PyQt5.QtGui import QPainter
PATH = os.path.dirname(os.path.abspath(__file__))
TEMPLATE_ENVIRONMENT = Environment(
autoescape=False,
loader=FileSystemLoader(os.path.join(PATH, 'userfiles/templates')),
trim_blocks=False)
def render_template(template_filename, context):
return TEMPLATE_ENVIRONMENT.get_template(template_filename).render(context)
def test():
something = ['something1', 'something2', 'something3']
context = {
'something': something,
'invoicenumber': "17012"
}
# Generate the HTML data with the template without creating a file, on-the-fly
html = render_template('invoices.html', context)
# add html to a webengine/widget
web = QtWebEngineWidgets.QWebEngineView()
web.page().setHtml(html)
# Print that widget
printer = QPrinter()
setupdialog = QPrintDialog(printer)
if setupdialog.exec() == QPrintDialog.Accepted:
web.render(printer)
print("done")
test()
Where the HTML template is:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Factura {{ invoicenumber }}</title>
</head>
<body>
<center>
<h1>Factura {{ invoicenumber }}</h1>
<p>{{ something|length }} something</p>
</center>
<ol align="left">
{{ something[0] }}
{{ something[1] }}
{{ something[2] }}
</ol>
</body>
</html>
I will probably spend more weeks and months trying to make that works. Printing is something that I though it should had been really easy, as every random program in the market seems to be able to print or create PDFs, but it's now for me the worst part of my programming learning experience.
The goal: This question aims to find a permanent solution to my problem: how to print to a selectable (windows platform) printer my invoice widget? And then, if PDF printer is chosen, print it with endless quality, using vectors or anything that is just don't create an screenshot.
I'd also appreciate explanations of what I am doing wrong as I'm still learning (both programming, Python and Qt).