QT中使用QWebEngineView和QWebChannel與HTML+JS進行互操作


使用WebEngineView與WebChannel,實現QT與html數據傳輸和事件響應。

1. 准備工作

1.1 項目配置

(1)使用QMake時,在pro文件中加入 

QT += webchannel webengine

(2)使用CMake時,在CMakeList.txt中加入

find_package(Qt5 COMPONENTS Widgets WebEngineWidgets WebChannel REQUIRED)
target_link_libraries(webEngineTest PRIVATE Qt5::Widgets Qt5::WebEngineWidgets Qt5::WebChannel)

注意在QMake中不需要大小寫區分,而CMake時就需要將大小寫分開。

1.2 加入webEngineView和webChannel

在MainWindow.h中加入兩個變量

    QWebEngineView *webView = nullptr;
    QWebChannel *webChannel = nullptr;

在MainWindow.cpp的相關函數中(可以是MainWindow的構造函數,也可以是菜單響應函數中)加入webEngineView

    webView = new QWebEngineView(this);
    QString fpath = QCoreApplication::applicationDirPath();
    webView->load( QUrl("file:///" + fpath + "/test.html"));
    webView->show();

加入webChannel

    webChannel = new QWebChannel;
    webView->page()->setWebChannel(webChannel);

一定要注意:必須將webChannel設置為webEngineVIew的webChannel,才能通過webChannel與網頁進行通信。

2. 准備交互的QT類

與html交互的主要工作需要一個QT類實現,這個類需要通過webChannel進行注冊才能由js訪問

下面是可以由js訪問的WebClass類

#ifndef WEBCLASS_H
#define WEBCLASS_H
#include <QObject>
#include <QMessageBox>
class WebClass : public QObject
{
    Q_OBJECT
    //Q_PROPERTY(QString content MEMBER m_content)
    Q_PROPERTY(QString content MEMBER m_content NOTIFY contentChanged) //該屬性可由頁面訪問

public:
    WebClass(){};
    QString getContent(){return m_content;}
signals:
    void contentChanged(QString nc);
public slots:
    void jscallme(const QString &text) //該函數是頁面端調用的
    {
        QMessageBox::information(NULL, "jscallme", text);
    }
    void jscallme() //該函數是頁面端調用的
    {
        QMessageBox::information(NULL, "jscallme", m_content);
    }
private:
    QString m_content;
};

#endif // WEBCLASS_H

它有幾個約束:

(1)必須由QObject繼承,並且添加了Q_OBJECT宏

(2)必須將JS可訪問的函數設置為public slots

(3)如果JS需要訪問其成員變量,除定義該變量(QString m_content;)外需要用Q_PROPERTY宏

  Q_PROPERTY(QString content MEMBER m_content NOTIFY contentChanged) //該屬性可由頁面訪問

其中content 為JS訪問的變量名,m_content為本類的變量名;

NOTIFY contentChanged可以缺省,它表示當在C++變量發生變化時的發出的消息,該消息可由JS響應。

為了發送該消息,我們必須在WebClass類中聲明一個信號函數,即

signals:
    void contentChanged(QString nc);

這個函數只能聲明不能實現(它會由MOC編譯實現),而響應函數由JS中指定:

webobj.contentChanged.connect(updateattribute);

這里webobj是WebClass注冊后,在頁面中的一個實例,contentChanged則是WebClass中的contentChanged信號函數,這一行將contentChanged信號與響應函數updateattribute進行關聯,從而一旦C++對象中m_content成員的值由webobject->setProperty("content", v)函數進行修改(必須使用此函數,其他函數修改了不會引起信號)時,JS就會響應,它可以更新頁面中相關元素的值。

3. 將交互類作為成員加入主類中

(1)加入類成員。我們這里的主類為MainWindow類

    WebClass *webobj = nullptr;

(2)注冊該成員

在主類的相應位置,注冊該成員變量。我們在主類的構造函數中加入:

    webChannel = new QWebChannel;
    webobj = new WebClass();
    webChannel->registerObject("webobj", webobj);
    webView->page()->setWebChannel(webChannel);

注意,這里是在webEngineView設置webChannel之前完成了注冊交互實體的工作。其中 webChannel->registerObject("webobj", webobj); 函數就是注冊函數,"webobj"是JS訪問該對象時的對象名, 后面的webobj則是C++對象實例的指針。通過WebChannel的注冊工作,WebChannel就知道了該類的結構,並代理JS完成函數調用和成員訪問。

4. 准備HTML頁面

該頁面中包含有一個特殊的JS文件,它是Qt目錄"C:\Qt\Qt5.12.1\Examples\Qt-5.12.1\webchannel\shared"下的qwebchannel.js文件,要使用QWebChannel,必須引用該文件。因為QWebChannel

是由該文件定義,所以必須首先加載該文件。我們將該文件拷貝到html目錄下(當然也可以是需要的其他目錄),然后編輯例子文件:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title></title>
</head>
<body>
  <script src="qwebchannel.js"></script> 
  <script type="text/javascript">
  var webChannel = new QWebChannel(qt.webChannelTransport,    //這里的webChannel是全局的變量,可以在其它位置訪問
  function(channel){
  var webobj = channel.objects.webobj;
  window.foo = webobj;   //將此webobj賦給了window.foo,則可以在其他函數中訪問該對象(其中foo是任意合法名稱,表示給window增加了一個成員)
  webobj.content = 'sdfef中文';
  webobj.jscallme();
  document.getElementById("ctext").innerHTML = webobj.content;

  webobj.contentChanged.connect(updateattribute);
  });
  
  var updateattribute=function(text)
  {    
    //document.write(text);    
    //var webobj = webChannel.objects.webobj; //訪問全局變量webChannel
    alert(window.foo.content); //這里可以訪問全局的window.foo,它就是我們注冊的webobj
    document.getElementById("mytext").innerHTML = text;
    //alert(webobj.content);
  }

  </script>
  <p id="mytext">This is my first html</p>
  <p id="ctext"></p>
</body>
</html>

(1)頁面文件首先包含了qwebchannel.js,它是使用WebChannel的基礎;

(2)頁面創建了一個QWebChannel實例,作為全局變量,這樣別的JS代碼也可以訪問它了;

(3)QWebChannel實例的構造函數有兩個參數,第一個不清楚,且保持原樣。第二個參數是一個回調函數(姑且這樣稱),該函數在創建時調用,函數的參數就是本次創建的QWebChannel實例;

(4)在var webobj = channel.objects.webobj;一行中,channel.objects.webobj是我們在QT主類中注冊的webobj對象實例,我們將它賦值給了一個變量,以便於引用;

(5)下面一行window.foo = webobj,則表示為全局的window實例增加一個成員foo(這個名字可以自己定),它是webobj的別名,這樣我們可以在別的地方訪問webobj;

(6)下面三行是對注冊實例webobj的訪問

  webobj.content = 'sdfef中文'; //訪問在QT中定義為content的成員變量,其在QT中的成員是m_content;
  webobj.jscallme(); //調用該對象的public slots函數
  document.getElementById("ctext").innerHTML = webobj.content; //使用該對象的成員為html元素賦值

(7)最后一行最為關鍵,它將QT的信號與JS的響應函數關聯,從而響應QT發出的信號

webobj.contentChanged.connect(updateattribute);

(8)其中updateattribute是JS定義的響應函數名稱,下面是它的定義:

  var updateattribute=function(text)
  {    
    //document.write(text);    
    //var webobj = webChannel.objects.webobj; //訪問全局變量webChannel
    alert(window.foo.content); //這里可以訪問全局的window.foo,它就是我們注冊的webobj
    document.getElementById("mytext").innerHTML = text;
  }

需要注意的是,這個響應函數應當與QT中C++的信號函數在參數上保持一致。

可以看出,這里可以訪問全局成員window.foo

5. 程序全部代碼

5.1 main.cpp

//main.cpp
#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

5.2 mainwindow類

//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QtWebEngineWidgets/QWebEngineView>
#include "webclass.h"
#include <QtWebChannel/QWebChannel>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;
    QWebEngineView *webView = nullptr;
    QWebChannel *webChannel = nullptr;
    WebClass *webobj = nullptr;
};
#endif // MAINWINDOW_H
//mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    webView = new QWebEngineView(this);
    QString fpath = QCoreApplication::applicationDirPath();
    webView->load( QUrl("file:///" + fpath + "/test.html"));
    webView->show();

    webView->move(0, 60);
    webView->resize(this->size().width(), this->size().height() - 60);
    //mainLayout->addWidget(webView);
    webChannel = new QWebChannel;
    webobj = new WebClass();
    webChannel->registerObject("webobj", webobj);
    webView->page()->setWebChannel(webChannel);
}

MainWindow::~MainWindow()
{
    delete ui;
}

int times = 1;
void MainWindow::on_pushButton_clicked()
{
    QString t = QString::number(times++);
    QString text = "1234567";
    text.append(t);
    QVariant v(text);
    qDebug() << v.typeName() << endl;
    webobj->setProperty("content",  v);
    qDebug() << webobj->getContent() << endl;
}

本類中,on_pushButton_clicked是mainWindow中加入的一個pushbutton的響應函數,這個函數調用webobj->setProperty("content", v);來改變名為content的成員變量的值,並發送contentChanged信號,由頁面響應。這里"content"是m_content在JS中的名稱,本函數實際上是調用了JS來改變對象webobj的成員變量值,然后觸發相關事件響應。

5.3 webclass類(交互注冊類)

//webclass.h
#ifndef WEBCLASS_H
#define WEBCLASS_H
#include <QObject>
#include <QMessageBox>
class WebClass : public QObject
{
    Q_OBJECT
    //Q_PROPERTY(QString content MEMBER m_content)
    Q_PROPERTY(QString content MEMBER m_content NOTIFY contentChanged) //該屬性可由頁面訪問

public:
    WebClass();
    QString getContent(){return m_content;}
signals:
    void contentChanged(QString nc);
public slots:
    void jscallme(const QString &text) //該函數是頁面端調用的
    {
        QMessageBox::information(NULL, "jscallme", text);
    }
    void jscallme() //該函數是頁面端調用的
    {
        QMessageBox::information(NULL, "jscallme", m_content);
    }
private:
    QString m_content;
};

#endif // WEBCLASS_H
//webclass.cpp
#include "webclass.h"

WebClass::WebClass()
{

}

WebClass有兩個slot可供JS調用,其中一個是帶參的。這個參數前面的const是不能去掉的,否則會產生錯誤:Could not convert argument QJsonValue(string, “sd”) to target type . 可見,WebChannel的通信是通過JSON進行的。

5.4 HTML頁面

見4.

5.5 CMakeList.txt

cmake_minimum_required(VERSION 3.5)

project(ScenarioBuilder LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_PREFIX_PATH $ENV{QT5_14_0_vs2017_64})

# QtCreator supports the following variables for Android, which are identical to qmake Android variables.
# Check http://doc.qt.io/qt-5/deployment-android.html for more information.
# They need to be set before the find_package(Qt5 ...) call.

#if(ANDROID)
#    set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
#    if (ANDROID_ABI STREQUAL "armeabi-v7a")
#        set(ANDROID_EXTRA_LIBS
#            ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so
#            ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so)
#    endif()
#endif()

find_package(Qt5 COMPONENTS Widgets WebEngineWidgets WebChannel REQUIRED)


if(ANDROID)
  add_library(ScenarioBuilder SHARED
    main.cpp
    mainwindow.cpp
    mainwindow.h
    mainwindow.ui
    interactor.h
    interactor.cpp
    mywebview.cpp
    mywebview.h
  )
else()
  add_executable(ScenarioBuilder
    main.cpp
    mainwindow.cpp
    mainwindow.h
    mainwindow.ui
    interactor.h
    interactor.cpp
    mywebview.cpp
    mywebview.h

  )
endif()

target_link_libraries(ScenarioBuilder PRIVATE Qt5::Widgets Qt5::WebEngineWidgets Qt5::WebChannel)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM