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)
}
posted @ 2023-04-27 09:44  miyanyan  阅读(255)  评论(0编辑  收藏  举报