2

ユーザーが webapp のさまざまな場所でレポートを作成できる Django アプリがあります。少し気の利いた機能として、これらのレポートの「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/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()、それでも機能します... しかし、それを持っていても害はありません。

もう 1 つ重要なこと: QApplication はメイン スレッドから作成する必要があります。したがって、djangoアプリ内で実行されている場合、おそらく機能しないため、サブプロセスを追加しました...

于 2012-04-29T19:31:21.917 に答える
0

PySide は、プロセス内に QCoreApplication のインスタンスが 1 つだけ存在するように設計されています。私が見つけることができたこれ(おそらく文書化されていない事実)への最良の参照はhttp://bugs.pyside.org/show_bug.cgi?id=855でした。

基本的に、qapplication へのダングリング参照が存在する可能性があり、ガベージ コレクターによるリープを防ぎます。そのため、アプリケーションを終了するように指示して参照削除しても、他の参照が横たわっている可能性があります。

于 2012-04-29T19:32:52.667 に答える