如何正确的使用QWebEngineView
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何正确的使用QWebEngineView相关的知识,希望对你有一定的参考价值。
自从5.6版本彻底移除qwebkit,打算使用新的web引擎,大体函数没有改变。但是一些发现了一些使用c++11的匿名函数作为参数的。比如:webengineview中:
void tohtml(FunctorOrLambda resultCallback) const
在webkit中:
QString QWebFrame::toHtml() const
按理说使用时是这样(当loadfinished后调用):
QString html;
m_view->page()->toHtml( [&](const QString &v )
qDebug() << v;//这样是可以的。
html = v;//这样是直接崩溃的
);
//后来做个测试外部
int test = 1;//添加个测试
//lambda内部
test = 2;//这句在外部输出仍然是1很奇怪
qDebug() << test; //仍然是1
不太能搞明白为什么新的函数要这么设计,明明一个返回值就可以的,为什么用lambda函数,本来QString html = ...->toHtml();一句搞定的,非得按照上面那样(最主要的是按照上面那样用,我压根取不到值!)
感觉被设计是不是有点为了C++11而C++11了。
c++11的匿名函数的作用是为了异步吧,是不是你的变量html离开了作用域了。
先看下面一段代码的输出
int test = 1;
auto f = [&]()
test = 5;
cout<<test<<endl;
;
cout<<test<<endl; // 输出 1
f(); // 输出 5
cout<<test<<endl; // 输出也是5
所以你后面说的两个test的输出,需要好好检查一下。
然后
m_view->page()->toHtml( [&](const QString &v )
qDebug() << v;//这样是可以的。
html = v;//这样是直接崩溃的
);
这里如果将v赋值给html会崩溃,那么就需要检查一下崩溃的原因了。
你可以先把QString html;前加上static看看是否还会崩溃。如果没有,那么说明这里的调用是异步的,在调用这个lambda的时候,html已经不存在了。 参考技术A html已经不存在了,runjavascript中的lambda异步的。
用信号槽:
m_TabWindow->currentWebView()->page()->runJavaScript(ui->lineEdit->text(),[this](const QVariant& r)emit haveAResultSignal(r.toString()););
或者:
参照http://blog.csdn.net/tujiaw/article/details/52075495
QString runJavaScriptResult;
QSharedPointer<QEventLoop> loop = QSharedPointer<QEventLoop>(new QEventLoop());
m_TabWindow->currentWebView()->page()->runJavaScript(ui->lineEdit->text(),[loop,&runJavaScriptResult] (const QVariant& r)
if(loop->isRunning())
runJavaScriptResult = r.toString();
loop->quit();
);
loop->exec();//会一直等待runJavaScript执行完毕
runJavaScriptResult2 = runJavaScriptResult;
如何从 QWebEngineView 打印
【中文标题】如何从 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的主要内容,如果未能解决你的问题,请参考以下文章
如何正确使用 Composer 安装 Laravel 扩展包
如何正确使用 Composer 安装 Laravel 扩展包