Qt基于QWebEngineView的网页快捷类

引言

QWebEngineView使得Qt软件具有内置浏览器的功能,但相对基础,实际开发中需要在此之上进行封装,方便混合开发,本文是基于实际项目进行的封装,增加如下功能:

  1. 集成QWebChannel以及通用配置,避免子类重复实现
  2. 快捷键(ctrl + alt + b)呼出调试弹窗,用于调试js代码
  3. 命令缓存,用于解决网页未加载完成,runJavaScript不生效问题
  4. 加载计时

WebViewBase中内置两个View,一个用来展示网页,另一个用来展示调试界面,通过快捷键呼出,不设置父窗体,方便调试时切换界面。

命令缓存,则是通过类内标志位m_isLoaded判断当前的js命令是否进入队列,未加载完成则放入队列中,等待加载完成后一并runJavaScript。防止出现c++代码在网页未加载完成调用js命令不生效的情况。

判断是否加载完成:

void WebViewBase::runJs(QString jsStr)
{
    if (m_isLoaded) {
        m_webView->page()->runJavaScript(jsStr);
    }
    else {
        m_cmdList << jsStr;
    }
}

加载完成后,将之前缓存的命令一并发送:

void WebViewBase::slot_loadFinished(bool isOK)
{
    if (!m_cmdList.isEmpty()) {
        auto tmpStr = m_cmdList.join(";\n");
        m_webView->page()->runJavaScript(tmpStr);
        m_cmdList.clear();
    }

    m_isLoaded = true;
    emit sig_loadFinish(isOK);
}

实现效果如下:
在这里插入图片描述

代码实现

#include <QWidget>
#include <QWebEngineView>
#include <QWebChannel>

class WebViewBase : public QWidget
{
    Q_OBJECT

public:
    WebViewBase(QWidget* parent);
    virtual ~WebViewBase();

    QWebEngineView* webView();
    void runJs(QString jsStr);

signals:
    void sig_loadFinish(bool isOK);

private slots:
    void slot_loadStarted();
    void slot_loadFinished(bool isOK);

protected:
    QWebEngineView* m_webView;
    QWebEngineView* m_webDebugView;
    QWebChannel* m_webChannel;

private:
    QList<QString> m_cmdList;// 未加载完成则先放入容器中
    bool m_isLoaded;

    QTime m_loadTime;
};
#include <QWebEngineSettings>
#include <QShortcut>
#include <QWebEngineProfile>
#include <QHBoxLayout>
#include <QDebug>

//
WebViewBase::WebViewBase(QWidget *parent)
    : QWidget(parent)
    , m_isLoaded(false)
{
    m_webView = new QWebEngineView(this);
    m_webChannel = new QWebChannel(m_webView->page());// 定义一个channel作为和JS或HTML交互
    m_webView->page()->setWebChannel(m_webChannel);// 把channel配置到page上,让channel作为其信使
    m_webChannel->registerObject("webObject", this);

    auto webSetting = m_webView->page()->settings();
    webSetting->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);//全屏支持
    webSetting->setAttribute(QWebEngineSettings::DnsPrefetchEnabled, true);//预取dns,加快浏览速度
    webSetting->setAttribute(QWebEngineSettings::ShowScrollBars, false);// 隐藏滚动条
    webSetting->setAttribute(QWebEngineSettings::DnsPrefetchEnabled, true);// 预取DNS条目以加快浏览速度

    QWebEngineProfile * engineProfile = m_webView->page()->profile();
    engineProfile->clearHttpCache();// 删除缓存

    //QWebEngineCookieStore *cookie = engineProfile->cookieStore();
    //cookie->deleteAllCookies();// 删除cookie

    // 关联调试快捷键
    QShortcut* shortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_B), this);
    QObject::connect(shortcut, &QShortcut::activated, this, [this]() {
        m_webDebugView = new QWebEngineView();
        m_webDebugView->setWindowFlags(m_webDebugView->windowFlags() | Qt::Window);
        m_webDebugView->resize(800, 600);
        m_webDebugView->setAttribute(Qt::WA_DeleteOnClose, true);
        connect(m_webDebugView, &QWidget::destroyed, m_webDebugView, [this] { m_webDebugView = nullptr; });
        m_webView->page()->setDevToolsPage(m_webDebugView->page());
        m_webDebugView->page()->triggerAction(QWebEnginePage::InspectElement);
        m_webDebugView->raise();
        m_webDebugView->show();
    });

    connect(m_webView, &QWebEngineView::loadStarted, this, &WebViewBase::slot_loadStarted);
    connect(m_webView, &QWebEngineView::loadFinished, this, &WebViewBase::slot_loadFinished);

    // 布局
    auto mainLayout = new QHBoxLayout(this);
    mainLayout->addWidget(m_webView);
}

WebViewBase::~WebViewBase()
{

}

QWebEngineView* WebViewBase::webView()
{
    return m_webView;
}

void WebViewBase::runJs(QString jsStr)
{
    if (m_isLoaded) {
        m_webView->page()->runJavaScript(jsStr);
    }
    else {
        m_cmdList << jsStr;
    }
}

void WebViewBase::slot_loadStarted()
{
    m_isLoaded = false;
    m_loadTime.start();
}

void WebViewBase::slot_loadFinished(bool isOK)
{
    if (!m_cmdList.isEmpty()) {
        auto tmpStr = m_cmdList.join(";\n");
        m_webView->page()->runJavaScript(tmpStr);
        m_cmdList.clear();
    }

    m_isLoaded = true;

    // 耗时打印
    qDebug() << "Web load time:" << m_loadTime.elapsed() / 1000.0 << "s";

    emit sig_loadFinish(isOK);
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    auto tmpWeb = new WebViewBase(this);
    tmpWeb->webView()->load(QString("http://127.0.0.1:8088"));
    tmpWeb->runJs("setCenter({latitude: 22.5336, longitude: 113.98}); setZoom(13)");

    setCentralWidget(tmpWeb);
}

性能优化

使用QWebEngineView的过程中,有个令人困扰的问题,就是资源占用较高,因为本身是浏览器,打开一个地图界面的资源占用甚至高达500M,且隐藏后并不会释放资源,如果存在四五个页签占用很容易高达3G,严重影响其他功能的使用。

为解决这个问题,采取了隐藏页面则卸载网页的方法,重新显示再将原来的网页加载出来,为保证隐藏再显示用户的看到的内容与之前一致,除了重新加载网页之外,还需要将c++调用的js命令重新再调用一遍,需要用到上述代码中的成员m_cmdList,对所有runJs命令进行保存,用于重新加载后的命令调用,代码修改如下:

void WebViewBase::slot_loadFinished(bool isOK)
{
	// 加载完成,发送命令,解决如下问题:
	// 1.未加载完成runJavaScript不成功
	// 2.隐藏卸载网页后,重新显示需要复原界面
	if (!m_cmdList.isEmpty() && m_oriUrl.isEmpty()) {
		auto tmpStr = m_cmdList.join(";\n");
		m_webView->page()->runJavaScript(tmpStr);
	}
	
	m_isLoaded = true;
	m_waitProgress->stop(isOK);

    emit sig_loadFinish(isOK);
}

增加对隐藏、显示事件处理:

void WebViewBase::showEvent(QShowEvent *)
{
	if (!m_oriUrl.isEmpty()) {
		m_webView->setUrl(m_oriUrl);
		m_oriUrl.clear();
	}
}

void WebViewBase::hideEvent(QHideEvent *)
{
	if (!m_webView->url().isEmpty()) {
		m_oriUrl = m_webView->url();
		m_webView->setUrl(QUrl(""));
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Arui丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值