QApplication(PySide)Django应用程序问题与退出

时间:2012-04-29 18:21:19

标签: python django qt pyqt pyside

我有一个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())

代码评论:

目前我已经通过使用当前的QApplication实例(以避免'实例存在错误')使try / except子句能够保持代码运行,但这只是没有好像没错?我的意思是,在我的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 / Foundation833.24 / Mini.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)

但我仍然遇到同样的问题。

2 个答案:

答案 0 :(得分:1)

我能说的是:

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

在这里致电 convertToPdf(),如果您想连接信号,请省略括号!

您也可以使用这种更清晰的语法:

web.loadFinished.connect(convertToPdf)

您可能还想为convertToPdf添加一个参数,因为它使用一个布尔值调用,表明天气加载成功与否。

使用app.exit()就足够了。

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

编辑:在连接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应用程序中运行,它可能无法正常工作,这就是为什么我添加了子进程的东西......

答案 1 :(得分:0)

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

基本上,可能有对qapplication的悬空引用阻止它被垃圾收集器收获,所以即使你告诉应用程序退出你删除你的引用,仍然可能其他参考资料。