专栏首页JackeyGao的博客使用PyQt5把网页打印成PDF

使用PyQt5把网页打印成PDF

使用PyQt5把网页打印成PDF

Posted December 03, 2018

最近制作诗词日历的 PDF 版本, 准备打印一下做成实体日历。之前我写过一篇优化 print 样式的文章,在 Google 上搜索page to pdf, 大多数都是把默认页面的样式打印, 而非使用 @media print 样式打印, 后面做了很多查询, 才发现 PyQt 可以使用 @media print 打印.

值得一提的是 PyQt 直接使用了Chrome 的内核, 而且在使用过程中我发现一些配置是可以共享的,比如 Chrome 的代理设置. 下面我通过打印日历的例子来介绍 PyQt 是怎么打印页面的。 而且 PyQt 基本上和 Chrome 的打印功能一致, 也可以通过QPageLayout控制打印的纸张大小, 以及边距的 margin 大小.

安装 pyQt5

在这里使用最新的PyQt5.

Bash

brew install PyQt5

其他系统的安装方法请请参考, 官方介绍: https://pypi.org/project/PyQt5/

使用

PyQt5 是一个Python的GUI编程框架, 它提供了很多 GUI 编程的组件,我们这里主要用到 QtWebEngineWidgets 的组件.

Python

import sys, os
from datetime import date, timedelta
from PyQt5.QtWidgets import QApplication
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
from PyQt5.QtCore import QMarginsF
from PyQt5.QtPrintSupport import QPrinter
from PyQt5.QtGui import QPageLayout, QPageSize


app = QtWidgets.QApplication(sys.argv)
loader = QtWebEngineWidgets.QWebEngineView()
loader.load(QtCore.QUrl('http://shici.store/poetry-calendar/'))

layout = QPageLayout(
    QPageSize(QPageSize.B5),
    QPageLayout.Portrait, QMarginsF(0, 0, 0, 0)
)

def printFinished():
    page = loader.page()
    print("%s Printing Finished!" % page.title())
    app.exit()

def printToPDF(finished):
    loader.show()
    page = loader.page()
    page.printToPdf("%s.pdf" % page.title(), layout)


loader.page().pdfPrintingFinished.connect(printFinished)
loader.loadFinished.connect(printToPDF)

app.exec_()

可以看到上面代码中是打印了http://shici.store/poetry-calendar/页面, 并且以 B5 纸张进行打印, 当然你可以修改为 QPageSize.A4来打印 A4的纸张. 并且四边距均为0(也就是不留白).

需要注意上面的代码有一些是异步的操作, 这里使用信号挂载的形式来检查页面成功加载的时候和打印 PDF 完成的时候, 来分别完成部分任务。 这里比较类似于 JS 的事件。

Python

loader.loadFinished.connect(printToPDF)

当页面加载完毕JS 执行完毕的时候再打印页面, 否则会出现打印空白页.

Python

loader.page().pdfPrintingFinished.connect(printFinished)

当 PDF 完成的时候, 调用printFinished逻辑, 打印相关的信息并退出此APP 实例。

由于是异步的,打印多个 URL 的时候可能会踩到一些坑。 我采用的方式是每一个页面都实例化一个 app, 当打印完后在printFinished退出此 APP。 下一个 URL 重新使用一个新的 URL 实例, 这是最简单的方式(理解PyQt5生命周期是个耗时的工作, 发量不够..).

执行多个 URL

Python

import sys, os
from datetime import date, timedelta
from PyQt5.QtWidgets import QApplication
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
from PyQt5.QtCore import QMarginsF
from PyQt5.QtPrintSupport import QPrinter
from PyQt5.QtGui import QPageLayout, QPageSize

def printPDF(t):
    app = QtWidgets.QApplication(sys.argv)
    loader = QtWebEngineWidgets.QWebEngineView()
    loader.setZoomFactor(1)
    loader.load(QtCore.QUrl('http://127.0.0.1:8090/?d=' + str(t)))

    layout = QPageLayout(
        QPageSize(QPageSize.B5),
        QPageLayout.Portrait, QMarginsF(0, 0, 0, 0)
    )

    def printFinished():
        page = loader.page()
        print("%s Printing Finished!" % page.title())
        app.exit()

    def printToPDF(finished):
        loader.show()
        page = loader.page()
        page.printToPdf("%s.pdf" % page.title(), layout)


    loader.page().pdfPrintingFinished.connect(printFinished)
    loader.loadFinished.connect(printToPDF)

    app.exec_()


if __name__ == '__main__':
    year = 2019

    d = date(year, 01, 01)

    while True:
        if d.year > year:
            break

        printPDF(d)

        d = d + timedelta(days=7)

诗词周历效果图

以下打印诗词周历效果图

总结

这提供了一个思路, 我们可以用这个来用Python制作书籍, 也可以使用HTML配合 jinja2的渲染生成, 然后通过 PyQt 来制作 PDF, 然后通过PyPDF2的PdfFileMerger功能把每个pdf 合并成一个大的 PDF 文件。 总之, 可以无限想象.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 如何做一个实体日历技术方面印刷方面

    本篇唠下做一本实体日历用到了哪些东西, 虽然工作量大部分是技术编码方面, 但剩余联系打印社及了解打印纸张和打印的质量等东西对我来说比编码难多了。 甚至和打印店讨...

    用户1416054
  • Cable : 基于Ansible运维Web管理平台

    Cable 在设计之初是 Ansible Tower 的替代品.基于WEB的ANSIBLE管理中心,使ANSIBLE更易于用于各种 IT 团队(需要有强烈的需求...

    用户1416054
  • Django小技巧13: 使用F()表达式

    在 Django QuerySets API 中, F() 用于直接在数据库中引用模型的值。假设你有一个带有price的 Product 模型, 你希望为所有的...

    用户1416054
  • 新手Web设计师应该避免的 6 宗罪

    新手Web设计师和开发人员往往以有缺陷的创作而告终,却不知道真正的故障出自于哪里。虽说有这么多的设计书籍,但即使都读了,也不会对最后的设计有任何意义。在第一个原...

    用户1289394
  • 新手Web设计师应该避免的 6 宗罪

    新手Web设计师和开发人员往往以有缺陷的创作而告终,却不知道真正的故障出自于哪里。虽说有这么多的设计书籍,但即使都读了,也不会对最后的设计有任何意义。在第一个原...

    哲洛不闹
  • 微服务全链路跟踪:jaeger集成hystrix

    微服务全链路跟踪:jaeger集成istio,并兼容uber-trace-id与b3

    一笠风雨任生平
  • 微信小程序|基本页面设置

    1 最近几年微信小程序特别的火,快捷容易不需要下载所以不占内存,给我们带来方便。支付,小游戏,应用等很多地方都需要小程序。其实小程序的开发和网页制作基本一样,都...

    算法与编程之美
  • 如何界定分析薪酬的分位值

    薪酬数据分析的训练营已经开始了好几天了,这几天有好多的同学来问一些薪酬中位值的问题,因为今天的课程刚好讲到了薪酬的分位置的计算,今天的一位同学的问题,我...

    王佩军
  • 初识国密算法

    国密算法是国家商用密码算法的简称,由国家密码管理局管理和发布标准。国家密码管理局的官方网站是:

    云水木石
  • 基于SVM、Pipeline、GridSearchCV的鸢尾花分类

    Iris(鸢尾花)数据集是多重变量分析的数据集。 数据集包含150行数据,分为3类,每类50行数据。 每行数据包括4个属性:Sepal Length(花萼长...

    潇洒坤

扫码关注云+社区

领取腾讯云代金券