Qt 的 html 支持
最近有一个需求,把一些数据按照一定格式打印成pdf,比如第一行显示两段文字,分别居中,第二行显示两张图片,分别占一半宽度,再加上标题、下划线等等格式,
我心想用一个个的QWidget拼起来肯定不现实,就去学了下html的知识
使用QLabel显示html
QLabel对html的支持并不是很完善,一些前端的效果都显示不出来,比如下面这样就不生效
<div style="display: flex;" >
<div style="flex:1;text-align: center;">A</div>
<div style="flex:1;text-align: center;">B</div>
</div>
<div style="width: 100%;" >
<div style="display: inline-block;width: 50%;text-align: center;float:left">A</div>
<div style="display: inline-block;width: 50%;text-align: center;">B</div>
</div>
最终发现只能用table实现了:
<table width="100%">
<tr>
<td width="50%" align="center">Text1</td>
<td width="50%" align="center">Text2</td>
</tr>
<tr>
<td width="50%" align="center"><img src=Img1></td>
<td width="50%" align="center"><img src=Img2></td>
</tr>
</table>
使用QWebEngine显示html
QWebEngine 是基于 chrome内核的,所以支持绝大部分的特性,而且支持加载html文件,非常的方便,
js和c++相互调用也比较简单:
- js调用c++,基于QWebChannel注册c++类到js
- c++调用js,直接调用runJavaScript函数
下面是一个简单封装了的支持html显示和打印的类:
ReportView.h
#pragma once
#include <QWidget>
#include <QWebEngineView>
#include <QWebChannel>
class ReportView;
// js <-> qt message
class ReportJsContext : public QObject
{
Q_OBJECT
public:
explicit ReportJsContext(ReportView* view, QObject* parent = nullptr);
public slots:
void onRecvMsg(const QString& msg);
void printToPdf(const QString& filePath, bool openWhenFinished);
signals:
void receivedMsgFromJs(const QString& msg);
private:
ReportView* m_view;
};
// html view
class ReportView : public QWebEngineView
{
Q_OBJECT
public:
explicit ReportView(QWidget *parent = nullptr);
void callJsFunction(const QString& func);
void printToPdf(const QString& filePath, bool openWhenFinished = false);
signals:
void receivedMsgFromJs(const QString& msg);
void pdfPrintingFinished(const QString& filePath, bool success);
private:
QWebChannel* m_webChannel;
ReportJsContext* m_jsContext;
};
ReportView.cpp
#include "ReportView.h"
#include <QDesktopServices>
#include <QDir>
#include <QFileInfo>
ReportJsContext::ReportJsContext(ReportView *view, QObject *parent)
: QObject(parent)
, m_view(view)
{
}
void ReportJsContext::onRecvMsg(const QString &msg)
{
emit receivedMsgFromJs(msg);
}
void ReportJsContext::printToPdf(const QString &filePath, bool openWhenFinished)
{
m_view->printToPdf(filePath, openWhenFinished);
}
ReportView::ReportView(QWidget *parent)
: QWebEngineView(parent)
, m_webChannel(new QWebChannel(this))
, m_jsContext(new ReportJsContext(this))
{
// init webChannel
m_webChannel->registerObject("jsContext", m_jsContext);
page()->setWebChannel(m_webChannel);
connect(m_jsContext, &ReportJsContext::receivedMsgFromJs, this, &ReportView::receivedMsgFromJs);
}
void ReportView::callJsFunction(const QString &func)
{
page()->runJavaScript(func);
}
void ReportView::printToPdf(const QString &filePath, bool openWhenFinished)
{
auto page = this->page();
page->disconnect();
connect(page, &QWebEnginePage::pdfPrintingFinished, this, [this, openWhenFinished](const QString &filePath, bool success){
if (success) {
if (openWhenFinished) {
QDesktopServices::openUrl(QUrl::fromLocalFile(filePath));
}
}
else {
}
emit pdfPrintingFinished(filePath, success);
});
// mkdir
QFileInfo fileInfo(filePath);
QDir dir = fileInfo.dir();
if (!dir.exists()) {
dir.mkpath(dir.absolutePath());
}
// print
page->printToPdf(filePath);
}
在html侧只需再引入配套的js文件,就能调用c++函数了:
var jsContext;
function loadScript(src, callback)
{
var script = document.createElement("script");
script.type = "text/javascript";
script.src = src;
script.onload = callback;
document.head.appendChild(script);
}
function init()
{
if (typeof qt != 'undefined') {
new QWebChannel(qt.webChannelTransport, function(channel) {
jsContext = channel.objects.jsContext;
});
}
else {
alert("QWebChannel init failed");
}
}
loadScript("qrc:/qtwebchannel/qwebchannel.js", function() {
console.log("qwebchannel.js is loaded");
init();
});
/**
* send message to qt
* @param msg string
*/
function sendToQt(msg)
{
jsContext.onRecvMsg(msg)
}
/**
* call qt to print the page to pdf
* @param filePath path to save, string
* @param openWhenFinished open pdf using system tool if finished, bool
*/
function printToPdf(filePath, openWhenFinished)
{
if (!filePath.endsWith(".pdf")) {
filePath += ".pdf"
}
jsContext.printToPdf(filePath, openWhenFinished)
}