如何从 QWebEngineView 打印 [英] How to print from QWebEngineView
问题描述
我正在尝试从我的应用程序中打印一份报告,其中包括文本和表格.由于 QTextDocument
不够用,我决定使用 QWebEngineView
和 Qt 富文本引擎不支持的更复杂的 HTML/CSS.
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.
我可以从视图创建 PDF,但是我有一些普遍的误解,因为有时它会崩溃,并且打印而不是 PDF 创建会崩溃.
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.
这是我的尝试:
方法一:创建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);
然而,这仅适用于对话框(?!).如果我像这样改变它:
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);
它将创建一个带有空白页的 PDF.所以我猜以上只是偶然的.
It is going to create a PDF with a blank page. So I guess the above only works by accident.
方法二:直接打印
这种方法崩溃了:
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
...
<小时>
方法三:等待webview加载完成,然后创建PDF
因此,由于似乎与阻止文件对话框有所不同,因此我认为在视图尚未加载 HTML 时可能会出现问题.我还读到 QWebEngineView
资源很重,所以我想我可以等待它完成加载.
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.
然而,这也会崩溃
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
<小时>
方法四:等待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
的工作打印功能吗?
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?
推荐答案
在第一种方法中,我认为它失败了,因为尚未加载 HTML 或 url 而您想要打印文本,因此可能的解决方案是使用loadFinished 信号开始打印并使用 pdfPrintingFinished 知道打印何时完成.
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();
}
在您使用 QPrinter 的情况下,我认为导致错误的原因是 QPrinter 是一个局部变量,该变量在函数执行完成时被消除,但 Qt 尝试异步访问该变量但该对象不存在.解决方案是扩展QPrinter的范围.
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();
}
这篇关于如何从 QWebEngineView 打印的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!