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突然打不開,就暫時沒傳代碼~

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