2

我有一个 Django 应用程序,用户可以在其中在整个 webapp 的各个位置创建报告。作为一个漂亮的功能,我想为这些报告创建一个“以 PDF 格式发送”功能,而且我快到了。

我所做的是,在通过 Django 作为 HttpResponse 返回报告之前,我通过一个小的 PySide/QT 片段(如下所示)发送原始 HTML 内容。

问题是我无法让 QApplication 退出。

我已经尝试过标准QCoreApplication.exit()但没有运气。如果我尝试在第一个报告之后立即转换新报告,控制台会显示“QApplication 实例已存在”。

我在 OS X 10.7.3(用于测试)上使用 Django 1.2.5、Python 2.7、QT 4.8 和 PySide 1.1。

代码:

def makepdf(response,filename):
    try:
        app = QApplication(sys.argv)
    except:
        app = QCoreApplication.instance()
    web = QWebView()

    stylelink = "%s%s" % (media_root,'images/css/reportGenerator.css') 

    web.settings().setUserStyleSheetUrl(QUrl.fromLocalFile(stylelink))
    web.setHtml(response)

    printer = QPrinter()
    printer.setPageSize(QPrinter.A4)
    printer.setOutputFormat(QPrinter.PdfFormat)
    printer.setOutputFileName(filename)

    def convertToPdf():
        web.print_(printer)
        #webresponse = web.close()        
        #QObject.disconnect()
        #print QCoreApplication.instance().children()[0].interrupt()
        #qthread = QCoreApplication.instance().thread()#.cleanup()
        #qthread.exit()
        QCoreApplication.exit()
    QObject.connect(web, SIGNAL("loadFinished(bool)"), convertToPdf())

代码注释:

目前我已经制作了一个try/except子句,以便能够通过使用当前的 QApplication 实例来保持代码运行(以避免“实例存在错误”),但这似乎不对?我的意思是,在我的 Apache 服务器期间运行 QAppliation(在生产中就是这种情况)似乎有点不对劲。PDF转换完成后不是应该可以退出吗?

除了QCoreApplication.exit()我尝试使用的sys.exit(app.exec_())方法之外,正如在示例和片段中经常看到的那样。然而,这只会导致错误并使 Python 崩溃,并且代码可以在没有这个的情况下完美运行。(它会创建 PDF,但不会退出)。

注释掉的所有行都是我之前为退出 QApplication 所做的尝试。

简而言之:我不知道它是什么,但它就是不会退出。谁能建议为什么?

更新:在最新答案之后,我编辑了代码以响应输入。这是最后一部分现在的样子:

def convertToPdf():
    web.print_(printer)
    app.exit()
web.loadFinished.connect(convertToPdf)
app.exec_()

但是,我仍然收到错误消息:

2012-04-30 00:16:10.791 Python[21241:1803] * +[NSUndoManager _endTopLevelGroupings] 中的断言失败,/SourceCache/Foundation/Foundation-833.24/Misc.subproj/NSUndoManager.m:324
Qt 已捕获抛出异常从事件处理程序。Qt 不支持从事件处理程序中抛出异常。您必须重新实现 QApplication::notify() 并在那里捕获所有异常。

此错误仅在我实施时发生app.exec_()。然而,没有app.exec_()它只是正常的问题,没有退出。

有任何想法吗?

更新 2:这是最新的代码,根据 matas 最新建议修复:

app = QApplication(sys.argv)
web = QWebView()
printer = QPrinter()
printer.setPageSize(QPrinter.A4)
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setOutputFileName(filename)
def convertToPdf():
    web.print_(printer)
    app.exit()
web.loadFinished.connect(convertToPdf)
web.setHtml(response)

但是,我仍然有同样的问题。

4

2 回答 2

1

我能说的是:

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

这里你调用 convertToPdf(),如果你想连接信号,省略括号!

您还可以使用更清晰的语法:

web.loadFinished.connect(convertToPdf)

您可能还想向 convertToPdf 添加一个参数,因为它是用一个布尔值调用的,表示加载是否成功。

并且使用app.exit()应该足够了。

哦,当你使用 Gui-Components 时,你需要使用QApplication. AQCoreApplication不会做!

编辑:连接信号web.setHtml 打电话很重要loadFinished!否则如果加载已经完成,你的函数将永远不会被执行!

编辑:这对我来说没有任何问题:

#!/usr/bin/env python
from PySide.QtGui import *
from PySide.QtWebKit import *
import sys
from subprocess import Popen, PIPE


def createPdf(html, filename):
    app = QApplication(sys.argv)
    web = QWebView()
    printer = QPrinter()
    printer.setPageSize(QPrinter.A4)
    printer.setOutputFormat(QPrinter.PdfFormat)
    printer.setOutputFileName(filename)
    def convertToPdf():
        web.print_(printer)
        app.exit()
        app.deleteLater()
    web.loadFinished.connect(convertToPdf)
    web.setHtml(html)
    app.processEvents()

def createPdfInSubprocess(html, filename):
       p = Popen(["python", __file__, filename], 
                stdin=PIPE, stdout=PIPE, stderr=PIPE)
       out, err = p.communicate(html)
       return (p.returncode, out, err)

if __name__ == '__main__':
    if len(sys.argv) > 1:
        # read html from stdin, filename from cmdline
        html = "\n".join(sys.stdin.readlines())
        createPdf(html, sys.argv[1])
        # print("done")
    else:
        # test if it's working
        ret = createPdfInSubprocess(
                "<html><h1>test</h1>it's working...</html>", "test.pdf")
        print(ret)

app.exit()即使没有对, app.deleteLater(),的所有调用app.processEvents(),它仍然有效......但拥有它不会有什么坏处。

更重要的一件事:必须从主线程创建 QApplications!因此,如果它在 django 应用程序中运行,它可能无法正常工作,这就是我添加子进程内容的原因......

于 2012-04-29T19:31:21.917 回答
0

PySide 被设计成在一个进程中只有一个 QCoreApplication 实例。我能找到的对此(可能是未记录的事实)的最佳参考是http://bugs.pyside.org/show_bug.cgi?id=855

Basically, there may be dangling references to the qapplication that prevent it from being reaped by the garbage collector, so even if you tell the application to exit and you delete your reference, there may still be other references lying around.

于 2012-04-29T19:32:52.667 回答