【发布时间】:2018-02-18 10:18:03
【问题描述】:
我已经为此测试了至少 20 种不同的解决方法。
我想打印一个小部件。
我的小部件是一个发票,它包含在许多其他小部件中,例如 QLineEdits、带有文本的 QLabels、充当图像的 QLabels、QDropDown 菜单等。这是一个屏幕截图(像 NIF 这样的所有数据都是临时占位符真正的企业):
所以应该很容易吧?好像不是这样的。
- 首先我尝试将它发送到打印机,仅此一项就很难处理许多不同的打印特定选项。经过我所有的研究,我发现 QPrintDialog 可以处理大部分内容
- 在测试了一段时间后,我注意到它可以很好地抓取小部件,但它会将它打印到一个非常小的部分,所以经过大量研究后,我找到了一种扩展它的方法,这就是我最终的结果。李>
代码:
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
它成功了,它按预期打印了整个 A4 页面,但是文本的质量很差。我发现它只是截取它而不是将数据保存为文本或矢量数据。
所以这对我不起作用,但我必须尝试。我尝试使用相同的方法(选择 PDF 作为打印机)打印为 PDF,它只是生成一个带有屏幕截图的 PDF。我无法选择或复制文本数据。
- 所以我研究了替代品。我找到了Reportlab,但无法让它工作。我也尝试过PyFPDF,但特别是在不选择打印机的情况下创建 PDF 会破坏将发票实际打印到其他打印机的能力。
- 好吧,我花了几天的挫折试图寻找替代方案,而不是从头开始重建我的 7000 行代码程序。我找到了this blog about jinja2 and qt 解决方法,并决定试一试。将整个发票重新创建为 HTML 模板将是一项相当大的工作(因为有更多不同的发票,而不仅仅是这张)。但是出现了一个新问题,PyQt5 没有 QtWebKit 或 QWebView,据我所知,使用此代码和 QtWebEngine 打印一个白色的空 PDF:
代码:
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()
HTML 模板在哪里:
<!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>
我可能会花费更多的几周和几个月的时间来尝试实现这一目标。打印本来应该很容易,因为市场上的每个随机程序似乎都能够打印或创建 PDF,但现在对我来说,这是我编程学习经历中最糟糕的部分。
目标: 这个问题旨在为我的问题找到永久解决方案:如何将我的发票小部件打印到可选(Windows 平台)打印机?然后,如果选择了 PDF 打印机,请使用矢量或其他任何不创建屏幕截图的方式以无限质量进行打印。
由于我仍在学习(编程、Python 和 Qt),我也希望能解释我做错了什么。
【问题讨论】:
-
您需要
show()网络视图才能获得一些可见的输出。然而,当我在 linux 上尝试这个时,结果并不是那么令人印象深刻。如果您使用的是 Windows,您可能想尝试像这样创建打印机:QPrinter(QPrinter.HighResolution)。 -
PS:AFAICS,通过网络视图渲染只会创建一个包含图像的 pdf。因此,如果我正确理解了您的要求,它似乎并没有真正让您有所收获。
-
嗯,那它确实也行不通。我该怎么办?
-
你读过 Qt Wiki 上的Handling PDF 吗?这有很多有用的建议。
-
实际上,我认为您可以使用
QTextDocument实现您想要的 - 我什至已经提供了如何在 SO 上做到这一点的示例!请参阅this answer 和this answer。我刚刚测试了第一个,并且可以确认可以选择和复制生成的 pdf 的单个文本元素。