2012-04-29 2 views
2

사용자가 웹 앱의 여러 곳에서 보고서를 작성할 수있는 장고 앱이 있습니다. 이 보고서에 대해 "PDF로 보내기"기능을 사용하고 싶습니다. 거의 유용합니다.QApplication (PySide) 종료시 장고 앱 문제

보고서가 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 절을 한

합니다 ('인스턴스를 피하기 위해 존재 오류 '),하지만 이건 그냥 보이지 않는가?. 제 말은, 제 아파치 서버 기간 동안 실행되는 QAppliation을 가지고 있다는 것입니다. PDF 변환이 끝난 후 종료 할 수 없어야합니까?

QCoreApplication.exit() 이외의 방법은 예제 및 스 니펫에서 자주 나타나는 것처럼 sys.exit(app.exec_()) 메서드를 사용하려고했습니다. 그러나 이것은 단지 오류를 일으키고 파이썬 충돌을 일으키고 코드는 이것을 사용하지 않고 완벽하게 작동합니다. (물론 PDF를 생성하지만 종료하지는 않습니다).

주석 처리 된 모든 행은 QApplication을 종료하기위한 이전 시도입니다.

간략하게 : 나는 그것이 무엇인지 모르지만, 그만 두지 않을 것입니다. 누구든지 이유를 제안 할 수 있습니까?

업데이트 : 최신 답변을받은 후 입력에 응답하는 코드를 편집했습니다. 이 마지막 부분은 지금 모습입니다 :

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

나는, 그러나, 여전히 에러가 발생합니까 : 나는 app.exec_()을 구현할 때

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
Qt has caught an exception thrown from an event handler. Throwing exceptions from an event handler is not supported in Qt. You must reimplement QApplication::notify() and catch all exceptions there.

이 오류가 발생합니다. 그러나 app.exec_()이 없으면 정상적인 문제이며 종료 할 필요가 없습니다.

아이디어가 있으십니까?

업데이트 2 :

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) 

는 아직도 그러나 같은 문제가 :이 마타스 최신 제안에 따라 고정 최신 코드입니다.당신이 신호를 연결하려는 경우

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

당신이 전화convertToPdf(), 괄호를 생략 :

답변

0

PySide는 프로세스 내 QCoreApplication 인스턴스가 하나만 있도록 설계되었습니다. 내가 찾을 수있는이 문서 (문서화되지 않은 사실 일 가능성이 있음)에 대한 최고의 참조는 http://bugs.pyside.org/show_bug.cgi?id=855입니다. 기본적으로

은 여전히 ​​주위에 거짓말을 다른 참조가있을 수 있습니다, 당신은 당신의 참조를 삭제 를 종료하려면 응용 프로그램을 말해 그렇게해도, 가비지 컬렉터에 의해 수확되는 것을 방지의 QApplication에 대한 참조를 매달려있을 수 있습니다 .

+0

안녕하세요. Nathan. 답장을 보내 주셔서 감사합니다. 이 QApplication 프로세스가 어떻게 든 포함될 수 있는지 여부를 알고 있습니까? 프로세스를 메인 장고/파이썬 프로세스에서 옮기고 수동으로 닫는 작업을하기 위해서? –

+0

메모리 오버 헤드를 무시하고 QApplication 인스턴스를 유지하면 문제가 발생하지 않습니다. 그것이 저라면, 단일 QApplication 인스턴스를 계속 실행하고 있습니다. 대안은 외부 응용 프로그램으로 변환하는 것이지만 보안 위험이 있는지 여부는 말할 수 없습니다. 필자는 보통 웹 애플리케이션이 아닌 데스크탑 Qt 애플리케이션을 작성한다. – Nathan

1

은 내가 말할 수있는 것은 이것이다! 이로드가 성공 여부를 wheather를 나타내는 boolean로 불리는 당신은 또한, convertToPdf에 매개 변수를 추가 할 수 있습니다

web.loadFinished.connect(convertToPdf) 

:

당신은이보다 명확 구문을 사용할 수 있습니다.

그리고 app.exit()을 사용하면 충분합니다.

오. Gui-Components를 사용할 때 QApplication을 사용해야합니다. QCoreApplication하지 않습니다!

편집 : 그것은 당신이 loadFinished 신호를 연결 한 web.setHtml 후 전화를하는 것이 중요합니다! 그렇지 않으면 로딩이 이미 끝난 경우 함수가 실행되지 않습니다!

편집이 나를 위해 아무 문제없이 작동합니다

#!/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(), 그것은 여전히 ​​작동 ...하지만이 다치게 할 수없는 모든 호출하지 않고.

한 가지 더 중요한 사항 : Q 응용 프로그램을 메인 스레드에서 만들어야합니다! 그래서 장고 앱에서 실행되고 있다면 아마 작동하지 않을 것입니다. 그래서 하위 프로세스를 추가했습니다 ...

+0

app.exec_()를 호출하면 Qt의 이벤트 루프로 제어권을 넘겨줍니다.이 루프는 피터가 자신의 웹 응용 프로그램에서 원했던 것이 아닙니다. QApplication.processEvents()를 호출하여 Qt가 큐의 이벤트를 처리하게 할 수 있습니다. 그러나 신호 핸들러는 응용 프로그램이 멀티 스레드가 아닌 한 신호/슬롯을 통해 연결될 때 즉시 호출됩니다. – Nathan

+0

입력에 따라 코드를 변경했습니다. (변경된 코드를 주석 필드에 붙여 넣을 수는 없습니까? 내 첫 번째 게시물에 붙여 넣었습니다) –

+0

고마워요. 그것은 재미있는 오류를 설명 할 수 있습니다. 이것이 어떻게 해결 될 수 있는지에 대한 아이디어가 있습니까? 아니면 상관 없습니까 ?? 서버가 시작될 때 단순히 QApplication을 시작할 수 있습니까? 그런 다음 동일한 QApplication을 사용하면됩니까? –