How to print from QWebEngineView

荒凉一梦 提交于 2020-11-29 17:59:10

问题


I am trying to print a report from within my application, which involves both text and tables. Since QTextDocument won't be sufficient, I've decided to go with QWebEngineView and more sophisticated HTML/CSS which is not supported by the Qt rich text engine.

I am able to create a PDF from the view, however I am having some general misconceptions, because at times its crashing, and also printing instead of PDF creation crashes.

Here is my attempts:


Approach 1: PDF creation

This is the only working variant:

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

This, however, only works because of the dialog (?!). If I change it like this:

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

It is going to create a PDF with a blank page. So I guess the above only works by accident.


Approach 2: Printing directly

This approach crashes:

    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    
...

Approach 3: Waiting for webview to be loaded, then create PDF

So since there seems to be a difference with the blocking file dialog, I thought it might have issues when the view has not yet loaded the HTML. I also read that QWebEngineView is resource heavy, so I thought I could wait for it to finish loading.

However, this also crashes

    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

Approach 4: Wait for webview to be loaded, then print

This also crashes:

    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    

So I feel pretty stupid because of the crashing all over the place, but I don't know what could be wrong. Can someone point me to a working print function using QWebEngineView please?


回答1:


In the first approach I think it fails because the HTML or url is not loaded yet and you want to print the text, so a possible solution is to use the loadFinished signal to start printing and use pdfPrintingFinished to know when the printing is finished.

#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();
}

In the case where you use QPrinter I think that the error is caused because QPrinter is a local variable that is eliminated when the function is finished executing but Qt tries to access that variable asynchronously but the object does not exist. The solution is to extend the scope of 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();
}


来源:https://stackoverflow.com/questions/59274653/how-to-print-from-qwebengineview

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!