Django视图使用PyQt将URL转换为PDF

时间:2014-03-08 18:05:27

标签: python django qt django-views pyqt4

我正在尝试编写一个Django视图,它将返回一个URL的PDF。

我正在使用PyQt webview.print创建PDF,但我不确定如何将pdf传递给Django响应,我已经尝试过QBuffer,但我似乎无法做到正确。

到目前为止,我的观点是:

def pdf(request):
    app = QApplication(sys.argv)

    bufferPdf = QBuffer()
    bufferPdf.open(QBuffer.ReadWrite)

    web = QWebView()
    web.load(QUrl("http://www.google.com")) #the desired url. 

    printer = QPrinter()
    printer.setPageSize(QPrinter.Letter)
    printer.setOrientation(QPrinter.Landscape);
    printer.setOutputFormat(QPrinter.PdfFormat)
    printer.setOutputFileName("file.pdf")

    def convertIt():
        web.print_(printer)
        print "Pdf generated"
        QApplication.exit()

    QObject.connect(web, SIGNAL("loadFinished(bool)"), convertIt)

    bufferPdf.seek(0)
    result = bufferPdf.readData(0)
    bufferPdf.close()

    sys.exit(app.exec_())

    response =  HttpResponse(result, mimetype='application/pdf')
    response['Content-Disposition'] = 'attachment; filename=coupon.pdf'
    return response

提前致谢。

2 个答案:

答案 0 :(得分:2)

来自ekhumoro的公认解决方案不正确。他为您提供了从命令行运行的代码,但永远不能在Django视图中运行。

很多人都注意到将Django与QT线程应用程序结合起来并不容易,而且可能完全不可能。您看到的错误是您尝试这样做时会看到的一个典型示例。

在我自己的项目中,我尝试了许多不同的组织和分组代码的排列,但从来没有找到解决方案。问题似乎是(我不是QT专家,所以如果有人有更多的信息请纠正我)事件驱动的QT应用程序(WebKit使用QT事件模型的任何东西)是围绕有效的单身“QApplication”构建的。您无法控制此子应用程序何时退出以及何时获得各种资源。因此,使用该库的任何多线程应用程序都需要非常仔细地管理它的资源 - 在处理各种Web应用程序的过程中您无法控制这些资源。

一种可能的(混乱和不专业)解决方案是创建一个接受命令行参数的脚本,然后从Django中调用所述脚本作为官方子流程。您可以使用临时文件进行输出,然后将其加载到您的应用程序中。在读取任何事件后,您只需清除磁盘上的文件即可。凌乱,但有效。

我个人希望听到任何明确知道为什么这么难或者是正确解决方案的人 - 在Stackoverflow上有几十个线程,对如何解决这个问题有不正确或不完整的解释... < / p>

答案 1 :(得分:0)

这是重写您应该做的事情的示例:

import sys
from PyQt4 import QtCore, QtGui, QtWebKit

class WebPage(QtWebKit.QWebPage):
    def __init__(self):
        QtWebKit.QWebPage.__init__(self)
        self.printer = QtGui.QPrinter()
        self.printer.setPageSize(QtGui.QPrinter.Letter)
        self.printer.setOrientation(QtGui.QPrinter.Landscape);
        self.printer.setOutputFormat(QtGui.QPrinter.PdfFormat)
        self.mainFrame().loadFinished.connect(self.handleLoadFinished)

    def start(self, url):
        self.mainFrame().load(QtCore.QUrl(url))
        QtGui.qApp.exec_()

    def handleLoadFinished(self):
        temp = QtCore.QTemporaryFile(
            QtCore.QDir.temp().filePath('webpage.XXXXXX.pdf'))
        # must open the file to get the filename.
        # file will be automatically deleted later
        temp.open()
        self.printer.setOutputFileName(temp.fileName())
        # ensure that the file can be written to
        temp.close()
        self.mainFrame().print_(self.printer)
        temp.open()
        self.pdf = temp.readAll().data()
        QtGui.qApp.quit()

def webpage2pdf(url):
    if not hasattr(WebPage, 'app'):
        # can only have one QApplication, and it must be created first
        WebPage.app = QtGui.QApplication(sys.argv)
    webpage = WebPage()
    webpage.start(url)
    return webpage.pdf

if __name__ == '__main__':

    if len(sys.argv) > 1:
        url = sys.argv[1]
    else:
        url = 'http://www.google.com'

    result = webpage2pdf(url)

    response =  HttpResponse(result, mimetype='application/pdf')
    response['Content-Disposition'] = 'attachment; filename=coupon.pdf'
    # do stuff with response...