如何从 QWebEngineView 打印
Posted
技术标签:
【中文标题】如何从 QWebEngineView 打印【英文标题】:How to print from 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
向我指出一个有效的打印功能吗?
【问题讨论】:
【参考方案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 作为我提出的代码。也许在第三种方法中,您的代码没有显示另一个错误。 是的。谢谢你的回复,我回家再详细看看。以上是关于如何从 QWebEngineView 打印的主要内容,如果未能解决你的问题,请参考以下文章
如何从 Qt QWebEngineView 获取 HTTP 状态码?