【问题标题】:How to print from QWebEngineView如何从 QWebEngineView 打印
【发布时间】:2020-04-04 01:52:18
【问题描述】:

我正在尝试从我的应用程序中打印一份报告,其中包括文本和表格。由于QTextDocument 不够用,我决定使用QWebEngineView 和Qt 富文本引擎不支持的更复杂的HTML/CSS。

我能够从视图创建 PDF,但是我有一些普遍的误解,因为有时它会崩溃,而且打印而不是 PDF 创建也会崩溃。

这是我的尝试:


方法一:创建PDF

这是唯一有效的变体:

    auto webView = new QWebEngineView();
    webView->setHtml(contents);

    const QString fn = QFileDialog::getSaveFileName(0, "Save pdf", ".", "PDF Files (*.pdf)");

    if (!fn.isEmpty())
       webView->page()->printToPdf(fn);

但是,这仅因对话框 (?!) 而有效。如果我这样改变它:

    QString fn ="/Users/s710/Downloads/test.pdf";
    auto webView = new QWebEngineView();
    webView->setHtml(contents);
    webView->page()->printToPdf(fn);

它将创建一个带有空白页面的 PDF。所以我猜以上只是偶然的。


方法二:直接打印

这种方法会崩溃:

    auto webView = new QWebEngineView();
    webView->setHtml(contents);

    QPrinter printer(QPrinter::QPrinter::ScreenResolution);
    printer.setOutputFormat(QPrinter::NativeFormat);
    printer.setPaperSize(QPrinter::A4);
    printer.setPageMargins(12, 16, 12, 20, QPrinter::Millimeter);

    webView->page()->print(&printer, [](bool){});

Crash:
1   QPrinter::pageRect() const                                                                                                                                                                                                                                 (x86_64) /Users/s710/Qt/5.12.6/clang_64/lib/QtPrintSupport.framework/Versions/5/QtPrintSupport                       0x100247fe4    
2   QWebEnginePagePrivate::didPrintPage(unsigned long long, QByteArray const&)                                                                                                                                                                                 (x86_64) /Users/s710/Qt/5.12.6/clang_64/lib/QtWebEngineWidgets.framework/Versions/5/QtWebEngineWidgets               0x100200f0a    
3   QtWebEngineCore::callbackOnPrintingFinished(QtWebEngineCore::WebContentsAdapterClient *, int, std::vector<char> const&)                                                                                                                                    (x86_64) /Users/s710/Qt/5.12.6/clang_64/lib/QtWebEngineCore.framework/Versions/5/QtWebEngineCore                     0x100899693    
4   base::debug::TaskAnnotator::RunTask(const char *, base::PendingTask *)                                                                                                                                                                                     (x86_64) /Users/s710/Qt/5.12.6/clang_64/lib/QtWebEngineCore.framework/Versions/5/QtWebEngineCore                     0x10295d402    
5   base::MessageLoop::RunTask(base::PendingTask *)                                                                                                                                                                                                            (x86_64) /Users/s710/Qt/5.12.6/clang_64/lib/QtWebEngineCore.framework/Versions/5/QtWebEngineCore                     0x10298395f    
6   base::MessageLoop::DoWork()                                                                                                                                                                                                                                (x86_64) /Users/s710/Qt/5.12.6/clang_64/lib/QtWebEngineCore.framework/Versions/5/QtWebEngineCore                     0x102983ef9    
7   std::__function::__func<QtWebEngineCore::(anonymous namespace)::MessagePumpForUIQt::MessagePumpForUIQt()::'lambda'(), std::allocator<QtWebEngineCore::(anonymous namespace)::MessagePumpForUIQt::MessagePumpForUIQt()::'lambda'()>, void ()>::operator()() (x86_64) /Users/s710/Qt/5.12.6/clang_64/lib/QtWebEngineCore.framework/Versions/5/QtWebEngineCore                     0x100839f99    
8   QObject::event(QEvent *)                                                                                                                                                                                                                                   (x86_64) /Users/s710/Qt/5.12.6/clang_64/lib/QtCore.framework/Versions/5/QtCore                                       0x10824bcf6    
...

方法3:等待webview加载完毕,然后创建PDF

因此,由于阻止文件对话框似乎有所不同,我认为当视图尚未加载 HTML 时可能会出现问题。我还读到QWebEngineView 资源很重,所以我想我可以等待它完成加载。

但是,这也会崩溃

    auto webView = new QWebEngineView();

    QObject::connect(webView, &QWebEngineView::loadFinished, this, [&webView](bool ok)
    {
        QString fn ="/Users/s710/Downloads/test.pdf";
        webView->page()->printToPdf(fn);
    });

    webView->setHtml(contents);

Crash:
1   QWebEnginePage::printToPdf(QString const&, QPageLayout const&)                                                                                                                                                          (x86_64) /Users/s710/Qt/5.12.6/clang_64/lib/QtWebEngineWidgets.framework/Versions/5/QtWebEngineWidgets     0x100204cd7    
2   Printer::print(MonthItem *, QWidget *)::$_0::operator()(bool) const                                                                                                                                                     printer.cpp                                                                                            203 0x100016eeb    
3   QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<bool>, void, Printer::print(MonthItem *, QWidget *)::$_0>::call(Printer::print(MonthItem *, QWidget *)::$_0&, void * *)                               qobjectdefs_impl.h                                                                                     146 0x100016dc8    
4   void QtPrivate::Functor<Printer::print(MonthItem *, QWidget *)::$_0, 1>::call<QtPrivate::List<bool>, void>(Printer::print(MonthItem *, QWidget *)::$_0&, void *, void * *)                                              qobjectdefs_impl.h                                                                                     256 0x100016d71    
5   QtPrivate::QFunctorSlotObject<Printer::print(MonthItem *, QWidget *)::$_0, 1, QtPrivate::List<bool>, void>::impl(int, QtPrivate::QSlotObjectBase *, QObject *, void * *, bool *)                                        qobjectdefs_impl.h                                                                                     439 0x100016d1d    
6   QMetaObject::activate(QObject *, int, int, void * *)                                                                                                                                                                    (x86_64) /Users/s710/Qt/5.12.6/clang_64/lib/QtCore.framework/Versions/5/QtCore                             0x10825153b    
7   QWebEngineView::loadFinished(bool)                                                                                                                                                                                      (x86_64) /Users/s710/Qt/5.12.6/clang_64/lib/QtWebEngineWidgets.framework/Versions/5/QtWebEngineWidgets     0x10020cb3f

方法4:等待webview加载完毕,然后打印

这也会崩溃:

    auto webView = new QWebEngineView();

    QObject::connect(webView, &QWebEngineView::loadFinished, this, [&webView](bool ok)
    {
        QPrinter printer(QPrinter::PrinterResolution);
        printer.setOutputFormat(QPrinter::NativeFormat);
        printer.setPaperSize(QPrinter::A4);
        printer.setPageMargins(12, 16, 12, 20, QPrinter::Millimeter);

        webView->page()->print(&printer, [](bool){});
    });

    webView->setHtml(contents);

Crash:
1   QWebEngineView::page() const                                                                                                                                                                                            (x86_64) /Users/s710/Qt/5.12.6/clang_64/lib/QtWebEngineWidgets.framework/Versions/5/QtWebEngineWidgets     0x10020f05d    
2   Printer::print(MonthItem *, QWidget *)::$_0::operator()(bool) const                                                                                                                                                     printer.cpp                                                                                            207 0x100016b35    
3   QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<bool>, void, Printer::print(MonthItem *, QWidget *)::$_0>::call(Printer::print(MonthItem *, QWidget *)::$_0&, void * *)                               qobjectdefs_impl.h                                                                                     146 0x100016a88    
4   void QtPrivate::Functor<Printer::print(MonthItem *, QWidget *)::$_0, 1>::call<QtPrivate::List<bool>, void>(Printer::print(MonthItem *, QWidget *)::$_0&, void *, void * *)                                              qobjectdefs_impl.h                                                                                     256 0x100016a31    
5   QtPrivate::QFunctorSlotObject<Printer::print(MonthItem *, QWidget *)::$_0, 1, QtPrivate::List<bool>, void>::impl(int, QtPrivate::QSlotObjectBase *, QObject *, void * *, bool *)                                        qobjectdefs_impl.h                                                                                     439 0x1000169dd    
6   QMetaObject::activate(QObject *, int, int, void * *)                                                                                                                                                                    (x86_64) /Users/s710/Qt/5.12.6/clang_64/lib/QtCore.framework/Versions/5/QtCore                             0x10825353b    
7   QWebEngineView::loadFinished(bool)                                                                                                                                                                                      (x86_64) /Users/s710/Qt/5.12.6/clang_64/lib/QtWebEngineWidgets.framework/Versions/5/QtWebEngineWidgets     0x10020eb3f    

所以我觉得很愚蠢,因为到处都是崩溃,但我不知道可能出了什么问题。有人可以使用QWebEngineView 向我指出一个有效的打印功能吗?

【问题讨论】:

    标签: qt printing qwebengineview qprinter


    【解决方案1】:

    在第一种方法中,我认为它失败了,因为尚未加载 HTML 或 url,并且您想要打印文本,因此可能的解决方案是使用 loadFinished 信号开始打印并使用 pdfPrintingFinished 来知道打印的时间完成了。

    #include <QtWebEngineWidgets>
    
    class Widget : public QWidget
    {
    public:
        explicit Widget(QWidget *parent = nullptr):
            QWidget(parent), button(new QPushButton), progressbar(new QProgressBar), view(new QWebEngineView)
        {
            button->setText(tr("Press me"));
            button->setEnabled(false);
    
            connect(button, &QPushButton::clicked, this, &Widget::onClicked);
            connect(view, &QWebEngineView::loadFinished, this, &Widget::onLoadFinished);
            connect(view->page(), &QWebEnginePage::pdfPrintingFinished, this, &Widget::onPdfPrintingFinished);
    
            QString html = R"(<!DOCTYPE html>
                           <html>
                           <head>
                           <style>
                           table {
                           font-family: arial, sans-serif;
                           border-collapse: collapse;
                           width: 100%;
                           }
    
                           td, th {
                           border: 1px solid #dddddd;
                           text-align: left;
                           padding: 8px;
                           }
    
                           tr:nth-child(even) {
                           background-color: #dddddd;
                           }
                           </style>
                           </head>
                           <body>
    
                           <h2>HTML Table</h2>
    
                           <table>
                           <tr>
                           <th>Company</th>
                           <th>Contact</th>
                           <th>Country</th>
                           </tr>
                           <tr>
                           <td>Alfreds Futterkiste</td>
                           <td>Maria Anders</td>
                           <td>Germany</td>
                           </tr>
                           <tr>
                           <td>Centro comercial Moctezuma</td>
                           <td>Francisco Chang</td>
                           <td>Mexico</td>
                           </tr>
                           <tr>
                           <td>Ernst Handel</td>
                           <td>Roland Mendel</td>
                           <td>Austria</td>
                           </tr>
                           <tr>
                           <td>Island Trading</td>
                           <td>Helen Bennett</td>
                           <td>UK</td>
                           </tr>
                           <tr>
                           <td>Laughing Bacchus Winecellars</td>
                           <td>Yoshi Tannamuri</td>
                           <td>Canada</td>
                           </tr>
                           <tr>
                           <td>Magazzini Alimentari Riuniti</td>
                           <td>Giovanni Rovelli</td>
                           <td>Italy</td>
                           </tr>
                           </table>
    
                           </body>
                           </html>
                           )";
    
            view->setHtml(html);
            auto lay = new QVBoxLayout(this);
            lay->addWidget(button);
            lay->addWidget(progressbar);
            lay->addWidget(view);
            resize(640, 480);
        }
    private:
        void onLoadFinished(bool ok){
            button->setEnabled(ok);
        }
        void onClicked(){
            progressbar->setRange(0, 0);
            QString fn = "/Users/s710/Downloads/test.pdf";
            view->page()->printToPdf(fn);
        }
        void onPdfPrintingFinished(const QString & filename, bool ok){
            qDebug() << filename << ok;
            progressbar->setRange(0, 1);
        }
    private:
        QPushButton *button;
        QProgressBar *progressbar;
        QWebEngineView *view;
    };
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        Widget w;
        w.show();
        return a.exec();
    }
    

    在您使用 QPrinter 的情况下,我认为该错误是由于 QPrinter 是一个局部变量,当函数完成执行但 Qt 尝试异步访问该变量但对象不存在时会被消除。解决方案是扩展QPrinter的范围。

    #include <QtWebEngineWidgets>
    
    class Widget : public QWidget
    {
    public:
        Widget(QWidget *parent = nullptr):
            QWidget(parent), button(new QPushButton), progressbar(new QProgressBar), view(new QWebEngineView)
        {
            button->setText(tr("Press me"));
            button->setEnabled(false);
    
            connect(button, &QPushButton::clicked, this, &Widget::onClicked);
            connect(view, &QWebEngineView::loadFinished, this, &Widget::onLoadFinished);
    
            printer.setResolution(QPrinter::PrinterResolution);
            printer.setOutputFormat(QPrinter::NativeFormat);
            printer.setPaperSize(QPrinter::A4);
            printer.setPageMargins(12, 16, 12, 20, QPrinter::Millimeter);
            QString html = R"(<!DOCTYPE html>
                           <html>
                           <head>
                           <style>
                           table {
                           font-family: arial, sans-serif;
                           border-collapse: collapse;
                           width: 100%;
                           }
    
                           td, th {
                           border: 1px solid #dddddd;
                           text-align: left;
                           padding: 8px;
                           }
    
                           tr:nth-child(even) {
                           background-color: #dddddd;
                           }
                           </style>
                           </head>
                           <body>
    
                           <h2>HTML Table</h2>
    
                           <table>
                           <tr>
                           <th>Company</th>
                           <th>Contact</th>
                           <th>Country</th>
                           </tr>
                           <tr>
                           <td>Alfreds Futterkiste</td>
                           <td>Maria Anders</td>
                           <td>Germany</td>
                           </tr>
                           <tr>
                           <td>Centro comercial Moctezuma</td>
                           <td>Francisco Chang</td>
                           <td>Mexico</td>
                           </tr>
                           <tr>
                           <td>Ernst Handel</td>
                           <td>Roland Mendel</td>
                           <td>Austria</td>
                           </tr>
                           <tr>
                           <td>Island Trading</td>
                           <td>Helen Bennett</td>
                           <td>UK</td>
                           </tr>
                           <tr>
                           <td>Laughing Bacchus Winecellars</td>
                           <td>Yoshi Tannamuri</td>
                           <td>Canada</td>
                           </tr>
                           <tr>
                           <td>Magazzini Alimentari Riuniti</td>
                           <td>Giovanni Rovelli</td>
                           <td>Italy</td>
                           </tr>
                           </table>
    
                           </body>
                           </html>
                           )";
    
            view->setHtml(html);
    
            auto lay = new QVBoxLayout(this);
            lay->addWidget(button);
            lay->addWidget(progressbar);
            lay->addWidget(view);
            resize(640, 480);
        }
    private:
        void onLoadFinished(bool ok){
            button->setEnabled(ok);
        }
        void onClicked(){
            progressbar->setRange(0, 0);
            QString fn = "/Users/s710/Downloads/test.pdf";
            printer.setOutputFileName(fn);
            view->page()->print(&printer, [this](bool ok){
                qDebug() << ok;
                progressbar->setRange(0, 1);
            });
        }
    private:
        QPushButton *button;
        QProgressBar *progressbar;
        QWebEngineView *view;
        QPrinter printer;
    };
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        Widget w;
        w.show();
        return a.exec();
    }
    

    【讨论】:

    • 天哪,真是个愚蠢的错误。你是对的,QPrinter 超出范围是一个问题。我回家后会试试这个。 然而,我的方法3等于你的解决方案,对吧?这种方法对我来说也崩溃了,没有对象超出范围可能导致崩溃?
    • @user826955 我真的不知道,因为你没有提供minimal reproducible example 你的方法,我已经从你的代码中推测了很多东西。我只指出最明显的错误,所以如果你想为每个案例提供详细的答案,你必须提供一个 MRE 作为我提出的代码。也许在第三种方法中,您的代码没有显示另一个错误。
    • 是的。谢谢你的回复,我回家再详细看看。
    猜你喜欢
    • 1970-01-01
    • 2020-04-13
    • 2021-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-17
    • 1970-01-01
    相关资源
    最近更新 更多