zoukankan      html  css  js  c++  java
  • 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:QtQt5.12.1ExamplesQt-5.12.1webchannelshared"下的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)
  • 相关阅读:
    【BUG】java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone
    IntelliJ IDEA控制台输出中文乱码问题
    CMD命令
    MongoDB学习笔记
    MyBatis生成序列ID
    MongoDB配置问题
    正确处理下载文件时HTTP头的编码问题(Content-Disposition)
    SpringJPA主键生成采用自定义ID,自定义ID采用年月日时间格式
    Java根据经纬度算出附近正方形的四个角的经纬度
    gradle
  • 原文地址:https://www.cnblogs.com/myboat/p/14168328.html
Copyright © 2011-2022 走看看