我有一个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)
但我仍然遇到同样的问题。
答案 0 :(得分:1)
我能说的是:
QObject.connect(web, SIGNAL("loadFinished(bool)"), convertToPdf())
在这里致电 convertToPdf()
,如果您想连接信号,请省略括号!
您也可以使用这种更清晰的语法:
web.loadFinished.connect(convertToPdf)
您可能还想为convertToPdf添加一个参数,因为它使用一个布尔值调用,表明天气加载成功与否。
使用app.exit()
就足够了。
哦,当你使用Gui-Components时,你需要使用QApplication
。 QCoreApplication
不会做!
编辑:在连接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的悬空引用阻止它被垃圾收集器收获,所以即使你告诉应用程序退出并你删除你的引用,仍然可能其他参考资料。