简体   繁体   English

QApplication(PySide)Django应用退出问题

[英]QApplication (PySide) Django app issue with quitting

I have a Django app in which the users can create reports at various places throughout the webapp. 我有一个Django应用,用户可以在其中通过整个Web应用在各个位置创建报告。 As a little nifty feature I would like to make a "send as PDF" feature for these reports, and I'm almost there. 作为一个小巧的功能,我想为这些报告创建“以PDF格式发送”功能,而我几乎可以使用了。

What I do is that before the report is returned as a HttpResponse through Django I send the raw HTML content through a small PySide/QT snippet (as seen below). 我要做的是,在通过Django将报告作为HttpResponse返回之前,我通过一个小的PySide / QT代码段发送了原始HTML内容(如下所示)。

The problem is that I can't get the QApplication to quit. 问题是我无法退出QApplication。

I have tried with the standard QCoreApplication.exit() but with no luck. 我尝试使用标准的QCoreApplication.exit()但是没有运气。 If I try to convert a new report right after the first one, the console says a "QApplication instance already exists". 如果我尝试在第一个报表后立即转换新报表,则控制台会显示“ QApplication实例已存在”。

I am using Django 1.2.5, Python 2.7, QT 4.8 and PySide 1.1 on OS X 10.7.3 (for testing). 我在OS X 10.7.3上使用Django 1.2.5,Python 2.7,QT 4.8和PySide 1.1(用于测试)。

Code: 码:

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())

Code comments: 代码注释:

Currently I have made a try / except clause to be able to keep the code running, by using the current QApplication instance (to avoid the 'instance exists error'), but this just doesn't seem right?. 目前,我已经使用try / except子句通过使用当前的QApplication实例来保持代码运行(以避免“实例存在错误”),但这似乎并不正确? I mean, having a QAppliation running for the duration of my Apache server (which will be the case in production) seems a bit off. 我的意思是,在我的Apache服务器运行期间运行QAppliation(在生产环境中会如此)似乎有些偏离。 Shouldn't it be possible to quit it after the PDF conversion is done? PDF转换完成后,是否应该退出它?

Other than the QCoreApplication.exit() I have tried to use the sys.exit(app.exec_()) method, as is often seen in examples and snippets. 除了QCoreApplication.exit()我还尝试使用sys.exit(app.exec_())方法,这在示例和摘录中经常看到。 This however just causes an error and makes Python crash and the code works perfectly without this. 但是,这只会导致错误并使Python崩溃,而没有此代码,代码将完美运行。 (well it creates the PDF, but won't quit). (它会创建PDF,但不会退出)。

All the lines commented out are the previous attempts I have made to quit the QApplication. 所有注释掉的行都是我之前退出QApplication的尝试。

In short: I don't know what it is, but it just won't quit. 简而言之:我不知道它是什么,但它不会退出。 Can anyone suggest why? 有人可以建议原因吗?

Update: After the latest answer I have edited the code to respond to the input. 更新:最新答案后,我已编辑代码以响应输入。 This is how the final part looks now: 现在这是最后一部分的样子:

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

I do, however, still get an error: 但是,我仍然收到错误消息:

2012-04-30 00:16:10.791 Python[21241:1803] * Assertion failure in +[NSUndoManager _endTopLevelGroupings], /SourceCache/Foundation/Foundation-833.24/Misc.subproj/NSUndoManager.m:324 2012-04-30 00:16:10.791 Python [21241:1803] * + [NSUndoManager _endTopLevelGroupings],/ SourceCache / Foundation / Foundation-833.24 / Misc.subproj / NSUndoManager.m:324中的断言失败
Qt has caught an exception thrown from an event handler. Qt捕获了事件处理程序引发的异常。 Throwing exceptions from an event handler is not supported in Qt. Qt不支持从事件处理程序引发异常。 You must reimplement QApplication::notify() and catch all exceptions there. 您必须重新实现QApplication :: notify()并在那里捕获所有异常。

This error only occurs when I implement app.exec_() . 仅当我实现app.exec_()时,才会发生此错误。 However without app.exec_() it is just the normal issue again, with no quitting. 但是,如果没有app.exec_()这又是正常问题,并且没有退出。

Any ideas? 有任何想法吗?

Update 2: this is the newest code, fixed in accordance with matas latest suggestion: 更新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)

I still have the same problem however. 我仍然有同样的问题。

what i can say is this: 我能说的是:

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

here you call convertToPdf() , if you want to connect the signal, omit the parenthesis! 在这里,您调用 convertToPdf() ,如果要连接信号,请省略括号!

you could also use this much clearer syntax: 您还可以使用以下更清晰的语法:

web.loadFinished.connect(convertToPdf)

you may also want to add a parameter to convertToPdf, as it is called with a boolean indicating wheather loading was successful or not. 您可能还想向convertToPdf添加一个参数,因为使用布尔值调用该参数表示是否成功加载了小麦。

And using app.exit() should be enough. 并且使用app.exit()应该足够了。

oh, and when you use Gui-Components you need to use a QApplication . 哦,当您使用Gui-Components时,您需要使用QApplication A QCoreApplication won't do! 一个QCoreApplication不会做!

edit: it's important to call web.setHtml after you've connected the loadFinished signal! 编辑:连接好loadFinished信号后,一定要调用web.setHtml otherwise if the loading already is finished, your function will never be executed! 否则,如果加载已经完成,则将永远不会执行您的功能!

edit : this works without any problem for me: 编辑 :这对我来说没有任何问题:

#!/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)

Even without all the calls to app.exit() , app.deleteLater() , app.processEvents() , it still works... but can't hurt to have that. 即使没有对app.exit()app.deleteLater()app.processEvents()所有调用,它仍然可以正常工作……但是拥有它不会受到任何伤害。

One more important thing: QApplications have to be created from the main thread! 更重要的一件事:必须从主线程创建QApplications! So if it's running inside a django app it's probably not going to work, that's why i added the subprocess stuff... 因此,如果它在Django应用程序中运行,则可能无法正常工作,这就是为什么我添加了子流程的原因...

PySide is designed such that there is only ever one instance of QCoreApplication within a process. PySide的设计使得在一个进程中只有一个QCoreApplication实例。 The best reference to this (possibly undocumented fact) that I could find was http://bugs.pyside.org/show_bug.cgi?id=855 . 我可以找到的对此最佳参考(可能是未记录的事实)是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. 基本上,可能存在对q应用程序的悬挂引用,以防止垃圾回收器对其进行盗用,因此,即使您告诉应用程序退出删除了引用,也可能还有其他引用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM