PDF 输出不适用于 Pyqt5 和 Python 3.5

Posted

技术标签:

【中文标题】PDF 输出不适用于 Pyqt5 和 Python 3.5【英文标题】:PDF output not working with Pyqt5 and Python 3.5 【发布时间】: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 的单个文本元素。 【参考方案1】:

虽然我自己在 python 中编程,但我对在其中编程 GUI 知之甚少。但是,我发现了几个问题和站点,可以作为您自己实施的起点。我知道其中一些是 QT4,但请看一看,我相信事情并没有太大变化(但我可能是错的):Pyqt take screenshot of certain screen areaScreenshot of a window using python@ 987654323@https://github.com/Python-Devs-Brasil/PyQt5-Tutorials/blob/master/Post-2(QtCore)/screenshot.pyhttps://github.com/baoboa/pyqt5/blob/master/examples/desktop/screenshot.pyHow to grab a desktop screenshot with PyQt?

编辑 您是否尝试过通过 matplotlib 保存到 pdf 文件?https://pythonspot.com/en/matplotlib-save-figure-to-image-file/saving images in python at a very high quality

编辑 2 另外,看看这个Create a pdf with fillable fields python

编辑 3 您还可以查看此问题中提到的库的文档和示例Python PDF library

【讨论】:

感谢您的回答,但我认为您错过了目标 :(。所有这些链接旨在捕获屏幕截图,我已经能够做到。我的目标不是那个。我的目标是将 PDF 打印为“不是”屏幕截图,因为否则您无法使用鼠标选择 PDF 上的数据。所需的解决方案需要将 PDF 呈现为布局(将转换视为 PDF 到 HTML 的示例。如果结果只是一个屏幕截图,那么它是 HTML 的意义是没有用的。PNG 可以工作。但你需要它 HTML 来选择数据/捕获/编辑数据)。我需要我的发票为 PDF,但不是屏幕截图。 第二次编辑可能是我需要的。我正在检查它。 请告诉我第二次编辑中的问题是否有帮助。

以上是关于PDF 输出不适用于 Pyqt5 和 Python 3.5的主要内容,如果未能解决你的问题,请参考以下文章

pyinstaller 不适用于 pyqt5 和 matplotlib

C# Response.Write pdf 不适用于 Android 浏览器

多个摄像头馈送不适用于 PyQt5 线程:

Pyqt5 鼠标事件不适用于我的自定义标签栏

chrome print to pdf不适用于文件

QComboBox setMaxVisible() 不适用于融合风格