Qt webengine 显示web页面、前后端通信以及下载详解

概述

官方文档:https://doc.qt.io/archives/qt-5.11/qtwebengine-overview.html

翻译文档:https://www.cnblogs.com/yaoyu126/p/7525603.html

从Qt5.5开始,Qt WebKit模块被废弃,被Qt WebEngine模块取代,Qt WebEngine模块提供了一个web浏览器的API(基于谷歌浏览器内核,libcef的库), 在不使用本地浏览器的情况下,它可以很容易地把Web内容嵌入到Qt应用程序中,Qt WebEngine为渲染HTML,XHTML和SVG文档,,使用CSS和JavaScript, 提供了C++类和QML类型。

功能模块:

Qt WebEngine的功能分成下列模块:

  • Qt WebEngine Widgets 模块: 用于创建基于Widget的web应用.
  • Qt WebEngine 模块: 用于创建基于Qt Quick的web应用.
  • Qt WebEngine Core 模块: 与Chromium交互

环境安装

注意的是亮点,1、建议使用qt5.8之后的版本,网上说对webengine支持的好一点前面的版本可能会有bug。2、vs2015的编译器,网上说只能使用qt-msvc版本+vs2015编译器,我只试了这个版本,好使,其他的版本,没使用过不保证没问题。

vs2015

http://219.83.160.146:5476/%E8%BD%AF%E4%BB%B6%E5%8C%85/cn_visual_studio_community_2015_with_update_3_x86_x64_web_installer_8922965.exe

安装选项:1、command tools for visual c++2015  2、python tools for visual studio

win10sdk

http://219.83.160.146:5476/%E8%BD%AF%E4%BB%B6%E5%8C%85/win10sdk/我自己存的

https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk

安装选项:只要装debugging tools for windows

Qt5.8

自己存的:http://219.83.160.146:5476/%E8%BD%AF%E4%BB%B6%E5%8C%85/qt-opensource-windows-x86-msvc2015_64-5.8.0.exe

官网的:http://download.qt.io/archive/qt/

构建套件的时候选择vs的编译器就可以了,debug选择win10sdk中的调试器

简单页面显示

打开Qt新建一个项目,在.pro文件里面添加QT += webenginewidgets之后跟着下面这个链接走就行了

https://blog.csdn.net/qq_28171461/article/details/81122399 很简单,就不多叙述了,网上的资料也很多。

我在使用过程中经常会遇到new一个对象但是提示链接错误的情况,其实代码是对的,可能是Qt ide的原因,可以试试把编译的目录全部删了,之后重新编译,有可能会修复哦。

与前端js交互

这一块很重要,也是我研究的比较多的地方,先给出两篇对我研究过程最有用的文档。

https://myprogrammingnotes.com/communication-c-javascript-qt-webengine.html//这一篇是最有用的

https://blog.csdn.net/tujiaw/article/details/51649031

https://blog.csdn.net/sunnyloves/article/details/88683090

Qt调研js:

这一步很简单,只要跑runJavaScript这个函数就好了,我也没用再多研究下去,因为我的程序是以web页面为主,Qt只是一个壳子,所以基本上都是js去调用我的函数。

void JsContext::sendMsg(QWebEnginePage* page, const QString& msg)
{
    page->runJavaScript(QString("recvMessage('%1');").arg(msg));
}

js调用Qt:

需要用到Qt注册一个js的对象,或者说Qt和js使用同一个对象,

qwebchannel.js是Qt提供的js方法,可以在这里下载:https://code.csdn.net/tujiaw/webengineview/tree/master/qwebchannel.js

里面提供了一些方法让qt和js共同使用一个对象。

1、需要把上面那个文件qwebchannel.js下载到html同级目录下

2、自己的js,这里面的init函数很重要,成功引用官方的qwebchannel.js之后就会找到qt对象,之后context = channel.objects.context;这个就说明js与一个叫”context“的对象绑定在一起,后面Qt中会使用到这个context
context.someattributeChanged.connect(updateattribute);下面这句话是表面给context这个对象设置一个回调函数,只要后面Qt中会使用到这个回调函数。

var context;
// 初始化
function init()
{
    var updateattribute=function(text)
    {
        alert(text);
        //$("#attrid").val(text);
    }
    if (typeof qt != 'undefined')
    {
        new QWebChannel(qt.webChannelTransport, function(channel)
        {
        context = channel.objects.context;
        context.someattributeChanged.connect(updateattribute);
        }
        );
    }
    else
    {
        alert("qt对象获取失败1!");
    }
}

// 接收qt发送的消息
function recvMessage(msg)
{
    alert(msg);
}

// 向qt发送消息
function sendMessage(msg)
{
    if(typeof context == 'undefined')
    {
        alert("context对象获取失败2!");
    }
    else
    {
        context.onMsg(msg);
        //context.setsomeattribute(msg);
        //updateattribute(context.m_someattribute);
    }
}
// 控件控制函数
function onBtnSendMsg()
{
    var cmd = document.getElementById("待发送消息").value;
    sendMessage(cmd);   
}
init();

3、html,写一个最简单的html程序,引用上面的两个js文件,实现一个按钮点击之后出发sendMessage这个函数,发送一个字符串过去。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv=Content-Type context="text/html;charset=utf-8">
    <title>webchannel test</title>
</head>
<body>
    <p>webchannel test</p>
    <script type="text/javascript" src="./qwebchannel.js"></script>
    <script type="text/javascript" src="./msgutils.js"></script>
    <input id="待发送消息" type="text" name="msgText" />
    <input type="button" value="send msg" onclick="onBtnSendMsg()" />
</body>
</html>

4、Qt端new一个Jscontext对象和前面js里的context对象绑定起来,很简单的几句话,在webchannel上绑定就好了。

    JsContext *jsContext = new JsContext();    
    QWebChannel *webChannel = new QWebChannel();
    Webpage *webPage = new Webpage();
    webChannel->registerObject("context", jsContext);
    webPage->setWebChannel(webChannel);

5、接收回调函数,在c++中实现下面这个类Jscontext,用来和js交互的对象,注意的是里面的public slots里面有一个OnMsg函数,如果在js里面被调用context.onMsg(msg);那么在Qt里面就会有相应,这一点很简单,但是如何使用上面实现的回调函数呢?

需要声明一个信号signal:void someattributeChanged(const QString &attr);然后再emit someattributeChanged(m_someattribute);触发他即可,触发的时候将自己的参数带进去,可以是json字符串。这里有个很重要的点也是我之前遇到的坑,就是https://myprogrammingnotes.com/communication-c-javascript-qt-webengine.html这篇文档中没写明白的,在使用信号的时候void someattributeChanged(const QString &attr);里面必须要用const不然的话,js里面收的对象都是NULL。说明白之后我这边的功能基本都能实现了,如果说有小伙伴想要实现更加精细的功能,js和Qt的对象共享数据,那么在类里面加这个Q_PROPERTY(QString someattribute MEMBER m_someattribute NOTIFY someattributeChanged)之后,定义的私有数据应该就能共享了,具体可以看上面那篇英文文档。

#ifndef JSCONTEXT_H
#define JSCONTEXT_H

#include <QObject>
#include <QWebEnginePage>
#include <QString>
#include "vcdlog4qt.h"

class JsContext : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString someattribute MEMBER m_someattribute NOTIFY someattributeChanged)

public:
    explicit JsContext(QObject *parent = 0);
    void sendMsg(QWebEnginePage* page, const QString& msg);

private:
    QString m_someattribute;

signals:
    void recvdMsg(const QString& msg);
    void someattributeChanged(const QString &attr);

public slots:
    void onMsg(const QString& msg);
    void setsomeattribute(QString attr);

};

#endif // JSCONTEXT_H
#include "jscontext.h"

JsContext::JsContext(QObject *parent) : QObject(parent) {
    VcdLog4Qt::getInstance()->info("new jscontext");
}

void JsContext::sendMsg(QWebEnginePage* page, const QString& msg)
{
    page->runJavaScript(QString("recvMessage('%1');").arg(msg));
}

void JsContext::onMsg(const QString &msg)
{
    emit recvdMsg(msg);
}

void JsContext::setsomeattribute(QString attr)
{
    m_someattribute = attr;
    emit someattributeChanged(m_someattribute);
}

有一个问题就是,webchannel在一个页面上只能开启一个通道,如果,页面多个按钮想要同时绑定这个对象进行同时传输数据是不行的,需要用总的通信通道通过协议分发给每个控件来做。

页面下载文件

下载文件我使用了官方给的方法QWebEngineDownloadItem 这个类里面实现了下载的信号以及方法,直接用即可,也可以自己实现下载,当页面点击的时候通过上面的通信的办法告诉Qt下载的地址,然后进行下载,但是下载之后的进度上面的都需要自己去实现。
connect(webPage->profile(), SIGNAL(downloadRequested(QWebEngineDownloadItem*)),this, SLOT(downloadRequested(QWebEngineDownloadItem*)));首先绑定下面这个信号,当页面有下载动作的时候会触发这个信号,下面是触发下载之后要做的事情,下载触发之后绑定下面两个信号downloadProgress,finished,在过程中会通知你进度(前端可以实现进度条)然后结束的时候也会通知你。一定要写download->accept()有了这个之后下载才会进行。

void MainWindow::downloadRequested(QWebEngineDownloadItem* download)
{
    download->accept();
    connect(download.data(), SIGNAL(downloadProgress(qint64,qint64)),
                this, SLOT(downloadProgress(qint64,qint64)));
    connect(download.data(), SIGNAL(finished()),
                this, SLOT(finished()));
}

注意:重要的点,如果说页面下载动作是当前页面保存链接,那么这个信号是会被触发的,但是如果页面是正常的下载,那么在浏览器里面效果是打开一个新的页面进行下载的,这样的话这个信号是不会被触发的,因为这个下载信号只绑定了当前页面,如果要实现触发,需要在新的页面中实现绑定才行,这也是我刚开始遇到的坑,分享给大家。

有兴趣的同学可以去看看Qt里面用webengine实现的浏览器,会学到很多哦。

最后祝大家学习愉快,有问题或者有需要源码的小伙伴可以私聊我哈,github突然打不开,就暂时没传代码~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章